@adhdev/daemon-standalone 1.0.32-rc.4 → 1.0.32-rc.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-standalone",
3
- "version": "1.0.32-rc.4",
3
+ "version": "1.0.32-rc.5",
4
4
  "description": "ADHDev standalone daemon — embedded HTTP/WS server for local dashboard",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -37,7 +37,7 @@
37
37
  "license": "AGPL-3.0-or-later",
38
38
  "dependencies": {
39
39
  "@adhdev/ghostty-vt-node": "*",
40
- "@adhdev/session-host-core": "1.0.32-rc.4",
40
+ "@adhdev/session-host-core": "1.0.32-rc.5",
41
41
  "@xterm/addon-serialize": "^0.14.0",
42
42
  "@xterm/xterm": "^6.0.0",
43
43
  "chalk": "^5.3.0",
@@ -1883,7 +1883,23 @@ function summarizeCompactSubmodules(submodules) {
1883
1883
  ...outOfSync.length > 0 ? { outOfSyncPaths: outOfSync } : {}
1884
1884
  };
1885
1885
  }
1886
- var MESH_COMPACT_PRESERVED_MARKER_FIELDS = ["dataFreshness"];
1886
+ var MESH_COMPACT_PRESERVED_MARKER_FIELDS = ["dataFreshness", "quota"];
1887
+ function summarizeNodeQuota(quota) {
1888
+ if (!quota || typeof quota !== "object" || Array.isArray(quota)) return void 0;
1889
+ const out = {};
1890
+ for (const [provider, snapshot] of Object.entries(quota)) {
1891
+ if (!snapshot || typeof snapshot !== "object") continue;
1892
+ const status = typeof snapshot.status === "string" ? snapshot.status : "unknown";
1893
+ if (status !== "ok") {
1894
+ const kind = typeof snapshot.metadata?.failureKind === "string" ? snapshot.metadata.failureKind : void 0;
1895
+ out[provider] = kind ? `${status}:${kind}` : status;
1896
+ continue;
1897
+ }
1898
+ const pct = (w) => w && Number.isFinite(w.usedPercent) ? `${Math.round(w.usedPercent)}%` : "\u2014";
1899
+ out[provider] = `${pct(snapshot.session)}/${pct(snapshot.weekly)}`;
1900
+ }
1901
+ return Object.keys(out).length > 0 ? out : void 0;
1902
+ }
1887
1903
  function compactMeshStatusNode(entry) {
1888
1904
  if (!entry || typeof entry !== "object") return entry;
1889
1905
  const next = { ...entry };
@@ -1921,6 +1937,11 @@ function compactMeshStatusNode(entry) {
1921
1937
  seeStaleDaemonBuilds: true
1922
1938
  };
1923
1939
  }
1940
+ if (next.quota !== void 0) {
1941
+ const summary = summarizeNodeQuota(next.quota);
1942
+ if (summary) next.quota = summary;
1943
+ else delete next.quota;
1944
+ }
1924
1945
  delete next.capabilityTagsByProvider;
1925
1946
  const elideSkip = /* @__PURE__ */ new Set(["git", "machine", "branchConvergence", "staleDaemonBuild", "sessions", ...MESH_COMPACT_PRESERVED_MARKER_FIELDS]);
1926
1947
  for (const k of Object.keys(next)) {
@@ -2011,7 +2032,7 @@ var STALE_ASSIGNED_QUEUE_MS = 30 * 6e4;
2011
2032
  var OLD_HISTORICAL_QUEUE_RECORD_MS = 7 * 24 * 60 * 6e4;
2012
2033
  var ACTIVE_QUEUE_STATUSES = /* @__PURE__ */ new Set(["pending", "assigned"]);
2013
2034
  var HISTORICAL_QUEUE_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
2014
- function buildQueueLivenessIndex(mesh) {
2035
+ function buildQueueLivenessIndex(mesh, liveVerifiedNodes) {
2015
2036
  const nodeIds = /* @__PURE__ */ new Set();
2016
2037
  const nodeSessionIds = /* @__PURE__ */ new Map();
2017
2038
  for (const node of Array.isArray(mesh?.nodes) ? mesh.nodes : []) {
@@ -2021,20 +2042,33 @@ function buildQueueLivenessIndex(mesh) {
2021
2042
  const sessions = collectNodeSessionIds(node);
2022
2043
  if (sessions.size > 0) nodeSessionIds.set(nodeId, sessions);
2023
2044
  }
2024
- return { nodeIds, nodeSessionIds };
2045
+ const verifiedLiveNodeIds = /* @__PURE__ */ new Set();
2046
+ for (const node of Array.isArray(liveVerifiedNodes) ? liveVerifiedNodes : []) {
2047
+ if (node?.__liveProbeVerified !== true) continue;
2048
+ const nodeId = readString(node.id) || readString(node.nodeId) || readString(node.node_id);
2049
+ if (!nodeId) continue;
2050
+ verifiedLiveNodeIds.add(nodeId);
2051
+ nodeSessionIds.set(nodeId, collectNodeSessionIds(node));
2052
+ }
2053
+ return { nodeIds, nodeSessionIds, verifiedLiveNodeIds };
2025
2054
  }
2055
+ var SESSION_LIVENESS_STARTUP_GRACE_MS = 2 * 6e4;
2026
2056
  function queueAssignmentStaleReason(task, liveness) {
2027
2057
  if (task?.status !== "assigned") return void 0;
2028
2058
  const nodeId = readString(task.assignedNodeId) || readString(task.nodeId) || readString(task.node_id) || readString(task.targetNodeId);
2029
2059
  const sessionId = readString(task.assignedSessionId) || readString(task.sessionId) || readString(task.session_id) || readString(task.targetSessionId);
2060
+ const updatedAt = new Date(task.updatedAt).getTime();
2061
+ const ageMs = Number.isFinite(updatedAt) ? Date.now() - updatedAt : null;
2030
2062
  if (nodeId && liveness.nodeIds.size > 0 && !liveness.nodeIds.has(nodeId)) {
2031
2063
  return "assigned node is not present in the current mesh snapshot";
2032
2064
  }
2033
2065
  if (nodeId && sessionId && liveness.nodeSessionIds.has(nodeId) && !liveness.nodeSessionIds.get(nodeId).has(sessionId)) {
2034
- return "assigned session is not live on the assigned node";
2066
+ const verifiedByProbe = liveness.verifiedLiveNodeIds.size === 0 || liveness.verifiedLiveNodeIds.has(nodeId);
2067
+ const pastStartupGrace = ageMs === null || ageMs >= SESSION_LIVENESS_STARTUP_GRACE_MS;
2068
+ if (verifiedByProbe && pastStartupGrace) {
2069
+ return "assigned session is not live on the assigned node";
2070
+ }
2035
2071
  }
2036
- const updatedAt = new Date(task.updatedAt).getTime();
2037
- const ageMs = Number.isFinite(updatedAt) ? Date.now() - updatedAt : null;
2038
2072
  if (!nodeId && ageMs !== null && ageMs >= STALE_ASSIGNED_QUEUE_MS) {
2039
2073
  return "assigned task has no assigned node metadata";
2040
2074
  }
@@ -2210,8 +2244,8 @@ function compactActiveWorkRecords(records) {
2210
2244
  const capped = records.slice(0, COMPACT_MAX_ACTIVE_WORK_ROWS).map(compactActiveWorkRecord);
2211
2245
  return { records: capped, omitted: Math.max(0, records.length - capped.length) };
2212
2246
  }
2213
- function annotateQueueStaleness(queue, mesh) {
2214
- const liveness = buildQueueLivenessIndex(mesh);
2247
+ function annotateQueueStaleness(queue, mesh, liveVerifiedNodes) {
2248
+ const liveness = buildQueueLivenessIndex(mesh, liveVerifiedNodes);
2215
2249
  const now = Date.now();
2216
2250
  return queue.map((task) => {
2217
2251
  const taskStatus = typeof task?.status === "string" ? task.status : void 0;
@@ -2771,6 +2805,13 @@ function extractSubmodules(value, ignorePaths) {
2771
2805
  const ignoreSet = new Set(ignorePaths);
2772
2806
  return subs.filter((s) => s?.path && !ignoreSet.has(s.path));
2773
2807
  }
2808
+ function extractReporterNodeFactsQuota(value) {
2809
+ const payload = unwrapCommandPayload(value);
2810
+ const facts = payload?.reporterNodeFacts ?? value?.reporterNodeFacts;
2811
+ const quota = facts?.quota;
2812
+ if (!quota || typeof quota !== "object" || Array.isArray(quota)) return void 0;
2813
+ return Object.keys(quota).length > 0 ? quota : void 0;
2814
+ }
2774
2815
  function assignFullGitSnapshot(entry, status) {
2775
2816
  if (!status || typeof status !== "object" || Array.isArray(status)) return;
2776
2817
  entry.git = status;
@@ -3278,6 +3319,14 @@ async function collectLiveStatusSessions(ctx, node) {
3278
3319
  return [];
3279
3320
  }
3280
3321
  }
3322
+ async function collectLiveStatusSessionsVerified(ctx, node) {
3323
+ try {
3324
+ const statusResult = await commandForNode(ctx, node, "get_status_metadata", {});
3325
+ return { sessions: extractStatusMetadataSessions(statusResult), verified: true };
3326
+ } catch {
3327
+ return { sessions: [], verified: false };
3328
+ }
3329
+ }
3281
3330
  async function collectLiveStatusProbe(ctx, node) {
3282
3331
  try {
3283
3332
  const statusResult = await commandForNode(ctx, node, "get_status_metadata", {}, { statusProbe: true });
@@ -3309,6 +3358,16 @@ async function collectMeshViewQueueNodesWithLiveSessions(ctx) {
3309
3358
  }));
3310
3359
  return nodes;
3311
3360
  }
3361
+ async function collectMeshViewQueueNodesWithLiveSessionsVerified(ctx) {
3362
+ const nodes = await Promise.all(ctx.mesh.nodes.map(async (node) => {
3363
+ const { sessions: liveSessions, verified } = await collectLiveStatusSessionsVerified(ctx, node);
3364
+ if (verified) {
3365
+ return { ...node, sessions: liveSessions, __liveProbeVerified: true };
3366
+ }
3367
+ return { ...node, __liveProbeVerified: false };
3368
+ }));
3369
+ return nodes;
3370
+ }
3312
3371
  function buildBranchConvergence(mesh, node, status, dirty, uncommittedChanges) {
3313
3372
  const defaultBranch = readString(mesh.defaultBranch) ?? "main";
3314
3373
  const branch = readString(status?.branch) ?? readString(node.worktreeBranch) ?? null;
@@ -3742,6 +3801,8 @@ async function meshStatus(ctx, args = {}) {
3742
3801
  if (status?.daemonBuildBehind && typeof status.daemonBuildBehind === "object") {
3743
3802
  entry.staleDaemonBuild = status.daemonBuildBehind;
3744
3803
  }
3804
+ const reportedQuota = extractReporterNodeFactsQuota(statusResult);
3805
+ if (reportedQuota) entry.quota = reportedQuota;
3745
3806
  const submodules = extractSubmodules(statusResult, node.policy?.submoduleIgnorePaths || []);
3746
3807
  if (submodules && submodules.some((s) => s?.outOfSync)) {
3747
3808
  entry.submoduleWarning = "One or more submodules are out of sync with the parent repo. Run `git submodule update` or check deployment readiness.";
@@ -4440,12 +4501,12 @@ async function meshViewQueue(ctx, args) {
4440
4501
  const depState = (0, import_daemon_core4.describeTaskDependencyState)(task, statusById);
4441
4502
  return { ...task, ...depState };
4442
4503
  });
4443
- const fullQueue = prioritizeActiveQueueRows(annotateQueueStaleness(withDependencies, ctx.mesh));
4504
+ const liveNodes = await collectMeshViewQueueNodesWithLiveSessionsVerified(ctx);
4505
+ const fullQueue = prioritizeActiveQueueRows(annotateQueueStaleness(withDependencies, ctx.mesh, liveNodes));
4444
4506
  const queue = filterQueueForView(fullQueue, view, statusFilter);
4445
4507
  const summary = buildQueueStatusSummary(fullQueue);
4446
4508
  const visibleSummary = buildQueueStatusSummary(queue);
4447
4509
  const maintenance = buildQueueMaintenanceReport(fullQueue);
4448
- const liveNodes = await collectMeshViewQueueNodesWithLiveSessions(ctx);
4449
4510
  let ledgerEntries = (0, import_daemon_core4.readLedgerEntries)(ctx.mesh.id, { tail: 200 });
4450
4511
  let directDispatches = (0, import_daemon_core4.getActiveDirectDispatches)(ctx.mesh.id);
4451
4512
  const directReconciliation = await reconcileDirectDispatchesFromTranscriptEvidence(ctx, liveNodes, directDispatches, ledgerEntries);