adhdev 0.9.82-rc.4 → 0.9.82-rc.40

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": {
@@ -3743,6 +3810,36 @@ function getQueuePath(meshId) {
3743
3810
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
3744
3811
  return (0, import_path4.join)(getLedgerDir(), `${safe}.queue.json`);
3745
3812
  }
3813
+ function getLockPath(meshId) {
3814
+ const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
3815
+ return (0, import_path4.join)(getLedgerDir(), `${safe}.queue.lock`);
3816
+ }
3817
+ function withQueueLock(meshId, fn) {
3818
+ const lockPath = getLockPath(meshId);
3819
+ let fd = -1;
3820
+ for (let i = 0; i < 10; i++) {
3821
+ try {
3822
+ fd = (0, import_fs4.openSync)(lockPath, "wx");
3823
+ break;
3824
+ } catch {
3825
+ const deadline = Date.now() + 30;
3826
+ while (Date.now() < deadline) {
3827
+ }
3828
+ }
3829
+ }
3830
+ try {
3831
+ return fn();
3832
+ } finally {
3833
+ if (fd !== -1) try {
3834
+ (0, import_fs4.closeSync)(fd);
3835
+ } catch {
3836
+ }
3837
+ try {
3838
+ (0, import_fs4.unlinkSync)(lockPath);
3839
+ } catch {
3840
+ }
3841
+ }
3842
+ }
3746
3843
  function readQueue(meshId) {
3747
3844
  const path42 = getQueuePath(meshId);
3748
3845
  if (!(0, import_fs4.existsSync)(path42)) return [];
@@ -3758,20 +3855,22 @@ function writeQueue(meshId, queue) {
3758
3855
  (0, import_fs4.writeFileSync)(path42, JSON.stringify(queue, null, 2), "utf-8");
3759
3856
  }
3760
3857
  function enqueueTask(meshId, message, opts) {
3761
- const queue = readQueue(meshId);
3762
- const entry = {
3763
- id: (0, import_crypto5.randomUUID)(),
3764
- meshId,
3765
- message,
3766
- status: "pending",
3767
- targetNodeId: opts?.targetNodeId,
3768
- targetSessionId: opts?.targetSessionId,
3769
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3770
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3771
- };
3772
- queue.push(entry);
3773
- writeQueue(meshId, queue);
3774
- return entry;
3858
+ return withQueueLock(meshId, () => {
3859
+ const queue = readQueue(meshId);
3860
+ const entry = {
3861
+ id: (0, import_crypto5.randomUUID)(),
3862
+ meshId,
3863
+ message,
3864
+ status: "pending",
3865
+ targetNodeId: opts?.targetNodeId,
3866
+ targetSessionId: opts?.targetSessionId,
3867
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3868
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3869
+ };
3870
+ queue.push(entry);
3871
+ writeQueue(meshId, queue);
3872
+ return entry;
3873
+ });
3775
3874
  }
3776
3875
  function getQueue(meshId, opts) {
3777
3876
  let queue = readQueue(meshId);
@@ -3782,100 +3881,111 @@ function getQueue(meshId, opts) {
3782
3881
  return queue;
3783
3882
  }
3784
3883
  function claimNextTask(meshId, nodeId, sessionId) {
3785
- const queue = readQueue(meshId);
3786
- const hasActiveAssignment = queue.some((q) => q.status === "assigned" && (q.assignedSessionId === sessionId || q.assignedNodeId === nodeId));
3787
- if (hasActiveAssignment) return null;
3788
- let targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetSessionId === sessionId);
3789
- if (targetIdx === -1) {
3790
- targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetNodeId === nodeId && !q.targetSessionId);
3791
- }
3792
- if (targetIdx === -1) {
3793
- targetIdx = queue.findIndex((q) => q.status === "pending" && !q.targetNodeId && !q.targetSessionId);
3794
- }
3795
- if (targetIdx === -1) return null;
3796
- const entry = queue[targetIdx];
3797
- entry.status = "assigned";
3798
- entry.assignedNodeId = nodeId;
3799
- entry.assignedSessionId = sessionId;
3800
- entry.dispatchTimestamp = (/* @__PURE__ */ new Date()).toISOString();
3801
- entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3802
- writeQueue(meshId, queue);
3803
- return entry;
3884
+ return withQueueLock(meshId, () => {
3885
+ const queue = readQueue(meshId);
3886
+ const hasActiveAssignment = queue.some((q) => q.status === "assigned" && (q.assignedSessionId === sessionId || q.assignedNodeId === nodeId));
3887
+ if (hasActiveAssignment) return null;
3888
+ let targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetSessionId === sessionId);
3889
+ if (targetIdx === -1) {
3890
+ targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetNodeId === nodeId && !q.targetSessionId);
3891
+ }
3892
+ if (targetIdx === -1) {
3893
+ targetIdx = queue.findIndex((q) => q.status === "pending" && !q.targetNodeId && !q.targetSessionId);
3894
+ }
3895
+ if (targetIdx === -1) return null;
3896
+ const entry = queue[targetIdx];
3897
+ entry.status = "assigned";
3898
+ entry.assignedNodeId = nodeId;
3899
+ entry.assignedSessionId = sessionId;
3900
+ entry.dispatchTimestamp = (/* @__PURE__ */ new Date()).toISOString();
3901
+ entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3902
+ writeQueue(meshId, queue);
3903
+ return entry;
3904
+ });
3804
3905
  }
3805
3906
  function updateTaskStatus(meshId, taskId, status) {
3806
- const queue = readQueue(meshId);
3807
- const idx = queue.findIndex((q) => q.id === taskId);
3808
- if (idx === -1) return null;
3809
- queue[idx].status = status;
3810
- queue[idx].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3811
- writeQueue(meshId, queue);
3812
- return queue[idx];
3907
+ return withQueueLock(meshId, () => {
3908
+ const queue = readQueue(meshId);
3909
+ const idx = queue.findIndex((q) => q.id === taskId);
3910
+ if (idx === -1) return null;
3911
+ queue[idx].status = status;
3912
+ queue[idx].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3913
+ writeQueue(meshId, queue);
3914
+ return queue[idx];
3915
+ });
3813
3916
  }
3814
3917
  function recordTaskAutoLaunch(meshId, taskId, autoLaunch) {
3815
- const queue = readQueue(meshId);
3816
- const idx = queue.findIndex((q) => q.id === taskId);
3817
- if (idx === -1) return null;
3818
- const now = (/* @__PURE__ */ new Date()).toISOString();
3819
- queue[idx].autoLaunch = {
3820
- ...autoLaunch,
3821
- updatedAt: now
3822
- };
3823
- queue[idx].updatedAt = now;
3824
- writeQueue(meshId, queue);
3825
- return queue[idx];
3918
+ return withQueueLock(meshId, () => {
3919
+ const queue = readQueue(meshId);
3920
+ const idx = queue.findIndex((q) => q.id === taskId);
3921
+ if (idx === -1) return null;
3922
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3923
+ queue[idx].autoLaunch = { ...autoLaunch, updatedAt: now };
3924
+ queue[idx].updatedAt = now;
3925
+ writeQueue(meshId, queue);
3926
+ return queue[idx];
3927
+ });
3826
3928
  }
3827
3929
  function cancelTask(meshId, taskId, opts) {
3828
- const queue = readQueue(meshId);
3829
- const idx = queue.findIndex((q) => q.id === taskId);
3830
- if (idx === -1) return null;
3831
- const now = (/* @__PURE__ */ new Date()).toISOString();
3832
- queue[idx].status = "cancelled";
3833
- queue[idx].updatedAt = now;
3834
- queue[idx].cancelledAt = now;
3835
- if (opts?.reason) queue[idx].cancelReason = opts.reason;
3836
- writeQueue(meshId, queue);
3837
- return queue[idx];
3930
+ return withQueueLock(meshId, () => {
3931
+ const queue = readQueue(meshId);
3932
+ const idx = queue.findIndex((q) => q.id === taskId);
3933
+ if (idx === -1) return null;
3934
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3935
+ queue[idx].status = "cancelled";
3936
+ queue[idx].updatedAt = now;
3937
+ queue[idx].cancelledAt = now;
3938
+ if (opts?.reason) queue[idx].cancelReason = opts.reason;
3939
+ writeQueue(meshId, queue);
3940
+ return queue[idx];
3941
+ });
3838
3942
  }
3839
3943
  function requeueTask(meshId, taskId, opts) {
3840
- const queue = readQueue(meshId);
3841
- const idx = queue.findIndex((q) => q.id === taskId);
3842
- if (idx === -1) return null;
3843
- const entry = queue[idx];
3844
- const now = (/* @__PURE__ */ new Date()).toISOString();
3845
- entry.status = "pending";
3846
- delete entry.assignedNodeId;
3847
- delete entry.assignedSessionId;
3848
- delete entry.cancelledAt;
3849
- delete entry.cancelReason;
3850
- if (opts?.clearTargetNode) delete entry.targetNodeId;
3851
- if (typeof opts?.targetNodeId === "string") entry.targetNodeId = opts.targetNodeId;
3852
- if (opts?.clearTargetSession !== false) delete entry.targetSessionId;
3853
- if (typeof opts?.targetSessionId === "string") entry.targetSessionId = opts.targetSessionId;
3854
- entry.updatedAt = now;
3855
- entry.requeuedAt = now;
3856
- entry.requeueCount = (entry.requeueCount || 0) + 1;
3857
- if (opts?.reason) entry.requeueReason = opts.reason;
3858
- writeQueue(meshId, queue);
3859
- return entry;
3860
- }
3861
- function updateSessionTaskStatus(meshId, sessionId, status) {
3862
- const queue = readQueue(meshId);
3863
- let bestIdx = -1;
3864
- let bestTime = 0;
3865
- for (let i = queue.length - 1; i >= 0; i--) {
3866
- if (queue[i].assignedSessionId === sessionId && queue[i].status === "assigned") {
3944
+ return withQueueLock(meshId, () => {
3945
+ const queue = readQueue(meshId);
3946
+ const idx = queue.findIndex((q) => q.id === taskId);
3947
+ if (idx === -1) return null;
3948
+ const entry = queue[idx];
3949
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3950
+ entry.status = "pending";
3951
+ delete entry.assignedNodeId;
3952
+ delete entry.assignedSessionId;
3953
+ delete entry.cancelledAt;
3954
+ delete entry.cancelReason;
3955
+ if (opts?.clearTargetNode) delete entry.targetNodeId;
3956
+ if (typeof opts?.targetNodeId === "string") entry.targetNodeId = opts.targetNodeId;
3957
+ if (opts?.clearTargetSession !== false) delete entry.targetSessionId;
3958
+ if (typeof opts?.targetSessionId === "string") entry.targetSessionId = opts.targetSessionId;
3959
+ entry.updatedAt = now;
3960
+ entry.requeuedAt = now;
3961
+ entry.requeueCount = (entry.requeueCount || 0) + 1;
3962
+ if (opts?.reason) entry.requeueReason = opts.reason;
3963
+ writeQueue(meshId, queue);
3964
+ return entry;
3965
+ });
3966
+ }
3967
+ function updateSessionTaskStatus(meshId, sessionId, status, opts) {
3968
+ return withQueueLock(meshId, () => {
3969
+ const queue = readQueue(meshId);
3970
+ const occurredAtTime = opts?.occurredAt ? new Date(opts.occurredAt).getTime() : Number.NaN;
3971
+ const hasOccurredAt = Number.isFinite(occurredAtTime);
3972
+ let bestIdx = -1;
3973
+ let bestTime = 0;
3974
+ for (let i = queue.length - 1; i >= 0; i--) {
3975
+ if (queue[i].assignedSessionId !== sessionId || queue[i].status !== "assigned") continue;
3867
3976
  const time3 = new Date(queue[i].dispatchTimestamp || queue[i].updatedAt).getTime();
3977
+ if (hasOccurredAt && Number.isFinite(time3) && time3 > occurredAtTime) continue;
3868
3978
  if (time3 > bestTime) {
3869
3979
  bestTime = time3;
3870
3980
  bestIdx = i;
3871
3981
  }
3872
3982
  }
3873
- }
3874
- if (bestIdx === -1) return null;
3875
- queue[bestIdx].status = status;
3876
- queue[bestIdx].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3877
- writeQueue(meshId, queue);
3878
- return queue[bestIdx];
3983
+ if (bestIdx === -1) return null;
3984
+ queue[bestIdx].status = status;
3985
+ queue[bestIdx].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3986
+ writeQueue(meshId, queue);
3987
+ return queue[bestIdx];
3988
+ });
3879
3989
  }
3880
3990
  function getMeshQueueStats(meshId) {
3881
3991
  const queue = readQueue(meshId);
@@ -4279,18 +4389,75 @@ __export(mesh_events_exports, {
4279
4389
  drainPendingMeshCoordinatorEvents: () => drainPendingMeshCoordinatorEvents,
4280
4390
  getPendingMeshCoordinatorEvents: () => getPendingMeshCoordinatorEvents,
4281
4391
  handleMeshForwardEvent: () => handleMeshForwardEvent,
4392
+ queuePendingMeshCoordinatorEvent: () => queuePendingMeshCoordinatorEvent,
4282
4393
  setupMeshEventForwarding: () => setupMeshEventForwarding,
4283
4394
  triggerMeshQueue: () => triggerMeshQueue,
4284
4395
  tryAssignQueueTask: () => tryAssignQueueTask
4285
4396
  });
4286
- function drainPendingMeshCoordinatorEvents() {
4287
- return pendingMeshCoordinatorEvents.splice(0);
4397
+ function sweepExpiredRemoteIdleSessions() {
4398
+ const now = Date.now();
4399
+ for (const [key, session] of remoteIdleSessions) {
4400
+ if (session.expiresAt <= now) remoteIdleSessions.delete(key);
4401
+ }
4402
+ }
4403
+ function getPendingEventsPath(meshId) {
4404
+ const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
4405
+ return (0, import_path5.join)(getLedgerDir(), `${safe}.pending-events.jsonl`);
4288
4406
  }
4289
- function getPendingMeshCoordinatorEvents() {
4290
- return pendingMeshCoordinatorEvents.slice();
4407
+ function queuePendingMeshCoordinatorEvent(event) {
4408
+ try {
4409
+ (0, import_fs6.appendFileSync)(getPendingEventsPath(event.meshId), JSON.stringify(event) + "\n", "utf-8");
4410
+ return true;
4411
+ } catch (e) {
4412
+ LOG.warn("MeshEvents", `Failed to persist pending coordinator event: ${e?.message || e}`);
4413
+ return false;
4414
+ }
4415
+ }
4416
+ function drainPendingMeshCoordinatorEvents(meshId) {
4417
+ if (!meshId) return [];
4418
+ const path42 = getPendingEventsPath(meshId);
4419
+ if (!(0, import_fs6.existsSync)(path42)) return [];
4420
+ try {
4421
+ const raw = (0, import_fs6.readFileSync)(path42, "utf-8");
4422
+ try {
4423
+ (0, import_fs6.unlinkSync)(path42);
4424
+ } catch {
4425
+ }
4426
+ return raw.split("\n").filter(Boolean).flatMap((line) => {
4427
+ try {
4428
+ return [JSON.parse(line)];
4429
+ } catch {
4430
+ return [];
4431
+ }
4432
+ });
4433
+ } catch {
4434
+ return [];
4435
+ }
4436
+ }
4437
+ function getPendingMeshCoordinatorEvents(meshId) {
4438
+ if (!meshId) return [];
4439
+ const path42 = getPendingEventsPath(meshId);
4440
+ if (!(0, import_fs6.existsSync)(path42)) return [];
4441
+ try {
4442
+ const raw = (0, import_fs6.readFileSync)(path42, "utf-8");
4443
+ return raw.split("\n").filter(Boolean).flatMap((line) => {
4444
+ try {
4445
+ return [JSON.parse(line)];
4446
+ } catch {
4447
+ return [];
4448
+ }
4449
+ });
4450
+ } catch {
4451
+ return [];
4452
+ }
4291
4453
  }
4292
- function clearPendingMeshCoordinatorEvents() {
4293
- pendingMeshCoordinatorEvents.splice(0);
4454
+ function clearPendingMeshCoordinatorEvents(meshId) {
4455
+ if (!meshId) return;
4456
+ const path42 = getPendingEventsPath(meshId);
4457
+ if ((0, import_fs6.existsSync)(path42)) try {
4458
+ (0, import_fs6.unlinkSync)(path42);
4459
+ } catch {
4460
+ }
4294
4461
  }
4295
4462
  function readNonEmptyString(value) {
4296
4463
  return typeof value === "string" && value.trim() ? value.trim() : "";
@@ -4336,6 +4503,38 @@ function shouldSuppressIntentionalCleanupStop(args) {
4336
4503
  if (isIntentionalCleanupStopMetadata(args.metadataEvent)) return true;
4337
4504
  return hasRecentIntentionalCleanupStop(args.meshId, args.sessionId, args.nodeId);
4338
4505
  }
4506
+ function readEventTimestamp(value) {
4507
+ if (typeof value === "number" && Number.isFinite(value)) return value;
4508
+ if (typeof value === "string" && value.trim()) {
4509
+ const numeric = Number(value);
4510
+ if (Number.isFinite(numeric)) return numeric;
4511
+ const parsed = Date.parse(value);
4512
+ if (Number.isFinite(parsed)) return parsed;
4513
+ }
4514
+ return null;
4515
+ }
4516
+ function buildMeshCompletionFingerprint(args) {
4517
+ const timestampPart = Number.isFinite(args.timestamp) ? String(args.timestamp) : readNonEmptyString(args.finalSummary).slice(0, 200);
4518
+ return [
4519
+ args.meshId,
4520
+ args.event,
4521
+ args.sessionId,
4522
+ args.providerType || "",
4523
+ args.providerSessionId || "",
4524
+ timestampPart
4525
+ ].join("::");
4526
+ }
4527
+ function isDuplicateMeshCompletionEvent(args) {
4528
+ const fingerprint = buildMeshCompletionFingerprint(args);
4529
+ if (!fingerprint) return false;
4530
+ const now = Date.now();
4531
+ for (const [key, seenAt] of recentCompletionFingerprints.entries()) {
4532
+ if (now - seenAt > RECENT_COMPLETION_FINGERPRINT_TTL_MS) recentCompletionFingerprints.delete(key);
4533
+ }
4534
+ if (recentCompletionFingerprints.has(fingerprint)) return true;
4535
+ recentCompletionFingerprints.set(fingerprint, now);
4536
+ return false;
4537
+ }
4339
4538
  function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
4340
4539
  const task = claimNextTask(meshId, nodeId, sessionId);
4341
4540
  if (!task) {
@@ -4354,7 +4553,16 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
4354
4553
  message: task.message
4355
4554
  }).catch((e) => {
4356
4555
  LOG.error("MeshQueue", `Failed to dispatch task via P2P to remote node ${nodeId}: ${e?.message}`);
4357
- updateTaskStatus(meshId, task.id, "failed");
4556
+ updateTaskStatus(meshId, task.id, "pending");
4557
+ try {
4558
+ appendLedgerEntry(meshId, {
4559
+ kind: "dispatch_failed",
4560
+ nodeId,
4561
+ sessionId,
4562
+ payload: { taskId: task.id, error: e?.message, retryable: true }
4563
+ });
4564
+ } catch {
4565
+ }
4358
4566
  });
4359
4567
  return true;
4360
4568
  }
@@ -4688,18 +4896,36 @@ function injectMeshSystemMessage(components, args) {
4688
4896
  LOG.info("MeshEvents", `Suppressed ${args.event} for intentionally cleanup-stopped session ${eventSessionId || "(unknown session)"}`);
4689
4897
  return { success: true, forwarded: 0, suppressed: true, intentionalCleanupStop: true };
4690
4898
  }
4899
+ const eventTimestamp = readEventTimestamp(args.metadataEvent.timestamp);
4900
+ if (args.event === "agent:generating_completed" && eventSessionId) {
4901
+ const duplicateCompletion = isDuplicateMeshCompletionEvent({
4902
+ meshId: args.meshId,
4903
+ event: args.event,
4904
+ sessionId: eventSessionId,
4905
+ providerType: readNonEmptyString(args.metadataEvent.providerType) || void 0,
4906
+ providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || void 0,
4907
+ timestamp: eventTimestamp,
4908
+ finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || void 0
4909
+ });
4910
+ if (duplicateCompletion) {
4911
+ LOG.info("MeshEvents", `Suppressed duplicate completion for mesh ${args.meshId} session ${eventSessionId}`);
4912
+ return { success: true, forwarded: 0, suppressed: true, duplicateCompletion: true };
4913
+ }
4914
+ }
4691
4915
  let completedTaskForLedger = null;
4692
4916
  if (args.event === "agent:generating_completed") {
4693
4917
  const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
4694
4918
  const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
4695
4919
  const providerType = readNonEmptyString(args.metadataEvent.providerType);
4696
4920
  if (sessionId) {
4697
- const completedTask = updateSessionTaskStatus(args.meshId, sessionId, "completed");
4921
+ const completedTask = updateSessionTaskStatus(args.meshId, sessionId, "completed", {
4922
+ occurredAt: eventTimestamp !== null ? new Date(eventTimestamp).toISOString() : void 0
4923
+ });
4698
4924
  completedTaskForLedger = completedTask ? { id: completedTask.id } : null;
4699
4925
  if (nodeId && providerType) {
4700
- setTimeout(() => {
4926
+ setImmediate(() => {
4701
4927
  tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
4702
- }, 500);
4928
+ });
4703
4929
  }
4704
4930
  }
4705
4931
  } else if (args.event === "agent:ready") {
@@ -4737,13 +4963,17 @@ function injectMeshSystemMessage(components, args) {
4737
4963
  }
4738
4964
  }
4739
4965
  if (sessionId && nodeId && providerType) {
4740
- remoteIdleSessions.set(`${nodeId}:${sessionId}`, { nodeId, sessionId, providerType });
4741
- setTimeout(() => {
4966
+ sweepExpiredRemoteIdleSessions();
4967
+ remoteIdleSessions.set(`${nodeId}:${sessionId}`, {
4968
+ nodeId,
4969
+ sessionId,
4970
+ providerType,
4971
+ expiresAt: Date.now() + REMOTE_IDLE_SESSION_TTL_MS
4972
+ });
4973
+ setImmediate(() => {
4742
4974
  const assigned = tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
4743
- if (assigned) {
4744
- remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
4745
- }
4746
- }, 500);
4975
+ if (assigned) remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
4976
+ });
4747
4977
  }
4748
4978
  } else if (args.event === "agent:generating_started") {
4749
4979
  const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
@@ -4854,17 +5084,18 @@ function injectMeshSystemMessage(components, args) {
4854
5084
  return true;
4855
5085
  });
4856
5086
  if (coordinatorInstances.length === 0) {
4857
- if (pendingMeshCoordinatorEvents.length < MAX_PENDING_EVENTS) {
4858
- pendingMeshCoordinatorEvents.push({
4859
- event: args.event,
4860
- meshId: args.meshId,
4861
- nodeLabel: args.nodeLabel,
4862
- metadataEvent: {
4863
- ...args.metadataEvent,
4864
- ...recoveryContext ? { recoveryContext } : {}
4865
- },
4866
- queuedAt: Date.now()
4867
- });
5087
+ if (queuePendingMeshCoordinatorEvent({
5088
+ event: args.event,
5089
+ meshId: args.meshId,
5090
+ nodeLabel: args.nodeLabel,
5091
+ nodeId: args.nodeId || void 0,
5092
+ workspace: readNonEmptyString(args.metadataEvent.workspace),
5093
+ metadataEvent: {
5094
+ ...args.metadataEvent,
5095
+ ...recoveryContext ? { recoveryContext } : {}
5096
+ },
5097
+ queuedAt: Date.now()
5098
+ })) {
4868
5099
  LOG.info("MeshEvents", `Queued ${args.event} for MCP coordinator (mesh ${args.meshId})`);
4869
5100
  }
4870
5101
  return { success: true, forwarded: 0 };
@@ -4903,6 +5134,7 @@ function handleMeshForwardEvent(components, payload) {
4903
5134
  providerType: readNonEmptyString(payload.providerType),
4904
5135
  providerSessionId: readNonEmptyString(payload.providerSessionId),
4905
5136
  finalSummary: readNonEmptyString(payload.finalSummary) || readNonEmptyString(payload.summary),
5137
+ ...payload.timestamp !== void 0 ? { timestamp: payload.timestamp } : {},
4906
5138
  intentional: payload.intentional === true,
4907
5139
  intentionalStop: payload.intentionalStop === true,
4908
5140
  operatorCleanup: payload.operatorCleanup === true,
@@ -4945,19 +5177,20 @@ function setupMeshEventForwarding(components) {
4945
5177
  });
4946
5178
  });
4947
5179
  }
4948
- var remoteIdleSessions, MAX_PENDING_EVENTS, pendingMeshCoordinatorEvents, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS;
5180
+ var import_fs6, import_path5, REMOTE_IDLE_SESSION_TTL_MS, remoteIdleSessions, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS, RECENT_COMPLETION_FINGERPRINT_TTL_MS, recentCompletionFingerprints, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS;
4949
5181
  var init_mesh_events = __esm({
4950
5182
  "../../oss/packages/daemon-core/src/mesh/mesh-events.ts"() {
4951
5183
  "use strict";
5184
+ import_fs6 = require("fs");
5185
+ import_path5 = require("path");
4952
5186
  init_config();
4953
5187
  init_mesh_config();
4954
5188
  init_cli_detector();
4955
5189
  init_logger();
4956
5190
  init_mesh_ledger();
4957
5191
  init_mesh_work_queue();
5192
+ REMOTE_IDLE_SESSION_TTL_MS = 5 * 60 * 1e3;
4958
5193
  remoteIdleSessions = /* @__PURE__ */ new Map();
4959
- MAX_PENDING_EVENTS = 50;
4960
- pendingMeshCoordinatorEvents = [];
4961
5194
  MESH_COORDINATOR_EVENTS = /* @__PURE__ */ new Set([
4962
5195
  "agent:generating_started",
4963
5196
  "agent:generating_completed",
@@ -4973,6 +5206,8 @@ var init_mesh_events = __esm({
4973
5206
  "monitor:long_generating": "task_stalled"
4974
5207
  };
4975
5208
  INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS = 30 * 60 * 1e3;
5209
+ RECENT_COMPLETION_FINGERPRINT_TTL_MS = 10 * 60 * 1e3;
5210
+ recentCompletionFingerprints = /* @__PURE__ */ new Map();
4976
5211
  autoLaunchInProgress = /* @__PURE__ */ new Set();
4977
5212
  autoLaunchCooldownUntil = /* @__PURE__ */ new Map();
4978
5213
  AUTO_LAUNCH_COOLDOWN_MS = 5e3;
@@ -5100,7 +5335,7 @@ function isPlainObject2(value) {
5100
5335
  return !!value && typeof value === "object" && !Array.isArray(value);
5101
5336
  }
5102
5337
  function getStatePath() {
5103
- return (0, import_path5.join)(getConfigDir(), "state.json");
5338
+ return (0, import_path6.join)(getConfigDir(), "state.json");
5104
5339
  }
5105
5340
  function normalizeState(raw) {
5106
5341
  const parsed = isPlainObject2(raw) ? raw : {};
@@ -5136,11 +5371,11 @@ function normalizeState(raw) {
5136
5371
  }
5137
5372
  function loadState() {
5138
5373
  const statePath = getStatePath();
5139
- if (!(0, import_fs6.existsSync)(statePath)) {
5374
+ if (!(0, import_fs7.existsSync)(statePath)) {
5140
5375
  return { ...DEFAULT_STATE };
5141
5376
  }
5142
5377
  try {
5143
- const raw = (0, import_fs6.readFileSync)(statePath, "utf-8");
5378
+ const raw = (0, import_fs7.readFileSync)(statePath, "utf-8");
5144
5379
  return normalizeState(JSON.parse(raw));
5145
5380
  } catch {
5146
5381
  return { ...DEFAULT_STATE };
@@ -5149,17 +5384,17 @@ function loadState() {
5149
5384
  function saveState(state) {
5150
5385
  const statePath = getStatePath();
5151
5386
  const normalized = normalizeState(state);
5152
- (0, import_fs6.writeFileSync)(statePath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
5387
+ (0, import_fs7.writeFileSync)(statePath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
5153
5388
  }
5154
5389
  function resetState() {
5155
5390
  saveState({ ...DEFAULT_STATE });
5156
5391
  }
5157
- var import_fs6, import_path5, DEFAULT_STATE;
5392
+ var import_fs7, import_path6, DEFAULT_STATE;
5158
5393
  var init_state_store = __esm({
5159
5394
  "../../oss/packages/daemon-core/src/config/state-store.ts"() {
5160
5395
  "use strict";
5161
- import_fs6 = require("fs");
5162
- import_path5 = require("path");
5396
+ import_fs7 = require("fs");
5397
+ import_path6 = require("path");
5163
5398
  init_config();
5164
5399
  DEFAULT_STATE = {
5165
5400
  recentActivity: [],
@@ -5192,7 +5427,7 @@ function findCliCommand(command) {
5192
5427
  if (path10.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
5193
5428
  const candidate = trimmed.startsWith("~") ? path10.join((0, import_os2.homedir)(), trimmed.slice(1)) : trimmed;
5194
5429
  const resolved = path10.isAbsolute(candidate) ? candidate : path10.resolve(candidate);
5195
- return (0, import_fs7.existsSync)(resolved) ? resolved : null;
5430
+ return (0, import_fs8.existsSync)(resolved) ? resolved : null;
5196
5431
  }
5197
5432
  try {
5198
5433
  const result = (0, import_child_process2.execSync)(
@@ -5223,9 +5458,9 @@ function checkPathExists(paths) {
5223
5458
  if (normalized.includes("*")) {
5224
5459
  const username = home.split(/[\\/]/).pop() || "";
5225
5460
  const resolved = normalized.replace("*", username);
5226
- if ((0, import_fs7.existsSync)(resolved)) return resolved;
5461
+ if ((0, import_fs8.existsSync)(resolved)) return resolved;
5227
5462
  } else {
5228
- if ((0, import_fs7.existsSync)(normalized)) return normalized;
5463
+ if ((0, import_fs8.existsSync)(normalized)) return normalized;
5229
5464
  }
5230
5465
  }
5231
5466
  return null;
@@ -5239,7 +5474,7 @@ async function detectIDEs(providerLoader) {
5239
5474
  let resolvedCli = cliPath;
5240
5475
  if (!resolvedCli && appPath && os32 === "darwin") {
5241
5476
  const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
5242
- if ((0, import_fs7.existsSync)(bundledCli)) resolvedCli = bundledCli;
5477
+ if ((0, import_fs8.existsSync)(bundledCli)) resolvedCli = bundledCli;
5243
5478
  }
5244
5479
  if (!resolvedCli && appPath && os32 === "win32") {
5245
5480
  const { dirname: dirname13 } = await import("path");
@@ -5252,7 +5487,7 @@ async function detectIDEs(providerLoader) {
5252
5487
  `${appDir}\\\\resources\\\\app\\\\bin\\\\${def.cli}.cmd`
5253
5488
  ];
5254
5489
  for (const c of candidates) {
5255
- if ((0, import_fs7.existsSync)(c)) {
5490
+ if ((0, import_fs8.existsSync)(c)) {
5256
5491
  resolvedCli = c;
5257
5492
  break;
5258
5493
  }
@@ -5273,12 +5508,12 @@ async function detectIDEs(providerLoader) {
5273
5508
  }
5274
5509
  return results;
5275
5510
  }
5276
- var import_child_process2, import_fs7, import_os2, path10, BUILTIN_IDE_DEFINITIONS, registeredIDEs;
5511
+ var import_child_process2, import_fs8, import_os2, path10, BUILTIN_IDE_DEFINITIONS, registeredIDEs;
5277
5512
  var init_ide_detector = __esm({
5278
5513
  "../../oss/packages/daemon-core/src/detection/ide-detector.ts"() {
5279
5514
  "use strict";
5280
5515
  import_child_process2 = require("child_process");
5281
- import_fs7 = require("fs");
5516
+ import_fs8 = require("fs");
5282
5517
  import_os2 = require("os");
5283
5518
  path10 = __toESM(require("path"));
5284
5519
  BUILTIN_IDE_DEFINITIONS = [];
@@ -7199,7 +7434,7 @@ var init_status_monitor = __esm({
7199
7434
  });
7200
7435
 
7201
7436
  // ../../oss/packages/daemon-core/src/providers/chat-message-normalization.ts
7202
- function extractFinalSummaryFromMessages(messages, maxChars = 500) {
7437
+ function extractFinalSummaryFromMessages(messages, maxChars = DEFAULT_FINAL_SUMMARY_MAX_CHARS) {
7203
7438
  if (!Array.isArray(messages) || messages.length === 0) return "";
7204
7439
  for (let i = messages.length - 1; i >= 0; i--) {
7205
7440
  const msg = messages[i];
@@ -7501,11 +7736,12 @@ function filterInternalChatMessages(messages) {
7501
7736
  function filterChatMessagesByVisibility(messages, surface) {
7502
7737
  return (Array.isArray(messages) ? messages : []).filter((message) => classifyChatMessageVisibility(message).surface === surface);
7503
7738
  }
7504
- var BUILTIN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_VISIBILITIES, CHAT_MESSAGE_TRANSCRIPT_VISIBILITIES, CHAT_MESSAGE_AUDIENCES, CHAT_MESSAGE_SOURCES, CHAT_MESSAGE_ACTIVITY_SOURCES, CHAT_MESSAGE_INTERNAL_SOURCES, KNOWN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_KIND_ALIASES, EXPLICIT_HIDDEN_VISIBILITIES, EXPLICIT_VISIBLE_VISIBILITIES, HIDDEN_AUDIENCES, ACTIVITY_SOURCE_SET, INTERNAL_SOURCE_SET;
7739
+ var DEFAULT_FINAL_SUMMARY_MAX_CHARS, BUILTIN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_VISIBILITIES, CHAT_MESSAGE_TRANSCRIPT_VISIBILITIES, CHAT_MESSAGE_AUDIENCES, CHAT_MESSAGE_SOURCES, CHAT_MESSAGE_ACTIVITY_SOURCES, CHAT_MESSAGE_INTERNAL_SOURCES, KNOWN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_KIND_ALIASES, EXPLICIT_HIDDEN_VISIBILITIES, EXPLICIT_VISIBLE_VISIBILITIES, HIDDEN_AUDIENCES, ACTIVITY_SOURCE_SET, INTERNAL_SOURCE_SET;
7505
7740
  var init_chat_message_normalization = __esm({
7506
7741
  "../../oss/packages/daemon-core/src/providers/chat-message-normalization.ts"() {
7507
7742
  "use strict";
7508
7743
  init_contracts();
7744
+ DEFAULT_FINAL_SUMMARY_MAX_CHARS = 4e3;
7509
7745
  BUILTIN_CHAT_MESSAGE_KINDS = ["standard", "thought", "tool", "terminal", "system"];
7510
7746
  CHAT_MESSAGE_VISIBILITIES = ["user", "debug", "internal", "hidden"];
7511
7747
  CHAT_MESSAGE_TRANSCRIPT_VISIBILITIES = ["visible", "chat", "user", "debug", "internal", "hidden"];
@@ -37725,7 +37961,7 @@ function commandExists(command) {
37725
37961
  const trimmed = command.trim();
37726
37962
  if (!trimmed) return false;
37727
37963
  if (isExplicitCommand(trimmed)) {
37728
- return (0, import_fs8.existsSync)(expandExecutable(trimmed));
37964
+ return (0, import_fs9.existsSync)(expandExecutable(trimmed));
37729
37965
  }
37730
37966
  try {
37731
37967
  (0, import_child_process6.execFileSync)(process.platform === "win32" ? "where" : "which", [trimmed], {
@@ -37746,10 +37982,10 @@ function hasCliArg(args, flag) {
37746
37982
  }
37747
37983
  function ensureEmptyDelegatedMcpConfig(workspace) {
37748
37984
  const baseDir = path19.join(os16.tmpdir(), "adhdev-delegated-agent-empty-mcp");
37749
- (0, import_fs8.mkdirSync)(baseDir, { recursive: true });
37985
+ (0, import_fs9.mkdirSync)(baseDir, { recursive: true });
37750
37986
  const workspaceHash = crypto4.createHash("sha256").update(path19.resolve(workspace || os16.tmpdir())).digest("hex").slice(0, 16);
37751
37987
  const filePath = path19.join(baseDir, `${workspaceHash}.json`);
37752
- (0, import_fs8.writeFileSync)(filePath, JSON.stringify({ mcpServers: {} }, null, 2), "utf-8");
37988
+ (0, import_fs9.writeFileSync)(filePath, JSON.stringify({ mcpServers: {} }, null, 2), "utf-8");
37753
37989
  return filePath;
37754
37990
  }
37755
37991
  function buildCoordinatorDelegatedCliLaunchOptions(input) {
@@ -37876,14 +38112,14 @@ function resolveCliSessionBinding(provider, normalizedType, cliArgs, requestedRe
37876
38112
  launchMode: "new"
37877
38113
  };
37878
38114
  }
37879
- var os16, path19, crypto4, import_fs8, import_child_process6, chalkModule, chalkApi, COORDINATOR_DELEGATED_ENV_UNSETS, DaemonCliManager;
38115
+ var os16, path19, crypto4, import_fs9, import_child_process6, chalkModule, chalkApi, COORDINATOR_DELEGATED_ENV_UNSETS, DaemonCliManager;
37880
38116
  var init_cli_manager = __esm({
37881
38117
  "../../oss/packages/daemon-core/src/commands/cli-manager.ts"() {
37882
38118
  "use strict";
37883
38119
  os16 = __toESM(require("os"));
37884
38120
  path19 = __toESM(require("path"));
37885
38121
  crypto4 = __toESM(require("crypto"));
37886
- import_fs8 = require("fs");
38122
+ import_fs9 = require("fs");
37887
38123
  import_child_process6 = require("child_process");
37888
38124
  init_source2();
37889
38125
  init_provider_cli_adapter();
@@ -38797,7 +39033,7 @@ function createFsWatchInstance(path42, options, listener, errHandler, emitRaw) {
38797
39033
  }
38798
39034
  };
38799
39035
  try {
38800
- return (0, import_fs9.watch)(path42, {
39036
+ return (0, import_fs10.watch)(path42, {
38801
39037
  persistent: options.persistent
38802
39038
  }, handleEvent);
38803
39039
  } catch (error48) {
@@ -38805,11 +39041,11 @@ function createFsWatchInstance(path42, options, listener, errHandler, emitRaw) {
38805
39041
  return void 0;
38806
39042
  }
38807
39043
  }
38808
- var import_fs9, import_promises5, sysPath, import_os3, STR_DATA, STR_END, STR_CLOSE, EMPTY_FN, pl, isWindows, isMacos, isLinux, isFreeBSD, isIBMi, EVENTS, EV, THROTTLE_MODE_WATCH, statMethods, KEY_LISTENERS, KEY_ERR, KEY_RAW, HANDLER_KEYS, binaryExtensions, isBinaryPath, foreach, addAndConvert, clearItem, delFromSet, isEmptySet, FsWatchInstances, fsWatchBroadcast, setFsWatchListener, FsWatchFileInstances, setFsWatchFileListener, NodeFsHandler;
39044
+ var import_fs10, import_promises5, sysPath, import_os3, STR_DATA, STR_END, STR_CLOSE, EMPTY_FN, pl, isWindows, isMacos, isLinux, isFreeBSD, isIBMi, EVENTS, EV, THROTTLE_MODE_WATCH, statMethods, KEY_LISTENERS, KEY_ERR, KEY_RAW, HANDLER_KEYS, binaryExtensions, isBinaryPath, foreach, addAndConvert, clearItem, delFromSet, isEmptySet, FsWatchInstances, fsWatchBroadcast, setFsWatchListener, FsWatchFileInstances, setFsWatchFileListener, NodeFsHandler;
38809
39045
  var init_handler2 = __esm({
38810
39046
  "../../oss/node_modules/chokidar/esm/handler.js"() {
38811
39047
  "use strict";
38812
- import_fs9 = require("fs");
39048
+ import_fs10 = require("fs");
38813
39049
  import_promises5 = require("fs/promises");
38814
39050
  sysPath = __toESM(require("path"), 1);
38815
39051
  import_os3 = require("os");
@@ -39213,7 +39449,7 @@ var init_handler2 = __esm({
39213
39449
  let cont = FsWatchFileInstances.get(fullPath);
39214
39450
  const copts = cont && cont.options;
39215
39451
  if (copts && (copts.persistent < options.persistent || copts.interval > options.interval)) {
39216
- (0, import_fs9.unwatchFile)(fullPath);
39452
+ (0, import_fs10.unwatchFile)(fullPath);
39217
39453
  cont = void 0;
39218
39454
  }
39219
39455
  if (cont) {
@@ -39224,7 +39460,7 @@ var init_handler2 = __esm({
39224
39460
  listeners: listener,
39225
39461
  rawEmitters: rawEmitter,
39226
39462
  options,
39227
- watcher: (0, import_fs9.watchFile)(fullPath, options, (curr, prev) => {
39463
+ watcher: (0, import_fs10.watchFile)(fullPath, options, (curr, prev) => {
39228
39464
  foreach(cont.rawEmitters, (rawEmitter2) => {
39229
39465
  rawEmitter2(EV.CHANGE, fullPath, { curr, prev });
39230
39466
  });
@@ -39241,7 +39477,7 @@ var init_handler2 = __esm({
39241
39477
  delFromSet(cont, KEY_RAW, rawEmitter);
39242
39478
  if (isEmptySet(cont.listeners)) {
39243
39479
  FsWatchFileInstances.delete(fullPath);
39244
- (0, import_fs9.unwatchFile)(fullPath);
39480
+ (0, import_fs10.unwatchFile)(fullPath);
39245
39481
  cont.options = cont.watcher = void 0;
39246
39482
  Object.freeze(cont);
39247
39483
  }
@@ -39619,11 +39855,11 @@ function watch(paths, options = {}) {
39619
39855
  watcher.add(paths);
39620
39856
  return watcher;
39621
39857
  }
39622
- var import_fs10, import_promises6, import_events2, sysPath2, SLASH, SLASH_SLASH, ONE_DOT, TWO_DOTS, STRING_TYPE, BACK_SLASH_RE, DOUBLE_SLASH_RE, DOT_RE, REPLACER_RE, isMatcherObject, unifyPaths, toUnix, normalizePathToUnix, normalizeIgnored, getAbsolutePath, EMPTY_SET, DirEntry, STAT_METHOD_F, STAT_METHOD_L, WatchHelper, FSWatcher;
39858
+ var import_fs11, import_promises6, import_events2, sysPath2, SLASH, SLASH_SLASH, ONE_DOT, TWO_DOTS, STRING_TYPE, BACK_SLASH_RE, DOUBLE_SLASH_RE, DOT_RE, REPLACER_RE, isMatcherObject, unifyPaths, toUnix, normalizePathToUnix, normalizeIgnored, getAbsolutePath, EMPTY_SET, DirEntry, STAT_METHOD_F, STAT_METHOD_L, WatchHelper, FSWatcher;
39623
39859
  var init_esm2 = __esm({
39624
39860
  "../../oss/node_modules/chokidar/esm/index.js"() {
39625
39861
  "use strict";
39626
- import_fs10 = require("fs");
39862
+ import_fs11 = require("fs");
39627
39863
  import_promises6 = require("fs/promises");
39628
39864
  import_events2 = require("events");
39629
39865
  sysPath2 = __toESM(require("path"), 1);
@@ -40101,7 +40337,7 @@ var init_esm2 = __esm({
40101
40337
  const now = /* @__PURE__ */ new Date();
40102
40338
  const writes = this._pendingWrites;
40103
40339
  function awaitWriteFinishFn(prevStat) {
40104
- (0, import_fs10.stat)(fullPath, (err, curStat) => {
40340
+ (0, import_fs11.stat)(fullPath, (err, curStat) => {
40105
40341
  if (err || !writes.has(path42)) {
40106
40342
  if (err && err.code !== "ENOENT")
40107
40343
  awfEmit(err);
@@ -46402,55 +46638,45 @@ function readBooleanValue(...values) {
46402
46638
  }
46403
46639
  return void 0;
46404
46640
  }
46405
- function buildCachedInlineMeshGitStatus(node) {
46406
- const cachedStatus = readObjectRecord(node?.cachedStatus);
46407
- const cachedGit = readObjectRecord(cachedStatus.git);
46408
- if (Object.keys(cachedGit).length) {
46409
- const conflictFiles2 = Array.isArray(cachedGit.conflictFiles) ? cachedGit.conflictFiles.filter((value) => typeof value === "string") : [];
46410
- const conflictCount2 = readNumberValue(cachedGit.conflicts) ?? conflictFiles2.length;
46411
- const hasConflicts2 = readBooleanValue(cachedGit.hasConflicts) ?? conflictCount2 > 0;
46412
- const isGitRepo2 = readBooleanValue(cachedGit.isGitRepo);
46413
- if (isGitRepo2 !== void 0) {
46414
- return {
46415
- workspace: readStringValue(cachedGit.workspace, node?.workspace) || "",
46416
- repoRoot: readStringValue(cachedGit.repoRoot, node?.repoRoot, node?.workspace) || null,
46417
- isGitRepo: isGitRepo2,
46418
- branch: readStringValue(cachedGit.branch) ?? null,
46419
- headCommit: readStringValue(cachedGit.headCommit) ?? null,
46420
- headMessage: readStringValue(cachedGit.headMessage) ?? null,
46421
- upstream: readStringValue(cachedGit.upstream) ?? null,
46422
- ahead: readNumberValue(cachedGit.ahead) ?? 0,
46423
- behind: readNumberValue(cachedGit.behind) ?? 0,
46424
- staged: readNumberValue(cachedGit.staged) ?? 0,
46425
- modified: readNumberValue(cachedGit.modified) ?? 0,
46426
- untracked: readNumberValue(cachedGit.untracked) ?? 0,
46427
- deleted: readNumberValue(cachedGit.deleted) ?? 0,
46428
- renamed: readNumberValue(cachedGit.renamed) ?? 0,
46429
- hasConflicts: hasConflicts2,
46430
- conflictFiles: conflictFiles2,
46431
- stashCount: readNumberValue(cachedGit.stashCount) ?? 0,
46432
- lastCheckedAt: readNumberValue(cachedGit.lastCheckedAt) ?? Date.now()
46433
- };
46434
- }
46435
- }
46436
- const rawGit = readObjectRecord(node?.lastGit ?? node?.last_git);
46437
- const gitResult = readObjectRecord(rawGit.result);
46438
- const directStatus = readObjectRecord(rawGit.status);
46439
- const nestedStatus = readObjectRecord(gitResult.status);
46440
- const rawProbe = readObjectRecord(node?.lastProbe ?? node?.last_probe);
46441
- const probeGit = readObjectRecord(rawProbe.git);
46442
- const probeGitResult = readObjectRecord(probeGit.result);
46443
- const probeDirectStatus = readObjectRecord(probeGit.status);
46444
- const probeNestedStatus = readObjectRecord(probeGitResult.status);
46445
- const status = Object.keys(directStatus).length ? directStatus : Object.keys(nestedStatus).length ? nestedStatus : Object.keys(probeDirectStatus).length ? probeDirectStatus : Object.keys(probeNestedStatus).length ? probeNestedStatus : {};
46641
+ function joinRepoPath(root, relativePath) {
46642
+ const normalizedRoot = typeof root === "string" ? root.trim().replace(/[\\/]+$/, "") : "";
46643
+ const normalizedPath = typeof relativePath === "string" ? relativePath.trim() : "";
46644
+ if (!normalizedPath) return void 0;
46645
+ if (/^(?:[A-Za-z]:[\\/]|\/)/.test(normalizedPath)) return normalizedPath;
46646
+ if (!normalizedRoot) return void 0;
46647
+ return `${normalizedRoot}/${normalizedPath.replace(/^[\\/]+/, "")}`;
46648
+ }
46649
+ function readGitSubmodules(value, parentRepoRoot) {
46650
+ if (!Array.isArray(value)) return void 0;
46651
+ const submodules = value.map((entry) => {
46652
+ const submodule = readObjectRecord(entry);
46653
+ const path42 = readStringValue(submodule.path);
46654
+ const commit = readStringValue(submodule.commit);
46655
+ const repoPath = readStringValue(submodule.repoPath, submodule.repo_root) ?? joinRepoPath(parentRepoRoot, path42);
46656
+ if (!path42 || !commit || !repoPath) return null;
46657
+ return {
46658
+ path: path42,
46659
+ commit,
46660
+ repoPath,
46661
+ dirty: readBooleanValue(submodule.dirty) ?? false,
46662
+ outOfSync: readBooleanValue(submodule.outOfSync, submodule.out_of_sync) ?? false,
46663
+ lastCheckedAt: readNumberValue(submodule.lastCheckedAt, submodule.last_checked_at) ?? Date.now(),
46664
+ ...readStringValue(submodule.error) ? { error: readStringValue(submodule.error) } : {}
46665
+ };
46666
+ }).filter((entry) => entry !== null);
46667
+ return submodules.length > 0 ? submodules : void 0;
46668
+ }
46669
+ function normalizeInlineMeshGitStatus(status, node, options) {
46446
46670
  const isGitRepo = readBooleanValue(status.isGitRepo);
46447
46671
  if (!Object.keys(status).length || isGitRepo === void 0) return void 0;
46448
46672
  const conflictFiles = Array.isArray(status.conflictFiles) ? status.conflictFiles.filter((value) => typeof value === "string") : [];
46449
46673
  const conflictCount = readNumberValue(status.conflicts) ?? conflictFiles.length;
46450
46674
  const hasConflicts = readBooleanValue(status.hasConflicts) ?? conflictCount > 0;
46675
+ const repoRoot = readStringValue(status.repoRoot, status.repo_root, node?.repoRoot, node?.repo_root, status.workspace, node?.workspace) || void 0;
46676
+ const submodules = readGitSubmodules(status.submodules, repoRoot);
46451
46677
  return {
46452
46678
  workspace: readStringValue(status.workspace, node?.workspace) || "",
46453
- repoRoot: readStringValue(status.repoRoot, node?.repoRoot, node?.workspace) || null,
46679
+ repoRoot: repoRoot ?? null,
46454
46680
  isGitRepo,
46455
46681
  branch: readStringValue(status.branch) ?? null,
46456
46682
  headCommit: readStringValue(status.headCommit) ?? null,
@@ -46466,29 +46692,412 @@ function buildCachedInlineMeshGitStatus(node) {
46466
46692
  hasConflicts,
46467
46693
  conflictFiles,
46468
46694
  stashCount: readNumberValue(status.stashCount) ?? 0,
46469
- lastCheckedAt: Date.now()
46695
+ lastCheckedAt: options?.lastCheckedAt ?? readNumberValue(status.lastCheckedAt) ?? Date.now(),
46696
+ ...submodules ? { submodules } : {}
46470
46697
  };
46471
46698
  }
46472
- function applyCachedInlineMeshNodeStatus(status, node) {
46699
+ function buildInlineMeshTransitGitStatus(node) {
46700
+ const rawGit = readObjectRecord(node?.lastGit ?? node?.last_git);
46701
+ const gitResult = readObjectRecord(rawGit.result);
46702
+ const directStatus = readObjectRecord(rawGit.status);
46703
+ const nestedStatus = readObjectRecord(gitResult.status);
46704
+ const rawProbe = readObjectRecord(node?.lastProbe ?? node?.last_probe);
46705
+ const probeGit = readObjectRecord(rawProbe.git);
46706
+ const probeGitResult = readObjectRecord(probeGit.result);
46707
+ const probeDirectStatus = readObjectRecord(probeGit.status);
46708
+ const probeNestedStatus = readObjectRecord(probeGitResult.status);
46709
+ const status = Object.keys(directStatus).length ? directStatus : Object.keys(nestedStatus).length ? nestedStatus : Object.keys(probeDirectStatus).length ? probeDirectStatus : Object.keys(probeNestedStatus).length ? probeNestedStatus : {};
46710
+ return normalizeInlineMeshGitStatus(status, node, { lastCheckedAt: Date.now() });
46711
+ }
46712
+ function recordInlineMeshDirectGitTruth(node, git, source) {
46713
+ if (!node || typeof node !== "object" || Array.isArray(node)) return;
46714
+ const checkedAt = readNumberValue(git.lastCheckedAt) ?? Date.now();
46715
+ const updatedAt = new Date(checkedAt).toISOString();
46716
+ const nextGit = {
46717
+ ...git,
46718
+ lastCheckedAt: checkedAt
46719
+ };
46720
+ node.lastGit = {
46721
+ source,
46722
+ checkedAt,
46723
+ status: nextGit
46724
+ };
46725
+ node.last_git = node.lastGit;
46726
+ node.machineStatus = "online";
46727
+ node.updatedAt = updatedAt;
46728
+ node.lastSeenAt = updatedAt;
46729
+ const repoRoot = readStringValue(nextGit.repoRoot);
46730
+ if (repoRoot && !readStringValue(node.repoRoot)) node.repoRoot = repoRoot;
46731
+ }
46732
+ function buildCachedInlineMeshGitStatus(node) {
46733
+ const liveGit = buildInlineMeshTransitGitStatus(node);
46734
+ if (liveGit) return liveGit;
46473
46735
  const cachedStatus = readObjectRecord(node?.cachedStatus);
46474
- const git = buildCachedInlineMeshGitStatus(node);
46475
- const error48 = readStringValue(cachedStatus.error, node?.error);
46476
- const health = readStringValue(cachedStatus.health, node?.health);
46736
+ const cachedGit = readObjectRecord(cachedStatus.git);
46737
+ if (!Object.keys(cachedGit).length) return void 0;
46738
+ return normalizeInlineMeshGitStatus(cachedGit, node);
46739
+ }
46740
+ function shouldDiscardCachedInlineMeshStatus(node) {
46741
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
46742
+ if (!Object.keys(cachedStatus).length) return false;
46743
+ const cachedGit = readObjectRecord(cachedStatus.git);
46744
+ const workspaceError = readStringValue(cachedStatus.error, node?.error);
46745
+ if (workspaceError && /workspace must be an existing directory/i.test(workspaceError)) return true;
46746
+ const isGitRepo = readBooleanValue(cachedGit.isGitRepo);
46747
+ const branch = readStringValue(cachedGit.branch);
46748
+ const headCommit = readStringValue(cachedGit.headCommit);
46749
+ return isGitRepo === false && !branch && !headCommit;
46750
+ }
46751
+ function stripInlineMeshTransientNodeState(node) {
46752
+ if (!node || typeof node !== "object" || Array.isArray(node)) return node;
46753
+ const {
46754
+ cachedStatus,
46755
+ lastGit: _lastGit,
46756
+ last_git: _lastGitLegacy,
46757
+ lastProbe: _lastProbe,
46758
+ last_probe: _lastProbeLegacy,
46759
+ error: _error,
46760
+ health: _health,
46761
+ machineStatus: _machineStatus,
46762
+ lastSeenAt: _lastSeenAt,
46763
+ last_seen_at: _lastSeenAtLegacy,
46764
+ updatedAt: _updatedAt,
46765
+ updated_at: _updatedAtLegacy,
46766
+ activeSession: _activeSession,
46767
+ active_session: _activeSessionLegacy,
46768
+ activeSessionId: _activeSessionId,
46769
+ active_session_id: _activeSessionIdLegacy,
46770
+ sessionId: _sessionId,
46771
+ session_id: _sessionIdLegacy,
46772
+ providerType: _providerType,
46773
+ provider_type: _providerTypeLegacy,
46774
+ ...rest
46775
+ } = node;
46776
+ if (cachedStatus && !shouldDiscardCachedInlineMeshStatus(node)) {
46777
+ return { ...rest, cachedStatus };
46778
+ }
46779
+ return rest;
46780
+ }
46781
+ function hasInlineMeshTransientNodeState(node) {
46782
+ if (!node || typeof node !== "object" || Array.isArray(node)) return false;
46783
+ return "cachedStatus" in node || "lastGit" in node || "last_git" in node || "lastProbe" in node || "last_probe" in node || "error" in node || "health" in node || "machineStatus" in node || "lastSeenAt" in node || "last_seen_at" in node || "updatedAt" in node || "updated_at" in node || "activeSession" in node || "active_session" in node || "activeSessionId" in node || "active_session_id" in node || "sessionId" in node || "session_id" in node || "providerType" in node || "provider_type" in node;
46784
+ }
46785
+ function inlineMeshCarriesTransientNodeTruth(inlineMesh) {
46786
+ if (!inlineMesh || typeof inlineMesh !== "object" || Array.isArray(inlineMesh)) return false;
46787
+ if (!Array.isArray(inlineMesh.nodes) || inlineMesh.nodes.length === 0) return false;
46788
+ return inlineMesh.nodes.some((node) => hasInlineMeshTransientNodeState(node));
46789
+ }
46790
+ function readInlineMeshNodeId(node) {
46791
+ return readStringValue(node?.id, node?.nodeId) || "";
46792
+ }
46793
+ function sanitizeInlineMesh(inlineMesh) {
46794
+ if (!inlineMesh || typeof inlineMesh !== "object" || Array.isArray(inlineMesh)) return inlineMesh;
46795
+ if (!Array.isArray(inlineMesh.nodes)) return inlineMesh;
46796
+ let changed = false;
46797
+ const nodes = inlineMesh.nodes.map((node) => {
46798
+ if (!hasInlineMeshTransientNodeState(node)) return node;
46799
+ changed = true;
46800
+ return stripInlineMeshTransientNodeState(node);
46801
+ });
46802
+ if (!changed) return inlineMesh;
46803
+ return {
46804
+ ...inlineMesh,
46805
+ nodes
46806
+ };
46807
+ }
46808
+ function reconcileInlineMeshCache(cached2, incoming) {
46809
+ if (!cached2 || typeof cached2 !== "object" || Array.isArray(cached2)) return incoming;
46810
+ if (!incoming || typeof incoming !== "object" || Array.isArray(incoming)) return cached2;
46811
+ const cachedNodes = Array.isArray(cached2.nodes) ? cached2.nodes : [];
46812
+ const incomingNodes = Array.isArray(incoming.nodes) ? incoming.nodes : [];
46813
+ if (!cachedNodes.length || !incomingNodes.length) return { ...cached2, ...incoming };
46814
+ const incomingById = /* @__PURE__ */ new Map();
46815
+ for (const node of incomingNodes) {
46816
+ const nodeId = readInlineMeshNodeId(node);
46817
+ if (nodeId) incomingById.set(nodeId, node);
46818
+ }
46819
+ const nodes = cachedNodes.map((cachedNode) => {
46820
+ const nodeId = readInlineMeshNodeId(cachedNode);
46821
+ const incomingNode = nodeId ? incomingById.get(nodeId) : void 0;
46822
+ if (!incomingNode) return cachedNode;
46823
+ if (hasInlineMeshTransientNodeState(incomingNode)) {
46824
+ return { ...cachedNode, ...incomingNode };
46825
+ }
46826
+ return { ...stripInlineMeshTransientNodeState(cachedNode), ...incomingNode };
46827
+ });
46828
+ return {
46829
+ ...cached2,
46830
+ ...incoming,
46831
+ nodes
46832
+ };
46833
+ }
46834
+ function hasGitWorktreeChanges(git) {
46835
+ if (!git) return false;
46836
+ return Number(git.staged || 0) + Number(git.modified || 0) + Number(git.untracked || 0) + Number(git.deleted || 0) + Number(git.renamed || 0) > 0;
46837
+ }
46838
+ function getGitSubmoduleDriftState(git) {
46839
+ const submodules = Array.isArray(git?.submodules) ? git.submodules : [];
46840
+ let dirty = false;
46841
+ let outOfSync = false;
46842
+ for (const entry of submodules) {
46843
+ const submodule = readObjectRecord(entry);
46844
+ if (readBooleanValue(submodule.dirty) === true) dirty = true;
46845
+ if (readBooleanValue(submodule.outOfSync) === true || !!readStringValue(submodule.error)) outOfSync = true;
46846
+ }
46847
+ return { dirty, outOfSync };
46848
+ }
46849
+ function deriveMeshNodeHealthFromGit(git) {
46850
+ if (!git || readBooleanValue(git.isGitRepo) === false) return "degraded";
46851
+ const branch = readStringValue(git.branch);
46852
+ if (!branch) return "degraded";
46853
+ const submoduleDrift = getGitSubmoduleDriftState(git);
46854
+ if (submoduleDrift.outOfSync) return "degraded";
46855
+ if (submoduleDrift.dirty || hasGitWorktreeChanges(git)) return "dirty";
46856
+ return "online";
46857
+ }
46858
+ function readCachedInlineMeshActiveSessions(node) {
46859
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
46860
+ const activeSession = readObjectRecord(cachedStatus.activeSession);
46861
+ const fallbackSession = Object.keys(activeSession).length ? activeSession : readObjectRecord(node?.activeSession ?? node?.active_session);
46862
+ const sessionId = readStringValue(fallbackSession.id, fallbackSession.sessionId, fallbackSession.session_id, node?.activeSessionId, node?.active_session_id, node?.sessionId, node?.session_id);
46863
+ return sessionId ? [sessionId] : [];
46864
+ }
46865
+ function readCachedInlineMeshActiveSessionDetails(node) {
46866
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
46867
+ const activeSession = readObjectRecord(cachedStatus.activeSession);
46868
+ const fallbackSession = Object.keys(activeSession).length ? activeSession : readObjectRecord(node?.activeSession ?? node?.active_session);
46869
+ const sessionId = readStringValue(
46870
+ fallbackSession.id,
46871
+ fallbackSession.sessionId,
46872
+ fallbackSession.session_id,
46873
+ node?.activeSessionId,
46874
+ node?.active_session_id,
46875
+ node?.sessionId,
46876
+ node?.session_id
46877
+ );
46878
+ if (!sessionId) return [];
46879
+ return [{
46880
+ sessionId,
46881
+ providerType: readStringValue(
46882
+ fallbackSession.providerType,
46883
+ fallbackSession.provider_type,
46884
+ fallbackSession.cliType,
46885
+ fallbackSession.cli_type,
46886
+ fallbackSession.provider,
46887
+ node?.providerType,
46888
+ node?.provider_type
46889
+ ),
46890
+ state: readStringValue(fallbackSession.status, fallbackSession.state, fallbackSession.lifecycle),
46891
+ lifecycle: readStringValue(fallbackSession.lifecycle),
46892
+ title: readStringValue(fallbackSession.title, fallbackSession.displayName, fallbackSession.display_name) ?? null,
46893
+ workspace: readStringValue(fallbackSession.workspace, node?.workspace) ?? null,
46894
+ lastActivityAt: readStringValue(fallbackSession.lastActivityAt, fallbackSession.last_activity_at) ?? null,
46895
+ recoveryState: readStringValue(fallbackSession.recoveryState, fallbackSession.recovery_state) ?? null,
46896
+ isCached: true
46897
+ }];
46898
+ }
46899
+ function readLiveMeshSessionState(record2) {
46900
+ return readStringValue(
46901
+ record2?.meta?.sessionStatus,
46902
+ record2?.meta?.status,
46903
+ record2?.meta?.providerStatus,
46904
+ record2?.status,
46905
+ record2?.state,
46906
+ record2?.lifecycle
46907
+ );
46908
+ }
46909
+ function toIsoTimestamp(value) {
46910
+ if (typeof value === "number" && Number.isFinite(value)) return new Date(value).toISOString();
46911
+ const stringValue = readStringValue(value);
46912
+ return stringValue || null;
46913
+ }
46914
+ function synthesizeMeshNodeFreshnessFromConnection(status) {
46915
+ const connection = readObjectRecord(status.connection);
46916
+ const connectionFreshAt = toIsoTimestamp(connection.lastCommandAt ?? connection.lastConnectedAt ?? connection.lastStateChangeAt);
46917
+ const git = readObjectRecord(status.git);
46918
+ const gitCheckedAt = toIsoTimestamp(git.lastCheckedAt);
46919
+ if (!status.lastSeenAt && connectionFreshAt) status.lastSeenAt = connectionFreshAt;
46920
+ if (!status.updatedAt && (gitCheckedAt || connectionFreshAt)) {
46921
+ status.updatedAt = gitCheckedAt ?? connectionFreshAt;
46922
+ }
46923
+ }
46924
+ function finalizeMeshNodeStatus(args) {
46925
+ const { status, node, daemonId, isSelfNode } = args;
46926
+ if (!readStringValue(status.machineStatus)) {
46927
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
46928
+ const machineStatus = readStringValue(cachedStatus.machineStatus, cachedStatus.machine_status, node?.machineStatus);
46929
+ if (machineStatus) status.machineStatus = machineStatus;
46930
+ }
46931
+ synthesizeMeshNodeFreshnessFromConnection(status);
46932
+ const connectionState = readStringValue(readObjectRecord(status.connection).state);
46933
+ status.launchReady = !!daemonId && (readStringValue(status.machineStatus) === "online" || connectionState === "connected" || isSelfNode);
46934
+ }
46935
+ async function probeRemoteMeshGitStatus(args) {
46936
+ if (!args.dispatchMeshCommand) return null;
46937
+ const remoteResult = await Promise.race([
46938
+ args.dispatchMeshCommand(args.daemonId, "git_status", { workspace: args.workspace }),
46939
+ new Promise((_2, reject) => setTimeout(() => reject(new Error("timeout")), args.timeoutMs))
46940
+ ]);
46941
+ const remoteGit = remoteResult?.status ?? remoteResult?.git ?? remoteResult;
46942
+ return remoteGit && typeof remoteGit === "object" && typeof remoteGit.isGitRepo === "boolean" ? remoteGit : null;
46943
+ }
46944
+ async function hydrateInlineMeshDirectTruth(args) {
46945
+ const nodes = Array.isArray(args.mesh?.nodes) ? args.mesh.nodes : [];
46946
+ if (!nodes.length) {
46947
+ return {
46948
+ directEvidenceCount: 0,
46949
+ localConfirmedCount: 0,
46950
+ peerAttemptedCount: 0,
46951
+ peerConfirmedCount: 0,
46952
+ unavailableNodeIds: []
46953
+ };
46954
+ }
46955
+ const selectedCoordinatorNodeId = readStringValue(
46956
+ args.mesh?.coordinator?.preferredNodeId,
46957
+ nodes[0]?.id,
46958
+ nodes[0]?.nodeId
46959
+ );
46960
+ let localConfirmedCount = 0;
46961
+ let peerAttemptedCount = 0;
46962
+ let peerConfirmedCount = 0;
46963
+ const unavailableNodeIds = [];
46964
+ for (const [nodeIndex, node] of nodes.entries()) {
46965
+ const nodeId = readStringValue(node?.id, node?.nodeId) || `node_${nodeIndex}`;
46966
+ const workspace = readStringValue(node?.workspace);
46967
+ const daemonId = readStringValue(node?.daemonId);
46968
+ const isSelfNode = Boolean(
46969
+ nodeId && selectedCoordinatorNodeId && nodeId === selectedCoordinatorNodeId
46970
+ ) || Boolean(
46971
+ daemonId && (daemonId === args.localMachineId || daemonId === args.statusInstanceId)
46972
+ ) || Boolean(args.meshSource !== "local_config" && nodeIndex === 0);
46973
+ if (!workspace) {
46974
+ if (!isSelfNode && daemonId) unavailableNodeIds.push(nodeId);
46975
+ continue;
46976
+ }
46977
+ if (isSelfNode && fs10.existsSync(workspace)) {
46978
+ try {
46979
+ const localGit = await getGitRepoStatus(workspace, { timeoutMs: 1e4, refreshUpstream: true });
46980
+ if (localGit?.isGitRepo) {
46981
+ recordInlineMeshDirectGitTruth(node, localGit, "selected_coordinator_local_git");
46982
+ localConfirmedCount += 1;
46983
+ continue;
46984
+ }
46985
+ } catch {
46986
+ }
46987
+ }
46988
+ if (!daemonId || !args.dispatchMeshCommand) {
46989
+ if (!isSelfNode) unavailableNodeIds.push(nodeId);
46990
+ continue;
46991
+ }
46992
+ peerAttemptedCount += 1;
46993
+ try {
46994
+ const remoteGit = await probeRemoteMeshGitStatus({
46995
+ dispatchMeshCommand: args.dispatchMeshCommand,
46996
+ daemonId,
46997
+ workspace,
46998
+ timeoutMs: 8e3
46999
+ });
47000
+ if (remoteGit) {
47001
+ recordInlineMeshDirectGitTruth(node, remoteGit, "selected_coordinator_mesh_p2p_git");
47002
+ peerConfirmedCount += 1;
47003
+ continue;
47004
+ }
47005
+ } catch {
47006
+ }
47007
+ unavailableNodeIds.push(nodeId);
47008
+ }
47009
+ return {
47010
+ directEvidenceCount: localConfirmedCount + peerConfirmedCount,
47011
+ localConfirmedCount,
47012
+ peerAttemptedCount,
47013
+ peerConfirmedCount,
47014
+ unavailableNodeIds
47015
+ };
47016
+ }
47017
+ function summarizeMeshSessionRecord(record2) {
47018
+ return {
47019
+ sessionId: readStringValue(record2?.sessionId) || "unknown",
47020
+ providerType: readStringValue(record2?.providerType),
47021
+ state: readLiveMeshSessionState(record2),
47022
+ lifecycle: readStringValue(record2?.lifecycle),
47023
+ surfaceKind: getSessionHostSurfaceKind(record2),
47024
+ recoveryState: readStringValue(record2?.meta?.runtimeRecoveryState) ?? null,
47025
+ workspace: readStringValue(record2?.workspace) ?? null,
47026
+ title: readStringValue(record2?.displayName, record2?.workspaceLabel) ?? null,
47027
+ lastActivityAt: toIsoTimestamp(record2?.updatedAt ?? record2?.lastActivityAt ?? record2?.last_activity_at),
47028
+ isCached: false
47029
+ };
47030
+ }
47031
+ function liveSessionRecordMatchesMeshNode(record2, meshId, nodeId) {
47032
+ const recordNodeId = readStringValue(record2?.meta?.meshNodeId);
47033
+ if (!recordNodeId || recordNodeId !== nodeId) return false;
47034
+ const recordMeshId = readStringValue(record2?.meta?.meshNodeFor);
47035
+ return !recordMeshId || recordMeshId === meshId;
47036
+ }
47037
+ function liveSessionRecordMatchesMeshWorkspace(record2, meshId, workspace) {
47038
+ const recordWorkspace = readStringValue(record2?.workspace);
47039
+ if (!recordWorkspace || !workspace || recordWorkspace !== workspace) return false;
47040
+ const recordMeshId = readStringValue(record2?.meta?.meshNodeFor);
47041
+ if (recordMeshId) return recordMeshId === meshId;
47042
+ return record2?.meta?.launchedByCoordinator === true || !!readStringValue(record2?.meta?.meshNodeId);
47043
+ }
47044
+ function readLiveMeshNodeWorkspace(args) {
47045
+ const directNodeWorkspace = args.liveSessionRecords.find((record2) => liveSessionRecordMatchesMeshNode(record2, args.meshId, args.nodeId) && readStringValue(record2?.workspace));
47046
+ if (directNodeWorkspace) {
47047
+ return readStringValue(directNodeWorkspace.workspace) || "";
47048
+ }
47049
+ if (args.allowCoordinatorSession) {
47050
+ const coordinatorWorkspace = args.liveSessionRecords.find((record2) => readStringValue(record2?.meta?.meshCoordinatorFor) === args.meshId && readStringValue(record2?.workspace));
47051
+ if (coordinatorWorkspace) {
47052
+ return readStringValue(coordinatorWorkspace.workspace) || "";
47053
+ }
47054
+ }
47055
+ return "";
47056
+ }
47057
+ function collectLiveMeshSessionRecords(args) {
47058
+ const matches = args.liveSessionRecords.filter((record2) => {
47059
+ const nodeWorkspace = readStringValue(args.node?.workspace);
47060
+ if (liveSessionRecordMatchesMeshNode(record2, args.meshId, args.nodeId)) return true;
47061
+ return !!nodeWorkspace && liveSessionRecordMatchesMeshWorkspace(record2, args.meshId, nodeWorkspace);
47062
+ });
47063
+ if (args.allowCoordinatorSession) {
47064
+ for (const record2 of args.liveSessionRecords) {
47065
+ if (readStringValue(record2?.meta?.meshCoordinatorFor) !== args.meshId) continue;
47066
+ const sessionId = readStringValue(record2?.sessionId);
47067
+ if (sessionId && matches.some((entry) => readStringValue(entry?.sessionId) === sessionId)) continue;
47068
+ matches.push(record2);
47069
+ }
47070
+ }
47071
+ return matches;
47072
+ }
47073
+ function applyCachedInlineMeshNodeStatus(status, node, options) {
47074
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
47075
+ const liveGit = buildInlineMeshTransitGitStatus(node);
47076
+ const git = options?.skipGit ? void 0 : liveGit ?? buildCachedInlineMeshGitStatus(node);
47077
+ const error48 = options?.skipError ? void 0 : liveGit ? void 0 : readStringValue(cachedStatus.error, node?.error);
47078
+ const health = options?.skipHealth ? void 0 : liveGit ? void 0 : readStringValue(cachedStatus.health, node?.health);
46477
47079
  const machineStatus = readStringValue(cachedStatus.machineStatus, node?.machineStatus);
46478
- if (!git && !error48 && !health) return false;
46479
- if (!machineStatus && !git && !error48) return false;
47080
+ const lastSeenAt = toIsoTimestamp(cachedStatus.lastSeenAt ?? cachedStatus.last_seen_at ?? node?.lastSeenAt ?? node?.last_seen_at);
47081
+ const updatedAt = toIsoTimestamp(cachedStatus.updatedAt ?? cachedStatus.updated_at ?? node?.updatedAt ?? node?.updated_at);
47082
+ const activeSessions = readCachedInlineMeshActiveSessions(node);
47083
+ const activeSessionDetails = readCachedInlineMeshActiveSessionDetails(node);
47084
+ if (!git && !error48 && !health && !machineStatus && !lastSeenAt && !updatedAt && activeSessions.length === 0) return false;
46480
47085
  if (git) status.git = git;
46481
47086
  if (error48) status.error = error48;
47087
+ if (machineStatus) status.machineStatus = machineStatus;
47088
+ if (lastSeenAt) status.lastSeenAt = lastSeenAt;
47089
+ if (updatedAt) status.updatedAt = updatedAt;
47090
+ if (activeSessions.length > 0) status.activeSessions = activeSessions;
47091
+ if (activeSessionDetails.length > 0) status.activeSessionDetails = activeSessionDetails;
46482
47092
  if (health) {
46483
47093
  status.health = health;
46484
47094
  return true;
46485
47095
  }
46486
47096
  if (git) {
46487
- const dirty = Number(git.staged || 0) + Number(git.modified || 0) + Number(git.untracked || 0) + Number(git.deleted || 0) + Number(git.renamed || 0) > 0;
46488
- status.health = git.isGitRepo === false ? "degraded" : dirty ? "dirty" : "online";
47097
+ status.health = deriveMeshNodeHealthFromGit(git);
46489
47098
  return true;
46490
47099
  }
46491
- return false;
47100
+ return activeSessions.length > 0 || !!machineStatus || !!lastSeenAt || !!updatedAt;
46492
47101
  }
46493
47102
  async function resolveProviderTypeFromPriority(args) {
46494
47103
  if (!args.providerPriority.length) {
@@ -46519,9 +47128,85 @@ function truncateValidationOutput(value) {
46519
47128
  return `${text.slice(0, REFINE_VALIDATION_SUMMARY_CHARS)}
46520
47129
  [truncated ${text.length - REFINE_VALIDATION_SUMMARY_CHARS} chars]`;
46521
47130
  }
47131
+ function recordMeshRefineStage(stages, stage, status, startedAt, details) {
47132
+ stages.push({
47133
+ stage,
47134
+ status,
47135
+ durationMs: Date.now() - startedAt,
47136
+ ...details || {}
47137
+ });
47138
+ }
47139
+ async function computeGitPatchId(cwd, fromRef, toRef) {
47140
+ const { execFileSync: execFileSync7 } = await import("child_process");
47141
+ const diff = execFileSync7("git", ["diff", "--patch", "--full-index", fromRef, toRef], {
47142
+ cwd,
47143
+ encoding: "utf8",
47144
+ maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES
47145
+ });
47146
+ if (!diff.trim()) return "";
47147
+ const patchId = execFileSync7("git", ["patch-id", "--stable"], {
47148
+ cwd,
47149
+ input: diff,
47150
+ encoding: "utf8",
47151
+ maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES
47152
+ }).trim();
47153
+ return patchId.split(/\s+/)[0] || "";
47154
+ }
47155
+ async function runMeshRefinePatchEquivalenceGate(repoRoot, baseHead, branchHead) {
47156
+ const startedAt = Date.now();
47157
+ try {
47158
+ const { execFileSync: execFileSync7 } = await import("child_process");
47159
+ const git = (args) => execFileSync7("git", args, {
47160
+ cwd: repoRoot,
47161
+ encoding: "utf8",
47162
+ maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES
47163
+ });
47164
+ const mergeBase = git(["merge-base", baseHead, branchHead]).trim();
47165
+ const mergeTreeStdout = git(["merge-tree", "--write-tree", baseHead, branchHead]);
47166
+ const mergedTree = mergeTreeStdout.trim().split(/\s+/)[0] || "";
47167
+ if (!mergeBase || !mergedTree) {
47168
+ return {
47169
+ status: "failed",
47170
+ equivalent: false,
47171
+ baseHead,
47172
+ branchHead,
47173
+ mergeBase: mergeBase || void 0,
47174
+ mergedTree: mergedTree || void 0,
47175
+ durationMs: Date.now() - startedAt,
47176
+ error: "patch equivalence preflight could not resolve merge-base or synthetic merge tree",
47177
+ stdout: truncateValidationOutput(mergeTreeStdout)
47178
+ };
47179
+ }
47180
+ const expectedPatchId = await computeGitPatchId(repoRoot, mergeBase, branchHead);
47181
+ const actualPatchId = await computeGitPatchId(repoRoot, baseHead, mergedTree);
47182
+ const equivalent = expectedPatchId === actualPatchId;
47183
+ return {
47184
+ status: equivalent ? "passed" : "failed",
47185
+ equivalent,
47186
+ baseHead,
47187
+ branchHead,
47188
+ mergeBase,
47189
+ mergedTree,
47190
+ expectedPatchId,
47191
+ actualPatchId,
47192
+ durationMs: Date.now() - startedAt
47193
+ };
47194
+ } catch (e) {
47195
+ return {
47196
+ status: "failed",
47197
+ equivalent: false,
47198
+ baseHead,
47199
+ branchHead,
47200
+ durationMs: Date.now() - startedAt,
47201
+ error: e?.message || String(e),
47202
+ stdout: truncateValidationOutput(e?.stdout),
47203
+ stderr: truncateValidationOutput(e?.stderr)
47204
+ };
47205
+ }
47206
+ }
46522
47207
  function readPackageScripts(workspace) {
46523
47208
  try {
46524
- const packageJsonPath = (0, import_path6.join)(workspace, "package.json");
47209
+ const packageJsonPath = (0, import_path7.join)(workspace, "package.json");
46525
47210
  const parsed = JSON.parse(fs10.readFileSync(packageJsonPath, "utf-8"));
46526
47211
  return parsed?.scripts && typeof parsed.scripts === "object" && !Array.isArray(parsed.scripts) ? parsed.scripts : {};
46527
47212
  } catch {
@@ -46729,13 +47414,13 @@ function serializeMeshCoordinatorMcpConfig(config2, format) {
46729
47414
  }
46730
47415
  function resolveHermesUserHome() {
46731
47416
  const explicitHome = process.env.HERMES_HOME?.trim();
46732
- return explicitHome || (0, import_path6.join)((0, import_os4.homedir)(), ".hermes");
47417
+ return explicitHome || (0, import_path7.join)((0, import_os4.homedir)(), ".hermes");
46733
47418
  }
46734
47419
  function loadHermesCoordinatorBaseConfig(targetConfigPath) {
46735
47420
  const sourceHome = resolveHermesUserHome();
46736
- const sourceConfigPath = (0, import_path6.join)(sourceHome, "config.yaml");
47421
+ const sourceConfigPath = (0, import_path7.join)(sourceHome, "config.yaml");
46737
47422
  if (!fs10.existsSync(sourceConfigPath)) return { config: {}, sourceHome, sourceConfigPath };
46738
- if ((0, import_path6.resolve)(sourceConfigPath) === (0, import_path6.resolve)(targetConfigPath)) return { config: {}, sourceHome, sourceConfigPath };
47423
+ if ((0, import_path7.resolve)(sourceConfigPath) === (0, import_path7.resolve)(targetConfigPath)) return { config: {}, sourceHome, sourceConfigPath };
46739
47424
  const parsed = parseMeshCoordinatorMcpConfig(fs10.readFileSync(sourceConfigPath, "utf-8"), "hermes_config_yaml");
46740
47425
  const { mcp_servers: _mcpServers, ...baseConfig } = parsed;
46741
47426
  return { config: baseConfig, sourceHome, sourceConfigPath };
@@ -46769,10 +47454,10 @@ function stripHermesCoordinatorTempModelProviderOverrides(config2) {
46769
47454
  return sanitized;
46770
47455
  }
46771
47456
  function copyHermesCoordinatorCredentialFiles(sourceHome, targetHome) {
46772
- if ((0, import_path6.resolve)(sourceHome) === (0, import_path6.resolve)(targetHome)) return;
47457
+ if ((0, import_path7.resolve)(sourceHome) === (0, import_path7.resolve)(targetHome)) return;
46773
47458
  for (const fileName of [".env", "auth.json"]) {
46774
- const sourcePath = (0, import_path6.join)(sourceHome, fileName);
46775
- const targetPath = (0, import_path6.join)(targetHome, fileName);
47459
+ const sourcePath = (0, import_path7.join)(sourceHome, fileName);
47460
+ const targetPath = (0, import_path7.join)(targetHome, fileName);
46776
47461
  if (!fs10.existsSync(sourcePath)) continue;
46777
47462
  try {
46778
47463
  fs10.copyFileSync(sourcePath, targetPath);
@@ -46862,7 +47547,7 @@ function summarizeSessionHostPruneResult(result) {
46862
47547
  keptCount: Array.isArray(value.keptSessionIds) ? value.keptSessionIds.length : void 0
46863
47548
  };
46864
47549
  }
46865
- var import_os4, import_path6, fs10, CHANNEL_NPM_TAG, CHANNEL_SERVER_URL, REFINE_VALIDATION_CATEGORIES, REFINE_VALIDATION_TIMEOUT_MS, REFINE_VALIDATION_OUTPUT_LIMIT_BYTES, REFINE_VALIDATION_SUMMARY_CHARS, REFINE_VALIDATION_MAX_COMMANDS, CHAT_COMMANDS, READ_DEBUG_ENABLED2, DaemonCommandRouter;
47550
+ var import_os4, import_path7, fs10, CHANNEL_NPM_TAG, CHANNEL_SERVER_URL, REFINE_VALIDATION_CATEGORIES, REFINE_VALIDATION_TIMEOUT_MS, REFINE_VALIDATION_OUTPUT_LIMIT_BYTES, REFINE_VALIDATION_SUMMARY_CHARS, REFINE_VALIDATION_MAX_COMMANDS, REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES, CHAT_COMMANDS, READ_DEBUG_ENABLED2, DaemonCommandRouter;
46866
47551
  var init_router = __esm({
46867
47552
  "../../oss/packages/daemon-core/src/commands/router.ts"() {
46868
47553
  "use strict";
@@ -46878,6 +47563,7 @@ var init_router = __esm({
46878
47563
  init_chat_history();
46879
47564
  init_ide_detector();
46880
47565
  init_cli_detector();
47566
+ init_git_status();
46881
47567
  init_logger();
46882
47568
  init_command_log();
46883
47569
  init_js_yaml();
@@ -46891,7 +47577,7 @@ var init_router = __esm({
46891
47577
  init_snapshot();
46892
47578
  init_upgrade_helper();
46893
47579
  import_os4 = require("os");
46894
- import_path6 = require("path");
47580
+ import_path7 = require("path");
46895
47581
  fs10 = __toESM(require("fs"));
46896
47582
  CHANNEL_NPM_TAG = { stable: "latest", preview: "next" };
46897
47583
  CHANNEL_SERVER_URL = {
@@ -46903,6 +47589,7 @@ var init_router = __esm({
46903
47589
  REFINE_VALIDATION_OUTPUT_LIMIT_BYTES = 128 * 1024;
46904
47590
  REFINE_VALIDATION_SUMMARY_CHARS = 2e3;
46905
47591
  REFINE_VALIDATION_MAX_COMMANDS = 4;
47592
+ REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES = 4 * 1024 * 1024;
46906
47593
  CHAT_COMMANDS = [
46907
47594
  "send_chat",
46908
47595
  "new_chat",
@@ -46917,30 +47604,97 @@ var init_router = __esm({
46917
47604
  * Allows the MCP server to query mesh data via get_mesh even when
46918
47605
  * the mesh doesn't exist in the local meshes.json file. */
46919
47606
  inlineMeshCache = /* @__PURE__ */ new Map();
47607
+ /** Coordinator-owned whole-mesh aggregate status snapshots. Browser callers read this by default. */
47608
+ aggregateMeshStatusCache = /* @__PURE__ */ new Map();
46920
47609
  constructor(deps) {
46921
47610
  this.deps = deps;
46922
47611
  }
47612
+ cloneJsonValue(value) {
47613
+ if (typeof structuredClone === "function") return structuredClone(value);
47614
+ return JSON.parse(JSON.stringify(value));
47615
+ }
47616
+ getCachedAggregateMeshStatus(meshId) {
47617
+ const cached2 = this.aggregateMeshStatusCache.get(meshId);
47618
+ if (!cached2?.snapshot || cached2.snapshot.success !== true || !Array.isArray(cached2.snapshot.nodes)) return null;
47619
+ const snapshot = this.cloneJsonValue(cached2.snapshot);
47620
+ const ageMs = Math.max(0, Date.now() - cached2.builtAt);
47621
+ const sourceOfTruth = snapshot.sourceOfTruth && typeof snapshot.sourceOfTruth === "object" ? snapshot.sourceOfTruth : {};
47622
+ snapshot.sourceOfTruth = {
47623
+ ...sourceOfTruth,
47624
+ aggregateSnapshot: {
47625
+ ...sourceOfTruth.aggregateSnapshot && typeof sourceOfTruth.aggregateSnapshot === "object" ? sourceOfTruth.aggregateSnapshot : {},
47626
+ owner: "coordinator_daemon_memory",
47627
+ cached: true,
47628
+ source: "memory",
47629
+ refreshReason: "memory_cache_hit",
47630
+ ageMs,
47631
+ cachedAt: new Date(cached2.builtAt).toISOString(),
47632
+ returnedAt: (/* @__PURE__ */ new Date()).toISOString()
47633
+ }
47634
+ };
47635
+ return snapshot;
47636
+ }
47637
+ rememberAggregateMeshStatus(meshId, snapshot, refreshReason) {
47638
+ if (!snapshot || typeof snapshot !== "object" || snapshot.success !== true || !Array.isArray(snapshot.nodes)) return snapshot;
47639
+ const builtAt = Date.now();
47640
+ const next = this.cloneJsonValue(snapshot);
47641
+ const sourceOfTruth = next.sourceOfTruth && typeof next.sourceOfTruth === "object" ? next.sourceOfTruth : {};
47642
+ next.sourceOfTruth = {
47643
+ ...sourceOfTruth,
47644
+ aggregateSnapshot: {
47645
+ owner: "coordinator_daemon_memory",
47646
+ cached: false,
47647
+ source: "live_refresh",
47648
+ refreshReason,
47649
+ ageMs: 0,
47650
+ cachedAt: new Date(builtAt).toISOString(),
47651
+ returnedAt: new Date(builtAt).toISOString()
47652
+ }
47653
+ };
47654
+ this.aggregateMeshStatusCache.set(meshId, { builtAt, snapshot: this.cloneJsonValue(next) });
47655
+ return next;
47656
+ }
46923
47657
  getCachedInlineMesh(meshId, inlineMesh) {
46924
47658
  if (inlineMesh && typeof inlineMesh === "object") {
46925
- this.inlineMeshCache.set(meshId, inlineMesh);
46926
- return inlineMesh;
47659
+ return this.warmInlineMeshCache(meshId, inlineMesh);
46927
47660
  }
46928
47661
  return this.inlineMeshCache.get(meshId);
46929
47662
  }
47663
+ warmInlineMeshCache(meshId, inlineMesh) {
47664
+ if (!inlineMesh || typeof inlineMesh !== "object") return void 0;
47665
+ const sanitizedInlineMesh = sanitizeInlineMesh(inlineMesh);
47666
+ const cached2 = this.inlineMeshCache.get(meshId);
47667
+ if (cached2) {
47668
+ const merged = reconcileInlineMeshCache(cached2, sanitizedInlineMesh);
47669
+ this.inlineMeshCache.set(meshId, merged);
47670
+ return merged;
47671
+ }
47672
+ this.inlineMeshCache.set(meshId, sanitizedInlineMesh);
47673
+ return sanitizedInlineMesh;
47674
+ }
46930
47675
  async getMeshForCommand(meshId, inlineMesh, options) {
46931
47676
  const preferInline = options?.preferInline === true;
46932
47677
  if (preferInline) {
46933
- const cached3 = this.getCachedInlineMesh(meshId, inlineMesh);
46934
- if (cached3) return { mesh: cached3, inline: true };
47678
+ const cached3 = this.getCachedInlineMesh(meshId);
47679
+ if (cached3) return { mesh: cached3, inline: true, source: "inline_cache" };
47680
+ if (inlineMeshCarriesTransientNodeTruth(inlineMesh)) {
47681
+ this.warmInlineMeshCache(meshId, inlineMesh);
47682
+ return { mesh: inlineMesh, inline: true, source: "inline_bootstrap" };
47683
+ }
46935
47684
  }
46936
47685
  try {
46937
47686
  const { getMesh: getMesh3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
46938
47687
  const mesh = getMesh3(meshId);
46939
- if (mesh) return { mesh, inline: false };
47688
+ if (mesh) return { mesh, inline: false, source: "local_config" };
46940
47689
  } catch {
46941
47690
  }
46942
- const cached2 = this.getCachedInlineMesh(meshId, inlineMesh);
46943
- return cached2 ? { mesh: cached2, inline: true } : null;
47691
+ const cached2 = this.getCachedInlineMesh(meshId);
47692
+ if (cached2) return { mesh: cached2, inline: true, source: "inline_cache" };
47693
+ const warmedInline = this.warmInlineMeshCache(meshId, inlineMesh);
47694
+ return warmedInline ? { mesh: warmedInline, inline: true, source: "inline_bootstrap" } : null;
47695
+ }
47696
+ invalidateAggregateMeshStatus(meshId) {
47697
+ this.aggregateMeshStatusCache.delete(meshId);
46944
47698
  }
46945
47699
  updateInlineMeshNode(meshId, mesh, node) {
46946
47700
  if (!mesh || !Array.isArray(mesh.nodes) || !node?.id) return;
@@ -46949,6 +47703,7 @@ var init_router = __esm({
46949
47703
  else mesh.nodes.push(node);
46950
47704
  mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
46951
47705
  this.inlineMeshCache.set(meshId, mesh);
47706
+ this.invalidateAggregateMeshStatus(meshId);
46952
47707
  }
46953
47708
  removeInlineMeshNode(meshId, mesh, nodeId) {
46954
47709
  if (!mesh || !Array.isArray(mesh.nodes)) return false;
@@ -46957,6 +47712,7 @@ var init_router = __esm({
46957
47712
  mesh.nodes.splice(idx, 1);
46958
47713
  mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
46959
47714
  this.inlineMeshCache.set(meshId, mesh);
47715
+ this.invalidateAggregateMeshStatus(meshId);
46960
47716
  return true;
46961
47717
  }
46962
47718
  normalizeMeshSessionCleanupMode(value) {
@@ -47005,7 +47761,7 @@ var init_router = __esm({
47005
47761
  }
47006
47762
  const { resolveWorktreePath: resolveWorktreePath2, listWorktrees: listWorktrees2, removeWorktree: removeWorktree2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
47007
47763
  const normalizePath3 = (value) => {
47008
- const resolved = (0, import_path6.resolve)(value);
47764
+ const resolved = (0, import_path7.resolve)(value);
47009
47765
  try {
47010
47766
  return fs10.realpathSync(resolved);
47011
47767
  } catch {
@@ -47169,6 +47925,7 @@ var init_router = __esm({
47169
47925
  const deletedSessionIds = [];
47170
47926
  const skippedSessionIds = [];
47171
47927
  const skippedLiveSessionIds = [];
47928
+ const skippedCoordinatorSessionIds = [];
47172
47929
  const deleteUnsupportedSessionIds = [];
47173
47930
  const recordsRemainSessionIds = [];
47174
47931
  const errors = [];
@@ -47201,6 +47958,12 @@ var init_router = __esm({
47201
47958
  const completed = this.isCompletedHostedSession(record2);
47202
47959
  const surfaceKind = getSessionHostSurfaceKind(record2);
47203
47960
  const liveRuntime = surfaceKind === "live_runtime";
47961
+ const coordinatorSession = readStringValue(record2?.meta?.meshCoordinatorFor) === args.meshId;
47962
+ if (!hasExplicitSessionIds && coordinatorSession) {
47963
+ skippedSessionIds.push(sessionId);
47964
+ skippedCoordinatorSessionIds.push(sessionId);
47965
+ continue;
47966
+ }
47204
47967
  if (!hasExplicitSessionIds && liveRuntime) {
47205
47968
  skippedSessionIds.push(sessionId);
47206
47969
  skippedLiveSessionIds.push(sessionId);
@@ -47266,6 +48029,7 @@ var init_router = __esm({
47266
48029
  deletedSessionIds,
47267
48030
  skippedSessionIds,
47268
48031
  skippedLiveSessionIds,
48032
+ skippedCoordinatorSessionIds,
47269
48033
  ...deleteUnsupported ? {
47270
48034
  deleteUnsupported: true,
47271
48035
  effectiveCleanup: args.mode === "stop_and_delete" ? "stopped_only_records_remain" : "delete_unsupported_records_remain",
@@ -47398,7 +48162,8 @@ var init_router = __esm({
47398
48162
  return handleMeshForwardEvent({ instanceManager: this.deps.instanceManager }, args);
47399
48163
  }
47400
48164
  case "get_pending_mesh_events": {
47401
- const events = drainPendingMeshCoordinatorEvents();
48165
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
48166
+ const events = drainPendingMeshCoordinatorEvents(meshId || void 0);
47402
48167
  return { success: true, events };
47403
48168
  }
47404
48169
  case "launch_cli":
@@ -47927,15 +48692,39 @@ var init_router = __esm({
47927
48692
  case "get_mesh": {
47928
48693
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
47929
48694
  if (!meshId) return { success: false, error: "meshId required" };
47930
- try {
47931
- const { getMesh: getMesh3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
47932
- const mesh = getMesh3(meshId);
47933
- if (mesh) return { success: true, mesh };
47934
- } catch {
48695
+ const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
48696
+ if (!meshRecord?.mesh) return { success: false, error: "Mesh not found" };
48697
+ const requireDirectPeerTruth = args?.requireDirectPeerTruth === true;
48698
+ const directTruth = await hydrateInlineMeshDirectTruth({
48699
+ mesh: meshRecord.mesh,
48700
+ meshSource: meshRecord.source,
48701
+ dispatchMeshCommand: this.deps.dispatchMeshCommand,
48702
+ statusInstanceId: this.deps.statusInstanceId,
48703
+ localMachineId: loadConfig().machineId || ""
48704
+ });
48705
+ const directTruthSatisfied = meshRecord.source !== "inline_bootstrap" || directTruth.directEvidenceCount > 0;
48706
+ const sourceOfTruth = {
48707
+ membership: meshRecord.source === "inline_cache" ? "coordinator_inline_mesh_cache" : meshRecord.source === "local_config" ? "local_mesh_config" : "inline_bootstrap_snapshot",
48708
+ coordinatorOwnsLiveTruth: directTruthSatisfied,
48709
+ directPeerTruth: {
48710
+ required: requireDirectPeerTruth,
48711
+ satisfied: directTruthSatisfied,
48712
+ directEvidenceCount: directTruth.directEvidenceCount,
48713
+ localConfirmedCount: directTruth.localConfirmedCount,
48714
+ peerAttemptedCount: directTruth.peerAttemptedCount,
48715
+ peerConfirmedCount: directTruth.peerConfirmedCount,
48716
+ unavailableNodeIds: directTruth.unavailableNodeIds
48717
+ }
48718
+ };
48719
+ if (requireDirectPeerTruth && !directTruthSatisfied) {
48720
+ return {
48721
+ success: false,
48722
+ code: "mesh_direct_peer_truth_unavailable",
48723
+ error: "Selected coordinator could not confirm direct mesh truth yet. Bootstrap inventory stays unavailable until direct get_mesh probes succeed.",
48724
+ sourceOfTruth
48725
+ };
47935
48726
  }
47936
- const cached2 = this.inlineMeshCache.get(meshId);
47937
- if (cached2) return { success: true, mesh: cached2 };
47938
- return { success: false, error: "Mesh not found" };
48727
+ return { success: true, mesh: meshRecord.mesh, sourceOfTruth };
47939
48728
  }
47940
48729
  case "create_mesh": {
47941
48730
  const name = typeof args?.name === "string" ? args.name.trim() : "";
@@ -47965,6 +48754,7 @@ var init_router = __esm({
47965
48754
  const mesh = updateMesh2(meshId, patch);
47966
48755
  if (!mesh) return { success: false, error: "Mesh not found" };
47967
48756
  this.inlineMeshCache.set(meshId, mesh);
48757
+ this.invalidateAggregateMeshStatus(meshId);
47968
48758
  return { success: true, mesh };
47969
48759
  } catch (e) {
47970
48760
  return { success: false, error: e.message };
@@ -48154,26 +48944,41 @@ var init_router = __esm({
48154
48944
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
48155
48945
  const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
48156
48946
  if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
48947
+ const refineStages = [];
48157
48948
  try {
48158
48949
  const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
48159
48950
  const mesh = meshRecord?.mesh;
48160
48951
  const node = mesh?.nodes?.find((n) => n.id === nodeId || n.nodeId === nodeId);
48161
- if (!node) return { success: false, error: `Node '${nodeId}' not found in mesh` };
48952
+ if (!node) return { success: false, error: `Node '${nodeId}' not found in mesh`, refineStages };
48162
48953
  if (!node.isLocalWorktree || !node.workspace) {
48163
- return { success: false, error: `Refinery requires a local worktree node` };
48954
+ return { success: false, error: `Refinery requires a local worktree node`, refineStages };
48164
48955
  }
48165
48956
  const sourceNode = node.clonedFromNodeId ? mesh?.nodes.find((n) => n.id === node.clonedFromNodeId || n.nodeId === node.clonedFromNodeId) : mesh?.nodes.find((n) => !n.isLocalWorktree);
48166
48957
  const repoRoot = sourceNode?.repoRoot || sourceNode?.workspace;
48167
- if (!repoRoot) return { success: false, error: "Source node repoRoot not found" };
48958
+ if (!repoRoot) return { success: false, error: "Source node repoRoot not found", refineStages };
48168
48959
  const { execFile: execFile3 } = await import("child_process");
48169
48960
  const { promisify: promisify3 } = await import("util");
48170
48961
  const execFileAsync3 = promisify3(execFile3);
48962
+ const resolveStarted = Date.now();
48171
48963
  const { stdout: branchStdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd: node.workspace, encoding: "utf8" });
48172
48964
  const branch = branchStdout.trim();
48173
- if (!branch) return { success: false, error: "Could not determine branch of the worktree node" };
48965
+ if (!branch) return { success: false, error: "Could not determine branch of the worktree node", refineStages };
48174
48966
  const { stdout: baseBranchStdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd: repoRoot, encoding: "utf8" });
48175
48967
  const baseBranch = baseBranchStdout.trim();
48968
+ const { stdout: baseHeadStdout } = await execFileAsync3("git", ["rev-parse", "HEAD"], { cwd: repoRoot, encoding: "utf8" });
48969
+ const { stdout: branchHeadStdout } = await execFileAsync3("git", ["rev-parse", branch], { cwd: node.workspace, encoding: "utf8" });
48970
+ const baseHead = baseHeadStdout.trim();
48971
+ const branchHead = branchHeadStdout.trim();
48972
+ recordMeshRefineStage(refineStages, "resolve_refs", "passed", resolveStarted, { branch, baseBranch, baseHead, branchHead });
48973
+ const validationStarted = Date.now();
48176
48974
  const validationSummary = await runMeshRefineValidationGate(mesh, node.workspace);
48975
+ recordMeshRefineStage(
48976
+ refineStages,
48977
+ "validation",
48978
+ validationSummary.status === "passed" ? "passed" : validationSummary.status === "failed" ? "failed" : "skipped",
48979
+ validationStarted,
48980
+ { validationStatus: validationSummary.status, commandsRun: validationSummary.commandsRun.length }
48981
+ );
48177
48982
  if (validationSummary.status === "failed") {
48178
48983
  return {
48179
48984
  success: false,
@@ -48183,6 +48988,7 @@ var init_router = __esm({
48183
48988
  branch,
48184
48989
  into: baseBranch,
48185
48990
  validationSummary,
48991
+ refineStages,
48186
48992
  finalBranchConvergenceState: {
48187
48993
  branch,
48188
48994
  baseBranch,
@@ -48202,6 +49008,7 @@ var init_router = __esm({
48202
49008
  branch,
48203
49009
  into: baseBranch,
48204
49010
  validationSummary,
49011
+ refineStages,
48205
49012
  finalBranchConvergenceState: {
48206
49013
  branch,
48207
49014
  baseBranch,
@@ -48212,37 +49019,121 @@ var init_router = __esm({
48212
49019
  }
48213
49020
  };
48214
49021
  }
49022
+ const patchEquivalenceStarted = Date.now();
49023
+ const patchEquivalence = await runMeshRefinePatchEquivalenceGate(repoRoot, baseHead, branchHead);
49024
+ recordMeshRefineStage(refineStages, "patch_equivalence", patchEquivalence.status, patchEquivalenceStarted, {
49025
+ equivalent: patchEquivalence.equivalent,
49026
+ expectedPatchId: patchEquivalence.expectedPatchId,
49027
+ actualPatchId: patchEquivalence.actualPatchId,
49028
+ error: patchEquivalence.error
49029
+ });
49030
+ if (!patchEquivalence.equivalent) {
49031
+ return {
49032
+ success: false,
49033
+ code: "patch_equivalence_failed",
49034
+ convergenceStatus: "blocked_review",
49035
+ error: "Refinery patch-equivalence preflight failed; merge/refine was not attempted.",
49036
+ branch,
49037
+ into: baseBranch,
49038
+ validationSummary,
49039
+ patchEquivalence,
49040
+ refineStages,
49041
+ finalBranchConvergenceState: {
49042
+ branch,
49043
+ baseBranch,
49044
+ merged: false,
49045
+ removed: false,
49046
+ validation: "passed",
49047
+ patchEquivalence: "failed",
49048
+ status: "blocked_review"
49049
+ }
49050
+ };
49051
+ }
49052
+ let mergeResult;
49053
+ const mergeStarted = Date.now();
48215
49054
  try {
48216
- await execFileAsync3("git", ["merge", "--no-ff", branch, "-m", `Auto-merge branch '${branch}' via Refinery`], { cwd: repoRoot, encoding: "utf8" });
49055
+ const result = await execFileAsync3("git", ["merge", "--no-ff", branch, "-m", `Auto-merge branch '${branch}' via Refinery`], { cwd: repoRoot, encoding: "utf8" });
49056
+ mergeResult = {
49057
+ stdout: truncateValidationOutput(result.stdout),
49058
+ stderr: truncateValidationOutput(result.stderr),
49059
+ durationMs: Date.now() - mergeStarted
49060
+ };
49061
+ recordMeshRefineStage(refineStages, "merge", "passed", mergeStarted, mergeResult);
48217
49062
  } catch (e) {
49063
+ recordMeshRefineStage(refineStages, "merge", "failed", mergeStarted, {
49064
+ error: e?.message || String(e),
49065
+ stdout: truncateValidationOutput(e?.stdout),
49066
+ stderr: truncateValidationOutput(e?.stderr)
49067
+ });
48218
49068
  return {
48219
49069
  success: false,
48220
49070
  error: `Merge failed (conflicts?): ${e.message}`,
48221
49071
  validationSummary,
49072
+ patchEquivalence,
49073
+ refineStages,
48222
49074
  finalBranchConvergenceState: {
48223
49075
  branch,
48224
49076
  baseBranch,
48225
49077
  merged: false,
48226
49078
  removed: false,
48227
49079
  validation: "passed",
49080
+ patchEquivalence: "passed",
48228
49081
  status: "not_mergeable"
48229
49082
  }
48230
49083
  };
48231
49084
  }
49085
+ const cleanupStarted = Date.now();
48232
49086
  const removeResult = await this.execute("remove_mesh_node", {
48233
49087
  meshId,
48234
49088
  nodeId,
48235
- sessionCleanupMode: "kill",
49089
+ sessionCleanupMode: "preserve",
48236
49090
  inlineMesh: args?.inlineMesh
48237
49091
  });
49092
+ recordMeshRefineStage(refineStages, "cleanup", removeResult?.success === false ? "failed" : "passed", cleanupStarted, {
49093
+ removed: removeResult?.removed,
49094
+ code: removeResult?.code,
49095
+ error: removeResult?.error
49096
+ });
49097
+ let ledgerError;
49098
+ const ledgerStarted = Date.now();
48238
49099
  try {
48239
49100
  const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
48240
49101
  appendLedgerEntry2(meshId, {
48241
49102
  kind: "node_removed",
48242
49103
  nodeId,
48243
- payload: { refined: true, mergedBranch: branch, into: baseBranch, validationSummary }
49104
+ payload: { refined: true, mergedBranch: branch, into: baseBranch, validationSummary, patchEquivalence }
48244
49105
  });
48245
- } catch {
49106
+ recordMeshRefineStage(refineStages, "ledger", "passed", ledgerStarted);
49107
+ } catch (e) {
49108
+ ledgerError = e?.message || String(e);
49109
+ recordMeshRefineStage(refineStages, "ledger", "failed", ledgerStarted, { error: ledgerError });
49110
+ }
49111
+ const finalBranchConvergenceState = {
49112
+ branch: baseBranch,
49113
+ mergedBranch: branch,
49114
+ baseBranch,
49115
+ merged: true,
49116
+ removed: removeResult?.success !== false,
49117
+ validation: "passed",
49118
+ patchEquivalence: "passed",
49119
+ status: removeResult?.success === false ? "merged_cleanup_failed" : "merged"
49120
+ };
49121
+ if (removeResult?.success === false) {
49122
+ return {
49123
+ success: false,
49124
+ code: "cleanup_failed",
49125
+ error: "Refinery merge completed but worktree cleanup failed; manual cleanup/retry is required.",
49126
+ merged: true,
49127
+ branch,
49128
+ into: baseBranch,
49129
+ removeResult,
49130
+ validationSummary,
49131
+ patchEquivalence,
49132
+ mergeResult,
49133
+ refineStages,
49134
+ ...ledgerError ? { ledgerError } : {},
49135
+ finalBranchConvergenceState
49136
+ };
48246
49137
  }
48247
49138
  return {
48248
49139
  success: true,
@@ -48251,18 +49142,14 @@ var init_router = __esm({
48251
49142
  into: baseBranch,
48252
49143
  removeResult,
48253
49144
  validationSummary,
48254
- finalBranchConvergenceState: {
48255
- branch: baseBranch,
48256
- mergedBranch: branch,
48257
- baseBranch,
48258
- merged: true,
48259
- removed: removeResult?.success !== false,
48260
- validation: "passed",
48261
- status: removeResult?.success === false ? "merged_cleanup_failed" : "merged"
48262
- }
49145
+ patchEquivalence,
49146
+ mergeResult,
49147
+ refineStages,
49148
+ ...ledgerError ? { ledgerError } : {},
49149
+ finalBranchConvergenceState
48263
49150
  };
48264
49151
  } catch (e) {
48265
- return { success: false, error: e.message };
49152
+ return { success: false, error: e.message, refineStages };
48266
49153
  }
48267
49154
  }
48268
49155
  case "remove_mesh_node": {
@@ -48303,6 +49190,7 @@ var init_router = __esm({
48303
49190
  } else {
48304
49191
  const { removeNode: removeNode3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
48305
49192
  removed = removeNode3(meshId, nodeId);
49193
+ if (removed) this.invalidateAggregateMeshStatus(meshId);
48306
49194
  }
48307
49195
  if (removed) {
48308
49196
  try {
@@ -48381,6 +49269,7 @@ var init_router = __esm({
48381
49269
  policy: { ...sourceNode.policy || {} }
48382
49270
  });
48383
49271
  if (!node) return { success: false, error: "Failed to register worktree node" };
49272
+ this.invalidateAggregateMeshStatus(meshId);
48384
49273
  }
48385
49274
  const initSubmodules = sourceNode.policy?.initSubmodulesOnClone !== false;
48386
49275
  if (initSubmodules) {
@@ -48456,7 +49345,14 @@ var init_router = __esm({
48456
49345
  cliType
48457
49346
  };
48458
49347
  }
48459
- const workspace = typeof coordinatorNode.workspace === "string" ? coordinatorNode.workspace.trim() : "";
49348
+ const sessionHostRecords = this.deps.sessionHostControl?.listSessions ? await this.deps.sessionHostControl.listSessions().catch(() => []) : [];
49349
+ const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
49350
+ const workspace = readLiveMeshNodeWorkspace({
49351
+ meshId,
49352
+ nodeId: String(coordinatorNode.id || coordinatorNode.nodeId || preferredCoordinatorNodeId || ""),
49353
+ liveSessionRecords: liveMeshSessions,
49354
+ allowCoordinatorSession: true
49355
+ }) || (typeof coordinatorNode.workspace === "string" ? coordinatorNode.workspace.trim() : "");
48460
49356
  if (!workspace) return { success: false, error: "Coordinator node workspace required", meshId, cliType };
48461
49357
  if (!cliType) {
48462
49358
  const resolved = await resolveProviderTypeFromPriority({
@@ -48618,7 +49514,7 @@ ${block}`);
48618
49514
  workspace
48619
49515
  };
48620
49516
  }
48621
- const { existsSync: existsSync32, readFileSync: readFileSync23, writeFileSync: writeFileSync18, copyFileSync: copyFileSync5, mkdirSync: mkdirSync22 } = await import("fs");
49517
+ const { existsSync: existsSync33, readFileSync: readFileSync24, writeFileSync: writeFileSync18, copyFileSync: copyFileSync5, mkdirSync: mkdirSync22 } = await import("fs");
48622
49518
  const { dirname: dirname13 } = await import("path");
48623
49519
  const mcpConfigPath = coordinatorSetup.configPath;
48624
49520
  const hermesManualFallback = cliType === "hermes-cli" && configFormat === "hermes_config_yaml" ? createHermesManualMeshCoordinatorSetup(meshId, workspace) : null;
@@ -48661,14 +49557,14 @@ ${block}`);
48661
49557
  if (hermesManualFallback) return returnManualFallback(message);
48662
49558
  return { success: false, code: "mesh_coordinator_config_write_failed", error: message, meshId, cliType, workspace };
48663
49559
  }
48664
- const hadExistingMcpConfig = existsSync32(mcpConfigPath);
49560
+ const hadExistingMcpConfig = existsSync33(mcpConfigPath);
48665
49561
  let existingMcpConfig = hermesBaseConfig?.config || {};
48666
49562
  if (hermesBaseConfig) {
48667
49563
  copyHermesCoordinatorCredentialFiles(hermesBaseConfig.sourceHome, dirname13(mcpConfigPath));
48668
49564
  }
48669
49565
  if (hadExistingMcpConfig) {
48670
49566
  try {
48671
- const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(readFileSync23(mcpConfigPath, "utf-8"), configFormat);
49567
+ const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(readFileSync24(mcpConfigPath, "utf-8"), configFormat);
48672
49568
  const existingCoordinatorConfig = hermesManualFallback ? stripHermesCoordinatorTempModelProviderOverrides(parsedExistingMcpConfig) : parsedExistingMcpConfig;
48673
49569
  existingMcpConfig = { ...existingMcpConfig, ...existingCoordinatorConfig };
48674
49570
  copyFileSync5(mcpConfigPath, mcpConfigPath + ".backup");
@@ -48758,110 +49654,264 @@ ${block}`);
48758
49654
  const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
48759
49655
  const mesh = meshRecord?.mesh;
48760
49656
  if (!mesh) return { success: false, error: "Mesh not found" };
49657
+ const refreshRequested = args?.refresh === true || args?.forceRefresh === true;
49658
+ if (!refreshRequested) {
49659
+ const cachedStatus = this.getCachedAggregateMeshStatus(meshId);
49660
+ if (cachedStatus) return cachedStatus;
49661
+ }
49662
+ const refreshReason = refreshRequested ? "explicit_refresh" : "cold_cache_miss";
48761
49663
  const { getMeshQueueStats: getMeshQueueStats2, getQueue: getQueue2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
48762
49664
  const queue = getQueue2(meshId);
48763
49665
  const queueSummary = getMeshQueueStats2(meshId);
48764
49666
  const { readLedgerEntries: readLedgerEntries2, getLedgerSummary: getLedgerSummary2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
48765
49667
  const ledgerEntries = readLedgerEntries2(meshId, { tail: 20 });
48766
49668
  const ledgerSummary = getLedgerSummary2(meshId);
49669
+ const sessionHostRecords = this.deps.sessionHostControl?.listSessions ? await this.deps.sessionHostControl.listSessions().catch(() => []) : [];
49670
+ const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
49671
+ const localMachineId = loadConfig().machineId || "";
49672
+ const requireDirectPeerTruth = args?.requireDirectPeerTruth === true;
49673
+ const directTruth = requireDirectPeerTruth ? await hydrateInlineMeshDirectTruth({
49674
+ mesh,
49675
+ meshSource: meshRecord.source,
49676
+ dispatchMeshCommand: this.deps.dispatchMeshCommand,
49677
+ statusInstanceId: this.deps.statusInstanceId,
49678
+ localMachineId
49679
+ }) : {
49680
+ directEvidenceCount: 0,
49681
+ localConfirmedCount: 0,
49682
+ peerAttemptedCount: 0,
49683
+ peerConfirmedCount: 0,
49684
+ unavailableNodeIds: []
49685
+ };
49686
+ const directTruthSatisfied = meshRecord.source !== "inline_bootstrap" || directTruth.directEvidenceCount > 0;
49687
+ if (requireDirectPeerTruth && !directTruthSatisfied) {
49688
+ return {
49689
+ success: false,
49690
+ code: "mesh_direct_peer_truth_unavailable",
49691
+ error: "Selected coordinator could not confirm direct mesh truth yet. Bootstrap inventory stays unavailable until direct mesh_status probes succeed.",
49692
+ sourceOfTruth: {
49693
+ membership: meshRecord.source === "inline_cache" ? "coordinator_inline_mesh_cache" : meshRecord.source === "local_config" ? "local_mesh_config" : "inline_bootstrap_snapshot",
49694
+ coordinatorOwnsLiveTruth: false,
49695
+ currentStatus: "direct_peer_truth_unavailable",
49696
+ directPeerTruth: {
49697
+ required: true,
49698
+ satisfied: false,
49699
+ directEvidenceCount: directTruth.directEvidenceCount,
49700
+ localConfirmedCount: directTruth.localConfirmedCount,
49701
+ peerAttemptedCount: directTruth.peerAttemptedCount,
49702
+ peerConfirmedCount: directTruth.peerConfirmedCount,
49703
+ unavailableNodeIds: directTruth.unavailableNodeIds
49704
+ }
49705
+ }
49706
+ };
49707
+ }
49708
+ const directTruthUnavailableNodeIds = new Set(directTruth.unavailableNodeIds);
49709
+ const selectedCoordinatorNodeId = readStringValue(
49710
+ mesh.coordinator?.preferredNodeId,
49711
+ mesh.nodes?.[0]?.id,
49712
+ mesh.nodes?.[0]?.nodeId
49713
+ );
49714
+ const inlineCoordinatorNodeId = meshRecord?.inline && Array.isArray(mesh.nodes) ? selectedCoordinatorNodeId : void 0;
49715
+ const refreshedAt = (/* @__PURE__ */ new Date()).toISOString();
48767
49716
  const nodeStatuses = [];
48768
- for (const node of mesh.nodes || []) {
49717
+ for (const [nodeIndex, node] of (mesh.nodes || []).entries()) {
49718
+ const nodeId = String(node.id || node.nodeId || "");
49719
+ const daemonId = readStringValue(node.daemonId);
49720
+ const providerPriority = readProviderPriorityFromPolicy(node.policy);
49721
+ const isSelfNode = Boolean(
49722
+ nodeId && inlineCoordinatorNodeId && nodeId === inlineCoordinatorNodeId
49723
+ ) || Boolean(
49724
+ daemonId && (daemonId === localMachineId || daemonId === this.deps.statusInstanceId)
49725
+ ) || Boolean(meshRecord?.inline && nodeIndex === 0);
48769
49726
  const status = {
48770
- nodeId: node.id || node.nodeId,
49727
+ nodeId,
48771
49728
  machineLabel: node.machineLabel || node.id || node.nodeId,
48772
49729
  workspace: node.workspace,
48773
49730
  repoRoot: node.repoRoot,
48774
49731
  isLocalWorktree: node.isLocalWorktree,
48775
49732
  worktreeBranch: node.worktreeBranch,
48776
- daemonId: node.daemonId,
49733
+ daemonId,
48777
49734
  machineId: node.machineId,
49735
+ machineStatus: node.machineStatus,
48778
49736
  health: "unknown",
48779
49737
  providers: node.providers || [],
48780
- activeSessions: []
49738
+ providerPriority,
49739
+ activeSessions: [],
49740
+ activeSessionDetails: [],
49741
+ launchReady: false
48781
49742
  };
48782
- if (node.workspace && typeof node.workspace === "string") {
48783
- if (!fs10.existsSync(node.workspace) && applyCachedInlineMeshNodeStatus(status, node)) {
48784
- nodeStatuses.push(status);
48785
- continue;
49743
+ if (isSelfNode) {
49744
+ status.connection = {
49745
+ perspective: "selected_coordinator",
49746
+ source: "mesh_peer_status",
49747
+ state: "self",
49748
+ transport: "local",
49749
+ reported: true,
49750
+ reason: "Selected coordinator daemon",
49751
+ lastStateChangeAt: refreshedAt
49752
+ };
49753
+ } else if (daemonId) {
49754
+ const connection = this.deps.getMeshPeerConnectionStatus?.(daemonId);
49755
+ status.connection = connection ?? {
49756
+ perspective: "selected_coordinator",
49757
+ source: "not_reported",
49758
+ state: "unknown",
49759
+ transport: "unknown",
49760
+ reported: false,
49761
+ reason: "No live mesh peer telemetry reported by the selected coordinator yet."
49762
+ };
49763
+ } else {
49764
+ status.connection = {
49765
+ perspective: "selected_coordinator",
49766
+ source: "not_reported",
49767
+ state: "unknown",
49768
+ transport: "unknown",
49769
+ reported: false,
49770
+ reason: "Node has no daemon id, so mesh transport cannot be reported from the selected coordinator."
49771
+ };
49772
+ }
49773
+ const matchedLiveSessionRecords = collectLiveMeshSessionRecords({
49774
+ meshId,
49775
+ node,
49776
+ nodeId,
49777
+ liveSessionRecords: liveMeshSessions,
49778
+ allowCoordinatorSession: nodeId === selectedCoordinatorNodeId
49779
+ });
49780
+ const workspace = readLiveMeshNodeWorkspace({
49781
+ meshId,
49782
+ nodeId,
49783
+ liveSessionRecords: matchedLiveSessionRecords,
49784
+ allowCoordinatorSession: nodeId === selectedCoordinatorNodeId
49785
+ }) || (typeof node.workspace === "string" ? node.workspace : "");
49786
+ status.workspace = workspace || node.workspace;
49787
+ if (matchedLiveSessionRecords.length > 0) {
49788
+ const sessionIds = matchedLiveSessionRecords.map((record2) => typeof record2?.sessionId === "string" ? record2.sessionId : "").filter(Boolean);
49789
+ const providerTypes = matchedLiveSessionRecords.map((record2) => readStringValue(record2?.providerType)).filter(Boolean);
49790
+ status.activeSessions = sessionIds;
49791
+ status.activeSessionDetails = matchedLiveSessionRecords.map(summarizeMeshSessionRecord);
49792
+ if (providerTypes.length > 0) {
49793
+ status.providers = Array.from(/* @__PURE__ */ new Set([...Array.isArray(status.providers) ? status.providers : [], ...providerTypes]));
48786
49794
  }
48787
- try {
48788
- const { execFile: execFile3 } = await import("child_process");
48789
- const { promisify: promisify3 } = await import("util");
48790
- const execFileAsync3 = promisify3(execFile3);
48791
- const runGit2 = async (args2) => {
48792
- const result = await execFileAsync3("git", ["-C", node.workspace, ...args2], {
48793
- encoding: "utf8",
48794
- timeout: 1e4
48795
- });
48796
- return result.stdout.trim();
48797
- };
48798
- const branch = await runGit2(["branch", "--show-current"]).catch(() => "");
48799
- const porc = await runGit2(["status", "--porcelain"]).catch(() => "");
48800
- const headCommit = await runGit2(["rev-parse", "--short", "HEAD"]).catch(() => null);
48801
- const headMessage = await runGit2(["log", "-1", "--format=%s"]).catch(() => null);
48802
- const upstream = await runGit2(["rev-parse", "--abbrev-ref", "@{upstream}"]).catch(() => null);
48803
- const aheadBehind = await runGit2(["rev-list", "--left-right", "--count", "@{upstream}...HEAD"]).catch(() => "");
48804
- const stashCount = await runGit2(["stash", "list"]).catch(() => "");
48805
- let ahead = 0, behind = 0;
48806
- if (aheadBehind) {
48807
- const parts = aheadBehind.split(/\s+/);
48808
- if (parts.length >= 2) {
48809
- behind = parseInt(parts[0], 10) || 0;
48810
- ahead = parseInt(parts[1], 10) || 0;
49795
+ }
49796
+ if (workspace) {
49797
+ if (!fs10.existsSync(workspace)) {
49798
+ const inlineTransitGit = buildInlineMeshTransitGitStatus(node);
49799
+ let remoteProbeApplied = false;
49800
+ if (inlineTransitGit) {
49801
+ status.git = inlineTransitGit;
49802
+ status.health = inlineTransitGit.isGitRepo ? deriveMeshNodeHealthFromGit(inlineTransitGit) : "degraded";
49803
+ remoteProbeApplied = true;
49804
+ } else if (!isSelfNode && daemonId && this.deps.dispatchMeshCommand && !directTruthUnavailableNodeIds.has(nodeId)) {
49805
+ try {
49806
+ const remoteGit = await probeRemoteMeshGitStatus({
49807
+ dispatchMeshCommand: this.deps.dispatchMeshCommand,
49808
+ daemonId,
49809
+ workspace,
49810
+ timeoutMs: 8e3
49811
+ });
49812
+ if (remoteGit) {
49813
+ status.git = remoteGit;
49814
+ status.health = remoteGit.isGitRepo ? deriveMeshNodeHealthFromGit(remoteGit) : "degraded";
49815
+ recordInlineMeshDirectGitTruth(node, remoteGit, "selected_coordinator_mesh_p2p_git");
49816
+ remoteProbeApplied = true;
49817
+ }
49818
+ } catch {
49819
+ const refreshedConnection = this.deps.getMeshPeerConnectionStatus?.(daemonId);
49820
+ const refreshedConnectionState = readStringValue(refreshedConnection?.state);
49821
+ if (refreshedConnection && refreshedConnectionState === "connected") {
49822
+ status.connection = refreshedConnection;
49823
+ try {
49824
+ const remoteGit = await probeRemoteMeshGitStatus({
49825
+ dispatchMeshCommand: this.deps.dispatchMeshCommand,
49826
+ daemonId,
49827
+ workspace,
49828
+ timeoutMs: 12e3
49829
+ });
49830
+ if (remoteGit) {
49831
+ status.git = remoteGit;
49832
+ status.health = remoteGit.isGitRepo ? deriveMeshNodeHealthFromGit(remoteGit) : "degraded";
49833
+ recordInlineMeshDirectGitTruth(node, remoteGit, "selected_coordinator_mesh_p2p_git");
49834
+ remoteProbeApplied = true;
49835
+ }
49836
+ } catch {
49837
+ }
49838
+ }
48811
49839
  }
48812
49840
  }
48813
- const dirty = porc.length > 0;
48814
- const lines = porc ? porc.split("\n").filter(Boolean) : [];
48815
- let staged = 0, modified = 0, untracked = 0, deleted = 0, renamed2 = 0;
48816
- for (const line of lines) {
48817
- const xy = line.slice(0, 2);
48818
- if (xy[0] !== " " && xy[0] !== "?") staged++;
48819
- if (xy[1] === "M") modified++;
48820
- if (xy[1] === "D") deleted++;
48821
- if (xy[0] === "R" || xy[1] === "R") renamed2++;
48822
- if (xy === "??") untracked++;
49841
+ if (!remoteProbeApplied) {
49842
+ const connectionState = readStringValue(status.connection?.state);
49843
+ const pendingPeerGitProbe = !inlineTransitGit && !isSelfNode && !!daemonId && (readStringValue(status.machineStatus) === "online" || readStringValue(status.health) === "online" || connectionState === "connecting" || connectionState === "connected" || connectionState === "unknown");
49844
+ if (pendingPeerGitProbe) {
49845
+ status.gitProbePending = true;
49846
+ status.health = "unknown";
49847
+ }
49848
+ if (applyCachedInlineMeshNodeStatus(
49849
+ status,
49850
+ node,
49851
+ pendingPeerGitProbe ? { skipGit: true, skipError: true, skipHealth: true } : void 0
49852
+ )) {
49853
+ finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
49854
+ nodeStatuses.push(status);
49855
+ continue;
49856
+ }
49857
+ if (meshRecord?.source === "inline_cache" && !isSelfNode) {
49858
+ finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
49859
+ nodeStatuses.push(status);
49860
+ continue;
49861
+ }
48823
49862
  }
48824
- status.git = {
48825
- workspace: node.workspace,
48826
- repoRoot: node.workspace,
48827
- isGitRepo: true,
48828
- branch: branch || null,
48829
- headCommit,
48830
- headMessage,
48831
- upstream,
48832
- ahead,
48833
- behind,
48834
- staged,
48835
- modified,
48836
- untracked,
48837
- deleted,
48838
- renamed: renamed2,
48839
- hasConflicts: false,
48840
- conflictFiles: [],
48841
- stashCount: stashCount ? stashCount.split("\n").filter(Boolean).length : 0,
48842
- lastCheckedAt: Date.now()
48843
- };
48844
- status.health = branch ? dirty ? "dirty" : "online" : "degraded";
48845
- } catch {
48846
- if (!applyCachedInlineMeshNodeStatus(status, node)) {
48847
- status.health = "degraded";
49863
+ } else {
49864
+ try {
49865
+ const gitStatus = await getGitRepoStatus(workspace, { timeoutMs: 1e4, refreshUpstream: true });
49866
+ status.git = gitStatus;
49867
+ recordInlineMeshDirectGitTruth(node, gitStatus, "selected_coordinator_local_git");
49868
+ if (gitStatus.isGitRepo) {
49869
+ status.health = deriveMeshNodeHealthFromGit(gitStatus);
49870
+ } else {
49871
+ status.health = "degraded";
49872
+ if (gitStatus.error && !status.error) status.error = gitStatus.error;
49873
+ }
49874
+ } catch {
49875
+ if (!applyCachedInlineMeshNodeStatus(status, node)) {
49876
+ status.health = "degraded";
49877
+ }
48848
49878
  }
48849
49879
  }
48850
49880
  } else {
48851
49881
  applyCachedInlineMeshNodeStatus(status, node);
48852
49882
  }
49883
+ finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
48853
49884
  nodeStatuses.push(status);
48854
49885
  }
48855
- return {
49886
+ const statusResult = {
48856
49887
  success: true,
48857
49888
  meshId: mesh.id,
48858
49889
  meshName: mesh.name,
48859
49890
  repoIdentity: mesh.repoIdentity,
48860
49891
  defaultBranch: mesh.defaultBranch,
49892
+ refreshedAt,
49893
+ sourceOfTruth: {
49894
+ membership: meshRecord?.source === "inline_cache" ? "coordinator_inline_mesh_cache" : meshRecord?.source === "local_config" ? "local_mesh_config" : "inline_bootstrap_snapshot",
49895
+ coordinatorOwnsLiveTruth: directTruthSatisfied,
49896
+ ...requireDirectPeerTruth ? {
49897
+ currentStatus: directTruthSatisfied ? "live_git_and_session_probes" : "direct_peer_truth_unavailable",
49898
+ directPeerTruth: {
49899
+ required: true,
49900
+ satisfied: directTruthSatisfied,
49901
+ directEvidenceCount: directTruth.directEvidenceCount,
49902
+ localConfirmedCount: directTruth.localConfirmedCount,
49903
+ peerAttemptedCount: directTruth.peerAttemptedCount,
49904
+ peerConfirmedCount: directTruth.peerConfirmedCount,
49905
+ unavailableNodeIds: directTruth.unavailableNodeIds
49906
+ }
49907
+ } : {},
49908
+ historicalEvidenceOnly: ["recoveryHints", "ledger.summary", "queue.summary"]
49909
+ },
48861
49910
  nodes: nodeStatuses,
48862
49911
  queue: { tasks: queue, summary: queueSummary },
48863
49912
  ledger: { entries: ledgerEntries, summary: ledgerSummary }
48864
49913
  };
49914
+ return this.rememberAggregateMeshStatus(meshId, statusResult, refreshReason);
48865
49915
  } catch (e) {
48866
49916
  return { success: false, error: e.message };
48867
49917
  }
@@ -56936,6 +57986,7 @@ async function initDaemonComponents(config2) {
56936
57986
  sessionHostControl: config2.sessionHostControl,
56937
57987
  statusInstanceId: config2.statusInstanceId,
56938
57988
  statusVersion: config2.statusVersion,
57989
+ getMeshPeerConnectionStatus: config2.getMeshPeerConnectionStatus,
56939
57990
  getCdpLogFn: config2.getCdpLogFn || ((ideType) => LOG.forComponent(`CDP:${ideType}`).asLogFn())
56940
57991
  });
56941
57992
  poller = new AgentStreamPoller({
@@ -57237,6 +58288,7 @@ __export(src_exports, {
57237
58288
  prepareSessionChatTailUpdate: () => prepareSessionChatTailUpdate,
57238
58289
  prepareSessionModalUpdate: () => prepareSessionModalUpdate,
57239
58290
  probeCdpPort: () => probeCdpPort,
58291
+ queuePendingMeshCoordinatorEvent: () => queuePendingMeshCoordinatorEvent,
57240
58292
  readChatHistory: () => readChatHistory,
57241
58293
  readLedgerEntries: () => readLedgerEntries,
57242
58294
  readLedgerSlice: () => readLedgerSlice,
@@ -72139,7 +73191,7 @@ var require_buffer_list = __commonJS({
72139
73191
  }
72140
73192
  }, {
72141
73193
  key: "join",
72142
- value: function join39(s) {
73194
+ value: function join40(s) {
72143
73195
  if (this.length === 0) return "";
72144
73196
  var p = this.head;
72145
73197
  var ret = "" + p.data;
@@ -86198,13 +87250,13 @@ function splitStringBySpace(str2) {
86198
87250
  }
86199
87251
  return pieces;
86200
87252
  }
86201
- var import_chardet, import_child_process12, import_fs11, import_node_path3, import_node_os3, import_node_crypto3, import_iconv_lite, ExternalEditor;
87253
+ var import_chardet, import_child_process12, import_fs12, import_node_path3, import_node_os3, import_node_crypto3, import_iconv_lite, ExternalEditor;
86202
87254
  var init_esm4 = __esm({
86203
87255
  "../../node_modules/@inquirer/external-editor/dist/esm/index.js"() {
86204
87256
  "use strict";
86205
87257
  import_chardet = __toESM(require_lib(), 1);
86206
87258
  import_child_process12 = require("child_process");
86207
- import_fs11 = require("fs");
87259
+ import_fs12 = require("fs");
86208
87260
  import_node_path3 = __toESM(require("path"), 1);
86209
87261
  import_node_os3 = __toESM(require("os"), 1);
86210
87262
  import_node_crypto3 = require("crypto");
@@ -86280,14 +87332,14 @@ var init_esm4 = __esm({
86280
87332
  if (Object.prototype.hasOwnProperty.call(this.fileOptions, "mode")) {
86281
87333
  opt.mode = this.fileOptions.mode;
86282
87334
  }
86283
- (0, import_fs11.writeFileSync)(this.tempFile, this.text, opt);
87335
+ (0, import_fs12.writeFileSync)(this.tempFile, this.text, opt);
86284
87336
  } catch (createFileError) {
86285
87337
  throw new CreateFileError(createFileError);
86286
87338
  }
86287
87339
  }
86288
87340
  readTemporaryFile() {
86289
87341
  try {
86290
- const tempFileBuffer = (0, import_fs11.readFileSync)(this.tempFile);
87342
+ const tempFileBuffer = (0, import_fs12.readFileSync)(this.tempFile);
86291
87343
  if (tempFileBuffer.length === 0) {
86292
87344
  this.text = "";
86293
87345
  } else {
@@ -86303,7 +87355,7 @@ var init_esm4 = __esm({
86303
87355
  }
86304
87356
  removeTemporaryFile() {
86305
87357
  try {
86306
- (0, import_fs11.unlinkSync)(this.tempFile);
87358
+ (0, import_fs12.unlinkSync)(this.tempFile);
86307
87359
  } catch (removeFileError) {
86308
87360
  throw new RemoveFileError(removeFileError);
86309
87361
  }
@@ -88005,25 +89057,25 @@ function resolvePackageVersion(options) {
88005
89057
  const injectedVersion = options?.injectedVersion || "unknown";
88006
89058
  const dir = options?.dirname || __dirname;
88007
89059
  const possiblePaths = [
88008
- (0, import_path7.join)(dir, "..", "..", "package.json"),
88009
- (0, import_path7.join)(dir, "..", "package.json"),
88010
- (0, import_path7.join)(dir, "package.json")
89060
+ (0, import_path8.join)(dir, "..", "..", "package.json"),
89061
+ (0, import_path8.join)(dir, "..", "package.json"),
89062
+ (0, import_path8.join)(dir, "package.json")
88011
89063
  ];
88012
89064
  for (const p of possiblePaths) {
88013
89065
  try {
88014
- const data = JSON.parse((0, import_fs12.readFileSync)(p, "utf-8"));
89066
+ const data = JSON.parse((0, import_fs13.readFileSync)(p, "utf-8"));
88015
89067
  if (data.version) return data.version;
88016
89068
  } catch {
88017
89069
  }
88018
89070
  }
88019
89071
  return injectedVersion;
88020
89072
  }
88021
- var import_fs12, import_path7;
89073
+ var import_fs13, import_path8;
88022
89074
  var init_version = __esm({
88023
89075
  "src/version.ts"() {
88024
89076
  "use strict";
88025
- import_fs12 = require("fs");
88026
- import_path7 = require("path");
89077
+ import_fs13 = require("fs");
89078
+ import_path8 = require("path");
88027
89079
  }
88028
89080
  });
88029
89081
 
@@ -88144,7 +89196,9 @@ var init_server_connection = __esm({
88144
89196
  const requestId = `mesh_${crypto.randomUUID()}`;
88145
89197
  const timer = setTimeout(() => {
88146
89198
  this.off("daemon_mesh_result", handler);
88147
- reject(new Error(`Mesh command timed out after ${timeoutMs}ms`));
89199
+ reject(new Error(
89200
+ `Mesh command '${command}' to ${targetDaemonId.slice(0, 12)} timed out after ${timeoutMs}ms (requestId=${requestId})`
89201
+ ));
88148
89202
  }, timeoutMs);
88149
89203
  const handler = (msg) => {
88150
89204
  const body = msg.payload && typeof msg.payload === "object" ? { ...msg, ...msg.payload } : msg;
@@ -89959,7 +91013,7 @@ var require_filesystem = __commonJS({
89959
91013
  var LDD_PATH = "/usr/bin/ldd";
89960
91014
  var SELF_PATH = "/proc/self/exe";
89961
91015
  var MAX_LENGTH = 2048;
89962
- var readFileSync23 = (path42) => {
91016
+ var readFileSync24 = (path42) => {
89963
91017
  const fd = fs24.openSync(path42, "r");
89964
91018
  const buffer = Buffer.alloc(MAX_LENGTH);
89965
91019
  const bytesRead = fs24.readSync(fd, buffer, 0, MAX_LENGTH, 0);
@@ -89984,7 +91038,7 @@ var require_filesystem = __commonJS({
89984
91038
  module2.exports = {
89985
91039
  LDD_PATH,
89986
91040
  SELF_PATH,
89987
- readFileSync: readFileSync23,
91041
+ readFileSync: readFileSync24,
89988
91042
  readFile: readFile2
89989
91043
  };
89990
91044
  }
@@ -90033,7 +91087,7 @@ var require_detect_libc = __commonJS({
90033
91087
  "use strict";
90034
91088
  var childProcess = require("child_process");
90035
91089
  var { isLinux: isLinux2, getReport } = require_process();
90036
- var { LDD_PATH, SELF_PATH, readFile: readFile2, readFileSync: readFileSync23 } = require_filesystem();
91090
+ var { LDD_PATH, SELF_PATH, readFile: readFile2, readFileSync: readFileSync24 } = require_filesystem();
90037
91091
  var { interpreterPath } = require_elf();
90038
91092
  var cachedFamilyInterpreter;
90039
91093
  var cachedFamilyFilesystem;
@@ -90125,7 +91179,7 @@ var require_detect_libc = __commonJS({
90125
91179
  }
90126
91180
  cachedFamilyFilesystem = null;
90127
91181
  try {
90128
- const lddContent = readFileSync23(LDD_PATH);
91182
+ const lddContent = readFileSync24(LDD_PATH);
90129
91183
  cachedFamilyFilesystem = getFamilyFromLddContent(lddContent);
90130
91184
  } catch (e) {
90131
91185
  }
@@ -90150,7 +91204,7 @@ var require_detect_libc = __commonJS({
90150
91204
  }
90151
91205
  cachedFamilyInterpreter = null;
90152
91206
  try {
90153
- const selfContent = readFileSync23(SELF_PATH);
91207
+ const selfContent = readFileSync24(SELF_PATH);
90154
91208
  const path42 = interpreterPath(selfContent);
90155
91209
  cachedFamilyInterpreter = familyFromInterpreterPath(path42);
90156
91210
  } catch (e) {
@@ -90214,7 +91268,7 @@ var require_detect_libc = __commonJS({
90214
91268
  }
90215
91269
  cachedVersionFilesystem = null;
90216
91270
  try {
90217
- const lddContent = readFileSync23(LDD_PATH);
91271
+ const lddContent = readFileSync24(LDD_PATH);
90218
91272
  const versionMatch = lddContent.match(RE_GLIBC_VERSION);
90219
91273
  if (versionMatch) {
90220
91274
  cachedVersionFilesystem = versionMatch[1];
@@ -97213,11 +98267,37 @@ var init_daemon_mesh_manager = __esm({
97213
98267
  nodeDatachannel = null;
97214
98268
  peers = /* @__PURE__ */ new Map();
97215
98269
  // Map<targetDaemonId, PeerEntry>
98270
+ peerSnapshots = /* @__PURE__ */ new Map();
97216
98271
  pendingRequests = /* @__PURE__ */ new Map();
97217
98272
  commandCallback;
97218
98273
  p2pFailure(message, command, targetDaemonId) {
97219
98274
  return new P2pRelayFailureError(message, { command, targetDaemonId });
97220
98275
  }
98276
+ logMeshCommandEvent(event, fields) {
98277
+ try {
98278
+ LOG.info("Mesh", `[MeshCommand] ${JSON.stringify({ event, ...fields })}`);
98279
+ } catch {
98280
+ LOG.info("Mesh", `[MeshCommand] ${event}`);
98281
+ }
98282
+ }
98283
+ updatePeerSnapshot(targetDaemonId, state, patch = {}) {
98284
+ const previous = this.peerSnapshots.get(targetDaemonId);
98285
+ const now = (/* @__PURE__ */ new Date()).toISOString();
98286
+ this.peerSnapshots.set(targetDaemonId, {
98287
+ perspective: "selected_coordinator",
98288
+ source: "mesh_peer_status",
98289
+ reported: true,
98290
+ state,
98291
+ transport: patch.transport ?? previous?.transport ?? "unknown",
98292
+ reason: patch.reason ?? previous?.reason,
98293
+ lastStateChangeAt: now,
98294
+ lastConnectedAt: patch.lastConnectedAt ?? previous?.lastConnectedAt,
98295
+ lastCommandAt: patch.lastCommandAt ?? previous?.lastCommandAt
98296
+ });
98297
+ }
98298
+ getPeerConnectionStatus(targetDaemonId) {
98299
+ return this.peerSnapshots.get(targetDaemonId) ?? null;
98300
+ }
97221
98301
  invalidatePeer(targetDaemonId, reason, options = {}) {
97222
98302
  const peer = this.peers.get(targetDaemonId);
97223
98303
  if (peer?.commandQueue) {
@@ -97232,6 +98312,11 @@ var init_daemon_mesh_manager = __esm({
97232
98312
  pending.reject(this.p2pFailure(reason, pending.command, targetDaemonId));
97233
98313
  }
97234
98314
  }
98315
+ const snapshotState = peer?.state === "closed" ? "closed" : peer?.state === "disconnected" ? "disconnected" : "failed";
98316
+ this.updatePeerSnapshot(targetDaemonId, snapshotState, {
98317
+ reason,
98318
+ transport: peer?.isRelay === true ? "relay" : peer?.isRelay === false ? "direct" : "unknown"
98319
+ });
97235
98320
  if (options.closeResources !== false && peer) {
97236
98321
  try {
97237
98322
  peer.dataChannel?.close?.();
@@ -97264,6 +98349,7 @@ var init_daemon_mesh_manager = __esm({
97264
98349
  "send_chat",
97265
98350
  "read_chat",
97266
98351
  "get_chat_debug_bundle",
98352
+ "get_pending_mesh_events",
97267
98353
  "git_status",
97268
98354
  "git_diff_summary",
97269
98355
  "launch_cli",
@@ -97324,6 +98410,13 @@ var init_daemon_mesh_manager = __esm({
97324
98410
  const peer = this.peers.get(targetDaemonId);
97325
98411
  if (!peer || peer.state !== "connected" || !peer.dataChannel?.isOpen()) {
97326
98412
  LOG.warn("Mesh", `[Mesh] Cannot send result for ${requestId}, P2P not open with ${targetDaemonId.slice(0, 12)}`);
98413
+ this.logMeshCommandEvent("response_send_failed", {
98414
+ requestId,
98415
+ targetDaemonId,
98416
+ sentAt: (/* @__PURE__ */ new Date()).toISOString(),
98417
+ peerState: peer?.state ?? "missing",
98418
+ error: "P2P not open"
98419
+ });
97327
98420
  return;
97328
98421
  }
97329
98422
  try {
@@ -97334,8 +98427,22 @@ var init_daemon_mesh_manager = __esm({
97334
98427
  result,
97335
98428
  error: error48
97336
98429
  }));
98430
+ this.logMeshCommandEvent("response_sent", {
98431
+ requestId,
98432
+ targetDaemonId,
98433
+ sentAt: (/* @__PURE__ */ new Date()).toISOString(),
98434
+ peerState: peer.state,
98435
+ success: !error48
98436
+ });
97337
98437
  } catch (err) {
97338
98438
  LOG.warn("Mesh", `[Mesh] Failed to send command result: ${err.message}`);
98439
+ this.logMeshCommandEvent("response_send_failed", {
98440
+ requestId,
98441
+ targetDaemonId,
98442
+ sentAt: (/* @__PURE__ */ new Date()).toISOString(),
98443
+ peerState: peer.state,
98444
+ error: err?.message || "Failed to send command result"
98445
+ });
97339
98446
  }
97340
98447
  }
97341
98448
  /** Convenience: send a one-off mesh command without a rule. */
@@ -97351,11 +98458,37 @@ var init_daemon_mesh_manager = __esm({
97351
98458
  if (!peer) {
97352
98459
  throw this.p2pFailure("Failed to initiate P2P connection entry", command, targetDaemonId);
97353
98460
  }
98461
+ const lastCommandAt = (/* @__PURE__ */ new Date()).toISOString();
98462
+ if (peer.state === "connected") {
98463
+ this.updatePeerSnapshot(targetDaemonId, "connected", {
98464
+ transport: peer.isRelay === true ? "relay" : peer.isRelay === false ? "direct" : "unknown",
98465
+ lastConnectedAt: this.peerSnapshots.get(targetDaemonId)?.lastConnectedAt,
98466
+ lastCommandAt
98467
+ });
98468
+ } else {
98469
+ this.updatePeerSnapshot(targetDaemonId, "connecting", {
98470
+ transport: peer.isRelay === true ? "relay" : peer.isRelay === false ? "direct" : "unknown",
98471
+ reason: "Waiting for mesh DataChannel to open.",
98472
+ lastCommandAt
98473
+ });
98474
+ }
97354
98475
  return new Promise((resolve23, reject) => {
97355
98476
  const requestId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
98477
+ const queuedAt = (/* @__PURE__ */ new Date()).toISOString();
97356
98478
  const timer = setTimeout(() => {
98479
+ const pending = this.pendingRequests.get(requestId);
97357
98480
  this.pendingRequests.delete(requestId);
98481
+ const timedOutAt = (/* @__PURE__ */ new Date()).toISOString();
97358
98482
  const message = `P2P DataChannel command '${command}' to ${targetDaemonId.slice(0, 12)} timed out after 30s`;
98483
+ this.logMeshCommandEvent("timeout", {
98484
+ requestId,
98485
+ command,
98486
+ targetDaemonId,
98487
+ queuedAt: pending?.queuedAt ?? queuedAt,
98488
+ sentAt: pending?.sentAt,
98489
+ timedOutAt,
98490
+ peerState: peer?.state
98491
+ });
97359
98492
  this.invalidatePeer(targetDaemonId, message, { rejectPending: true, excludeRequestId: requestId });
97360
98493
  reject(this.p2pFailure(message, command, targetDaemonId));
97361
98494
  }, 3e4);
@@ -97372,7 +98505,8 @@ var init_daemon_mesh_manager = __esm({
97372
98505
  },
97373
98506
  timer,
97374
98507
  targetDaemonId,
97375
- command
98508
+ command,
98509
+ queuedAt
97376
98510
  });
97377
98511
  const payload = {
97378
98512
  type: "mesh_command",
@@ -97381,18 +98515,47 @@ var init_daemon_mesh_manager = __esm({
97381
98515
  args
97382
98516
  };
97383
98517
  if (peer.state === "connected" && peer.dataChannel?.isOpen()) {
98518
+ const sentAt = (/* @__PURE__ */ new Date()).toISOString();
98519
+ const pending = this.pendingRequests.get(requestId);
98520
+ if (pending) pending.sentAt = sentAt;
97384
98521
  LOG.info("Mesh", `[Mesh] Sending '${command}' via P2P DataChannel to ${targetDaemonId.slice(0, 12)}`);
98522
+ this.logMeshCommandEvent("sent", {
98523
+ requestId,
98524
+ command,
98525
+ targetDaemonId,
98526
+ queuedAt,
98527
+ sentAt,
98528
+ peerState: peer.state,
98529
+ transport: peer.isRelay === true ? "relay" : peer.isRelay === false ? "direct" : "unknown"
98530
+ });
97385
98531
  try {
97386
98532
  peer.dataChannel.sendMessage(JSON.stringify(payload));
97387
98533
  } catch (err) {
97388
98534
  const req = this.pendingRequests.get(requestId);
97389
98535
  const message = err?.message || "P2P DataChannel send failed";
98536
+ this.logMeshCommandEvent("send_failed", {
98537
+ requestId,
98538
+ command,
98539
+ targetDaemonId,
98540
+ queuedAt,
98541
+ sentAt,
98542
+ failedAt: (/* @__PURE__ */ new Date()).toISOString(),
98543
+ error: message
98544
+ });
97390
98545
  this.invalidatePeer(targetDaemonId, message, { rejectPending: true, excludeRequestId: requestId });
97391
98546
  if (req) req.reject(this.p2pFailure(message, command, targetDaemonId));
97392
98547
  }
97393
98548
  return;
97394
98549
  }
97395
98550
  LOG.info("Mesh", `[Mesh] Queuing '${command}' for ${targetDaemonId.slice(0, 12)} (state: ${peer.state})`);
98551
+ this.logMeshCommandEvent("queued", {
98552
+ requestId,
98553
+ command,
98554
+ targetDaemonId,
98555
+ queuedAt,
98556
+ peerState: peer.state,
98557
+ transport: peer.isRelay === true ? "relay" : peer.isRelay === false ? "direct" : "unknown"
98558
+ });
97396
98559
  if (!peer.commandQueue) {
97397
98560
  peer.commandQueue = [];
97398
98561
  }
@@ -97400,6 +98563,7 @@ var init_daemon_mesh_manager = __esm({
97400
98563
  command,
97401
98564
  args,
97402
98565
  requestId,
98566
+ queuedAt,
97403
98567
  reject: (err) => {
97404
98568
  const req = this.pendingRequests.get(requestId);
97405
98569
  if (req) req.reject(err);
@@ -97438,7 +98602,18 @@ var init_daemon_mesh_manager = __esm({
97438
98602
  peer.pendingCandidates = [];
97439
98603
  }
97440
98604
  } catch (err) {
97441
- LOG.warn("Mesh", `[Mesh] Failed to set remote desc: ${err.message}`);
98605
+ const errorMessage = err?.message || "Failed to set remote description";
98606
+ const isDuplicateStableAnswer = type2 === "mesh_p2p_answer" && /Unexpected remote answer description in signaling state stable/i.test(errorMessage);
98607
+ this.logMeshCommandEvent(isDuplicateStableAnswer ? "remote_desc_duplicate_ignored" : "remote_desc_failed", {
98608
+ targetDaemonId,
98609
+ signalType: type2,
98610
+ receivedAt: (/* @__PURE__ */ new Date()).toISOString(),
98611
+ peerState: peer.state,
98612
+ error: errorMessage
98613
+ });
98614
+ if (isDuplicateStableAnswer) return;
98615
+ LOG.warn("Mesh", `[Mesh] Failed to set remote desc for ${type2} from ${targetDaemonId.slice(0, 12)}: ${errorMessage}`);
98616
+ this.invalidatePeer(targetDaemonId, `P2P remote description failed: ${errorMessage}`, { rejectPending: true, closeResources: true });
97442
98617
  }
97443
98618
  } else if (type2 === "mesh_p2p_ice") {
97444
98619
  try {
@@ -97494,6 +98669,9 @@ var init_daemon_mesh_manager = __esm({
97494
98669
  remoteDescriptionSet: false
97495
98670
  };
97496
98671
  this.peers.set(targetDaemonId, entry);
98672
+ this.updatePeerSnapshot(targetDaemonId, "connecting", {
98673
+ reason: isInitiator ? "P2P mesh connection initiated by the selected coordinator." : "Waiting for the remote daemon to finish the mesh DataChannel handshake."
98674
+ });
97497
98675
  pc.onLocalDescription((sdp, type2) => {
97498
98676
  this.serverConn.sendMeshCommand(targetDaemonId, type2 === "offer" ? "mesh_p2p_offer" : "mesh_p2p_answer", { sdp, type: type2 });
97499
98677
  });
@@ -97504,7 +98682,26 @@ var init_daemon_mesh_manager = __esm({
97504
98682
  LOG.info("Mesh", `[Mesh] P2P state with ${targetDaemonId.slice(0, 12)}: ${state}`);
97505
98683
  if (state === "connected") {
97506
98684
  entry.state = "connected";
98685
+ let transport = "unknown";
98686
+ try {
98687
+ const pair = pc.getSelectedCandidatePair?.();
98688
+ if (pair) {
98689
+ const localType = pair.local?.type || "unknown";
98690
+ const remoteType = pair.remote?.type || "unknown";
98691
+ entry.isRelay = localType === "relay" || remoteType === "relay";
98692
+ transport = entry.isRelay ? "relay" : "direct";
98693
+ LOG.info("Mesh", `[Mesh] Candidate pair with ${targetDaemonId.slice(0, 12)}: local=${localType} remote=${remoteType} \u2192 ${transport}`);
98694
+ }
98695
+ } catch {
98696
+ transport = entry.isRelay === true ? "relay" : entry.isRelay === false ? "direct" : "unknown";
98697
+ }
98698
+ this.updatePeerSnapshot(targetDaemonId, "connected", {
98699
+ transport,
98700
+ reason: transport === "relay" ? "Connected over TURN relay." : transport === "direct" ? "Connected directly peer-to-peer." : "Connected, but selected candidate pair details are unavailable.",
98701
+ lastConnectedAt: (/* @__PURE__ */ new Date()).toISOString()
98702
+ });
97507
98703
  } else if (state === "failed" || state === "closed" || state === "disconnected") {
98704
+ entry.state = state;
97508
98705
  this.invalidatePeer(targetDaemonId, `P2P state changed to ${state}`, { rejectPending: true, closeResources: false });
97509
98706
  }
97510
98707
  });
@@ -97522,12 +98719,30 @@ var init_daemon_mesh_manager = __esm({
97522
98719
  dc.onOpen(() => {
97523
98720
  LOG.info("Mesh", `[Mesh] DataChannel OPEN with ${targetDaemonId.slice(0, 12)}`);
97524
98721
  entry.state = "connected";
98722
+ this.updatePeerSnapshot(targetDaemonId, "connected", {
98723
+ transport: entry.isRelay === true ? "relay" : entry.isRelay === false ? "direct" : "unknown",
98724
+ reason: entry.isRelay === true ? "Connected over TURN relay." : entry.isRelay === false ? "Connected directly peer-to-peer." : "DataChannel open; transport details not reported yet.",
98725
+ lastConnectedAt: (/* @__PURE__ */ new Date()).toISOString()
98726
+ });
97525
98727
  if (entry.commandQueue) {
97526
98728
  const queue = entry.commandQueue;
97527
98729
  entry.commandQueue = [];
97528
98730
  for (const item of queue) {
97529
98731
  try {
98732
+ const sentAt = (/* @__PURE__ */ new Date()).toISOString();
98733
+ const pending = this.pendingRequests.get(item.requestId);
98734
+ if (pending) pending.sentAt = sentAt;
97530
98735
  LOG.info("Mesh", `[Mesh] Flushing queued '${item.command}' to ${targetDaemonId.slice(0, 12)}`);
98736
+ this.logMeshCommandEvent("sent", {
98737
+ requestId: item.requestId,
98738
+ command: item.command,
98739
+ targetDaemonId,
98740
+ queuedAt: item.queuedAt,
98741
+ sentAt,
98742
+ peerState: entry.state,
98743
+ transport: entry.isRelay === true ? "relay" : entry.isRelay === false ? "direct" : "unknown",
98744
+ flushed: true
98745
+ });
97531
98746
  dc.sendMessage(JSON.stringify({
97532
98747
  type: "mesh_command",
97533
98748
  requestId: item.requestId,
@@ -97535,6 +98750,14 @@ var init_daemon_mesh_manager = __esm({
97535
98750
  args: item.args
97536
98751
  }));
97537
98752
  } catch (err) {
98753
+ this.logMeshCommandEvent("send_failed", {
98754
+ requestId: item.requestId,
98755
+ command: item.command,
98756
+ targetDaemonId,
98757
+ queuedAt: item.queuedAt,
98758
+ failedAt: (/* @__PURE__ */ new Date()).toISOString(),
98759
+ error: err?.message || "P2P DataChannel send failed while flushing command queue"
98760
+ });
97538
98761
  item.reject(this.p2pFailure(err?.message || "P2P DataChannel send failed while flushing command queue", item.command, targetDaemonId));
97539
98762
  }
97540
98763
  }
@@ -97549,6 +98772,12 @@ var init_daemon_mesh_manager = __esm({
97549
98772
  const str2 = typeof msg === "string" ? msg : msg.toString("utf8");
97550
98773
  const data = JSON.parse(str2);
97551
98774
  if (data.type === "mesh_command" && data.command) {
98775
+ this.logMeshCommandEvent("incoming", {
98776
+ requestId: data.requestId,
98777
+ command: data.command,
98778
+ senderDaemonId: targetDaemonId,
98779
+ receivedAt: (/* @__PURE__ */ new Date()).toISOString()
98780
+ });
97552
98781
  if (this.commandCallback) {
97553
98782
  this.commandCallback(targetDaemonId, data.command, data.args, data.requestId).catch((e) => {
97554
98783
  LOG.warn("Mesh", `[Mesh] Error handling incoming P2P command: ${e.message}`);
@@ -97557,11 +98786,27 @@ var init_daemon_mesh_manager = __esm({
97557
98786
  } else if (data.type === "mesh_command_result" && data.requestId) {
97558
98787
  const pending = this.pendingRequests.get(data.requestId);
97559
98788
  if (pending) {
98789
+ this.logMeshCommandEvent("response_received", {
98790
+ requestId: data.requestId,
98791
+ command: pending.command,
98792
+ targetDaemonId: pending.targetDaemonId,
98793
+ queuedAt: pending.queuedAt,
98794
+ sentAt: pending.sentAt,
98795
+ receivedAt: (/* @__PURE__ */ new Date()).toISOString(),
98796
+ success: data.success === true
98797
+ });
97560
98798
  if (data.success) {
97561
98799
  pending.resolve(data.result);
97562
98800
  } else {
97563
98801
  pending.reject(new Error(data.error || "P2P Command failed"));
97564
98802
  }
98803
+ } else {
98804
+ this.logMeshCommandEvent("response_orphan", {
98805
+ requestId: data.requestId,
98806
+ targetDaemonId,
98807
+ receivedAt: (/* @__PURE__ */ new Date()).toISOString(),
98808
+ success: data.success === true
98809
+ });
97565
98810
  }
97566
98811
  }
97567
98812
  } catch (e) {
@@ -97801,6 +99046,7 @@ var init_adhdev_daemon = __esm({
97801
99046
  "use strict";
97802
99047
  init_server_connection();
97803
99048
  init_src();
99049
+ init_mesh_events();
97804
99050
  init_daemon_p2p2();
97805
99051
  init_screenshot_controller();
97806
99052
  init_session_host();
@@ -97817,7 +99063,7 @@ var init_adhdev_daemon = __esm({
97817
99063
  init_version();
97818
99064
  init_src();
97819
99065
  init_runtime_defaults();
97820
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.82-rc.4" });
99066
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.82-rc.40" });
97821
99067
  AdhdevDaemon = class _AdhdevDaemon {
97822
99068
  localHttpServer = null;
97823
99069
  localWss = null;
@@ -98341,6 +99587,7 @@ ${err?.stack || ""}`);
98341
99587
  if (!this.meshManager) throw new Error("Mesh manager not initialized");
98342
99588
  return this.meshManager.sendCommand(daemonId, command, args);
98343
99589
  },
99590
+ getMeshPeerConnectionStatus: (daemonId) => this.meshManager?.getPeerConnectionStatus(daemonId) ?? null,
98344
99591
  onStatusChange: () => {
98345
99592
  this.invalidateHotChatSnapshotCache();
98346
99593
  this.statusReporter?.onStatusChange();
@@ -98710,6 +99957,7 @@ ${err?.stack || ""}`);
98710
99957
  const meshId = this.readMeshString(settings.meshNodeFor);
98711
99958
  const coordinatorDaemonId = this.readMeshString(settings.meshCoordinatorDaemonId);
98712
99959
  if (!meshId || !coordinatorDaemonId) return;
99960
+ const relayTimestamp = typeof event.timestamp === "number" && Number.isFinite(event.timestamp) ? event.timestamp : this.readMeshString(event.timestamp) || void 0;
98713
99961
  const payload = {
98714
99962
  event: this.readMeshString(event.event),
98715
99963
  meshId,
@@ -98718,7 +99966,8 @@ ${err?.stack || ""}`);
98718
99966
  targetSessionId: this.readMeshString(event.targetSessionId) || instanceId,
98719
99967
  providerType: this.readMeshString(event.providerType),
98720
99968
  providerSessionId: this.readMeshString(event.providerSessionId),
98721
- finalSummary: this.readMeshString(event.finalSummary) || this.readMeshString(event.summary)
99969
+ finalSummary: this.readMeshString(event.finalSummary) || this.readMeshString(event.summary),
99970
+ ...relayTimestamp !== void 0 ? { timestamp: relayTimestamp } : {}
98722
99971
  };
98723
99972
  if (coordinatorDaemonId === localDaemonId) {
98724
99973
  try {
@@ -98733,6 +99982,22 @@ ${err?.stack || ""}`);
98733
99982
  await this.meshManager.sendCommand(coordinatorDaemonId, "mesh_forward_event", payload);
98734
99983
  LOG.info("MeshEvents", `Relayed ${payload.event} for mesh ${meshId} to coordinator daemon ${coordinatorDaemonId.slice(0, 12)}\u2026`);
98735
99984
  } catch (error48) {
99985
+ queuePendingMeshCoordinatorEvent({
99986
+ event: payload.event,
99987
+ meshId,
99988
+ nodeLabel: payload.nodeId ? `Node '${payload.nodeId}'` : payload.workspace ? `Agent at ${payload.workspace}` : "Remote agent",
99989
+ nodeId: payload.nodeId || void 0,
99990
+ workspace: payload.workspace || void 0,
99991
+ metadataEvent: {
99992
+ targetSessionId: payload.targetSessionId,
99993
+ providerType: payload.providerType,
99994
+ providerSessionId: payload.providerSessionId,
99995
+ finalSummary: payload.finalSummary,
99996
+ workspace: payload.workspace,
99997
+ ...payload.timestamp !== void 0 ? { timestamp: payload.timestamp } : {}
99998
+ },
99999
+ queuedAt: Date.now()
100000
+ });
98736
100001
  LOG.warn("MeshEvents", `Failed to relay ${payload.event} for mesh ${meshId}: ${error48?.message || error48}`);
98737
100002
  }
98738
100003
  }
@@ -99639,6 +100904,420 @@ var init_runtime_target_trace = __esm({
99639
100904
  }
99640
100905
  });
99641
100906
 
100907
+ // src/cli/service-commands.ts
100908
+ var service_commands_exports = {};
100909
+ __export(service_commands_exports, {
100910
+ buildPlist: () => buildPlist,
100911
+ installAutoStartServiceForCurrentProcess: () => installAutoStartServiceForCurrentProcess,
100912
+ isAutoStartServiceInstalled: () => isAutoStartServiceInstalled,
100913
+ registerServiceCommands: () => registerServiceCommands
100914
+ });
100915
+ function getDarwinPlistPath() {
100916
+ return import_node_path5.default.join(import_node_os4.default.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
100917
+ }
100918
+ function getWindowsStartupDir() {
100919
+ const appData = process.env.APPDATA || import_node_path5.default.join(import_node_os4.default.homedir(), "AppData", "Roaming");
100920
+ return import_node_path5.default.join(appData, "Microsoft", "Windows", "Start Menu", "Programs", "Startup");
100921
+ }
100922
+ function getWindowsVbsPath() {
100923
+ return import_node_path5.default.join(getWindowsStartupDir(), "adhdev-daemon.vbs");
100924
+ }
100925
+ function resolveCliPath() {
100926
+ return import_node_fs4.default.realpathSync(process.argv[1]);
100927
+ }
100928
+ function ensureDir(dir) {
100929
+ if (!import_node_fs4.default.existsSync(dir)) import_node_fs4.default.mkdirSync(dir, { recursive: true });
100930
+ }
100931
+ async function fetchHealth() {
100932
+ const controller = new AbortController();
100933
+ const timer = setTimeout(() => controller.abort(), DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS2);
100934
+ try {
100935
+ const res = await fetch(`http://127.0.0.1:${DEFAULT_DAEMON_PORT}/health`, { signal: controller.signal });
100936
+ if (!res.ok) return null;
100937
+ return await res.json();
100938
+ } catch {
100939
+ return null;
100940
+ } finally {
100941
+ clearTimeout(timer);
100942
+ }
100943
+ }
100944
+ function getProcessInfo(pid) {
100945
+ try {
100946
+ if (process.platform === "win32") {
100947
+ const out = (0, import_node_child_process4.execSync)(`tasklist /FI "PID eq ${pid}" /FO CSV /NH`, { encoding: "utf-8" });
100948
+ const match = out.match(/"(\d[\d,]+)\sK"/);
100949
+ const memKB = match ? parseInt(match[1].replace(/,/g, ""), 10) : 0;
100950
+ return { uptime: "-", memMB: Math.round(memKB / 1024) };
100951
+ } else {
100952
+ const out = (0, import_node_child_process4.execSync)(`ps -o etime=,rss= -p ${pid}`, { encoding: "utf-8" }).trim();
100953
+ const parts = out.split(/\s+/);
100954
+ const etime = parts[0] || "-";
100955
+ const rssKB = parseInt(parts[1] || "0", 10);
100956
+ return { uptime: formatElapsed(etime), memMB: Math.round(rssKB / 1024) };
100957
+ }
100958
+ } catch {
100959
+ return null;
100960
+ }
100961
+ }
100962
+ function formatElapsed(etime) {
100963
+ const parts = etime.replace("-", ":").split(":").map(Number);
100964
+ if (parts.length === 4) return `${parts[0]}d ${parts[1]}h ${parts[2]}m`;
100965
+ if (parts.length === 3) return `${parts[0]}h ${parts[1]}m`;
100966
+ if (parts.length === 2) return `${parts[0]}m ${parts[1]}s`;
100967
+ return etime;
100968
+ }
100969
+ function rotateLogIfNeeded(logPath) {
100970
+ try {
100971
+ if (!import_node_fs4.default.existsSync(logPath)) return;
100972
+ const stat5 = import_node_fs4.default.statSync(logPath);
100973
+ if (stat5.size > MAX_LOG_SIZE2) {
100974
+ const rotated = logPath + ".old";
100975
+ if (import_node_fs4.default.existsSync(rotated)) import_node_fs4.default.unlinkSync(rotated);
100976
+ import_node_fs4.default.renameSync(logPath, rotated);
100977
+ import_node_fs4.default.writeFileSync(logPath, `[log rotated at ${(/* @__PURE__ */ new Date()).toISOString()}]
100978
+ `, "utf-8");
100979
+ }
100980
+ } catch {
100981
+ }
100982
+ }
100983
+ function rotateLogs() {
100984
+ rotateLogIfNeeded(LOG_OUT);
100985
+ rotateLogIfNeeded(LOG_ERR);
100986
+ }
100987
+ function normalizeLaunchdPathEntry(entry) {
100988
+ const trimmed = String(entry || "").trim();
100989
+ if (!trimmed) return null;
100990
+ if (trimmed.startsWith("~")) {
100991
+ return import_node_path5.default.join(import_node_os4.default.homedir(), trimmed.slice(1));
100992
+ }
100993
+ return import_node_path5.default.isAbsolute(trimmed) ? trimmed : null;
100994
+ }
100995
+ function buildLaunchdPath(nodeExe, currentPath = process.env.PATH || "") {
100996
+ const brewPrefix = import_node_fs4.default.existsSync("/opt/homebrew/bin") ? "/opt/homebrew/bin" : "/usr/local/bin";
100997
+ const entries = [];
100998
+ const seen = /* @__PURE__ */ new Set();
100999
+ const addEntry = (value) => {
101000
+ if (!value) return;
101001
+ const normalized = normalizeLaunchdPathEntry(value);
101002
+ if (!normalized || seen.has(normalized)) return;
101003
+ seen.add(normalized);
101004
+ entries.push(normalized);
101005
+ };
101006
+ addEntry(import_node_path5.default.dirname(nodeExe));
101007
+ for (const entry of String(currentPath || "").split(import_node_path5.default.delimiter)) {
101008
+ addEntry(entry);
101009
+ }
101010
+ for (const entry of [brewPrefix, "/usr/local/bin", "/usr/bin", "/bin", "/usr/sbin", "/sbin"]) {
101011
+ addEntry(entry);
101012
+ }
101013
+ return entries.join(":");
101014
+ }
101015
+ function buildPlist(nodeExe, cliExe, currentPath = process.env.PATH || "") {
101016
+ const pathValue = buildLaunchdPath(nodeExe, currentPath);
101017
+ return `<?xml version="1.0" encoding="UTF-8"?>
101018
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
101019
+ <plist version="1.0">
101020
+ <dict>
101021
+ <key>Label</key>
101022
+ <string>${LAUNCHD_LABEL}</string>
101023
+ <key>ProgramArguments</key>
101024
+ <array>
101025
+ <string>${nodeExe}</string>
101026
+ <string>${cliExe}</string>
101027
+ <string>daemon</string>
101028
+ </array>
101029
+ <key>RunAtLoad</key>
101030
+ <true/>
101031
+ <key>KeepAlive</key>
101032
+ <dict>
101033
+ <key>SuccessfulExit</key>
101034
+ <false/>
101035
+ </dict>
101036
+ <key>ThrottleInterval</key>
101037
+ <integer>30</integer>
101038
+ <key>StandardOutPath</key>
101039
+ <string>${LOG_OUT}</string>
101040
+ <key>StandardErrorPath</key>
101041
+ <string>${LOG_ERR}</string>
101042
+ <key>EnvironmentVariables</key>
101043
+ <dict>
101044
+ <key>PATH</key>
101045
+ <string>${pathValue}</string>
101046
+ </dict>
101047
+ </dict>
101048
+ </plist>`;
101049
+ }
101050
+ function installDarwin(nodeExe, cliExe) {
101051
+ const plistPath = getDarwinPlistPath();
101052
+ ensureDir(ADHDEV_DIR);
101053
+ ensureDir(import_node_path5.default.dirname(plistPath));
101054
+ import_node_fs4.default.writeFileSync(plistPath, buildPlist(nodeExe, cliExe), "utf-8");
101055
+ console.log(source_default.gray(` Plist: ${plistPath}`));
101056
+ try {
101057
+ (0, import_node_child_process4.execSync)(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "ignore" });
101058
+ } catch {
101059
+ }
101060
+ try {
101061
+ (0, import_node_child_process4.execSync)(`launchctl load -w "${plistPath}"`, { stdio: "ignore" });
101062
+ console.log(source_default.green("\n \u2713 Registered as LaunchAgent \u2014 daemon will start on login."));
101063
+ console.log(source_default.gray(` Logs: ~/.adhdev/daemon-launchd.{out,err}`));
101064
+ } catch (e) {
101065
+ console.log(source_default.red(`
101066
+ \u2717 launchctl load failed: ${e.message}`));
101067
+ }
101068
+ }
101069
+ function uninstallDarwin() {
101070
+ const plistPath = getDarwinPlistPath();
101071
+ if (!import_node_fs4.default.existsSync(plistPath)) {
101072
+ console.log(source_default.yellow("\n \u26A0 Service is not installed."));
101073
+ return;
101074
+ }
101075
+ try {
101076
+ (0, import_node_child_process4.execSync)(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "ignore" });
101077
+ } catch {
101078
+ }
101079
+ import_node_fs4.default.unlinkSync(plistPath);
101080
+ console.log(source_default.green("\n \u2713 Removed LaunchAgent. Daemon will no longer auto-start."));
101081
+ }
101082
+ function isInstalledDarwin() {
101083
+ return import_node_fs4.default.existsSync(getDarwinPlistPath());
101084
+ }
101085
+ function buildVbs(nodeExe, cliExe) {
101086
+ const logFile = import_node_path5.default.join(ADHDEV_DIR, "daemon-service.log").replace(/\\/g, "\\\\");
101087
+ const escapedNodeExe = nodeExe.replace(/\\/g, "\\\\");
101088
+ const escapedCliExe = cliExe.replace(/\\/g, "\\\\");
101089
+ return `' ADHDev Daemon Auto-Start (generated by adhdev service install)
101090
+ Set WshShell = CreateObject("WScript.Shell")
101091
+ WshShell.Run "cmd.exe /c """"${escapedNodeExe}"""" """"${escapedCliExe}"""" daemon >> """"${logFile}"""" 2>&1", 0, False
101092
+ `;
101093
+ }
101094
+ function installWindows(nodeExe, cliExe) {
101095
+ const vbsPath = getWindowsVbsPath();
101096
+ ensureDir(ADHDEV_DIR);
101097
+ ensureDir(import_node_path5.default.dirname(vbsPath));
101098
+ import_node_fs4.default.writeFileSync(vbsPath, buildVbs(nodeExe, cliExe), "utf-8");
101099
+ console.log(source_default.gray(` Startup script: ${vbsPath}`));
101100
+ console.log(source_default.green("\n \u2713 Registered in Startup folder \u2014 daemon will start on login (hidden)."));
101101
+ console.log(source_default.gray(` Logs: ${import_node_path5.default.join(ADHDEV_DIR, "daemon-service.log")}`));
101102
+ console.log(source_default.gray(" To start now without rebooting, run: adhdev daemon"));
101103
+ }
101104
+ function uninstallWindows() {
101105
+ const vbsPath = getWindowsVbsPath();
101106
+ if (!import_node_fs4.default.existsSync(vbsPath)) {
101107
+ console.log(source_default.yellow("\n \u26A0 Service is not installed."));
101108
+ return;
101109
+ }
101110
+ import_node_fs4.default.unlinkSync(vbsPath);
101111
+ console.log(source_default.green("\n \u2713 Removed Startup script. Daemon will no longer auto-start."));
101112
+ console.log(source_default.gray(" Note: a currently running daemon is not affected. Stop with: adhdev daemon:stop"));
101113
+ }
101114
+ function isInstalledWindows() {
101115
+ return import_node_fs4.default.existsSync(getWindowsVbsPath());
101116
+ }
101117
+ function isAutoStartServiceInstalled(platform12 = import_node_os4.default.platform()) {
101118
+ if (platform12 === "darwin") return isInstalledDarwin();
101119
+ if (platform12 === "win32") return isInstalledWindows();
101120
+ return false;
101121
+ }
101122
+ function installAutoStartServiceForCurrentProcess(platform12 = import_node_os4.default.platform()) {
101123
+ const nodeExe = process.execPath;
101124
+ const cliExe = resolveCliPath();
101125
+ if (platform12 === "darwin") {
101126
+ installDarwin(nodeExe, cliExe);
101127
+ return true;
101128
+ }
101129
+ if (platform12 === "win32") {
101130
+ installWindows(nodeExe, cliExe);
101131
+ return true;
101132
+ }
101133
+ return false;
101134
+ }
101135
+ function registerServiceCommands(program2) {
101136
+ const svc = program2.command("service").description("\u{1F50C} Manage ADHDev as an OS background auto-start service");
101137
+ svc.command("install").description("Register ADHDev daemon to start automatically on login").action(async () => {
101138
+ console.log(source_default.bold("\n \u{1F680} Installing ADHDev Background Service"));
101139
+ const platform12 = import_node_os4.default.platform();
101140
+ const nodeExe = process.execPath;
101141
+ const cliExe = resolveCliPath();
101142
+ console.log(source_default.gray(` Node: ${nodeExe}`));
101143
+ console.log(source_default.gray(` CLI: ${cliExe}`));
101144
+ console.log(source_default.gray(` Platform: ${platform12}`));
101145
+ if (!installAutoStartServiceForCurrentProcess(platform12)) {
101146
+ console.log(source_default.yellow("\n \u26A0 Auto-start service install is not supported on this platform."));
101147
+ console.log(source_default.gray(" On Linux, create a systemd user unit manually:"));
101148
+ console.log(source_default.gray(" ~/.config/systemd/user/adhdev-daemon.service"));
101149
+ }
101150
+ console.log();
101151
+ });
101152
+ svc.command("uninstall").description("Remove the OS background service").action(async () => {
101153
+ console.log(source_default.bold("\n \u{1F5D1}\uFE0F Removing ADHDev Background Service"));
101154
+ const platform12 = import_node_os4.default.platform();
101155
+ if (platform12 === "darwin") {
101156
+ uninstallDarwin();
101157
+ } else if (platform12 === "win32") {
101158
+ uninstallWindows();
101159
+ } else {
101160
+ console.log(source_default.yellow("\n \u26A0 Not supported on this platform."));
101161
+ }
101162
+ console.log();
101163
+ });
101164
+ svc.command("status").description("Show service installation state and live daemon health").action(async () => {
101165
+ const platform12 = import_node_os4.default.platform();
101166
+ const installed = isAutoStartServiceInstalled(platform12);
101167
+ if (installed) {
101168
+ console.log(source_default.green("\n \u2713 Service is installed."));
101169
+ if (platform12 === "darwin") console.log(source_default.gray(` Plist: ${getDarwinPlistPath()}`));
101170
+ else console.log(source_default.gray(` Script: ${getWindowsVbsPath()}`));
101171
+ } else {
101172
+ console.log(source_default.gray("\n \u2717 Service is not installed. Run: adhdev service install"));
101173
+ }
101174
+ const health = await fetchHealth();
101175
+ if (health?.ok && health.pid) {
101176
+ const info = getProcessInfo(health.pid);
101177
+ if (info) {
101178
+ console.log(source_default.green(` \u2713 Daemon running \u2014 PID ${health.pid}, uptime ${info.uptime}, ${info.memMB} MB`));
101179
+ } else {
101180
+ console.log(source_default.green(` \u2713 Daemon running \u2014 PID ${health.pid}`));
101181
+ }
101182
+ } else {
101183
+ console.log(source_default.yellow(" \u2717 Daemon is not running."));
101184
+ }
101185
+ const outSize = import_node_fs4.default.existsSync(LOG_OUT) ? import_node_fs4.default.statSync(LOG_OUT).size : 0;
101186
+ const errSize = import_node_fs4.default.existsSync(LOG_ERR) ? import_node_fs4.default.statSync(LOG_ERR).size : 0;
101187
+ if (outSize > 0 || errSize > 0) {
101188
+ console.log(source_default.gray(` Logs: stdout ${formatBytes(outSize)}, stderr ${formatBytes(errSize)}`));
101189
+ }
101190
+ console.log();
101191
+ });
101192
+ svc.command("logs").description("View daemon service logs").option("--err", "Show stderr log instead of stdout").option("--clear", "Truncate all log files").option("-n, --lines <count>", "Number of lines to show", "30").action(async (options) => {
101193
+ if (options.clear) {
101194
+ for (const f of [LOG_OUT, LOG_ERR]) {
101195
+ if (import_node_fs4.default.existsSync(f)) import_node_fs4.default.writeFileSync(f, "", "utf-8");
101196
+ }
101197
+ console.log(source_default.green("\n \u2713 Logs cleared.\n"));
101198
+ return;
101199
+ }
101200
+ const logFile = options.err ? LOG_ERR : LOG_OUT;
101201
+ if (!import_node_fs4.default.existsSync(logFile)) {
101202
+ console.log(source_default.gray(`
101203
+ No log file found: ${logFile}
101204
+ `));
101205
+ return;
101206
+ }
101207
+ const lines = parseInt(options.lines, 10) || 30;
101208
+ console.log(source_default.gray(`
101209
+ \u2500\u2500 ${options.err ? "stderr" : "stdout"}: ${logFile} (last ${lines} lines) \u2500\u2500
101210
+ `));
101211
+ const content = import_node_fs4.default.readFileSync(logFile, "utf-8");
101212
+ const allLines = content.split("\n");
101213
+ const lastLines = allLines.slice(-lines).join("\n");
101214
+ if (lastLines.trim()) console.log(lastLines);
101215
+ console.log(source_default.gray("\n (watching for new output, Ctrl+C to stop)\n"));
101216
+ let offset = Buffer.byteLength(content, "utf-8");
101217
+ const watcher = import_node_fs4.default.watchFile(logFile, { interval: 500 }, () => {
101218
+ try {
101219
+ const stat5 = import_node_fs4.default.statSync(logFile);
101220
+ if (stat5.size > offset) {
101221
+ const fd = import_node_fs4.default.openSync(logFile, "r");
101222
+ const buf = Buffer.alloc(stat5.size - offset);
101223
+ import_node_fs4.default.readSync(fd, buf, 0, buf.length, offset);
101224
+ import_node_fs4.default.closeSync(fd);
101225
+ process.stdout.write(buf.toString("utf-8"));
101226
+ offset = stat5.size;
101227
+ } else if (stat5.size < offset) {
101228
+ offset = 0;
101229
+ }
101230
+ } catch {
101231
+ }
101232
+ });
101233
+ const cleanup = () => {
101234
+ import_node_fs4.default.unwatchFile(logFile);
101235
+ process.exit(0);
101236
+ };
101237
+ process.on("SIGINT", cleanup);
101238
+ process.on("SIGTERM", cleanup);
101239
+ });
101240
+ svc.command("restart").description("Restart the daemon process (service will auto-relaunch)").action(async () => {
101241
+ const platform12 = import_node_os4.default.platform();
101242
+ const installed = isAutoStartServiceInstalled(platform12);
101243
+ if (!installed) {
101244
+ console.log(source_default.yellow("\n \u26A0 Service is not installed. Use `adhdev daemon:restart` for manual restart."));
101245
+ console.log(source_default.gray(" Or install the service first: adhdev service install\n"));
101246
+ return;
101247
+ }
101248
+ rotateLogs();
101249
+ if (platform12 === "darwin") {
101250
+ console.log(source_default.cyan("\n Refreshing LaunchAgent definition and reloading service..."));
101251
+ installAutoStartServiceForCurrentProcess(platform12);
101252
+ console.log();
101253
+ return;
101254
+ }
101255
+ const health = await fetchHealth();
101256
+ if (!health?.pid) {
101257
+ console.log(source_default.yellow("\n \u26A0 Daemon is not currently running."));
101258
+ console.log(source_default.gray(" Start with: adhdev daemon\n"));
101259
+ return;
101260
+ }
101261
+ console.log(source_default.cyan(`
101262
+ Stopping daemon (PID ${health.pid})...`));
101263
+ try {
101264
+ process.kill(health.pid, "SIGTERM");
101265
+ } catch {
101266
+ console.log(source_default.yellow(" Could not send SIGTERM. Process may have already exited."));
101267
+ }
101268
+ await new Promise((r) => setTimeout(r, 2e3));
101269
+ if (platform12 === "win32") {
101270
+ const vbsPath = getWindowsVbsPath();
101271
+ if (import_node_fs4.default.existsSync(vbsPath)) {
101272
+ try {
101273
+ (0, import_node_child_process4.execSync)(`wscript.exe "${vbsPath}"`, { stdio: "ignore", windowsHide: true });
101274
+ } catch {
101275
+ }
101276
+ }
101277
+ }
101278
+ let restarted = false;
101279
+ for (let i = 0; i < 8; i++) {
101280
+ await new Promise((r) => setTimeout(r, 1e3));
101281
+ const newHealth = await fetchHealth();
101282
+ if (newHealth?.ok && newHealth.pid !== health.pid) {
101283
+ restarted = true;
101284
+ const info = getProcessInfo(newHealth.pid);
101285
+ console.log(source_default.green(` \u2713 Daemon restarted \u2014 new PID ${newHealth.pid}${info ? `, ${info.memMB} MB` : ""}`));
101286
+ break;
101287
+ }
101288
+ }
101289
+ if (!restarted) {
101290
+ console.log(source_default.yellow(" \u26A0 Daemon did not restart within 10s."));
101291
+ console.log(source_default.gray(" Check: adhdev service logs --err"));
101292
+ }
101293
+ console.log();
101294
+ });
101295
+ }
101296
+ function formatBytes(bytes) {
101297
+ if (bytes === 0) return "0 B";
101298
+ if (bytes < 1024) return `${bytes} B`;
101299
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
101300
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
101301
+ }
101302
+ var import_node_fs4, import_node_path5, import_node_os4, import_node_child_process4, DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS2, LAUNCHD_LABEL, ADHDEV_DIR, LOG_OUT, LOG_ERR, MAX_LOG_SIZE2;
101303
+ var init_service_commands = __esm({
101304
+ "src/cli/service-commands.ts"() {
101305
+ "use strict";
101306
+ import_node_fs4 = __toESM(require("fs"));
101307
+ import_node_path5 = __toESM(require("path"));
101308
+ import_node_os4 = __toESM(require("os"));
101309
+ import_node_child_process4 = require("child_process");
101310
+ init_source();
101311
+ init_src();
101312
+ DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS2 = 1500;
101313
+ LAUNCHD_LABEL = "dev.adhf.daemon";
101314
+ ADHDEV_DIR = import_node_path5.default.join(import_node_os4.default.homedir(), ".adhdev");
101315
+ LOG_OUT = import_node_path5.default.join(ADHDEV_DIR, "daemon-launchd.out");
101316
+ LOG_ERR = import_node_path5.default.join(ADHDEV_DIR, "daemon-launchd.err");
101317
+ MAX_LOG_SIZE2 = 10 * 1024 * 1024;
101318
+ }
101319
+ });
101320
+
99642
101321
  // ../../oss/packages/web-core/src/constants/supported.ts
99643
101322
  var supported_exports = {};
99644
101323
  __export(supported_exports, {
@@ -101612,7 +103291,7 @@ function formatDebugTraceEntryLine(entry) {
101612
103291
  }
101613
103292
 
101614
103293
  // src/cli/daemon-commands.ts
101615
- var DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS2 = 1500;
103294
+ var DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS3 = 1500;
101616
103295
  var DEFAULT_TRACE_FOLLOW_INTERVAL_MS = 1500;
101617
103296
  var DEFAULT_DAEMON_PORT_TEXT = String(DEFAULT_DAEMON_PORT);
101618
103297
  var DEV_SERVER_PORT2 = 19280;
@@ -101640,6 +103319,15 @@ async function resolveConfiguredUpdateChannel() {
101640
103319
  function releaseChannelLabel(channel) {
101641
103320
  return `${channel} (${CHANNEL_NPM_TAG3[channel]})`;
101642
103321
  }
103322
+ function buildDaemonRestartCommandArgv(options = {}) {
103323
+ const cliPath = options.cliPath || process.argv[1];
103324
+ const daemonPort = options.daemonPort || DEFAULT_DAEMON_PORT_TEXT;
103325
+ const platform12 = options.platform || process.platform;
103326
+ if (platform12 === "darwin" && options.serviceInstalled) {
103327
+ return [cliPath, "service", "install"];
103328
+ }
103329
+ return [cliPath, "daemon", "-p", daemonPort];
103330
+ }
101643
103331
  async function persistReleaseChannel(channel) {
101644
103332
  const { updateConfig: updateConfig2 } = await Promise.resolve().then(() => (init_src(), src_exports));
101645
103333
  updateConfig2({ updateChannel: channel, serverUrl: CHANNEL_SERVER_URL3[channel] });
@@ -101650,7 +103338,7 @@ function hideCommand(command) {
101650
103338
  }
101651
103339
  async function fetchLocalDaemonHealth(port) {
101652
103340
  const controller = new AbortController();
101653
- const timer = setTimeout(() => controller.abort(), DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS2);
103341
+ const timer = setTimeout(() => controller.abort(), DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS3);
101654
103342
  try {
101655
103343
  const res = await fetch(`http://127.0.0.1:${port}/health`, { signal: controller.signal });
101656
103344
  if (!res.ok) return null;
@@ -102016,6 +103704,9 @@ async function runDaemonUpgrade(options, pkgVersion3) {
102016
103704
  } else {
102017
103705
  console.log(source_default.cyan(`
102018
103706
  Upgrading v${currentVersion} \u2192 v${latest}...`));
103707
+ const { isAutoStartServiceInstalled: isAutoStartServiceInstalled2 } = await Promise.resolve().then(() => (init_service_commands(), service_commands_exports));
103708
+ const serviceInstalled = isAutoStartServiceInstalled2();
103709
+ const shouldRefreshInstalledService = options.restart !== false && process.platform === "darwin" && serviceInstalled;
102019
103710
  const daemonWasRunning = options.restart !== false && isDaemonRunning2();
102020
103711
  if (daemonWasRunning) {
102021
103712
  stopDaemon2();
@@ -102027,10 +103718,22 @@ async function runDaemonUpgrade(options, pkgVersion3) {
102027
103718
  packageName: "adhdev",
102028
103719
  targetVersion: latest,
102029
103720
  parentPid: process.pid,
102030
- restartArgv: daemonWasRunning ? [process.argv[1], "daemon", "-p", DEFAULT_DAEMON_PORT_TEXT] : [],
103721
+ restartArgv: shouldRefreshInstalledService ? buildDaemonRestartCommandArgv({
103722
+ cliPath: process.argv[1],
103723
+ daemonPort: DEFAULT_DAEMON_PORT_TEXT,
103724
+ platform: process.platform,
103725
+ serviceInstalled
103726
+ }) : daemonWasRunning ? buildDaemonRestartCommandArgv({
103727
+ cliPath: process.argv[1],
103728
+ daemonPort: DEFAULT_DAEMON_PORT_TEXT,
103729
+ platform: process.platform,
103730
+ serviceInstalled
103731
+ }) : [],
102031
103732
  cwd: process.cwd()
102032
103733
  });
102033
- if (daemonWasRunning) {
103734
+ if (shouldRefreshInstalledService) {
103735
+ console.log(source_default.cyan(" Upgrading and refreshing background service in background..."));
103736
+ } else if (daemonWasRunning) {
102034
103737
  console.log(source_default.cyan(" Upgrading and restarting daemon in background..."));
102035
103738
  } else {
102036
103739
  console.log(source_default.cyan(" Upgrading in background..."));
@@ -102042,29 +103745,44 @@ async function runDaemonUpgrade(options, pkgVersion3) {
102042
103745
  return;
102043
103746
  }
102044
103747
  }
102045
- if (options.restart !== false && isDaemonRunning2()) {
102046
- console.log(source_default.yellow("\n Restarting daemon..."));
102047
- stopDaemon2();
102048
- await new Promise((r) => setTimeout(r, 2e3));
102049
- stopManagedSessionHostProcess2();
102050
- await new Promise((r) => setTimeout(r, 500));
102051
- const child = spawn7(process.execPath, [process.argv[1], "daemon", "-p", DEFAULT_DAEMON_PORT_TEXT], {
102052
- detached: true,
102053
- stdio: "ignore",
102054
- windowsHide: true,
102055
- env: { ...process.env }
102056
- });
102057
- child.unref();
102058
- await new Promise((r) => setTimeout(r, 3e3));
103748
+ if (options.restart !== false) {
103749
+ const { installAutoStartServiceForCurrentProcess: installAutoStartServiceForCurrentProcess2, isAutoStartServiceInstalled: isAutoStartServiceInstalled2 } = await Promise.resolve().then(() => (init_service_commands(), service_commands_exports));
103750
+ const serviceInstalled = isAutoStartServiceInstalled2();
103751
+ if (process.platform === "darwin" && serviceInstalled) {
103752
+ console.log(source_default.yellow("\n Refreshing LaunchAgent definition..."));
103753
+ installAutoStartServiceForCurrentProcess2(process.platform);
103754
+ console.log();
103755
+ return;
103756
+ }
102059
103757
  if (isDaemonRunning2()) {
102060
- console.log(source_default.green(` \u2713 Daemon restarted with new version
103758
+ console.log(source_default.yellow("\n Restarting daemon..."));
103759
+ stopDaemon2();
103760
+ await new Promise((r) => setTimeout(r, 2e3));
103761
+ stopManagedSessionHostProcess2();
103762
+ await new Promise((r) => setTimeout(r, 500));
103763
+ const child = spawn7(process.execPath, buildDaemonRestartCommandArgv({
103764
+ cliPath: process.argv[1],
103765
+ daemonPort: DEFAULT_DAEMON_PORT_TEXT,
103766
+ platform: process.platform,
103767
+ serviceInstalled
103768
+ }), {
103769
+ detached: true,
103770
+ stdio: "ignore",
103771
+ windowsHide: true,
103772
+ env: { ...process.env }
103773
+ });
103774
+ child.unref();
103775
+ await new Promise((r) => setTimeout(r, 3e3));
103776
+ if (isDaemonRunning2()) {
103777
+ console.log(source_default.green(` \u2713 Daemon restarted with new version
102061
103778
  `));
102062
- } else {
102063
- console.log(source_default.yellow(` \u26A0 Daemon not detected. Start manually: adhdev daemon
103779
+ } else {
103780
+ console.log(source_default.yellow(` \u26A0 Daemon not detected. Start manually: adhdev daemon
102064
103781
  `));
103782
+ }
103783
+ } else {
103784
+ console.log(source_default.gray("\n Daemon was not running. Start with: adhdev daemon\n"));
102065
103785
  }
102066
- } else if (options.restart !== false) {
102067
- console.log(source_default.gray("\n Daemon was not running. Start with: adhdev daemon\n"));
102068
103786
  } else {
102069
103787
  console.log(source_default.green("\n \u2713 Upgrade complete (daemon not restarted)\n"));
102070
103788
  }
@@ -102235,7 +103953,7 @@ function registerDaemonCommands(program2, pkgVersion3) {
102235
103953
  }
102236
103954
  try {
102237
103955
  const controller = new AbortController();
102238
- const timer = setTimeout(() => controller.abort(), DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS2);
103956
+ const timer = setTimeout(() => controller.abort(), DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS3);
102239
103957
  const res = await fetch(`${DEV_SERVER_BASE_URL}/api/status`, { signal: controller.signal });
102240
103958
  clearTimeout(timer);
102241
103959
  if (res.ok) {
@@ -102782,373 +104500,8 @@ function buildDoctorAdvice(input) {
102782
104500
  return advice;
102783
104501
  }
102784
104502
 
102785
- // src/cli/service-commands.ts
102786
- var import_node_fs4 = __toESM(require("fs"));
102787
- var import_node_path5 = __toESM(require("path"));
102788
- var import_node_os4 = __toESM(require("os"));
102789
- var import_node_child_process4 = require("child_process");
102790
- init_source();
102791
- init_src();
102792
- var DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS3 = 1500;
102793
- var LAUNCHD_LABEL = "dev.adhf.daemon";
102794
- var ADHDEV_DIR = import_node_path5.default.join(import_node_os4.default.homedir(), ".adhdev");
102795
- var LOG_OUT = import_node_path5.default.join(ADHDEV_DIR, "daemon-launchd.out");
102796
- var LOG_ERR = import_node_path5.default.join(ADHDEV_DIR, "daemon-launchd.err");
102797
- var MAX_LOG_SIZE2 = 10 * 1024 * 1024;
102798
- function getDarwinPlistPath() {
102799
- return import_node_path5.default.join(import_node_os4.default.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
102800
- }
102801
- function getWindowsStartupDir() {
102802
- const appData = process.env.APPDATA || import_node_path5.default.join(import_node_os4.default.homedir(), "AppData", "Roaming");
102803
- return import_node_path5.default.join(appData, "Microsoft", "Windows", "Start Menu", "Programs", "Startup");
102804
- }
102805
- function getWindowsVbsPath() {
102806
- return import_node_path5.default.join(getWindowsStartupDir(), "adhdev-daemon.vbs");
102807
- }
102808
- function resolveCliPath() {
102809
- return import_node_fs4.default.realpathSync(process.argv[1]);
102810
- }
102811
- function ensureDir(dir) {
102812
- if (!import_node_fs4.default.existsSync(dir)) import_node_fs4.default.mkdirSync(dir, { recursive: true });
102813
- }
102814
- async function fetchHealth() {
102815
- const controller = new AbortController();
102816
- const timer = setTimeout(() => controller.abort(), DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS3);
102817
- try {
102818
- const res = await fetch(`http://127.0.0.1:${DEFAULT_DAEMON_PORT}/health`, { signal: controller.signal });
102819
- if (!res.ok) return null;
102820
- return await res.json();
102821
- } catch {
102822
- return null;
102823
- } finally {
102824
- clearTimeout(timer);
102825
- }
102826
- }
102827
- function getProcessInfo(pid) {
102828
- try {
102829
- if (process.platform === "win32") {
102830
- const out = (0, import_node_child_process4.execSync)(`tasklist /FI "PID eq ${pid}" /FO CSV /NH`, { encoding: "utf-8" });
102831
- const match = out.match(/"(\d[\d,]+)\sK"/);
102832
- const memKB = match ? parseInt(match[1].replace(/,/g, ""), 10) : 0;
102833
- return { uptime: "-", memMB: Math.round(memKB / 1024) };
102834
- } else {
102835
- const out = (0, import_node_child_process4.execSync)(`ps -o etime=,rss= -p ${pid}`, { encoding: "utf-8" }).trim();
102836
- const parts = out.split(/\s+/);
102837
- const etime = parts[0] || "-";
102838
- const rssKB = parseInt(parts[1] || "0", 10);
102839
- return { uptime: formatElapsed(etime), memMB: Math.round(rssKB / 1024) };
102840
- }
102841
- } catch {
102842
- return null;
102843
- }
102844
- }
102845
- function formatElapsed(etime) {
102846
- const parts = etime.replace("-", ":").split(":").map(Number);
102847
- if (parts.length === 4) return `${parts[0]}d ${parts[1]}h ${parts[2]}m`;
102848
- if (parts.length === 3) return `${parts[0]}h ${parts[1]}m`;
102849
- if (parts.length === 2) return `${parts[0]}m ${parts[1]}s`;
102850
- return etime;
102851
- }
102852
- function rotateLogIfNeeded(logPath) {
102853
- try {
102854
- if (!import_node_fs4.default.existsSync(logPath)) return;
102855
- const stat5 = import_node_fs4.default.statSync(logPath);
102856
- if (stat5.size > MAX_LOG_SIZE2) {
102857
- const rotated = logPath + ".old";
102858
- if (import_node_fs4.default.existsSync(rotated)) import_node_fs4.default.unlinkSync(rotated);
102859
- import_node_fs4.default.renameSync(logPath, rotated);
102860
- import_node_fs4.default.writeFileSync(logPath, `[log rotated at ${(/* @__PURE__ */ new Date()).toISOString()}]
102861
- `, "utf-8");
102862
- }
102863
- } catch {
102864
- }
102865
- }
102866
- function rotateLogs() {
102867
- rotateLogIfNeeded(LOG_OUT);
102868
- rotateLogIfNeeded(LOG_ERR);
102869
- }
102870
- function buildPlist(nodeExe, cliExe) {
102871
- const brewPrefix = import_node_fs4.default.existsSync("/opt/homebrew/bin") ? "/opt/homebrew/bin" : "/usr/local/bin";
102872
- const nodeDir = import_node_path5.default.dirname(nodeExe);
102873
- const pathEntries = /* @__PURE__ */ new Set([nodeDir, brewPrefix, "/usr/local/bin", "/usr/bin", "/bin", "/usr/sbin", "/sbin"]);
102874
- const pathValue = Array.from(pathEntries).join(":");
102875
- return `<?xml version="1.0" encoding="UTF-8"?>
102876
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
102877
- <plist version="1.0">
102878
- <dict>
102879
- <key>Label</key>
102880
- <string>${LAUNCHD_LABEL}</string>
102881
- <key>ProgramArguments</key>
102882
- <array>
102883
- <string>${nodeExe}</string>
102884
- <string>${cliExe}</string>
102885
- <string>daemon</string>
102886
- </array>
102887
- <key>RunAtLoad</key>
102888
- <true/>
102889
- <key>KeepAlive</key>
102890
- <dict>
102891
- <key>SuccessfulExit</key>
102892
- <false/>
102893
- </dict>
102894
- <key>ThrottleInterval</key>
102895
- <integer>30</integer>
102896
- <key>StandardOutPath</key>
102897
- <string>${LOG_OUT}</string>
102898
- <key>StandardErrorPath</key>
102899
- <string>${LOG_ERR}</string>
102900
- <key>EnvironmentVariables</key>
102901
- <dict>
102902
- <key>PATH</key>
102903
- <string>${pathValue}</string>
102904
- </dict>
102905
- </dict>
102906
- </plist>`;
102907
- }
102908
- function installDarwin(nodeExe, cliExe) {
102909
- const plistPath = getDarwinPlistPath();
102910
- ensureDir(ADHDEV_DIR);
102911
- ensureDir(import_node_path5.default.dirname(plistPath));
102912
- import_node_fs4.default.writeFileSync(plistPath, buildPlist(nodeExe, cliExe), "utf-8");
102913
- console.log(source_default.gray(` Plist: ${plistPath}`));
102914
- try {
102915
- (0, import_node_child_process4.execSync)(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "ignore" });
102916
- } catch {
102917
- }
102918
- try {
102919
- (0, import_node_child_process4.execSync)(`launchctl load -w "${plistPath}"`, { stdio: "ignore" });
102920
- console.log(source_default.green("\n \u2713 Registered as LaunchAgent \u2014 daemon will start on login."));
102921
- console.log(source_default.gray(` Logs: ~/.adhdev/daemon-launchd.{out,err}`));
102922
- } catch (e) {
102923
- console.log(source_default.red(`
102924
- \u2717 launchctl load failed: ${e.message}`));
102925
- }
102926
- }
102927
- function uninstallDarwin() {
102928
- const plistPath = getDarwinPlistPath();
102929
- if (!import_node_fs4.default.existsSync(plistPath)) {
102930
- console.log(source_default.yellow("\n \u26A0 Service is not installed."));
102931
- return;
102932
- }
102933
- try {
102934
- (0, import_node_child_process4.execSync)(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "ignore" });
102935
- } catch {
102936
- }
102937
- import_node_fs4.default.unlinkSync(plistPath);
102938
- console.log(source_default.green("\n \u2713 Removed LaunchAgent. Daemon will no longer auto-start."));
102939
- }
102940
- function isInstalledDarwin() {
102941
- return import_node_fs4.default.existsSync(getDarwinPlistPath());
102942
- }
102943
- function buildVbs(nodeExe, cliExe) {
102944
- const logFile = import_node_path5.default.join(ADHDEV_DIR, "daemon-service.log").replace(/\\/g, "\\\\");
102945
- const escapedNodeExe = nodeExe.replace(/\\/g, "\\\\");
102946
- const escapedCliExe = cliExe.replace(/\\/g, "\\\\");
102947
- return `' ADHDev Daemon Auto-Start (generated by adhdev service install)
102948
- Set WshShell = CreateObject("WScript.Shell")
102949
- WshShell.Run "cmd.exe /c """"${escapedNodeExe}"""" """"${escapedCliExe}"""" daemon >> """"${logFile}"""" 2>&1", 0, False
102950
- `;
102951
- }
102952
- function installWindows(nodeExe, cliExe) {
102953
- const vbsPath = getWindowsVbsPath();
102954
- ensureDir(ADHDEV_DIR);
102955
- ensureDir(import_node_path5.default.dirname(vbsPath));
102956
- import_node_fs4.default.writeFileSync(vbsPath, buildVbs(nodeExe, cliExe), "utf-8");
102957
- console.log(source_default.gray(` Startup script: ${vbsPath}`));
102958
- console.log(source_default.green("\n \u2713 Registered in Startup folder \u2014 daemon will start on login (hidden)."));
102959
- console.log(source_default.gray(` Logs: ${import_node_path5.default.join(ADHDEV_DIR, "daemon-service.log")}`));
102960
- console.log(source_default.gray(" To start now without rebooting, run: adhdev daemon"));
102961
- }
102962
- function uninstallWindows() {
102963
- const vbsPath = getWindowsVbsPath();
102964
- if (!import_node_fs4.default.existsSync(vbsPath)) {
102965
- console.log(source_default.yellow("\n \u26A0 Service is not installed."));
102966
- return;
102967
- }
102968
- import_node_fs4.default.unlinkSync(vbsPath);
102969
- console.log(source_default.green("\n \u2713 Removed Startup script. Daemon will no longer auto-start."));
102970
- console.log(source_default.gray(" Note: a currently running daemon is not affected. Stop with: adhdev daemon:stop"));
102971
- }
102972
- function isInstalledWindows() {
102973
- return import_node_fs4.default.existsSync(getWindowsVbsPath());
102974
- }
102975
- function registerServiceCommands(program2) {
102976
- const svc = program2.command("service").description("\u{1F50C} Manage ADHDev as an OS background auto-start service");
102977
- svc.command("install").description("Register ADHDev daemon to start automatically on login").action(async () => {
102978
- console.log(source_default.bold("\n \u{1F680} Installing ADHDev Background Service"));
102979
- const platform12 = import_node_os4.default.platform();
102980
- const nodeExe = process.execPath;
102981
- const cliExe = resolveCliPath();
102982
- console.log(source_default.gray(` Node: ${nodeExe}`));
102983
- console.log(source_default.gray(` CLI: ${cliExe}`));
102984
- console.log(source_default.gray(` Platform: ${platform12}`));
102985
- if (platform12 === "darwin") {
102986
- installDarwin(nodeExe, cliExe);
102987
- } else if (platform12 === "win32") {
102988
- installWindows(nodeExe, cliExe);
102989
- } else {
102990
- console.log(source_default.yellow("\n \u26A0 Auto-start service install is not supported on this platform."));
102991
- console.log(source_default.gray(" On Linux, create a systemd user unit manually:"));
102992
- console.log(source_default.gray(" ~/.config/systemd/user/adhdev-daemon.service"));
102993
- }
102994
- console.log();
102995
- });
102996
- svc.command("uninstall").description("Remove the OS background service").action(async () => {
102997
- console.log(source_default.bold("\n \u{1F5D1}\uFE0F Removing ADHDev Background Service"));
102998
- const platform12 = import_node_os4.default.platform();
102999
- if (platform12 === "darwin") {
103000
- uninstallDarwin();
103001
- } else if (platform12 === "win32") {
103002
- uninstallWindows();
103003
- } else {
103004
- console.log(source_default.yellow("\n \u26A0 Not supported on this platform."));
103005
- }
103006
- console.log();
103007
- });
103008
- svc.command("status").description("Show service installation state and live daemon health").action(async () => {
103009
- const platform12 = import_node_os4.default.platform();
103010
- const installed = platform12 === "darwin" ? isInstalledDarwin() : platform12 === "win32" ? isInstalledWindows() : false;
103011
- if (installed) {
103012
- console.log(source_default.green("\n \u2713 Service is installed."));
103013
- if (platform12 === "darwin") console.log(source_default.gray(` Plist: ${getDarwinPlistPath()}`));
103014
- else console.log(source_default.gray(` Script: ${getWindowsVbsPath()}`));
103015
- } else {
103016
- console.log(source_default.gray("\n \u2717 Service is not installed. Run: adhdev service install"));
103017
- }
103018
- const health = await fetchHealth();
103019
- if (health?.ok && health.pid) {
103020
- const info = getProcessInfo(health.pid);
103021
- if (info) {
103022
- console.log(source_default.green(` \u2713 Daemon running \u2014 PID ${health.pid}, uptime ${info.uptime}, ${info.memMB} MB`));
103023
- } else {
103024
- console.log(source_default.green(` \u2713 Daemon running \u2014 PID ${health.pid}`));
103025
- }
103026
- } else {
103027
- console.log(source_default.yellow(" \u2717 Daemon is not running."));
103028
- }
103029
- const outSize = import_node_fs4.default.existsSync(LOG_OUT) ? import_node_fs4.default.statSync(LOG_OUT).size : 0;
103030
- const errSize = import_node_fs4.default.existsSync(LOG_ERR) ? import_node_fs4.default.statSync(LOG_ERR).size : 0;
103031
- if (outSize > 0 || errSize > 0) {
103032
- console.log(source_default.gray(` Logs: stdout ${formatBytes(outSize)}, stderr ${formatBytes(errSize)}`));
103033
- }
103034
- console.log();
103035
- });
103036
- svc.command("logs").description("View daemon service logs").option("--err", "Show stderr log instead of stdout").option("--clear", "Truncate all log files").option("-n, --lines <count>", "Number of lines to show", "30").action(async (options) => {
103037
- if (options.clear) {
103038
- for (const f of [LOG_OUT, LOG_ERR]) {
103039
- if (import_node_fs4.default.existsSync(f)) import_node_fs4.default.writeFileSync(f, "", "utf-8");
103040
- }
103041
- console.log(source_default.green("\n \u2713 Logs cleared.\n"));
103042
- return;
103043
- }
103044
- const logFile = options.err ? LOG_ERR : LOG_OUT;
103045
- if (!import_node_fs4.default.existsSync(logFile)) {
103046
- console.log(source_default.gray(`
103047
- No log file found: ${logFile}
103048
- `));
103049
- return;
103050
- }
103051
- const lines = parseInt(options.lines, 10) || 30;
103052
- console.log(source_default.gray(`
103053
- \u2500\u2500 ${options.err ? "stderr" : "stdout"}: ${logFile} (last ${lines} lines) \u2500\u2500
103054
- `));
103055
- const content = import_node_fs4.default.readFileSync(logFile, "utf-8");
103056
- const allLines = content.split("\n");
103057
- const lastLines = allLines.slice(-lines).join("\n");
103058
- if (lastLines.trim()) console.log(lastLines);
103059
- console.log(source_default.gray("\n (watching for new output, Ctrl+C to stop)\n"));
103060
- let offset = Buffer.byteLength(content, "utf-8");
103061
- const watcher = import_node_fs4.default.watchFile(logFile, { interval: 500 }, () => {
103062
- try {
103063
- const stat5 = import_node_fs4.default.statSync(logFile);
103064
- if (stat5.size > offset) {
103065
- const fd = import_node_fs4.default.openSync(logFile, "r");
103066
- const buf = Buffer.alloc(stat5.size - offset);
103067
- import_node_fs4.default.readSync(fd, buf, 0, buf.length, offset);
103068
- import_node_fs4.default.closeSync(fd);
103069
- process.stdout.write(buf.toString("utf-8"));
103070
- offset = stat5.size;
103071
- } else if (stat5.size < offset) {
103072
- offset = 0;
103073
- }
103074
- } catch {
103075
- }
103076
- });
103077
- const cleanup = () => {
103078
- import_node_fs4.default.unwatchFile(logFile);
103079
- process.exit(0);
103080
- };
103081
- process.on("SIGINT", cleanup);
103082
- process.on("SIGTERM", cleanup);
103083
- });
103084
- svc.command("restart").description("Restart the daemon process (service will auto-relaunch)").action(async () => {
103085
- const platform12 = import_node_os4.default.platform();
103086
- const installed = platform12 === "darwin" ? isInstalledDarwin() : platform12 === "win32" ? isInstalledWindows() : false;
103087
- if (!installed) {
103088
- console.log(source_default.yellow("\n \u26A0 Service is not installed. Use `adhdev daemon:restart` for manual restart."));
103089
- console.log(source_default.gray(" Or install the service first: adhdev service install\n"));
103090
- return;
103091
- }
103092
- rotateLogs();
103093
- const health = await fetchHealth();
103094
- if (!health?.pid) {
103095
- console.log(source_default.yellow("\n \u26A0 Daemon is not currently running."));
103096
- if (platform12 === "darwin") {
103097
- console.log(source_default.gray(" Starting via launchctl..."));
103098
- try {
103099
- (0, import_node_child_process4.execSync)(`launchctl start ${LAUNCHD_LABEL}`, { stdio: "ignore" });
103100
- console.log(source_default.green(" \u2713 Started.\n"));
103101
- } catch {
103102
- console.log(source_default.red(" \u2717 Failed to start. Check: adhdev service logs --err\n"));
103103
- }
103104
- } else {
103105
- console.log(source_default.gray(" Start with: adhdev daemon\n"));
103106
- }
103107
- return;
103108
- }
103109
- console.log(source_default.cyan(`
103110
- Stopping daemon (PID ${health.pid})...`));
103111
- try {
103112
- process.kill(health.pid, "SIGTERM");
103113
- } catch {
103114
- console.log(source_default.yellow(" Could not send SIGTERM. Process may have already exited."));
103115
- }
103116
- await new Promise((r) => setTimeout(r, 2e3));
103117
- if (platform12 === "win32") {
103118
- const vbsPath = getWindowsVbsPath();
103119
- if (import_node_fs4.default.existsSync(vbsPath)) {
103120
- try {
103121
- (0, import_node_child_process4.execSync)(`wscript.exe "${vbsPath}"`, { stdio: "ignore", windowsHide: true });
103122
- } catch {
103123
- }
103124
- }
103125
- }
103126
- let restarted = false;
103127
- for (let i = 0; i < 8; i++) {
103128
- await new Promise((r) => setTimeout(r, 1e3));
103129
- const newHealth = await fetchHealth();
103130
- if (newHealth?.ok && newHealth.pid !== health.pid) {
103131
- restarted = true;
103132
- const info = getProcessInfo(newHealth.pid);
103133
- console.log(source_default.green(` \u2713 Daemon restarted \u2014 new PID ${newHealth.pid}${info ? `, ${info.memMB} MB` : ""}`));
103134
- break;
103135
- }
103136
- }
103137
- if (!restarted) {
103138
- console.log(source_default.yellow(" \u26A0 Daemon did not restart within 10s."));
103139
- console.log(source_default.gray(" Check: adhdev service logs --err"));
103140
- }
103141
- console.log();
103142
- });
103143
- }
103144
- function formatBytes(bytes) {
103145
- if (bytes === 0) return "0 B";
103146
- if (bytes < 1024) return `${bytes} B`;
103147
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
103148
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
103149
- }
103150
-
103151
104503
  // src/cli/doctor-commands.ts
104504
+ init_service_commands();
103152
104505
  function resolvePackageRoot() {
103153
104506
  return path39.resolve(__dirname, "..", "..");
103154
104507
  }
@@ -104640,6 +105993,9 @@ function registerCdpCommands(program2) {
104640
105993
  });
104641
105994
  }
104642
105995
 
105996
+ // src/cli/index.ts
105997
+ init_service_commands();
105998
+
104643
105999
  // src/cli/mcp-commands.ts
104644
106000
  var import_node_child_process5 = require("child_process");
104645
106001
  var fs23 = __toESM(require("fs"));