@adhdev/daemon-standalone 1.0.39-rc.2 → 1.0.39-rc.4

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/public/index.html CHANGED
@@ -7,7 +7,7 @@
7
7
  <meta name="description" content="ADHDev self-hosted dashboard for controlling AI agents" />
8
8
  <link rel="icon" href="/otter-logo.png" />
9
9
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet" />
10
- <script type="module" crossorigin src="/assets/index-CO0_zScg.js"></script>
10
+ <script type="module" crossorigin src="/assets/index-Dw-CNAfy.js"></script>
11
11
  <link rel="modulepreload" crossorigin href="/assets/vendor-DgFmqOGd.js">
12
12
  <link rel="stylesheet" crossorigin href="/assets/index-BRvsAKU_.css">
13
13
  </head>
@@ -937,7 +937,7 @@ function isLocalControlPlaneNode(ctx, node) {
937
937
  // src/tools/mesh-tool-schemas.ts
938
938
  var MESH_STATUS_TOOL = {
939
939
  name: "mesh_status",
940
- description: "Get the current status of all nodes in the repo mesh \u2014 health, git state, active sessions, recovery hints, and recommended next steps. Use this to decide which node to send work to or how to recover from failures. Also reports the running daemon build per daemonId under top-level daemonBuilds ({commit, commitShort, version}); when a live daemon was built from a commit BEHIND its workspace HEAD it adds staleDaemonBuilds[] + staleDaemonBuildWarning \u2014 meaning a just-merged refinery/mesh-tool fix is NOT yet live on that daemon (awaiting deploy/restart; a local dist rebuild does not update a cloud daemon). Do not repeatedly call this to wait for generating delegated work; wait for pendingCoordinatorEvents/completion events or an explicit user status request.",
940
+ description: `Get the current status of all nodes in the repo mesh \u2014 health, git state, active sessions, recovery hints, and recommended next steps. Use this to decide which node to send work to or how to recover from failures. Also reports the running daemon build per daemonId under top-level daemonBuilds ({commit, commitShort, version}); when a live daemon was built from a commit BEHIND its workspace HEAD it adds staleDaemonBuilds[] + staleDaemonBuildWarning \u2014 meaning a just-merged refinery/mesh-tool fix is NOT yet live on that daemon (awaiting deploy/restart; a local dist rebuild does not update a cloud daemon). When a daemon has a durable failed-upgrade notice on record it adds daemonUpgradeFailures{daemonId \u2192 {summary, recordedAt, ageLabel, targetVersion, noticePath, logPath}} + daemonUpgradeFailureWarning \u2014 meaning that daemon's LAST upgrade attempt failed and was rolled back, so it is still on the PREVIOUS version (an upgrade/restart response only ever reports "scheduled", never success). Do not repeatedly call this to wait for generating delegated work; wait for pendingCoordinatorEvents/completion events or an explicit user status request.`,
941
941
  inputSchema: {
942
942
  type: "object",
943
943
  properties: {
@@ -1884,7 +1884,7 @@ function summarizeCompactSubmodules(submodules) {
1884
1884
  ...outOfSync.length > 0 ? { outOfSyncPaths: outOfSync } : {}
1885
1885
  };
1886
1886
  }
1887
- var MESH_COMPACT_PRESERVED_MARKER_FIELDS = ["dataFreshness", "quota"];
1887
+ var MESH_COMPACT_PRESERVED_MARKER_FIELDS = ["dataFreshness", "quota", "isLocalWorktree"];
1888
1888
  function summarizeNodeQuota(quota) {
1889
1889
  if (!quota || typeof quota !== "object" || Array.isArray(quota)) return void 0;
1890
1890
  const out = {};
@@ -1960,6 +1960,27 @@ function compactNodeSeverity(entry) {
1960
1960
  if (entry.staleDaemonBuild || entry.submodulesOutOfSync || entry.recoveryHints) return 1;
1961
1961
  return 0;
1962
1962
  }
1963
+ function pinnedRepresentativeNodeIds(compacted) {
1964
+ const byDaemon = /* @__PURE__ */ new Map();
1965
+ for (const n of compacted) {
1966
+ if (!n || typeof n !== "object") continue;
1967
+ const daemonId = typeof n.daemonId === "string" && n.daemonId ? n.daemonId : "";
1968
+ if (!daemonId) continue;
1969
+ const current = byDaemon.get(daemonId);
1970
+ if (!current) {
1971
+ byDaemon.set(daemonId, n);
1972
+ continue;
1973
+ }
1974
+ if (current.isLocalWorktree === true && n.isLocalWorktree !== true) {
1975
+ byDaemon.set(daemonId, n);
1976
+ }
1977
+ }
1978
+ const pinned = /* @__PURE__ */ new Set();
1979
+ for (const n of byDaemon.values()) {
1980
+ if (n?.nodeId !== void 0) pinned.add(String(n.nodeId));
1981
+ }
1982
+ return pinned;
1983
+ }
1963
1984
  function isNoteworthyCompactNode(entry) {
1964
1985
  if (!entry || typeof entry !== "object") return true;
1965
1986
  if (entry.health && entry.health !== "online") return true;
@@ -2832,8 +2853,8 @@ function assignFullGitSnapshot(entry, status) {
2832
2853
  if (!status || typeof status !== "object" || Array.isArray(status)) return;
2833
2854
  entry.git = status;
2834
2855
  }
2835
- var COMPACT_DETAILED_NODES_BYTE_BUDGET = 9e3;
2836
- var COMPACT_NODES_TOTAL_BYTE_BUDGET = 11500;
2856
+ var COMPACT_DETAILED_NODES_BYTE_BUDGET = 32e3;
2857
+ var COMPACT_NODES_TOTAL_BYTE_BUDGET = 4e4;
2837
2858
  var COMPACT_MISSIONS_BYTE_BUDGET = 6e3;
2838
2859
  function extractLaunchPayload(value) {
2839
2860
  return findNestedPayload(value, (payload) => Boolean(payload?.sessionId || payload?.id || payload?.runtimeSessionId));
@@ -3348,12 +3369,35 @@ async function collectLiveStatusProbe(ctx, node) {
3348
3369
  const statusResult = await commandForNode(ctx, node, "get_status_metadata", {}, { statusProbe: true });
3349
3370
  return {
3350
3371
  sessions: extractStatusMetadataSessions(statusResult),
3351
- daemonBuild: extractDaemonBuildInfo(statusResult)
3372
+ daemonBuild: extractDaemonBuildInfo(statusResult),
3373
+ upgradeFailure: extractUpgradeFailureSummary(statusResult)
3352
3374
  };
3353
3375
  } catch {
3354
3376
  return { sessions: [] };
3355
3377
  }
3356
3378
  }
3379
+ var UPGRADE_FAILURE_SUMMARY_MAX_CHARS = 200;
3380
+ function extractUpgradeFailureSummary(value) {
3381
+ const payload = unwrapCommandPayload(value);
3382
+ const raw = payload?.upgradeFailure && typeof payload.upgradeFailure === "object" ? payload.upgradeFailure : value?.upgradeFailure && typeof value.upgradeFailure === "object" ? value.upgradeFailure : void 0;
3383
+ if (!raw) return void 0;
3384
+ const notice = readString(raw.notice) || "";
3385
+ const noticePath = readString(raw.noticePath) || "";
3386
+ if (!notice && !noticePath) return void 0;
3387
+ const bodyLine = notice.split(/\r?\n/).map((line) => line.trim()).find((line) => line && !/^\[[^\]\n]+\]$/.test(line)) || "";
3388
+ const summary = bodyLine.length > UPGRADE_FAILURE_SUMMARY_MAX_CHARS ? `${bodyLine.slice(0, UPGRADE_FAILURE_SUMMARY_MAX_CHARS)}\u2026` : bodyLine;
3389
+ const recordedAt = readString(raw.recordedAt);
3390
+ const ageLabel = readString(raw.ageLabel);
3391
+ const targetVersion = readString(raw.targetVersion);
3392
+ return {
3393
+ summary,
3394
+ ...recordedAt ? { recordedAt } : {},
3395
+ ...ageLabel ? { ageLabel } : {},
3396
+ ...targetVersion ? { targetVersion } : {},
3397
+ noticePath,
3398
+ logPath: readString(raw.logPath) || ""
3399
+ };
3400
+ }
3357
3401
  function extractDaemonBuildInfo(value) {
3358
3402
  const payload = unwrapCommandPayload(value);
3359
3403
  const build = payload?.daemonBuild && typeof payload.daemonBuild === "object" ? payload.daemonBuild : value?.daemonBuild && typeof value.daemonBuild === "object" ? value.daemonBuild : void 0;
@@ -3787,6 +3831,10 @@ async function meshStatus(ctx, args = {}) {
3787
3831
  machine: buildNodeMachineIdentity(ctx, node),
3788
3832
  daemonId: readNodeDaemonId(node),
3789
3833
  machineId: readNodeMachineId(node),
3834
+ // Needed by the compact fold to distinguish a machine (repo-root) node
3835
+ // from a worktree node: the per-daemon representative pin keeps machine
3836
+ // nodes out of the fold so a deploy roster can never lose a machine.
3837
+ isLocalWorktree: node.isLocalWorktree === true,
3790
3838
  ...getNodeLaunchReadiness(node),
3791
3839
  ...buildNodeCapabilityExposure(node)
3792
3840
  };
@@ -3896,6 +3944,7 @@ async function meshStatus(ctx, args = {}) {
3896
3944
  const statusProbe = await collectLiveStatusProbe(ctx, node);
3897
3945
  const liveSessions = statusProbe.sessions;
3898
3946
  if (statusProbe.daemonBuild) entry.daemonBuild = statusProbe.daemonBuild;
3947
+ if (statusProbe.upgradeFailure) entry.upgradeFailure = statusProbe.upgradeFailure;
3899
3948
  if (liveSessions.length > 0) {
3900
3949
  entry.sessions = liveSessions.map((s) => {
3901
3950
  const coordinatorMeshId = typeof s.coordinator?.meshId === "string" ? s.coordinator.meshId : void 0;
@@ -3993,6 +4042,21 @@ async function meshStatus(ctx, args = {}) {
3993
4042
  daemonBuilds[daemonId] = entry.daemonBuild;
3994
4043
  }
3995
4044
  }
4045
+ const daemonMachines = {};
4046
+ const daemonQuotas = {};
4047
+ for (const entry of results) {
4048
+ const daemonId = typeof entry?.daemonId === "string" && entry.daemonId ? entry.daemonId : "";
4049
+ if (!daemonId) continue;
4050
+ if (entry?.machine && !(daemonId in daemonMachines)) daemonMachines[daemonId] = entry.machine;
4051
+ if (entry?.quota && !(daemonId in daemonQuotas)) daemonQuotas[daemonId] = entry.quota;
4052
+ }
4053
+ const daemonUpgradeFailures = {};
4054
+ for (const entry of results) {
4055
+ const daemonId = typeof entry?.daemonId === "string" && entry.daemonId ? entry.daemonId : "";
4056
+ if (daemonId && entry?.upgradeFailure && !(daemonId in daemonUpgradeFailures)) {
4057
+ daemonUpgradeFailures[daemonId] = entry.upgradeFailure;
4058
+ }
4059
+ }
3996
4060
  const staleDaemonBuilds = [];
3997
4061
  const seenStale = /* @__PURE__ */ new Set();
3998
4062
  for (const entry of results) {
@@ -4055,20 +4119,36 @@ async function meshStatus(ctx, args = {}) {
4055
4119
  if (!includeSessions) delete next.sessions;
4056
4120
  }
4057
4121
  if (next.daemonBuild !== void 0) delete next.daemonBuild;
4122
+ if (next.machine && typeof next.machine === "object") {
4123
+ const m = next.machine;
4124
+ next.machine = {
4125
+ daemonId: m.daemonId,
4126
+ displayName: m.displayName,
4127
+ sameMachine: m.sameMachine,
4128
+ seeDaemonMachines: true
4129
+ };
4130
+ }
4131
+ if (next.upgradeFailure !== void 0) delete next.upgradeFailure;
4058
4132
  return next;
4059
4133
  });
4134
+ const pinnedIds = pinnedRepresentativeNodeIds(compacted);
4060
4135
  const noteworthy = compacted.filter((n) => n && typeof n === "object" && isNoteworthyCompactNode(n));
4061
- const ranked = [...noteworthy].sort((a, b) => compactNodeSeverity(b) - compactNodeSeverity(a));
4136
+ const bySeverityDesc = (a, b) => compactNodeSeverity(b) - compactNodeSeverity(a);
4137
+ const isPinned = (n) => pinnedIds.has(String(n?.nodeId));
4138
+ const ranked = [
4139
+ ...compacted.filter((n) => n && typeof n === "object" && isPinned(n)).sort(bySeverityDesc),
4140
+ ...noteworthy.filter((n) => !isPinned(n)).sort(bySeverityDesc)
4141
+ ];
4062
4142
  const detailedIds = /* @__PURE__ */ new Set();
4063
4143
  let detailSpent = 0;
4064
4144
  for (const n of ranked) {
4065
4145
  const cost = JSON.stringify(n).length + 1;
4066
- if (detailedIds.size === 0 || detailSpent + cost <= COMPACT_DETAILED_NODES_BYTE_BUDGET) {
4146
+ if (detailedIds.size === 0 || isPinned(n) || detailSpent + cost <= COMPACT_DETAILED_NODES_BYTE_BUDGET) {
4067
4147
  detailedIds.add(String(n.nodeId));
4068
4148
  detailSpent += cost;
4069
4149
  }
4070
4150
  }
4071
- const stubOrder = [...compacted].filter((n) => n && typeof n === "object").sort((a, b) => compactNodeSeverity(b) - compactNodeSeverity(a));
4151
+ const stubOrder = [...compacted].filter((n) => n && typeof n === "object").sort(bySeverityDesc);
4072
4152
  const keptIds = new Set(detailedIds);
4073
4153
  let totalSpent = detailSpent;
4074
4154
  for (const n of stubOrder) {
@@ -4141,6 +4221,14 @@ async function meshStatus(ctx, args = {}) {
4141
4221
  ...compact && foldedNodesSummary ? { foldedNodes: foldedNodesSummary } : {},
4142
4222
  ...compact && Object.keys(daemonSessions).length > 0 ? { daemonSessions } : {},
4143
4223
  ...Object.keys(daemonBuilds).length > 0 ? { daemonBuilds } : {},
4224
+ // Per-daemon machine identity / provider quota, recorded once per daemonId
4225
+ // instead of repeated on every node sharing that daemon. Both modes.
4226
+ ...Object.keys(daemonMachines).length > 0 ? { daemonMachines } : {},
4227
+ ...Object.keys(daemonQuotas).length > 0 ? { daemonQuotas } : {},
4228
+ ...Object.keys(daemonUpgradeFailures).length > 0 ? {
4229
+ daemonUpgradeFailures,
4230
+ daemonUpgradeFailureWarning: `One or more daemons have a failed-upgrade notice on record: that daemon's LAST upgrade failed and rolled back, so it is still on the PREVIOUS version. An upgrade/restart response only reports "scheduled", never success \u2014 do not read a prior success as proof the version changed. The notice persists until a later upgrade succeeds, so check targetVersion/recordedAt: a target other than the running version is a stale earlier attempt. Full body at noticePath, trace at logPath.`
4231
+ } : {},
4144
4232
  ...staleDaemonBuilds.length > 0 ? { staleDaemonBuilds } : {},
4145
4233
  ...daemonAffectingStaleBuilds.length > 0 ? {
4146
4234
  staleDaemonBuildWarning: "One or more live daemons were built from a commit behind the workspace HEAD with daemon-runtime package changes. Merged refinery/mesh-tool fixes are NOT live on those daemons until they are rebuilt/redeployed and restarted \u2014 a local daemon-core dist rebuild does not update a cloud daemon. Do not assume a just-merged fix is active."
@@ -4271,7 +4359,7 @@ async function meshStatus(ctx, args = {}) {
4271
4359
  }
4272
4360
  } catch {
4273
4361
  }
4274
- return JSON.stringify(response, null, 2);
4362
+ return JSON.stringify(response);
4275
4363
  }
4276
4364
  async function meshListNodes(ctx) {
4277
4365
  await refreshMeshFromDaemon(ctx);