@adhdev/daemon-standalone 0.9.77-rc.34 → 0.9.77-rc.36
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-
|
|
10
|
+
<script type="module" crossorigin src="/assets/index-Crr3Vqz7.js"></script>
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/vendor-CLec0455.js">
|
|
12
12
|
<link rel="stylesheet" crossorigin href="/assets/index-DftJ2WZr.css">
|
|
13
13
|
</head>
|
|
@@ -26416,12 +26416,14 @@ Do NOT retry on this node. Consider reassigning to a different node or asking th
|
|
|
26416
26416
|
return "";
|
|
26417
26417
|
}
|
|
26418
26418
|
function injectMeshSystemMessage(components, args) {
|
|
26419
|
+
let completedTaskForLedger = null;
|
|
26419
26420
|
if (args.event === "agent:generating_completed") {
|
|
26420
26421
|
const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
|
|
26421
26422
|
const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
|
|
26422
26423
|
const providerType = readNonEmptyString(args.metadataEvent.providerType);
|
|
26423
26424
|
if (sessionId) {
|
|
26424
|
-
updateSessionTaskStatus(args.meshId, sessionId, "completed");
|
|
26425
|
+
const completedTask = updateSessionTaskStatus(args.meshId, sessionId, "completed");
|
|
26426
|
+
completedTaskForLedger = completedTask ? { id: completedTask.id } : null;
|
|
26425
26427
|
if (nodeId && providerType) {
|
|
26426
26428
|
setTimeout(() => {
|
|
26427
26429
|
tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
|
|
@@ -26434,6 +26436,7 @@ function injectMeshSystemMessage(components, args) {
|
|
|
26434
26436
|
const providerType = readNonEmptyString(args.metadataEvent.providerType);
|
|
26435
26437
|
const completedTask = sessionId ? updateSessionTaskStatus(args.meshId, sessionId, "completed") : null;
|
|
26436
26438
|
if (completedTask) {
|
|
26439
|
+
completedTaskForLedger = { id: completedTask.id };
|
|
26437
26440
|
try {
|
|
26438
26441
|
appendLedgerEntry(args.meshId, {
|
|
26439
26442
|
kind: "task_completed",
|
|
@@ -26445,7 +26448,8 @@ function injectMeshSystemMessage(components, args) {
|
|
|
26445
26448
|
nodeLabel: args.nodeLabel,
|
|
26446
26449
|
taskId: completedTask.id,
|
|
26447
26450
|
completedViaReady: true,
|
|
26448
|
-
providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || void 0
|
|
26451
|
+
providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || void 0,
|
|
26452
|
+
finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || void 0
|
|
26449
26453
|
}
|
|
26450
26454
|
});
|
|
26451
26455
|
} catch (e) {
|
|
@@ -26488,7 +26492,9 @@ function injectMeshSystemMessage(components, args) {
|
|
|
26488
26492
|
payload: {
|
|
26489
26493
|
event: args.event,
|
|
26490
26494
|
nodeLabel: args.nodeLabel,
|
|
26491
|
-
|
|
26495
|
+
taskId: completedTaskForLedger?.id || void 0,
|
|
26496
|
+
providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || void 0,
|
|
26497
|
+
finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || void 0
|
|
26492
26498
|
}
|
|
26493
26499
|
});
|
|
26494
26500
|
} catch (e) {
|
|
@@ -26603,7 +26609,8 @@ function handleMeshForwardEvent(components, payload) {
|
|
|
26603
26609
|
metadataEvent: {
|
|
26604
26610
|
targetSessionId: readNonEmptyString(payload.targetSessionId) || readNonEmptyString(payload.sessionId) || readNonEmptyString(payload.instanceId),
|
|
26605
26611
|
providerType: readNonEmptyString(payload.providerType),
|
|
26606
|
-
providerSessionId: readNonEmptyString(payload.providerSessionId)
|
|
26612
|
+
providerSessionId: readNonEmptyString(payload.providerSessionId),
|
|
26613
|
+
finalSummary: readNonEmptyString(payload.finalSummary) || readNonEmptyString(payload.summary)
|
|
26607
26614
|
}
|
|
26608
26615
|
});
|
|
26609
26616
|
}
|
|
@@ -42098,7 +42105,7 @@ ${lastSnapshot}`;
|
|
|
42098
42105
|
}
|
|
42099
42106
|
getFreshParsedStatusCache() {
|
|
42100
42107
|
const cached2 = this.parsedStatusCache;
|
|
42101
|
-
if (cached2 && cached2.responseBuffer === this.responseBuffer && cached2.currentTurnScope === this.currentTurnScope && cached2.recentOutputBuffer === this.recentOutputBuffer && cached2.accumulatedBuffer === this.accumulatedBuffer && cached2.screenText === this.lastScreenText && cached2.currentStatus === this.currentStatus && cached2.activeModal === this.activeModal && cached2.cliName === this.cliName) {
|
|
42108
|
+
if (cached2 && cached2.responseBuffer === this.responseBuffer && cached2.currentTurnScope === this.currentTurnScope && cached2.recentOutputBuffer === this.recentOutputBuffer && cached2.accumulatedBuffer === this.accumulatedBuffer && cached2.accumulatedRawBuffer === this.accumulatedRawBuffer && cached2.screenText === this.lastScreenText && cached2.currentStatus === this.currentStatus && cached2.activeModal === this.activeModal && cached2.cliName === this.cliName) {
|
|
42102
42109
|
return cached2.result;
|
|
42103
42110
|
}
|
|
42104
42111
|
return null;
|
|
@@ -43073,6 +43080,11 @@ ${lastSnapshot}`;
|
|
|
43073
43080
|
};
|
|
43074
43081
|
}
|
|
43075
43082
|
// ─── Script Execution ──────────────────────────
|
|
43083
|
+
invokeCliScript(script, input) {
|
|
43084
|
+
const hasStateFactory = typeof this.cliScripts?.createState === "function";
|
|
43085
|
+
const expectsStateArgument = hasStateFactory || this.scriptState !== null || script.length >= 2;
|
|
43086
|
+
return expectsStateArgument ? script(this.scriptState, input) : script(input);
|
|
43087
|
+
}
|
|
43076
43088
|
runParseSession() {
|
|
43077
43089
|
if (typeof this.cliScripts?.parseSession !== "function") {
|
|
43078
43090
|
this.parseErrorMessage = `${this.cliType} parseSession unavailable`;
|
|
@@ -43093,7 +43105,10 @@ ${lastSnapshot}`;
|
|
|
43093
43105
|
scope: this.currentTurnScope,
|
|
43094
43106
|
runtimeSettings: this.runtimeSettings
|
|
43095
43107
|
});
|
|
43096
|
-
const session = this.
|
|
43108
|
+
const session = this.invokeCliScript(
|
|
43109
|
+
this.cliScripts.parseSession,
|
|
43110
|
+
{ ...input, tail, tailScreen: buildCliScreenSnapshot(tail) }
|
|
43111
|
+
);
|
|
43097
43112
|
this.parseErrorMessage = null;
|
|
43098
43113
|
return session && typeof session === "object" ? session : null;
|
|
43099
43114
|
} catch (e) {
|
|
@@ -43107,7 +43122,7 @@ ${lastSnapshot}`;
|
|
|
43107
43122
|
if (!this.cliScripts?.detectStatus) return null;
|
|
43108
43123
|
try {
|
|
43109
43124
|
const screenText = this.terminalScreen.getText();
|
|
43110
|
-
const status = this.cliScripts.detectStatus
|
|
43125
|
+
const status = this.invokeCliScript(this.cliScripts.detectStatus, {
|
|
43111
43126
|
tail: text.slice(-500),
|
|
43112
43127
|
screenText,
|
|
43113
43128
|
rawBuffer: this.accumulatedRawBuffer,
|
|
@@ -43126,7 +43141,7 @@ ${lastSnapshot}`;
|
|
|
43126
43141
|
try {
|
|
43127
43142
|
const screenText = this.terminalScreen.getText();
|
|
43128
43143
|
const buffer = screenText || this.accumulatedBuffer;
|
|
43129
|
-
return this.cliScripts.parseApproval
|
|
43144
|
+
return this.invokeCliScript(this.cliScripts.parseApproval, {
|
|
43130
43145
|
buffer,
|
|
43131
43146
|
screenText,
|
|
43132
43147
|
rawBuffer: this.accumulatedRawBuffer,
|
|
@@ -43182,7 +43197,7 @@ ${lastSnapshot}`;
|
|
|
43182
43197
|
const screenText = this.readTerminalScreenText();
|
|
43183
43198
|
const parseScreenText = this.getParseScreenText(screenText);
|
|
43184
43199
|
const cached2 = this.parsedStatusCache;
|
|
43185
|
-
if (cached2 && cached2.responseBuffer === this.responseBuffer && cached2.currentTurnScope === this.currentTurnScope && cached2.recentOutputBuffer === this.recentOutputBuffer && cached2.accumulatedBuffer === this.accumulatedBuffer && cached2.screenText === parseScreenText && cached2.currentStatus === this.currentStatus && cached2.activeModal === this.activeModal && cached2.cliName === this.cliName) {
|
|
43200
|
+
if (cached2 && cached2.responseBuffer === this.responseBuffer && cached2.currentTurnScope === this.currentTurnScope && cached2.recentOutputBuffer === this.recentOutputBuffer && cached2.accumulatedBuffer === this.accumulatedBuffer && cached2.accumulatedRawBuffer === this.accumulatedRawBuffer && cached2.screenText === parseScreenText && cached2.currentStatus === this.currentStatus && cached2.activeModal === this.activeModal && cached2.cliName === this.cliName) {
|
|
43186
43201
|
return cached2.result;
|
|
43187
43202
|
}
|
|
43188
43203
|
const parsed = this.runParseSession();
|
|
@@ -43210,6 +43225,7 @@ ${lastSnapshot}`;
|
|
|
43210
43225
|
currentTurnScope: this.currentTurnScope,
|
|
43211
43226
|
recentOutputBuffer: this.recentOutputBuffer,
|
|
43212
43227
|
accumulatedBuffer: this.accumulatedBuffer,
|
|
43228
|
+
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
43213
43229
|
screenText: parseScreenText,
|
|
43214
43230
|
currentStatus: this.currentStatus,
|
|
43215
43231
|
activeModal: this.activeModal,
|
|
@@ -53375,7 +53391,13 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
53375
53391
|
appendLedgerEntry2(meshId, {
|
|
53376
53392
|
kind: "node_removed",
|
|
53377
53393
|
nodeId,
|
|
53378
|
-
payload: {
|
|
53394
|
+
payload: {
|
|
53395
|
+
worktree: !!node?.isLocalWorktree,
|
|
53396
|
+
sessionCleanupMode,
|
|
53397
|
+
workspace: typeof node?.workspace === "string" ? node.workspace : void 0,
|
|
53398
|
+
daemonId: typeof node?.daemonId === "string" ? node.daemonId : void 0,
|
|
53399
|
+
worktreeBranch: typeof node?.worktreeBranch === "string" ? node.worktreeBranch : void 0
|
|
53400
|
+
}
|
|
53379
53401
|
});
|
|
53380
53402
|
} catch {
|
|
53381
53403
|
}
|
|
@@ -57531,6 +57553,11 @@ function annotateRapidReadChatAdvisory(payload, options) {
|
|
|
57531
57553
|
// src/tools/mesh-tools.ts
|
|
57532
57554
|
init_dist2();
|
|
57533
57555
|
var meshSessionProviderMetadata = /* @__PURE__ */ new Map();
|
|
57556
|
+
function readString(value) {
|
|
57557
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
57558
|
+
}
|
|
57559
|
+
var DUPLICATE_DISPATCH_WINDOW_MS = 6e4;
|
|
57560
|
+
var STALE_ASSIGNED_QUEUE_MS = 30 * 6e4;
|
|
57534
57561
|
async function refreshMeshFromDaemon(ctx) {
|
|
57535
57562
|
if (!(ctx.transport instanceof IpcTransport)) return;
|
|
57536
57563
|
try {
|
|
@@ -57551,6 +57578,138 @@ async function findNodeWithRefresh(ctx, nodeId) {
|
|
|
57551
57578
|
if (!refreshed) throw new Error(`Node '${nodeId}' is not a member of mesh '${ctx.mesh.name}'`);
|
|
57552
57579
|
return refreshed;
|
|
57553
57580
|
}
|
|
57581
|
+
async function findOptionalNodeWithRefresh(ctx, nodeId) {
|
|
57582
|
+
const hit = ctx.mesh.nodes.find((n) => n.id === nodeId);
|
|
57583
|
+
if (hit) return hit;
|
|
57584
|
+
await refreshMeshFromDaemon(ctx);
|
|
57585
|
+
return ctx.mesh.nodes.find((n) => n.id === nodeId) ?? null;
|
|
57586
|
+
}
|
|
57587
|
+
function hasRecentDuplicateDispatch(ctx, args) {
|
|
57588
|
+
const now = Date.now();
|
|
57589
|
+
const normalizedMessage = args.message.trim();
|
|
57590
|
+
for (const task of getQueue(ctx.mesh.id)) {
|
|
57591
|
+
const timestamp2 = new Date(task.updatedAt || task.createdAt).getTime();
|
|
57592
|
+
if (!Number.isFinite(timestamp2) || now - timestamp2 > DUPLICATE_DISPATCH_WINDOW_MS) continue;
|
|
57593
|
+
if (task.targetNodeId && task.targetNodeId !== args.node_id) continue;
|
|
57594
|
+
if (task.assignedNodeId && task.assignedNodeId !== args.node_id) continue;
|
|
57595
|
+
if (args.session_id && task.targetSessionId !== args.session_id && task.assignedSessionId !== args.session_id) continue;
|
|
57596
|
+
if (task.message?.trim() === normalizedMessage) {
|
|
57597
|
+
return { duplicate: true, entry: task, source: "queue" };
|
|
57598
|
+
}
|
|
57599
|
+
}
|
|
57600
|
+
const entries = readLedgerEntries(ctx.mesh.id, { tail: 200 });
|
|
57601
|
+
for (let i = entries.length - 1; i >= 0; i -= 1) {
|
|
57602
|
+
const entry = entries[i];
|
|
57603
|
+
const timestamp2 = new Date(entry.timestamp).getTime();
|
|
57604
|
+
if (Number.isFinite(timestamp2) && now - timestamp2 > DUPLICATE_DISPATCH_WINDOW_MS) break;
|
|
57605
|
+
if (entry.kind !== "task_dispatched") continue;
|
|
57606
|
+
if (entry.nodeId !== args.node_id) continue;
|
|
57607
|
+
if (args.session_id && entry.sessionId !== args.session_id) continue;
|
|
57608
|
+
if (typeof entry.payload?.message !== "string") continue;
|
|
57609
|
+
if (entry.payload.message.trim() === normalizedMessage) {
|
|
57610
|
+
return { duplicate: true, entry, source: "ledger" };
|
|
57611
|
+
}
|
|
57612
|
+
}
|
|
57613
|
+
return { duplicate: false };
|
|
57614
|
+
}
|
|
57615
|
+
function buildMissingNodeReadChatRecovery(ctx, args) {
|
|
57616
|
+
const entries = readLedgerEntries(ctx.mesh.id, { tail: 300 });
|
|
57617
|
+
const relatedEntries = entries.filter((entry) => entry.nodeId === args.node_id || entry.sessionId === args.session_id);
|
|
57618
|
+
const completedEntries = relatedEntries.filter((entry) => entry.kind === "task_completed");
|
|
57619
|
+
const lastDispatch = [...relatedEntries].reverse().find((entry) => entry.kind === "task_dispatched");
|
|
57620
|
+
const lastTerminal = [...relatedEntries].reverse().find((entry) => entry.kind === "task_completed" || entry.kind === "task_failed" || entry.kind === "task_stalled");
|
|
57621
|
+
const lastRemoved = [...relatedEntries].reverse().find((entry) => entry.kind === "node_removed");
|
|
57622
|
+
const lastLaunch = [...relatedEntries].reverse().find((entry) => entry.kind === "session_launched");
|
|
57623
|
+
const providerSessionId = args.provider_session_id || readString(lastTerminal?.payload?.providerSessionId) || readString(lastLaunch?.payload?.providerSessionId) || readString(lastDispatch?.payload?.providerSessionId);
|
|
57624
|
+
const finalSummary = readString(lastTerminal?.payload?.finalSummary) || readString(lastTerminal?.payload?.compactSummary) || readString(lastTerminal?.payload?.summary);
|
|
57625
|
+
const ledger = {
|
|
57626
|
+
taskCompletedFound: completedEntries.length > 0,
|
|
57627
|
+
nodeRemovedFound: !!lastRemoved,
|
|
57628
|
+
providerType: lastTerminal?.providerType || lastLaunch?.providerType || lastDispatch?.providerType,
|
|
57629
|
+
providerSessionId,
|
|
57630
|
+
nodeRemovedAt: lastRemoved?.timestamp,
|
|
57631
|
+
sessionCleanupMode: readString(lastRemoved?.payload?.sessionCleanupMode),
|
|
57632
|
+
readDebugLocator: readString(lastTerminal?.payload?.readDebugLocator) || readString(lastTerminal?.payload?.debugBundlePath)
|
|
57633
|
+
};
|
|
57634
|
+
if (finalSummary) {
|
|
57635
|
+
return {
|
|
57636
|
+
success: true,
|
|
57637
|
+
compact: args.compact === true,
|
|
57638
|
+
recoveredFromLedger: true,
|
|
57639
|
+
nodeId: args.node_id,
|
|
57640
|
+
sessionId: args.session_id,
|
|
57641
|
+
summary: finalSummary,
|
|
57642
|
+
ledger,
|
|
57643
|
+
messages: [{ role: "assistant", content: finalSummary, isHistorical: true }]
|
|
57644
|
+
};
|
|
57645
|
+
}
|
|
57646
|
+
return {
|
|
57647
|
+
success: false,
|
|
57648
|
+
recoverable: true,
|
|
57649
|
+
code: "mesh_removed_node_transcript_unavailable",
|
|
57650
|
+
error: `Node '${args.node_id}' is not a current member of mesh '${ctx.mesh.name}'.`,
|
|
57651
|
+
nodeId: args.node_id,
|
|
57652
|
+
sessionId: args.session_id,
|
|
57653
|
+
providerSessionId,
|
|
57654
|
+
reason: "node_not_in_current_mesh_snapshot",
|
|
57655
|
+
ledger,
|
|
57656
|
+
completedSessionSeenInLedger: ledger.taskCompletedFound,
|
|
57657
|
+
lastDispatch: lastDispatch ? {
|
|
57658
|
+
timestamp: lastDispatch.timestamp,
|
|
57659
|
+
sessionId: lastDispatch.sessionId,
|
|
57660
|
+
providerType: lastDispatch.providerType,
|
|
57661
|
+
taskId: typeof lastDispatch.payload?.taskId === "string" ? lastDispatch.payload.taskId : void 0,
|
|
57662
|
+
messagePreview: typeof lastDispatch.payload?.message === "string" ? lastDispatch.payload.message.slice(0, 500) : void 0
|
|
57663
|
+
} : null,
|
|
57664
|
+
lastTerminalEvent: lastTerminal ? {
|
|
57665
|
+
kind: lastTerminal.kind,
|
|
57666
|
+
timestamp: lastTerminal.timestamp,
|
|
57667
|
+
sessionId: lastTerminal.sessionId,
|
|
57668
|
+
providerType: lastTerminal.providerType,
|
|
57669
|
+
taskId: typeof lastTerminal.payload?.taskId === "string" ? lastTerminal.payload.taskId : void 0,
|
|
57670
|
+
payload: lastTerminal.payload
|
|
57671
|
+
} : null,
|
|
57672
|
+
nextSteps: [
|
|
57673
|
+
providerSessionId ? `Retry mesh_read_chat with provider_session_id='${providerSessionId}' on a current live node for the same daemon if one exists.` : "If the node UI shows a provider transcript id, retry mesh_read_chat/mesh_read_debug with provider_session_id.",
|
|
57674
|
+
"Use mesh_read_debug with the provider_session_id or daemon-side debug bundle locator if available.",
|
|
57675
|
+
"Check mesh_task_history for task_completed and node_removed entries before redispatching; do not resend solely because transcript recovery failed.",
|
|
57676
|
+
"If this node was removed with stop_and_delete, the runtime transcript may be gone; rely on the ledger summary/locator or ask the operator for the saved UI output."
|
|
57677
|
+
],
|
|
57678
|
+
recoveryHints: [
|
|
57679
|
+
"The worktree/node may have been removed or the mesh snapshot may be stale after task completion.",
|
|
57680
|
+
"If you have a provider_session_id, retry mesh_read_chat with that value while targeting a live node for the same daemon if available.",
|
|
57681
|
+
"Use mesh_read_debug with provider_session_id, or inspect the daemon/session-host history locator if the transcript has already been archived.",
|
|
57682
|
+
"Avoid redispatching the same task solely because read_chat could not recover the transcript; check task_history and git status first."
|
|
57683
|
+
]
|
|
57684
|
+
};
|
|
57685
|
+
}
|
|
57686
|
+
function annotateQueueStaleness(queue) {
|
|
57687
|
+
const now = Date.now();
|
|
57688
|
+
return queue.map((task) => {
|
|
57689
|
+
const taskStatus = typeof task?.status === "string" ? task.status : void 0;
|
|
57690
|
+
const annotated = {
|
|
57691
|
+
...task,
|
|
57692
|
+
taskStatus,
|
|
57693
|
+
dispatchedAt: task?.createdAt,
|
|
57694
|
+
...taskStatus === "assigned" ? { activeTaskId: task.id } : {},
|
|
57695
|
+
...taskStatus === "completed" || taskStatus === "failed" ? {
|
|
57696
|
+
isHistorical: true,
|
|
57697
|
+
completedAt: task.updatedAt
|
|
57698
|
+
} : {}
|
|
57699
|
+
};
|
|
57700
|
+
if (taskStatus !== "assigned") return annotated;
|
|
57701
|
+
const updatedAt = new Date(task.updatedAt).getTime();
|
|
57702
|
+
const ageMs = Number.isFinite(updatedAt) ? now - updatedAt : null;
|
|
57703
|
+
if (ageMs === null || ageMs < STALE_ASSIGNED_QUEUE_MS) return annotated;
|
|
57704
|
+
return {
|
|
57705
|
+
...annotated,
|
|
57706
|
+
stale: true,
|
|
57707
|
+
staleAssigned: true,
|
|
57708
|
+
staleReason: "assigned task has not reached a terminal state within 30 minutes",
|
|
57709
|
+
assignedAgeMs: ageMs
|
|
57710
|
+
};
|
|
57711
|
+
});
|
|
57712
|
+
}
|
|
57554
57713
|
function unwrapCommandPayload(value) {
|
|
57555
57714
|
let current = value;
|
|
57556
57715
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -58180,8 +58339,16 @@ async function meshEnqueueTask(ctx, args) {
|
|
|
58180
58339
|
}
|
|
58181
58340
|
async function meshViewQueue(ctx, args) {
|
|
58182
58341
|
try {
|
|
58183
|
-
const queue = getQueue(ctx.mesh.id, { status: args.status });
|
|
58184
|
-
|
|
58342
|
+
const queue = annotateQueueStaleness(getQueue(ctx.mesh.id, { status: args.status }));
|
|
58343
|
+
const staleAssignedTasks = queue.filter((task) => task?.status === "assigned" && task?.staleAssigned);
|
|
58344
|
+
return JSON.stringify({
|
|
58345
|
+
success: true,
|
|
58346
|
+
queue,
|
|
58347
|
+
staleAssignedTasks,
|
|
58348
|
+
staleAssignedCount: staleAssignedTasks.length,
|
|
58349
|
+
// Back-compat alias for callers already reading the first hardening payload.
|
|
58350
|
+
staleAssignments: staleAssignedTasks
|
|
58351
|
+
}, null, 2);
|
|
58185
58352
|
} catch (e) {
|
|
58186
58353
|
return JSON.stringify({ success: false, error: e.message });
|
|
58187
58354
|
}
|
|
@@ -58226,6 +58393,24 @@ async function meshSendTask(ctx, args) {
|
|
|
58226
58393
|
if (node.policy?.readOnly) {
|
|
58227
58394
|
return JSON.stringify({ error: `Node '${args.node_id}' is read-only` });
|
|
58228
58395
|
}
|
|
58396
|
+
const duplicate = hasRecentDuplicateDispatch(ctx, args);
|
|
58397
|
+
if (duplicate.duplicate) {
|
|
58398
|
+
return JSON.stringify({
|
|
58399
|
+
success: true,
|
|
58400
|
+
duplicate: true,
|
|
58401
|
+
dispatched: false,
|
|
58402
|
+
warning: "Duplicate mesh_send_task suppressed: the same node/session/message was dispatched recently.",
|
|
58403
|
+
nodeId: args.node_id,
|
|
58404
|
+
sessionId: args.session_id,
|
|
58405
|
+
source: duplicate.source,
|
|
58406
|
+
previousDispatch: duplicate.entry ? {
|
|
58407
|
+
id: duplicate.entry.id,
|
|
58408
|
+
timestamp: duplicate.entry.timestamp || duplicate.entry.updatedAt || duplicate.entry.createdAt,
|
|
58409
|
+
nodeId: duplicate.entry.nodeId || duplicate.entry.targetNodeId || duplicate.entry.assignedNodeId,
|
|
58410
|
+
sessionId: duplicate.entry.sessionId || duplicate.entry.targetSessionId || duplicate.entry.assignedSessionId
|
|
58411
|
+
} : void 0
|
|
58412
|
+
});
|
|
58413
|
+
}
|
|
58229
58414
|
try {
|
|
58230
58415
|
if (!isLocalTransport(ctx.transport) && node.daemonId) {
|
|
58231
58416
|
const res = await ctx.transport.meshEnqueueTask(node.daemonId, {
|
|
@@ -58244,17 +58429,22 @@ async function meshSendTask(ctx, args) {
|
|
|
58244
58429
|
providerType: cached2?.providerType
|
|
58245
58430
|
});
|
|
58246
58431
|
if (result.success) {
|
|
58432
|
+
const dispatchedSessionId = args.session_id || result.sessionId;
|
|
58247
58433
|
try {
|
|
58248
58434
|
appendLedgerEntry(ctx.mesh.id, {
|
|
58249
58435
|
kind: "task_dispatched",
|
|
58250
58436
|
nodeId: args.node_id,
|
|
58251
|
-
sessionId:
|
|
58252
|
-
payload: {
|
|
58437
|
+
sessionId: dispatchedSessionId,
|
|
58438
|
+
payload: {
|
|
58439
|
+
message: args.message,
|
|
58440
|
+
via: "p2p_direct",
|
|
58441
|
+
...dispatchedSessionId ? { targetSessionId: dispatchedSessionId } : {}
|
|
58442
|
+
}
|
|
58253
58443
|
});
|
|
58254
58444
|
} catch {
|
|
58255
58445
|
}
|
|
58256
58446
|
}
|
|
58257
|
-
return JSON.stringify({ ...result, nodeId: args.node_id });
|
|
58447
|
+
return JSON.stringify({ ...result, nodeId: args.node_id, dispatched: result.success === true });
|
|
58258
58448
|
}
|
|
58259
58449
|
if (args.session_id && isLocalTransport(ctx.transport)) {
|
|
58260
58450
|
const cached2 = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
|
|
@@ -58299,7 +58489,10 @@ async function meshSendTask(ctx, args) {
|
|
|
58299
58489
|
}
|
|
58300
58490
|
}
|
|
58301
58491
|
async function meshReadChat(ctx, args) {
|
|
58302
|
-
const node = await
|
|
58492
|
+
const node = await findOptionalNodeWithRefresh(ctx, args.node_id);
|
|
58493
|
+
if (!node) {
|
|
58494
|
+
return JSON.stringify(buildMissingNodeReadChatRecovery(ctx, args), null, 2);
|
|
58495
|
+
}
|
|
58303
58496
|
if (isLocalTransport(ctx.transport)) {
|
|
58304
58497
|
const cached2 = meshSessionProviderMetadata.get(meshSessionCacheKey(args.node_id, args.session_id));
|
|
58305
58498
|
const providerSessionId = typeof args.provider_session_id === "string" && args.provider_session_id.trim() ? args.provider_session_id.trim() : cached2?.providerSessionId;
|