adhdev 0.9.82-rc.2 → 0.9.82-rc.21
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 +958 -499
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +477 -109
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/vendor/mcp-server/index.js +175 -19
- package/vendor/mcp-server/index.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adhdev",
|
|
3
|
-
"version": "0.9.82-rc.
|
|
3
|
+
"version": "0.9.82-rc.21",
|
|
4
4
|
"description": "ADHDev — Agent Dashboard Hub for Dev. Remote-control AI coding agents from anywhere.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"node": ">=18"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@adhdev/daemon-core": "0.9.82-rc.
|
|
50
|
+
"@adhdev/daemon-core": "0.9.82-rc.21",
|
|
51
51
|
"@adhdev/ghostty-vt-node": "*",
|
|
52
52
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
53
53
|
"@xterm/addon-serialize": "^0.14.0",
|
|
@@ -143,6 +143,10 @@ function isLocalTransport(transport) {
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
// src/tools/chat-compact.ts
|
|
146
|
+
function isAssistantLike(message) {
|
|
147
|
+
const role = String(message?.role ?? "").toLowerCase();
|
|
148
|
+
return role === "assistant" || role === "agent";
|
|
149
|
+
}
|
|
146
150
|
function messageContent(message) {
|
|
147
151
|
const content = message?.content;
|
|
148
152
|
if (typeof content === "string") return content;
|
|
@@ -161,16 +165,22 @@ function isCoordinatorVisibleMessage(message) {
|
|
|
161
165
|
if (meta?.internal === true || meta?.debug === true || meta?.control === true || meta?.userVisible === false || meta?.user_visible === false) return false;
|
|
162
166
|
return role === "user" || role === "assistant" || role === "agent";
|
|
163
167
|
}
|
|
168
|
+
function buildCompactMessageTail(visibleMessages, opts) {
|
|
169
|
+
const summary = typeof opts.summary === "string" ? opts.summary.trim() : "";
|
|
170
|
+
const shouldOmitSummaryMessage = !!summary && !!opts.finalAssistant && isAssistantLike(opts.finalAssistant) && messageContent(opts.finalAssistant).trim() === summary;
|
|
171
|
+
const sourceMessages = shouldOmitSummaryMessage ? visibleMessages.filter((message) => message !== opts.finalAssistant) : visibleMessages;
|
|
172
|
+
return sourceMessages.slice(-opts.limit);
|
|
173
|
+
}
|
|
164
174
|
function compactChatPayload(payload, opts = {}) {
|
|
165
175
|
const rawMessages = Array.isArray(payload?.messages) ? payload.messages : [];
|
|
166
176
|
const visible = rawMessages.filter(isCoordinatorVisibleMessage);
|
|
167
177
|
const limit = Math.max(1, Math.min(opts.limit ?? 10, 10));
|
|
168
|
-
const messages = visible.slice(-limit);
|
|
169
178
|
const finalAssistant = [...visible].reverse().find((message) => {
|
|
170
179
|
const role = String(message?.role ?? "").toLowerCase();
|
|
171
180
|
return (role === "assistant" || role === "agent") && messageContent(message).trim();
|
|
172
181
|
});
|
|
173
182
|
const summary = typeof payload?.summary === "string" && payload.summary.trim() ? payload.summary.trim() : messageContent(finalAssistant).trim();
|
|
183
|
+
const messages = buildCompactMessageTail(visible, { summary, finalAssistant, limit });
|
|
174
184
|
return {
|
|
175
185
|
success: payload?.success !== false,
|
|
176
186
|
compact: true,
|
|
@@ -246,15 +256,24 @@ async function refreshMeshFromDaemon(ctx) {
|
|
|
246
256
|
const result = await ctx.transport.command("get_mesh", { meshId: ctx.mesh.id });
|
|
247
257
|
if (!result?.success || !Array.isArray(result.mesh?.nodes)) return;
|
|
248
258
|
const refreshedNodes = result.mesh.nodes.filter((n) => n?.id).map((n) => n);
|
|
249
|
-
if (!refreshedNodes.length) return;
|
|
250
259
|
ctx.mesh.nodes.splice(0, ctx.mesh.nodes.length, ...refreshedNodes);
|
|
251
260
|
ctx.mesh.updatedAt = result.mesh.updatedAt ?? ctx.mesh.updatedAt;
|
|
252
261
|
} catch {
|
|
253
262
|
}
|
|
254
263
|
}
|
|
264
|
+
async function syncCoordinatorDaemonMeshCache(ctx) {
|
|
265
|
+
if (!(ctx.transport instanceof IpcTransport)) return;
|
|
266
|
+
try {
|
|
267
|
+
await ctx.transport.command("get_mesh", {
|
|
268
|
+
meshId: ctx.mesh.id,
|
|
269
|
+
inlineMesh: ctx.mesh
|
|
270
|
+
});
|
|
271
|
+
} catch {
|
|
272
|
+
}
|
|
273
|
+
}
|
|
255
274
|
async function findNodeWithRefresh(ctx, nodeId) {
|
|
256
275
|
const hit = ctx.mesh.nodes.find((n) => n.id === nodeId);
|
|
257
|
-
if (hit) return hit;
|
|
276
|
+
if (hit && !hit.isLocalWorktree) return hit;
|
|
258
277
|
await refreshMeshFromDaemon(ctx);
|
|
259
278
|
const refreshed = ctx.mesh.nodes.find((n) => n.id === nodeId);
|
|
260
279
|
if (!refreshed) throw new Error(`Node '${nodeId}' is not a member of mesh '${ctx.mesh.name}'`);
|
|
@@ -262,7 +281,7 @@ async function findNodeWithRefresh(ctx, nodeId) {
|
|
|
262
281
|
}
|
|
263
282
|
async function findOptionalNodeWithRefresh(ctx, nodeId) {
|
|
264
283
|
const hit = ctx.mesh.nodes.find((n) => n.id === nodeId);
|
|
265
|
-
if (hit) return hit;
|
|
284
|
+
if (hit && !hit.isLocalWorktree) return hit;
|
|
266
285
|
await refreshMeshFromDaemon(ctx);
|
|
267
286
|
return ctx.mesh.nodes.find((n) => n.id === nodeId) ?? null;
|
|
268
287
|
}
|
|
@@ -314,9 +333,26 @@ function buildMissingNodeReadChatRecovery(ctx, args) {
|
|
|
314
333
|
readDebugLocator: readString(lastTerminal?.payload?.readDebugLocator) || readString(lastTerminal?.payload?.debugBundlePath)
|
|
315
334
|
};
|
|
316
335
|
if (finalSummary) {
|
|
336
|
+
if (args.compact === true) {
|
|
337
|
+
return {
|
|
338
|
+
...compactChatPayload({
|
|
339
|
+
success: true,
|
|
340
|
+
status: "idle",
|
|
341
|
+
providerSessionId,
|
|
342
|
+
summary: finalSummary,
|
|
343
|
+
messages: [{ role: "assistant", content: finalSummary, isHistorical: true }]
|
|
344
|
+
}, {
|
|
345
|
+
nodeId: args.node_id,
|
|
346
|
+
sessionId: args.session_id,
|
|
347
|
+
limit: args.tail ?? 10
|
|
348
|
+
}),
|
|
349
|
+
recoveredFromLedger: true,
|
|
350
|
+
ledger
|
|
351
|
+
};
|
|
352
|
+
}
|
|
317
353
|
return {
|
|
318
354
|
success: true,
|
|
319
|
-
compact:
|
|
355
|
+
compact: false,
|
|
320
356
|
recoveredFromLedger: true,
|
|
321
357
|
nodeId: args.node_id,
|
|
322
358
|
sessionId: args.session_id,
|
|
@@ -583,10 +619,18 @@ function isIdleSessionRecord(session) {
|
|
|
583
619
|
function chooseDispatchableSession(sessions, providerType, meshId, nodeId) {
|
|
584
620
|
const live = sessions.filter((session) => !isTerminalSessionRecord(session));
|
|
585
621
|
const matchingProvider = (session) => !providerType || session?.providerType === providerType || session?.cliType === providerType;
|
|
622
|
+
const isMeshOwnedDelegateSession = (session) => {
|
|
623
|
+
const settings = session?.settings;
|
|
624
|
+
const sessionMeshId = typeof settings?.meshNodeFor === "string" ? settings.meshNodeFor.trim() : "";
|
|
625
|
+
const coordinatorDaemonId = typeof settings?.meshCoordinatorDaemonId === "string" ? settings.meshCoordinatorDaemonId.trim() : "";
|
|
626
|
+
const sessionNodeId = typeof settings?.meshNodeId === "string" ? settings.meshNodeId.trim() : "";
|
|
627
|
+
if (sessionMeshId !== meshId || !coordinatorDaemonId) return false;
|
|
628
|
+
return !sessionNodeId || sessionNodeId === nodeId;
|
|
629
|
+
};
|
|
586
630
|
const meshSessions = live.filter(
|
|
587
|
-
(session) => session
|
|
631
|
+
(session) => isMeshOwnedDelegateSession(session)
|
|
588
632
|
);
|
|
589
|
-
return meshSessions.find((session) => isIdleSessionRecord(session) && matchingProvider(session)) || meshSessions.find(matchingProvider) ||
|
|
633
|
+
return meshSessions.find((session) => isIdleSessionRecord(session) && matchingProvider(session)) || meshSessions.find(matchingProvider) || void 0;
|
|
590
634
|
}
|
|
591
635
|
function findNestedPayload(value, predicate) {
|
|
592
636
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -872,6 +916,10 @@ function summarizeRelatedRepoStatus(repo, status) {
|
|
|
872
916
|
workspace: repo.workspace,
|
|
873
917
|
isGitRepo: status?.isGitRepo === true,
|
|
874
918
|
branch: status?.branch ?? null,
|
|
919
|
+
upstream: status?.upstream ?? null,
|
|
920
|
+
upstreamStatus: typeof status?.upstreamStatus === "string" ? status.upstreamStatus : status?.upstream ? "unchecked" : "no_upstream",
|
|
921
|
+
upstreamFetchedAt: Number.isFinite(Number(status?.upstreamFetchedAt)) ? Number(status.upstreamFetchedAt) : null,
|
|
922
|
+
upstreamFetchError: typeof status?.upstreamFetchError === "string" ? status.upstreamFetchError : null,
|
|
875
923
|
ahead: Number.isFinite(Number(status?.ahead)) ? Number(status.ahead) : 0,
|
|
876
924
|
behind: Number.isFinite(Number(status?.behind)) ? Number(status.behind) : 0,
|
|
877
925
|
dirty,
|
|
@@ -888,7 +936,7 @@ async function collectRelatedRepoStatuses(ctx, node) {
|
|
|
888
936
|
const results = [];
|
|
889
937
|
for (const repo of relatedRepos) {
|
|
890
938
|
try {
|
|
891
|
-
const statusResult = !isLocalTransport(ctx.transport) && node.daemonId ? await ctx.transport.gitStatus(node.daemonId, repo.workspace, false) : await commandForNode(ctx, node, "git_status", { workspace: repo.workspace });
|
|
939
|
+
const statusResult = !isLocalTransport(ctx.transport) && node.daemonId ? await ctx.transport.gitStatus(node.daemonId, repo.workspace, false, true) : await commandForNode(ctx, node, "git_status", { workspace: repo.workspace, refreshUpstream: true });
|
|
892
940
|
const status = extractGitStatus(statusResult);
|
|
893
941
|
results.push(summarizeRelatedRepoStatus(repo, status));
|
|
894
942
|
} catch (e) {
|
|
@@ -936,11 +984,13 @@ function buildBranchConvergence(mesh, node, status, dirty, uncommittedChanges) {
|
|
|
936
984
|
const ahead = readNumeric(status?.ahead);
|
|
937
985
|
const behind = readNumeric(status?.behind);
|
|
938
986
|
const upstream = readString(status?.upstream) ?? null;
|
|
987
|
+
const upstreamStatus = readString(status?.upstreamStatus) ?? (upstream ? "unchecked" : "no_upstream");
|
|
939
988
|
const hasConflicts = status?.hasConflicts === true || Array.isArray(status?.conflictFiles) && status.conflictFiles.length > 0;
|
|
940
989
|
const base = {
|
|
941
990
|
defaultBranch,
|
|
942
991
|
branch,
|
|
943
992
|
upstream,
|
|
993
|
+
upstreamStatus,
|
|
944
994
|
ahead,
|
|
945
995
|
behind,
|
|
946
996
|
isWorktree: node.isLocalWorktree === true,
|
|
@@ -974,6 +1024,15 @@ function buildBranchConvergence(mesh, node, status, dirty, uncommittedChanges) {
|
|
|
974
1024
|
};
|
|
975
1025
|
}
|
|
976
1026
|
if (branch === defaultBranch) {
|
|
1027
|
+
if (upstream && upstreamStatus !== "fresh") {
|
|
1028
|
+
return {
|
|
1029
|
+
...base,
|
|
1030
|
+
status: "blocked_review",
|
|
1031
|
+
needsConvergence: true,
|
|
1032
|
+
reason: "default_branch_upstream_unverified",
|
|
1033
|
+
nextStep: `Refresh ${defaultBranch}'s upstream refs or resolve the fetch failure before declaring convergence complete for node '${node.id}'.`
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
977
1036
|
if (ahead > 0 || behind > 0) {
|
|
978
1037
|
return {
|
|
979
1038
|
...base,
|
|
@@ -1000,6 +1059,15 @@ function buildBranchConvergence(mesh, node, status, dirty, uncommittedChanges) {
|
|
|
1000
1059
|
nextStep: `Run mesh_refine_node(node_id: "${node.id}") or explicitly classify this worktree as blocked_review/not_mergeable before ending the task.`
|
|
1001
1060
|
};
|
|
1002
1061
|
}
|
|
1062
|
+
if (upstream && upstreamStatus !== "fresh") {
|
|
1063
|
+
return {
|
|
1064
|
+
...base,
|
|
1065
|
+
status: "blocked_review",
|
|
1066
|
+
needsConvergence: true,
|
|
1067
|
+
reason: "feature_branch_upstream_unverified",
|
|
1068
|
+
nextStep: `Refresh branch '${branch}' upstream refs or resolve the fetch failure before deciding whether it is ready to merge into ${defaultBranch}.`
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1003
1071
|
if (!upstream || ahead > 0 || behind > 0) {
|
|
1004
1072
|
return {
|
|
1005
1073
|
...base,
|
|
@@ -1043,6 +1111,71 @@ async function commandForNode(ctx, node, command, args = {}) {
|
|
|
1043
1111
|
}
|
|
1044
1112
|
throw new Error(`Command '${command}' requires daemon IPC/local transport for node '${node.id}'`);
|
|
1045
1113
|
}
|
|
1114
|
+
function normalizePendingMeshCoordinatorEvents(value) {
|
|
1115
|
+
const payload = unwrapCommandPayload(value);
|
|
1116
|
+
const events = Array.isArray(payload?.events) ? payload.events : Array.isArray(value?.events) ? value.events : [];
|
|
1117
|
+
return events.filter((event) => event && typeof event === "object");
|
|
1118
|
+
}
|
|
1119
|
+
function buildMeshForwardPayloadFromPendingEvent(event) {
|
|
1120
|
+
const metadataEvent = event?.metadataEvent && typeof event.metadataEvent === "object" ? event.metadataEvent : {};
|
|
1121
|
+
return {
|
|
1122
|
+
event: readString(event?.event),
|
|
1123
|
+
meshId: readString(event?.meshId),
|
|
1124
|
+
nodeId: readString(event?.nodeId) || readString(metadataEvent.meshNodeId),
|
|
1125
|
+
workspace: readString(event?.workspace) || readString(metadataEvent.workspace),
|
|
1126
|
+
targetSessionId: readString(metadataEvent.targetSessionId) || readString(metadataEvent.sessionId) || readString(metadataEvent.instanceId),
|
|
1127
|
+
providerType: readString(metadataEvent.providerType),
|
|
1128
|
+
providerSessionId: readString(metadataEvent.providerSessionId),
|
|
1129
|
+
finalSummary: readString(metadataEvent.finalSummary) || readString(metadataEvent.summary),
|
|
1130
|
+
...metadataEvent.intentional === true ? { intentional: true } : {},
|
|
1131
|
+
...metadataEvent.intentionalStop === true ? { intentionalStop: true } : {},
|
|
1132
|
+
...metadataEvent.operatorCleanup === true ? { operatorCleanup: true } : {},
|
|
1133
|
+
...readString(metadataEvent.reason) ? { reason: readString(metadataEvent.reason) } : {},
|
|
1134
|
+
...readString(metadataEvent.stopReason) ? { stopReason: readString(metadataEvent.stopReason) } : {},
|
|
1135
|
+
...readString(metadataEvent.cleanupReason) ? { cleanupReason: readString(metadataEvent.cleanupReason) } : {},
|
|
1136
|
+
...readString(metadataEvent.source) ? { source: readString(metadataEvent.source) } : {}
|
|
1137
|
+
};
|
|
1138
|
+
}
|
|
1139
|
+
async function drainCoordinatorPendingEvents(ctx, opts) {
|
|
1140
|
+
const requestedNodeIds = opts?.nodeIds?.length ? new Set(opts.nodeIds) : null;
|
|
1141
|
+
const matchesCurrentMesh = (event) => readString(event?.meshId) === ctx.mesh.id;
|
|
1142
|
+
if (ctx.transport instanceof IpcTransport) {
|
|
1143
|
+
const surfacedEvents = [];
|
|
1144
|
+
try {
|
|
1145
|
+
surfacedEvents.push(
|
|
1146
|
+
...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", {})).filter(matchesCurrentMesh)
|
|
1147
|
+
);
|
|
1148
|
+
} catch {
|
|
1149
|
+
}
|
|
1150
|
+
for (const node of ctx.mesh.nodes) {
|
|
1151
|
+
if (!node.daemonId || isLocalControlPlaneNode(ctx, node)) continue;
|
|
1152
|
+
if (requestedNodeIds && !requestedNodeIds.has(node.id)) continue;
|
|
1153
|
+
try {
|
|
1154
|
+
const remoteEvents = normalizePendingMeshCoordinatorEvents(
|
|
1155
|
+
await ctx.transport.meshCommand(node.daemonId, "get_pending_mesh_events", {})
|
|
1156
|
+
).filter(matchesCurrentMesh);
|
|
1157
|
+
if (remoteEvents.length === 0) continue;
|
|
1158
|
+
for (const event of remoteEvents) {
|
|
1159
|
+
const payload = buildMeshForwardPayloadFromPendingEvent(event);
|
|
1160
|
+
if (!payload.event || !payload.meshId) continue;
|
|
1161
|
+
await ctx.transport.command("mesh_forward_event", payload);
|
|
1162
|
+
}
|
|
1163
|
+
} catch {
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
try {
|
|
1167
|
+
surfacedEvents.push(
|
|
1168
|
+
...normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", {})).filter(matchesCurrentMesh)
|
|
1169
|
+
);
|
|
1170
|
+
} catch {
|
|
1171
|
+
}
|
|
1172
|
+
return surfacedEvents;
|
|
1173
|
+
}
|
|
1174
|
+
if (isLocalTransport(ctx.transport)) {
|
|
1175
|
+
return (0, import_daemon_core.drainPendingMeshCoordinatorEvents)().filter(matchesCurrentMesh);
|
|
1176
|
+
}
|
|
1177
|
+
return [];
|
|
1178
|
+
}
|
|
1046
1179
|
function isP2pTransportUnavailableError(error) {
|
|
1047
1180
|
return (0, import_daemon_core.isP2pRelayTransportFailure)(error);
|
|
1048
1181
|
}
|
|
@@ -1344,7 +1477,7 @@ async function meshStatus(ctx) {
|
|
|
1344
1477
|
};
|
|
1345
1478
|
try {
|
|
1346
1479
|
if (!isLocalTransport(transport) && node.daemonId) {
|
|
1347
|
-
const result = await transport.gitStatus(node.daemonId, node.workspace, false);
|
|
1480
|
+
const result = await transport.gitStatus(node.daemonId, node.workspace, false, true);
|
|
1348
1481
|
const status = extractGitStatus(result);
|
|
1349
1482
|
const uncommittedChanges = countUncommittedChanges(status);
|
|
1350
1483
|
const dirty = isGitStatusDirty(status);
|
|
@@ -1362,6 +1495,7 @@ async function meshStatus(ctx) {
|
|
|
1362
1495
|
const autoDiscover = node.policy?.autoDiscoverSubmodules !== false;
|
|
1363
1496
|
const statusResult = await commandForNode(ctx, node, "git_status", {
|
|
1364
1497
|
workspace: node.workspace,
|
|
1498
|
+
refreshUpstream: true,
|
|
1365
1499
|
includeSubmodules: autoDiscover,
|
|
1366
1500
|
submoduleIgnorePaths: node.policy?.submoduleIgnorePaths || void 0
|
|
1367
1501
|
});
|
|
@@ -1452,6 +1586,11 @@ async function meshStatus(ctx) {
|
|
|
1452
1586
|
repoIdentity: mesh.repoIdentity,
|
|
1453
1587
|
policy: mesh.policy,
|
|
1454
1588
|
refreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1589
|
+
sourceOfTruth: {
|
|
1590
|
+
membership: "coordinator_daemon_live_mesh",
|
|
1591
|
+
currentStatus: "live_git_and_session_probes",
|
|
1592
|
+
historicalEvidenceOnly: ["recoveryHints", "ledgerSummary"]
|
|
1593
|
+
},
|
|
1455
1594
|
nodes: results,
|
|
1456
1595
|
branchConvergenceSummary: summarizeBranchConvergence(results)
|
|
1457
1596
|
};
|
|
@@ -1460,13 +1599,7 @@ async function meshStatus(ctx) {
|
|
|
1460
1599
|
} catch {
|
|
1461
1600
|
}
|
|
1462
1601
|
try {
|
|
1463
|
-
|
|
1464
|
-
if (ctx.transport instanceof IpcTransport) {
|
|
1465
|
-
const eventsResult = await ctx.transport.command("get_pending_mesh_events", {});
|
|
1466
|
-
pendingEvents = Array.isArray(eventsResult?.events) ? eventsResult.events : [];
|
|
1467
|
-
} else if (isLocalTransport(ctx.transport)) {
|
|
1468
|
-
pendingEvents = (0, import_daemon_core.drainPendingMeshCoordinatorEvents)();
|
|
1469
|
-
}
|
|
1602
|
+
const pendingEvents = await drainCoordinatorPendingEvents(ctx);
|
|
1470
1603
|
if (pendingEvents.length > 0) {
|
|
1471
1604
|
response.pendingCoordinatorEvents = pendingEvents;
|
|
1472
1605
|
}
|
|
@@ -1476,6 +1609,7 @@ async function meshStatus(ctx) {
|
|
|
1476
1609
|
}
|
|
1477
1610
|
async function meshTaskHistory(ctx, args) {
|
|
1478
1611
|
const { mesh } = ctx;
|
|
1612
|
+
await drainCoordinatorPendingEvents(ctx);
|
|
1479
1613
|
const tail = typeof args.tail === "number" && args.tail > 0 ? args.tail : 20;
|
|
1480
1614
|
const kind = typeof args.kind === "string" && args.kind.trim() ? [args.kind.trim()] : void 0;
|
|
1481
1615
|
const entries = (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail, kind });
|
|
@@ -1823,6 +1957,9 @@ async function meshReadChat(ctx, args) {
|
|
|
1823
1957
|
if (!node) {
|
|
1824
1958
|
return JSON.stringify(buildMissingNodeReadChatRecovery(ctx, args), null, 2);
|
|
1825
1959
|
}
|
|
1960
|
+
if (ctx.transport instanceof IpcTransport || isLocalTransport(ctx.transport)) {
|
|
1961
|
+
await drainCoordinatorPendingEvents(ctx, { nodeIds: [args.node_id] });
|
|
1962
|
+
}
|
|
1826
1963
|
if (isLocalTransport(ctx.transport)) {
|
|
1827
1964
|
const cached = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
|
|
1828
1965
|
const providerSessionId = typeof args.provider_session_id === "string" && args.provider_session_id.trim() ? args.provider_session_id.trim() : cached?.providerSessionId;
|
|
@@ -2028,7 +2165,7 @@ async function meshGitStatus(ctx, args) {
|
|
|
2028
2165
|
const submoduleIgnorePaths = node.policy?.submoduleIgnorePaths || [];
|
|
2029
2166
|
try {
|
|
2030
2167
|
if (!isLocalTransport(ctx.transport) && node.daemonId) {
|
|
2031
|
-
const result = await ctx.transport.gitStatus(node.daemonId, node.workspace, true);
|
|
2168
|
+
const result = await ctx.transport.gitStatus(node.daemonId, node.workspace, true, true);
|
|
2032
2169
|
return JSON.stringify({
|
|
2033
2170
|
nodeId: args.node_id,
|
|
2034
2171
|
workspace: node.workspace,
|
|
@@ -2040,6 +2177,7 @@ async function meshGitStatus(ctx, args) {
|
|
|
2040
2177
|
} else if (isLocalTransport(ctx.transport)) {
|
|
2041
2178
|
const statusResult = await commandForNode(ctx, node, "git_status", {
|
|
2042
2179
|
workspace: node.workspace,
|
|
2180
|
+
refreshUpstream: true,
|
|
2043
2181
|
includeSubmodules: autoDiscoverSubmodules,
|
|
2044
2182
|
submoduleIgnorePaths: submoduleIgnorePaths.length > 0 ? submoduleIgnorePaths : void 0
|
|
2045
2183
|
});
|
|
@@ -2154,6 +2292,7 @@ async function meshCloneNode(ctx, args) {
|
|
|
2154
2292
|
if (existingIndex >= 0) ctx.mesh.nodes[existingIndex] = clonePayload.node;
|
|
2155
2293
|
else ctx.mesh.nodes.push(clonePayload.node);
|
|
2156
2294
|
ctx.mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2295
|
+
await syncCoordinatorDaemonMeshCache(ctx);
|
|
2157
2296
|
}
|
|
2158
2297
|
return JSON.stringify(result, null, 2);
|
|
2159
2298
|
} else if (!isLocalTransport(ctx.transport) && sourceNode.daemonId) {
|
|
@@ -2171,6 +2310,7 @@ async function meshCloneNode(ctx, args) {
|
|
|
2171
2310
|
if (existingIndex >= 0) ctx.mesh.nodes[existingIndex] = clonePayload.node;
|
|
2172
2311
|
else ctx.mesh.nodes.push(clonePayload.node);
|
|
2173
2312
|
ctx.mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2313
|
+
await syncCoordinatorDaemonMeshCache(ctx);
|
|
2174
2314
|
}
|
|
2175
2315
|
return JSON.stringify(res, null, 2);
|
|
2176
2316
|
} catch (e) {
|
|
@@ -2511,8 +2651,8 @@ var CloudTransport = class {
|
|
|
2511
2651
|
if (!res.ok) throw new Error(`Approve failed: ${res.status}`);
|
|
2512
2652
|
return res.json();
|
|
2513
2653
|
}
|
|
2514
|
-
async gitStatus(daemonId, workspace, includeDiff = true) {
|
|
2515
|
-
const params = new URLSearchParams({ workspace, includeDiff: String(includeDiff) });
|
|
2654
|
+
async gitStatus(daemonId, workspace, includeDiff = true, refreshUpstream = false) {
|
|
2655
|
+
const params = new URLSearchParams({ workspace, includeDiff: String(includeDiff), refreshUpstream: String(refreshUpstream) });
|
|
2516
2656
|
const res = await fetch(
|
|
2517
2657
|
`${this.baseUrl}/api/v1/shortcuts/${encodeURIComponent(daemonId)}/git-status?${params}`,
|
|
2518
2658
|
{ headers: this.headers() }
|
|
@@ -2907,6 +3047,22 @@ function formatChatResult(result, sessionId, format, limit = 50, compact = false
|
|
|
2907
3047
|
}))
|
|
2908
3048
|
}, null, 2);
|
|
2909
3049
|
}
|
|
3050
|
+
if ((format === "text" || format === void 0) && compact && compactPayload) {
|
|
3051
|
+
const lines2 = outputMessages.slice(-limit).map((m) => {
|
|
3052
|
+
const role = m.role === "user" ? "User" : m.role === "assistant" ? "Agent" : m.role;
|
|
3053
|
+
const content = messageContent(m);
|
|
3054
|
+
const truncated = content.length > 500 ? `${content.slice(0, 500)}\u2026` : content;
|
|
3055
|
+
return `[${role}] ${truncated}`;
|
|
3056
|
+
});
|
|
3057
|
+
if (compactPayload.summary) {
|
|
3058
|
+
const truncatedSummary = compactPayload.summary.length > 500 ? `${compactPayload.summary.slice(0, 500)}\u2026` : compactPayload.summary;
|
|
3059
|
+
lines2.push(`[Summary] ${truncatedSummary}`);
|
|
3060
|
+
}
|
|
3061
|
+
if (result?.pollingAdvisory) {
|
|
3062
|
+
lines2.push(`Advisory: ${result.pollingAdvisory.message}`);
|
|
3063
|
+
}
|
|
3064
|
+
return lines2.length > 0 ? lines2.join("\n\n") : "No messages in chat.";
|
|
3065
|
+
}
|
|
2910
3066
|
if (outputMessages.length === 0) {
|
|
2911
3067
|
return result?.pollingAdvisory ? `No messages in chat.
|
|
2912
3068
|
|