@adhdev/daemon-standalone 0.9.82-rc.136 → 0.9.82-rc.138

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-BmnJCMQ2.js"></script>
10
+ <script type="module" crossorigin src="/assets/index-CNP9SlXf.js"></script>
11
11
  <link rel="modulepreload" crossorigin href="/assets/vendor-DNk1FT1R.js">
12
12
  <link rel="stylesheet" crossorigin href="/assets/index-If4eNpuX.css">
13
13
  </head>
@@ -50,6 +50,113 @@ var IPC_COMMAND_TIMEOUTS_MS = {
50
50
  fast_forward_mesh_node: 12e4,
51
51
  mesh_status: 12e4
52
52
  };
53
+ var WS_CONNECTING = 0;
54
+ var WS_OPEN = 1;
55
+ var POOL_IDLE_EVICT_MS = 5 * 6e4;
56
+ var POOL_MAX_AGE_MS = 10 * 6e4;
57
+ var connectionPool = /* @__PURE__ */ new Map();
58
+ function buildRequestId() {
59
+ return `mcp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
60
+ }
61
+ function getTimeoutMs(type, nestedCommand) {
62
+ return Math.max(
63
+ IPC_COMMAND_TIMEOUTS_MS[type] ?? DEFAULT_IPC_COMMAND_TIMEOUT_MS,
64
+ IPC_COMMAND_TIMEOUTS_MS[nestedCommand] ?? DEFAULT_IPC_COMMAND_TIMEOUT_MS
65
+ );
66
+ }
67
+ function getOrCreateConnection(WebSocketCtor, url) {
68
+ const existing = connectionPool.get(url);
69
+ if (existing) {
70
+ const { readyState } = existing.ws;
71
+ const now2 = Date.now();
72
+ const isAlive = readyState === WS_CONNECTING || readyState === WS_OPEN;
73
+ const isIdle = now2 - existing.lastUsedAt > POOL_IDLE_EVICT_MS && existing.pending.size === 0;
74
+ const isTooOld = now2 - existing.createdAt > POOL_MAX_AGE_MS && existing.pending.size === 0;
75
+ if (isAlive && !isIdle && !isTooOld) {
76
+ return existing;
77
+ }
78
+ if (isAlive && (isIdle || isTooOld)) {
79
+ try {
80
+ existing.ws.close();
81
+ } catch {
82
+ }
83
+ connectionPool.delete(url);
84
+ }
85
+ connectionPool.delete(url);
86
+ }
87
+ const now = Date.now();
88
+ const conn = {
89
+ ws: new WebSocketCtor(url),
90
+ ready: false,
91
+ commandQueue: [],
92
+ pending: /* @__PURE__ */ new Map(),
93
+ lastUsedAt: now,
94
+ createdAt: now
95
+ };
96
+ connectionPool.set(url, conn);
97
+ const drainQueue = () => {
98
+ conn.ready = true;
99
+ for (const { type, args, requestId } of conn.commandQueue) {
100
+ conn.ws.send(JSON.stringify({ type: "ext:command", payload: { command: type, args, requestId } }));
101
+ }
102
+ conn.commandQueue = [];
103
+ };
104
+ let tornDown = false;
105
+ const teardown = (error) => {
106
+ if (tornDown) return;
107
+ tornDown = true;
108
+ connectionPool.delete(url);
109
+ conn.ready = false;
110
+ for (const [, req] of conn.pending) {
111
+ clearTimeout(req.timer);
112
+ req.reject(error);
113
+ }
114
+ conn.pending.clear();
115
+ conn.commandQueue = [];
116
+ };
117
+ conn.ws.addEventListener("open", () => {
118
+ conn.ws.send(JSON.stringify({
119
+ type: "ext:register",
120
+ payload: {
121
+ ideType: "mcp-server",
122
+ ideVersion: "1.0.0",
123
+ extensionVersion: "1.0.0",
124
+ instanceId: `mcp-server-${process.pid}`,
125
+ machineId: "mcp-server",
126
+ workspaceFolders: []
127
+ }
128
+ }));
129
+ });
130
+ conn.ws.addEventListener("message", (event) => {
131
+ try {
132
+ const raw = typeof event.data === "string" ? event.data : String(event.data);
133
+ const msg = JSON.parse(raw);
134
+ if (msg?.type === "daemon:welcome") {
135
+ drainQueue();
136
+ return;
137
+ }
138
+ if (msg?.type !== "ext:command_result") return;
139
+ const req = conn.pending.get(msg?.payload?.requestId);
140
+ if (!req) return;
141
+ conn.pending.delete(msg.payload.requestId);
142
+ clearTimeout(req.timer);
143
+ const payload = msg.payload;
144
+ if (payload?.success === false) {
145
+ req.reject(new Error(payload.error || "Daemon IPC command failed"));
146
+ } else {
147
+ req.resolve(payload?.result ?? payload);
148
+ }
149
+ } catch {
150
+ }
151
+ });
152
+ conn.ws.addEventListener("error", () => {
153
+ teardown(new Error(`Cannot connect to daemon IPC at ${url}`));
154
+ });
155
+ conn.ws.addEventListener("close", () => {
156
+ teardown(new Error(`Daemon IPC connection closed: ${url}`));
157
+ });
158
+ return conn;
159
+ }
53
160
  var IpcTransport = class {
54
161
  port;
55
162
  path;
@@ -78,86 +185,41 @@ var IpcTransport = class {
78
185
  args
79
186
  });
80
187
  }
81
- async sendIpcCommand(type, args) {
188
+ sendIpcCommand(type, args) {
82
189
  const WebSocketCtor = globalThis.WebSocket;
83
190
  if (!WebSocketCtor) {
84
- throw new Error("WebSocket is not available in this Node runtime; Node 20+ is required for daemon IPC mode");
191
+ return Promise.reject(new Error("WebSocket is not available in this Node runtime; Node 20+ is required for daemon IPC mode"));
85
192
  }
193
+ const requestId = buildRequestId();
194
+ const nestedCommand = typeof args?.command === "string" ? args.command : "";
195
+ const timeoutMs = getTimeoutMs(type, nestedCommand);
196
+ const targetDaemonId = typeof args?.targetDaemonId === "string" ? args.targetDaemonId : "";
197
+ const diagnosticParts = [
198
+ `command='${type}'`,
199
+ ...nestedCommand ? [`relayedCommand='${nestedCommand}'`] : [],
200
+ ...targetDaemonId ? [`targetDaemonId='${targetDaemonId.slice(0, 12)}'`] : [],
201
+ ...typeof args?.nodeId === "string" ? [`nodeId='${args.nodeId}'`] : [],
202
+ ...typeof args?.workspace === "string" ? [`workspace='${args.workspace}'`] : []
203
+ ];
204
+ const url = `ws://127.0.0.1:${this.port}${this.path}`;
86
205
  return new Promise((resolve, reject) => {
87
- const requestId = `mcp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
88
- const ws = new WebSocketCtor(`ws://127.0.0.1:${this.port}${this.path}`);
89
- let settled = false;
90
- const finish = (fn) => {
91
- if (settled) return;
92
- settled = true;
93
- clearTimeout(timeout);
94
- try {
95
- ws.close();
96
- } catch {
97
- }
98
- fn();
99
- };
100
- const nestedCommand = typeof args?.command === "string" ? args.command : "";
101
- const targetDaemonId = typeof args?.targetDaemonId === "string" ? args.targetDaemonId : "";
102
- const effectiveType = type === "mesh_relay_command" && nestedCommand ? nestedCommand : type;
103
- const timeoutMs = Math.max(
104
- IPC_COMMAND_TIMEOUTS_MS[type] ?? DEFAULT_IPC_COMMAND_TIMEOUT_MS,
105
- IPC_COMMAND_TIMEOUTS_MS[effectiveType] ?? DEFAULT_IPC_COMMAND_TIMEOUT_MS
106
- );
107
- const diagnosticParts = [
108
- `command='${type}'`,
109
- ...nestedCommand ? [`relayedCommand='${nestedCommand}'`] : [],
110
- ...targetDaemonId ? [`targetDaemonId='${targetDaemonId.slice(0, 12)}'`] : [],
111
- ...typeof args?.nodeId === "string" ? [`nodeId='${args.nodeId}'`] : [],
112
- ...typeof args?.workspace === "string" ? [`workspace='${args.workspace}'`] : []
113
- ];
114
- const timeout = setTimeout(() => {
115
- finish(() => reject(new Error(`Daemon IPC ${diagnosticParts.join(" ")} timed out after ${Math.round(timeoutMs / 1e3)}s (requestId=${requestId})`)));
206
+ let conn;
207
+ try {
208
+ conn = getOrCreateConnection(WebSocketCtor, url);
209
+ } catch (e) {
210
+ return reject(new Error(`Failed to create IPC connection: ${e?.message || e}`));
211
+ }
212
+ const timer = setTimeout(() => {
213
+ conn.pending.delete(requestId);
214
+ reject(new Error(`Daemon IPC ${diagnosticParts.join(" ")} timed out after ${Math.round(timeoutMs / 1e3)}s (requestId=${requestId})`));
116
215
  }, timeoutMs);
117
- let commandSent = false;
118
- const send = () => {
119
- if (commandSent) return;
120
- commandSent = true;
121
- ws.send(JSON.stringify({
122
- type: "ext:command",
123
- payload: { command: type, args, requestId }
124
- }));
125
- };
126
- ws.addEventListener("open", () => {
127
- ws.send(JSON.stringify({
128
- type: "ext:register",
129
- payload: {
130
- ideType: "mcp-server",
131
- ideVersion: "1.0.0",
132
- extensionVersion: "1.0.0",
133
- instanceId: `mcp-server-${process.pid}`,
134
- machineId: "mcp-server",
135
- workspaceFolders: []
136
- }
137
- }));
138
- });
139
- ws.addEventListener("message", (event) => {
140
- try {
141
- const raw = typeof event.data === "string" ? event.data : String(event.data);
142
- const msg = JSON.parse(raw);
143
- if (msg?.type === "daemon:welcome") {
144
- send();
145
- return;
146
- }
147
- if (msg?.type !== "ext:command_result") return;
148
- if (msg?.payload?.requestId !== requestId) return;
149
- const payload = msg.payload;
150
- if (payload?.success === false) {
151
- finish(() => reject(new Error(payload.error || `Daemon IPC command '${type}' failed`)));
152
- return;
153
- }
154
- finish(() => resolve(payload?.result ?? payload));
155
- } catch {
156
- }
157
- });
158
- ws.addEventListener("error", () => {
159
- finish(() => reject(new Error(`Cannot connect to daemon IPC at ws://127.0.0.1:${this.port}${this.path}`)));
160
- });
216
+ conn.pending.set(requestId, { resolve, reject, timer });
217
+ conn.lastUsedAt = Date.now();
218
+ if (conn.ready) {
219
+ conn.ws.send(JSON.stringify({ type: "ext:command", payload: { command: type, args, requestId } }));
220
+ } else {
221
+ conn.commandQueue.push({ type, args, requestId });
222
+ }
161
223
  });
162
224
  }
163
225
  };
@@ -266,7 +328,17 @@ function annotateRapidReadChatAdvisory(payload, options) {
266
328
 
267
329
  // src/tools/mesh-tools.ts
268
330
  var import_daemon_core = require("@adhdev/daemon-core");
331
+ var SESSION_PROVIDER_METADATA_TTL_MS = 30 * 6e4;
269
332
  var meshSessionProviderMetadata = /* @__PURE__ */ new Map();
333
+ function getSessionMetadata(key) {
334
+ const entry = meshSessionProviderMetadata.get(key);
335
+ if (!entry) return void 0;
336
+ if (entry.expiresAt <= Date.now()) {
337
+ meshSessionProviderMetadata.delete(key);
338
+ return void 0;
339
+ }
340
+ return entry;
341
+ }
270
342
  var ACTIVE_WORK_POLLING_BACKOFF_MS = 6e4;
271
343
  function buildActiveWorkPollingGuidance(summary, now = Date.now()) {
272
344
  if (!summary || summary.generatingCount <= 0) return void 0;
@@ -313,7 +385,7 @@ var OLD_HISTORICAL_QUEUE_RECORD_MS = 7 * 24 * 60 * 6e4;
313
385
  var ACTIVE_QUEUE_STATUSES = /* @__PURE__ */ new Set(["pending", "assigned"]);
314
386
  var HISTORICAL_QUEUE_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
315
387
  async function refreshMeshFromDaemon(ctx) {
316
- if (!(ctx.transport instanceof IpcTransport)) return;
388
+ if (!isLocalTransport(ctx.transport)) return;
317
389
  try {
318
390
  const result = await ctx.transport.command("get_mesh", { meshId: ctx.mesh.id });
319
391
  if (!result?.success || !Array.isArray(result.mesh?.nodes)) return;
@@ -728,9 +800,8 @@ function isIdleSessionRecord(session) {
728
800
  function isMeshOwnedDelegateSession(session, meshId, nodeId) {
729
801
  const settings = session?.settings;
730
802
  const sessionMeshId = typeof settings?.meshNodeFor === "string" ? settings.meshNodeFor.trim() : "";
731
- const coordinatorDaemonId = typeof settings?.meshCoordinatorDaemonId === "string" ? settings.meshCoordinatorDaemonId.trim() : "";
732
803
  const sessionNodeId = typeof settings?.meshNodeId === "string" ? settings.meshNodeId.trim() : "";
733
- if (sessionMeshId !== meshId || !coordinatorDaemonId) return false;
804
+ if (sessionMeshId !== meshId) return false;
734
805
  return !sessionNodeId || sessionNodeId === nodeId;
735
806
  }
736
807
  function chooseDispatchableSession(sessions, providerType, meshId, nodeId) {
@@ -1220,10 +1291,11 @@ function rememberMeshSessionProviderMetadata(nodeId, runtimeSessionId, metadata)
1220
1291
  const providerType = readString(metadata.providerType);
1221
1292
  const providerSessionId = readString(metadata.providerSessionId);
1222
1293
  if (!providerType && !providerSessionId) return;
1223
- const existing = meshSessionProviderMetadata.get(meshSessionCacheKey(keyNodeId, keySessionId)) || { providerType: "" };
1294
+ const existing = getSessionMetadata(meshSessionCacheKey(keyNodeId, keySessionId)) || { providerType: "" };
1224
1295
  meshSessionProviderMetadata.set(meshSessionCacheKey(keyNodeId, keySessionId), {
1225
1296
  providerType: providerType || existing.providerType,
1226
- providerSessionId: providerSessionId || existing.providerSessionId
1297
+ providerSessionId: providerSessionId || existing.providerSessionId,
1298
+ expiresAt: Date.now() + SESSION_PROVIDER_METADATA_TTL_MS
1227
1299
  });
1228
1300
  }
1229
1301
  function rememberMeshSessionProviderMetadataFromEvent(event) {
@@ -1236,7 +1308,7 @@ function rememberMeshSessionProviderMetadataFromEvent(event) {
1236
1308
  });
1237
1309
  }
1238
1310
  function resolveMeshSessionProviderMetadataFromLedger(ctx, nodeId, runtimeSessionId) {
1239
- const entries = (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 500 });
1311
+ const entries = (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 50 });
1240
1312
  for (let i = entries.length - 1; i >= 0; i -= 1) {
1241
1313
  const entry = entries[i];
1242
1314
  const payload = entry.payload && typeof entry.payload === "object" && !Array.isArray(entry.payload) ? entry.payload : {};
@@ -1255,7 +1327,7 @@ function resolveMeshSessionProviderMetadataFromLedger(ctx, nodeId, runtimeSessio
1255
1327
  return void 0;
1256
1328
  }
1257
1329
  function resolveMeshSessionProviderMetadata(ctx, nodeId, runtimeSessionId) {
1258
- const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(nodeId, runtimeSessionId));
1330
+ const cached = getSessionMetadata(meshSessionCacheKey(nodeId, runtimeSessionId));
1259
1331
  if (cached?.providerType || cached?.providerSessionId) return cached;
1260
1332
  const fromLedger = resolveMeshSessionProviderMetadataFromLedger(ctx, nodeId, runtimeSessionId);
1261
1333
  if (fromLedger) rememberMeshSessionProviderMetadata(nodeId, runtimeSessionId, fromLedger);
@@ -1273,6 +1345,20 @@ function isGitStatusDirty(status) {
1273
1345
  if (typeof status?.dirty === "boolean") return status.dirty;
1274
1346
  return countUncommittedChanges(status) > 0;
1275
1347
  }
1348
+ function slimLedgerPayload(payload) {
1349
+ const slim = {};
1350
+ for (const [k, v] of Object.entries(payload)) {
1351
+ if (k === "message" || k === "taskSummary") {
1352
+ slim[k] = typeof v === "string" && v.length > 200 ? v.slice(0, 200) + "\u2026" : v;
1353
+ } else if (k === "evidence" || k === "workerResult" || k === "gitStatus" || k === "validationResults") {
1354
+ } else if (k === "finalSummary") {
1355
+ slim[k] = typeof v === "string" && v.length > 300 ? v.slice(0, 300) + "\u2026" : v;
1356
+ } else {
1357
+ slim[k] = v;
1358
+ }
1359
+ }
1360
+ return slim;
1361
+ }
1276
1362
  function readRelatedRepos(node) {
1277
1363
  const raw = Array.isArray(node.relatedRepos) ? node.relatedRepos : Array.isArray(node.policy?.relatedRepos) ? node.policy.relatedRepos : [];
1278
1364
  return raw.map((entry) => ({
@@ -1592,7 +1678,7 @@ async function drainCoordinatorPendingEvents(ctx, opts) {
1592
1678
  return surfacedEvents;
1593
1679
  }
1594
1680
  if (isLocalTransport(ctx.transport)) {
1595
- const events = (0, import_daemon_core.drainPendingMeshCoordinatorEvents)(ctx.mesh.id).filter(matchesCurrentMesh);
1681
+ const events = (0, import_daemon_core.drainPendingMeshCoordinatorEvents)(ctx.mesh.id, ctx.localDaemonId).filter(matchesCurrentMesh);
1596
1682
  events.forEach(rememberMeshSessionProviderMetadataFromEvent);
1597
1683
  return events;
1598
1684
  }
@@ -1951,9 +2037,8 @@ var ALL_MESH_TOOLS = [
1951
2037
  async function meshStatus(ctx, args = {}) {
1952
2038
  await refreshMeshFromDaemon(ctx);
1953
2039
  const { mesh, transport } = ctx;
1954
- const results = [];
1955
2040
  const ledgerSummary = (0, import_daemon_core.getLedgerSummary)(mesh.id);
1956
- for (const node of mesh.nodes) {
2041
+ const results = await Promise.all(mesh.nodes.map(async (node) => {
1957
2042
  const entry = {
1958
2043
  nodeId: node.id,
1959
2044
  workspace: node.workspace,
@@ -2027,7 +2112,7 @@ async function meshStatus(ctx, args = {}) {
2027
2112
  if (recoveryContext.consecutiveNodeFailures > 0) {
2028
2113
  entry.recoveryHints = {
2029
2114
  consecutiveFailures: recoveryContext.consecutiveNodeFailures,
2030
- lastTaskMessage: recoveryContext.lastTaskMessage,
2115
+ lastTaskMessage: typeof recoveryContext.lastTaskMessage === "string" ? recoveryContext.lastTaskMessage.slice(0, 100) + (recoveryContext.lastTaskMessage.length > 100 ? "\u2026" : "") : recoveryContext.lastTaskMessage,
2031
2116
  advice: recoveryContext.advice,
2032
2117
  retryRecommended: recoveryContext.retryRecommended
2033
2118
  };
@@ -2069,11 +2154,16 @@ async function meshStatus(ctx, args = {}) {
2069
2154
  if (relatedRepos.length) entry.relatedRepos = relatedRepos;
2070
2155
  const liveSessions = await collectLiveStatusSessions(ctx, node);
2071
2156
  if (liveSessions.length > 0) {
2072
- entry.sessions = liveSessions;
2157
+ entry.sessions = liveSessions.map((s) => ({
2158
+ id: s.instanceId ?? s.id ?? s.sessionId,
2159
+ status: s.status ?? s.lifecycle ?? s.state,
2160
+ providerType: s.providerType ?? s.cliType ?? s.type,
2161
+ ...s.activeChat?.status ? { chatStatus: s.activeChat.status } : {}
2162
+ })).filter((s) => s.id);
2073
2163
  }
2074
- results.push(entry);
2075
- }
2076
- const ledgerEntries = (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail: 500 });
2164
+ return entry;
2165
+ }));
2166
+ const ledgerEntries = (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail: 200 });
2077
2167
  const activeWorkEvidence = (0, import_daemon_core.buildMeshActiveWork)({
2078
2168
  meshId: mesh.id,
2079
2169
  queue: (0, import_daemon_core.getQueue)(mesh.id),
@@ -2101,7 +2191,8 @@ async function meshStatus(ctx, args = {}) {
2101
2191
  activeWork: activeWorkEvidence.activeWork,
2102
2192
  staleDirectWorkSummary,
2103
2193
  ...args.includeStaleDirectWorkDetails === true ? { staleDirectWork: activeWorkEvidence.staleDirectWork } : {},
2104
- terminalDirectWork: activeWorkEvidence.terminalDirectWork,
2194
+ // terminalDirectWork is historical (completed/failed direct dispatches) — opt-in only.
2195
+ ...args.includeTerminalDirectWork === true ? { terminalDirectWork: activeWorkEvidence.terminalDirectWork } : {},
2105
2196
  activeWorkSummary: activeWorkEvidence.summary,
2106
2197
  ...pollingGuidance ? { pollingGuidance } : {},
2107
2198
  branchConvergenceSummary: summarizeBranchConvergence(results)
@@ -2132,7 +2223,11 @@ async function meshTaskHistory(ctx, args) {
2132
2223
  const pendingEvents = await drainCoordinatorPendingEvents(ctx);
2133
2224
  const tail = typeof args.tail === "number" && args.tail > 0 ? args.tail : 20;
2134
2225
  const kind = typeof args.kind === "string" && args.kind.trim() ? [args.kind.trim()] : void 0;
2135
- const entries = (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail, kind });
2226
+ const rawEntries = (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail, kind });
2227
+ const entries = rawEntries.map((e) => ({
2228
+ ...e,
2229
+ payload: e.payload ? slimLedgerPayload(e.payload) : e.payload
2230
+ }));
2136
2231
  const summary = (0, import_daemon_core.getLedgerSummary)(mesh.id);
2137
2232
  return JSON.stringify({
2138
2233
  meshId: mesh.id,
@@ -2282,7 +2377,21 @@ async function meshEnqueueTask(ctx, args) {
2282
2377
  } catch {
2283
2378
  }
2284
2379
  }
2285
- }).catch(() => {
2380
+ }).catch((err) => {
2381
+ try {
2382
+ (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
2383
+ kind: "p2p_dispatch_failed",
2384
+ nodeId: node.id,
2385
+ payload: {
2386
+ source: "queue",
2387
+ via: "p2p_direct",
2388
+ taskId: task.id,
2389
+ error: err?.message || String(err),
2390
+ dispatchFailedAt: (/* @__PURE__ */ new Date()).toISOString()
2391
+ }
2392
+ });
2393
+ } catch {
2394
+ }
2286
2395
  })
2287
2396
  );
2288
2397
  }
@@ -2310,12 +2419,25 @@ async function meshViewQueue(ctx, args) {
2310
2419
  const visibleSummary = buildQueueStatusSummary(queue);
2311
2420
  const maintenance = buildQueueMaintenanceReport(fullQueue);
2312
2421
  const liveNodes = await collectMeshViewQueueNodesWithLiveSessions(ctx);
2422
+ (0, import_daemon_core.markStaleDirectDispatches)(ctx.mesh.id);
2423
+ const ledgerEntries = (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 200 });
2424
+ const directDispatches = (0, import_daemon_core.getActiveDirectDispatches)(ctx.mesh.id);
2313
2425
  const activeWorkEvidence = (0, import_daemon_core.buildMeshActiveWork)({
2314
2426
  meshId: ctx.mesh.id,
2315
2427
  queue: fullQueue,
2316
- ledgerEntries: (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 500 }),
2428
+ ledgerEntries,
2429
+ // Always pass BeadsDB records (may be empty). buildMeshActiveWork uses them for local
2430
+ // dispatches and falls through to ledger scan for remote P2P dispatches not in BeadsDB.
2431
+ directDispatches,
2317
2432
  nodes: liveNodes
2318
2433
  });
2434
+ const recentDispatchFailures = ledgerEntries.filter((e) => e.kind === "p2p_dispatch_failed").slice(-20).map((e) => ({
2435
+ nodeId: e.nodeId,
2436
+ taskId: e.payload?.taskId,
2437
+ error: e.payload?.error,
2438
+ via: e.payload?.via,
2439
+ failedAt: e.payload?.dispatchFailedAt || e.timestamp
2440
+ }));
2319
2441
  const staleAssignedTasks = maintenance.staleAssignedTasks || [];
2320
2442
  const requestedHistoricalRows = queue.some((task) => HISTORICAL_QUEUE_STATUSES.has(String(task?.status || "")));
2321
2443
  const pollingGuidance = buildActiveWorkPollingGuidance(activeWorkEvidence.summary);
@@ -2333,25 +2455,20 @@ async function meshViewQueue(ctx, args) {
2333
2455
  filtered: Boolean(statusFilter?.length) || view !== "all"
2334
2456
  },
2335
2457
  queue,
2336
- visibleQueue: queue,
2337
2458
  activeWork: activeWorkEvidence.activeWork,
2338
2459
  staleDirectWork: activeWorkEvidence.staleDirectWork,
2339
2460
  activeWorkSummary: activeWorkEvidence.summary,
2340
2461
  ...pollingGuidance ? { pollingGuidance } : {},
2341
- visibleSummary,
2342
2462
  summary,
2343
- activeCounts: summary.activeCounts,
2344
- historicalCounts: summary.historicalCounts,
2345
- activeCount: summary.activeCount,
2346
- historicalCount: summary.historicalCount,
2347
- visibleActiveCounts: visibleSummary.activeCounts,
2348
- visibleHistoricalCounts: visibleSummary.historicalCounts,
2349
- visibleActiveCount: visibleSummary.activeCount,
2350
- visibleHistoricalCount: visibleSummary.historicalCount,
2351
2463
  staleAssignedTasks,
2352
2464
  staleAssignedCount: maintenance.staleAssignedCount,
2353
2465
  queueMaintenance: maintenance,
2354
2466
  cleanupDryRun: maintenance,
2467
+ ...recentDispatchFailures.length > 0 ? {
2468
+ recentDispatchFailures,
2469
+ dispatchFailureCount: recentDispatchFailures.length,
2470
+ dispatchFailureNote: "Remote P2P dispatch attempts that failed. Affected tasks remain pending and may require mesh_queue_requeue if no idle session picks them up."
2471
+ } : {},
2355
2472
  ...view === "active" || statusFilter?.some((status) => ACTIVE_QUEUE_STATUSES.has(status)) ? {
2356
2473
  activeQueue: queue.filter((task) => ACTIVE_QUEUE_STATUSES.has(String(task?.status || "")))
2357
2474
  } : {},
@@ -2489,7 +2606,7 @@ async function meshSendTask(ctx, args) {
2489
2606
  }
2490
2607
  const isLocalNode = isLocalControlPlaneNode(ctx, node);
2491
2608
  if (ctx.transport instanceof IpcTransport && node.daemonId && !isLocalNode) {
2492
- const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id || ""));
2609
+ const cached = getSessionMetadata(meshSessionCacheKey(args.node_id, args.session_id || ""));
2493
2610
  const taskId = (0, import_node_crypto.randomUUID)();
2494
2611
  const result2 = await ipcDispatchToRemoteAgent(ctx, node, {
2495
2612
  session_id: args.session_id,
@@ -2527,7 +2644,7 @@ async function meshSendTask(ctx, args) {
2527
2644
  });
2528
2645
  }
2529
2646
  if (args.session_id && isLocalTransport(ctx.transport)) {
2530
- const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
2647
+ const cached = getSessionMetadata(meshSessionCacheKey(args.node_id, args.session_id));
2531
2648
  let resolvedProviderType = cached?.providerType || "";
2532
2649
  if (!resolvedProviderType) {
2533
2650
  let explicitSession = explicitTargetSession;
@@ -2582,7 +2699,8 @@ async function meshSendTask(ctx, args) {
2582
2699
  if (resolvedProviderType) {
2583
2700
  meshSessionProviderMetadata.set(meshSessionCacheKey(args.node_id, args.session_id), {
2584
2701
  providerType: resolvedProviderType,
2585
- providerSessionId: readString(explicitSession?.providerSessionId) || void 0
2702
+ providerSessionId: readString(explicitSession?.providerSessionId) || void 0,
2703
+ expiresAt: Date.now() + SESSION_PROVIDER_METADATA_TTL_MS
2586
2704
  });
2587
2705
  }
2588
2706
  }
@@ -2621,6 +2739,7 @@ async function meshSendTask(ctx, args) {
2621
2739
  });
2622
2740
  }
2623
2741
  const taskId = (0, import_node_crypto.randomUUID)();
2742
+ const dispatchedAt = (/* @__PURE__ */ new Date()).toISOString();
2624
2743
  try {
2625
2744
  (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
2626
2745
  kind: "task_dispatched",
@@ -2637,6 +2756,17 @@ async function meshSendTask(ctx, args) {
2637
2756
  });
2638
2757
  } catch {
2639
2758
  }
2759
+ (0, import_daemon_core.insertDirectDispatch)(ctx.mesh.id, {
2760
+ taskId,
2761
+ nodeId: args.node_id,
2762
+ sessionId: args.session_id,
2763
+ providerType: resolvedProviderType || void 0,
2764
+ message: args.message,
2765
+ taskMode: taskMode || void 0,
2766
+ via: "local_direct",
2767
+ dispatchedToIdleSession: sessionWasIdle,
2768
+ dispatchedAt
2769
+ });
2640
2770
  return JSON.stringify({
2641
2771
  success: true,
2642
2772
  dispatched: true,
@@ -2662,7 +2792,7 @@ async function meshSendTask(ctx, args) {
2662
2792
  ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
2663
2793
  });
2664
2794
  }
2665
- const pendingEvents = isLocalTransport(ctx.transport) ? (0, import_daemon_core.drainPendingMeshCoordinatorEvents)(ctx.mesh.id) : [];
2795
+ const pendingEvents = isLocalTransport(ctx.transport) ? (0, import_daemon_core.drainPendingMeshCoordinatorEvents)(ctx.mesh.id, ctx.localDaemonId) : [];
2666
2796
  const result = { success: true, source: "queue", nodeId: args.node_id, taskId: task.id, status: task.status, taskMode: task.taskMode };
2667
2797
  if (pendingEvents.length > 0) {
2668
2798
  result.pendingCoordinatorEvents = pendingEvents;
@@ -2695,18 +2825,19 @@ async function meshReadChat(ctx, args) {
2695
2825
  workspace: node.workspace,
2696
2826
  ...cached?.providerType ? { agentType: cached.providerType, providerType: cached.providerType } : {},
2697
2827
  ...providerSessionId ? { providerSessionId } : {},
2698
- tailLimit: args.tail ?? 10
2828
+ tailLimit: args.tail ?? 3
2699
2829
  });
2700
2830
  const payload = annotateRapidReadChatAdvisory(unwrapCommandPayload(result), {
2701
2831
  key: `mesh:${args.node_id}:${args.session_id}`,
2702
2832
  toolName: "mesh_read_chat",
2703
2833
  completionCallbackExpected: true
2704
2834
  });
2705
- if (args.compact) {
2835
+ const useCompact = args.compact !== false;
2836
+ if (useCompact) {
2706
2837
  const compactPayload = compactChatPayload(payload, {
2707
2838
  nodeId: args.node_id,
2708
2839
  sessionId: args.session_id,
2709
- limit: args.tail ?? 10
2840
+ limit: args.tail ?? 3
2710
2841
  });
2711
2842
  return JSON.stringify(
2712
2843
  payload.pollingAdvisory ? { ...compactPayload, pollingAdvisory: payload.pollingAdvisory } : compactPayload,
@@ -2719,7 +2850,7 @@ async function meshReadChat(ctx, args) {
2719
2850
  try {
2720
2851
  const targetId = `${node.daemonId}:session:${args.session_id}`;
2721
2852
  const res = await ctx.transport.readChat(targetId, {
2722
- limit: args.tail ?? 10,
2853
+ limit: args.tail ?? 3,
2723
2854
  sessionId: args.session_id
2724
2855
  });
2725
2856
  return JSON.stringify(res, null, 2);
@@ -2821,7 +2952,8 @@ async function meshLaunchSession(ctx, args) {
2821
2952
  if (runtimeSessionId) {
2822
2953
  meshSessionProviderMetadata.set(meshSessionCacheKey(args.node_id, runtimeSessionId), {
2823
2954
  providerType: resolvedProviderType,
2824
- ...providerSessionId ? { providerSessionId } : {}
2955
+ ...providerSessionId ? { providerSessionId } : {},
2956
+ expiresAt: Date.now() + SESSION_PROVIDER_METADATA_TTL_MS
2825
2957
  });
2826
2958
  }
2827
2959
  try {
@@ -3044,7 +3176,7 @@ async function meshCheckpoint(ctx, args) {
3044
3176
  async function meshApprove(ctx, args) {
3045
3177
  const node = await findNodeWithRefresh(ctx, args.node_id);
3046
3178
  if (isLocalTransport(ctx.transport)) {
3047
- const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
3179
+ const cached = getSessionMetadata(meshSessionCacheKey(args.node_id, args.session_id));
3048
3180
  const providerSessionId = cached?.providerSessionId;
3049
3181
  const result = await commandForNode(ctx, node, "resolve_action", {
3050
3182
  sessionId: args.session_id,