@adhdev/daemon-core 0.9.82-rc.374 → 0.9.82-rc.375
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/index.js +309 -57
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +309 -57
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/coordinator-prompt.d.ts +53 -0
- package/dist/mesh/mesh-ledger.d.ts +1 -1
- package/dist/providers/cli-provider-instance.d.ts +3 -0
- package/package.json +2 -2
- package/src/commands/high-family/mesh-coordinator-launch.ts +67 -2
- package/src/commands/med-family/cli-agent.ts +25 -13
- package/src/commands/med-family/fast-forward.ts +80 -48
- package/src/mesh/coordinator-prompt.ts +145 -0
- package/src/mesh/mesh-events-coordinator.ts +45 -1
- package/src/mesh/mesh-ledger.ts +5 -0
- package/src/mesh/mesh-reconcile-loop.ts +114 -1
- package/src/providers/cli-provider-instance.ts +69 -2
package/dist/index.js
CHANGED
|
@@ -316,10 +316,10 @@ function readInjected(value) {
|
|
|
316
316
|
}
|
|
317
317
|
function getDaemonBuildInfo() {
|
|
318
318
|
if (cached) return cached;
|
|
319
|
-
const commit = readInjected(true ? "
|
|
320
|
-
const commitShort = readInjected(true ? "
|
|
321
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
322
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
319
|
+
const commit = readInjected(true ? "b17fb9165bd52c9e2ab8cccf2ce80734e2165680" : void 0) ?? "unknown";
|
|
320
|
+
const commitShort = readInjected(true ? "b17fb916" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
321
|
+
const version = readInjected(true ? "0.9.82-rc.375" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
322
|
+
const builtAt = readInjected(true ? "2026-06-25T02:21:59.111Z" : void 0);
|
|
323
323
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
324
324
|
return cached;
|
|
325
325
|
}
|
|
@@ -3042,6 +3042,10 @@ Default branch: \`${mesh.defaultBranch}\`` : ""}`);
|
|
|
3042
3042
|
if (ctx.missionSection?.trim()) {
|
|
3043
3043
|
sections.push(ctx.missionSection.trim());
|
|
3044
3044
|
}
|
|
3045
|
+
const recentActivity = buildRecentActivitySection(ctx.recentActivity);
|
|
3046
|
+
if (recentActivity) sections.push(recentActivity);
|
|
3047
|
+
const operatingNotes = buildOperatingNotesSection(ctx.operatingNotes);
|
|
3048
|
+
if (operatingNotes) sections.push(operatingNotes);
|
|
3045
3049
|
sections.push(buildPolicySection({ ...DEFAULT_MESH_POLICY, ...mesh.policy || {} }));
|
|
3046
3050
|
sections.push(TOOLS_SECTION);
|
|
3047
3051
|
sections.push(TOOL_EXPOSURE_PREFLIGHT_SECTION);
|
|
@@ -3073,6 +3077,8 @@ function expandPromptPlaceholders(template, ctx) {
|
|
|
3073
3077
|
cliType: coordinatorCliType || "",
|
|
3074
3078
|
nodes: nodesSection,
|
|
3075
3079
|
mission: ctx.missionSection?.trim() || "",
|
|
3080
|
+
recentActivity: buildRecentActivitySection(ctx.recentActivity) || "",
|
|
3081
|
+
operatingNotes: buildOperatingNotesSection(ctx.operatingNotes) || "",
|
|
3076
3082
|
policy: buildPolicySection({ ...DEFAULT_MESH_POLICY, ...mesh.policy || {} }),
|
|
3077
3083
|
tools: TOOLS_SECTION,
|
|
3078
3084
|
workflow: WORKFLOW_SECTION,
|
|
@@ -3144,6 +3150,56 @@ function indentFollowing(text, pad) {
|
|
|
3144
3150
|
if (lines.length === 1) return lines[0];
|
|
3145
3151
|
return [lines[0], ...lines.slice(1).map((l) => pad + l)].join("\n");
|
|
3146
3152
|
}
|
|
3153
|
+
function buildRecentActivitySection(activity) {
|
|
3154
|
+
if (!activity) return "";
|
|
3155
|
+
const failures = Array.isArray(activity.recentFailures) ? activity.recentFailures : [];
|
|
3156
|
+
const pending = Number.isFinite(activity.pendingTasks) ? Number(activity.pendingTasks) : 0;
|
|
3157
|
+
const assigned = Number.isFinite(activity.assignedTasks) ? Number(activity.assignedTasks) : 0;
|
|
3158
|
+
const stalled = Number.isFinite(activity.stalledTasks) ? Number(activity.stalledTasks) : 0;
|
|
3159
|
+
const recentFailureCount = Number.isFinite(activity.recentFailureCount) ? Number(activity.recentFailureCount) : failures.length;
|
|
3160
|
+
if (failures.length === 0 && pending === 0 && assigned === 0 && stalled === 0 && recentFailureCount === 0) {
|
|
3161
|
+
return "";
|
|
3162
|
+
}
|
|
3163
|
+
const lines = ["## Recent Activity", ""];
|
|
3164
|
+
lines.push("A snapshot of this mesh's recent ledger/queue state at launch. Use it to decide what needs attention first; call `mesh_task_history` / `mesh_view_queue` for full detail.");
|
|
3165
|
+
lines.push("");
|
|
3166
|
+
const counts = [];
|
|
3167
|
+
if (pending > 0) counts.push(`**${pending}** pending`);
|
|
3168
|
+
if (assigned > 0) counts.push(`**${assigned}** assigned`);
|
|
3169
|
+
if (stalled > 0) counts.push(`**${stalled}** stalled`);
|
|
3170
|
+
if (recentFailureCount > 0) counts.push(`**${recentFailureCount}** failed in the last 30 min`);
|
|
3171
|
+
if (counts.length) lines.push(`- Queue/ledger: ${counts.join(", ")}.`);
|
|
3172
|
+
if (activity.lastActivityAt) lines.push(`- Last ledger activity: ${activity.lastActivityAt}.`);
|
|
3173
|
+
if (failures.length > 0) {
|
|
3174
|
+
const recent = failures.slice(-5).reverse();
|
|
3175
|
+
lines.push("", "Recent failures (newest first):");
|
|
3176
|
+
for (const f of recent) {
|
|
3177
|
+
const when = f.timestamp ? `${f.timestamp} ` : "";
|
|
3178
|
+
const node = f.nodeId ? `node \`${f.nodeId}\`` : "unknown node";
|
|
3179
|
+
const summary = (f.summary || "").trim();
|
|
3180
|
+
lines.push(`- ${when}${node}${summary ? ` \u2014 ${summary}` : ""}`);
|
|
3181
|
+
}
|
|
3182
|
+
lines.push("", "_Check `mesh_task_history` before retrying; repeated failures on the same node mean reassign or escalate, not retry._");
|
|
3183
|
+
}
|
|
3184
|
+
return lines.join("\n");
|
|
3185
|
+
}
|
|
3186
|
+
function buildOperatingNotesSection(notes) {
|
|
3187
|
+
const valid = Array.isArray(notes) ? notes.filter((n) => n && typeof n.text === "string" && n.text.trim()) : [];
|
|
3188
|
+
if (valid.length === 0) return "";
|
|
3189
|
+
const categoryLabel = {
|
|
3190
|
+
provider_quirk: "provider quirk",
|
|
3191
|
+
pattern_to_avoid: "pattern to avoid",
|
|
3192
|
+
recovery_lesson: "recovery lesson"
|
|
3193
|
+
};
|
|
3194
|
+
const lines = ["## Operating Notes", ""];
|
|
3195
|
+
lines.push("Lessons earlier coordinators on this mesh recorded via `mesh_record_note`. Treat them as accumulated operating knowledge \u2014 apply them. When you learn a durable lesson (a provider quirk, a pattern to avoid, a recovery lesson), record it with `mesh_record_note` so future coordinators inherit it.");
|
|
3196
|
+
lines.push("");
|
|
3197
|
+
for (const n of valid) {
|
|
3198
|
+
const cat = n.category && categoryLabel[n.category] ? `[${categoryLabel[n.category]}] ` : "";
|
|
3199
|
+
lines.push(`- ${cat}${n.text.trim()}`);
|
|
3200
|
+
}
|
|
3201
|
+
return lines.join("\n");
|
|
3202
|
+
}
|
|
3147
3203
|
function buildPolicySection(policy) {
|
|
3148
3204
|
const rules = [];
|
|
3149
3205
|
if (policy.requirePreTaskCheckpoint) rules.push("- Create a git checkpoint **before** starting each task");
|
|
@@ -3207,6 +3263,7 @@ var init_coordinator_prompt = __esm({
|
|
|
3207
3263
|
| \`mesh_read_chat\` | Read recent chat messages from a delegated agent session |
|
|
3208
3264
|
| \`mesh_read_debug\` | Collect a daemon-side chat/parser debug bundle for a session |
|
|
3209
3265
|
| \`mesh_task_history\` | Read the task ledger \u2014 dispatches, completions, failures. Use to understand what has been done before deciding next steps |
|
|
3266
|
+
| \`mesh_record_note\` | Record a durable, provider-neutral operating note (provider quirk / pattern to avoid / recovery lesson). Future coordinators see it under "## Operating Notes" at launch |
|
|
3210
3267
|
| \`mesh_git_status\` | Check git status on a specific node |
|
|
3211
3268
|
| \`mesh_read_node_logs\` | Fetch a remote node's daemon log tail directly over P2P (grep/since/byte-bounded, secrets redacted) \u2014 no session/PowerShell needed to debug a node's daemon |
|
|
3212
3269
|
| \`mesh_fast_forward_node\` | Safely dry-run or explicitly execute an obvious clean fast-forward without launching an agent session |
|
|
@@ -12940,6 +12997,18 @@ function isWeakTerminalLedgerPayload(payload) {
|
|
|
12940
12997
|
const diag = readRecord4(payload.completionDiagnostic);
|
|
12941
12998
|
return diag?.finalAssistantPresent === false || diag?.blockReason === "missing_final_assistant";
|
|
12942
12999
|
}
|
|
13000
|
+
function supersedesTruncatedTerminalSummary(args) {
|
|
13001
|
+
if (!args.terminalTaskId || !args.eventTaskId || args.terminalTaskId !== args.eventTaskId) return false;
|
|
13002
|
+
if (!isGenuineCompletionEvidence(args.metadataEvent)) return false;
|
|
13003
|
+
const terminalSummary = readNonEmptyString2(args.terminalPayload.finalSummary);
|
|
13004
|
+
const eventSummary = readNonEmptyString2(args.metadataEvent.finalSummary);
|
|
13005
|
+
if (!eventSummary) return false;
|
|
13006
|
+
if (terminalSummary === eventSummary) return false;
|
|
13007
|
+
if (isWeakTerminalLedgerPayload(args.terminalPayload)) return false;
|
|
13008
|
+
if (!terminalSummary) return true;
|
|
13009
|
+
if (eventSummary.startsWith(terminalSummary)) return true;
|
|
13010
|
+
return eventSummary.length > terminalSummary.length + 32;
|
|
13011
|
+
}
|
|
12943
13012
|
function resolveActiveDirectDispatchTaskId(meshId, sessionId) {
|
|
12944
13013
|
try {
|
|
12945
13014
|
const matches = getActiveDirectDispatches(meshId).filter((d) => d.sessionId === sessionId);
|
|
@@ -13856,7 +13925,13 @@ function evaluateMeshEventSuppression(args, ctx) {
|
|
|
13856
13925
|
const terminalTaskId = readNonEmptyString2(terminal.payload.taskId);
|
|
13857
13926
|
const eventTaskId = readNonEmptyString2(args.metadataEvent.taskId);
|
|
13858
13927
|
const distinctTaskCompletion = !!eventTaskId && !!terminalTaskId && eventTaskId !== terminalTaskId;
|
|
13859
|
-
|
|
13928
|
+
const supersedesTruncatedTerminal = supersedesTruncatedTerminalSummary({
|
|
13929
|
+
terminalPayload: terminal.payload,
|
|
13930
|
+
metadataEvent: args.metadataEvent,
|
|
13931
|
+
terminalTaskId,
|
|
13932
|
+
eventTaskId
|
|
13933
|
+
});
|
|
13934
|
+
if (!newDispatchAfterTerminal && !supersedesWeakTerminal && !distinctTaskCompletion && !supersedesTruncatedTerminal) {
|
|
13860
13935
|
const terminalProviderSessionId = readNonEmptyString2(terminal.payload.providerSessionId);
|
|
13861
13936
|
const terminalFinalSummary = readNonEmptyString2(terminal.payload.finalSummary);
|
|
13862
13937
|
const eventProviderSessionId = readNonEmptyString2(args.metadataEvent.providerSessionId);
|
|
@@ -14633,6 +14708,16 @@ function findLiveCoordinators(components) {
|
|
|
14633
14708
|
const status = readNonEmptyString2(state.status).toLowerCase();
|
|
14634
14709
|
const modalParked = status === "waiting_choice" || status === "waiting_approval";
|
|
14635
14710
|
const sessionId = readNonEmptyString2(state.instanceId);
|
|
14711
|
+
const stateKey = `${meshId}::${sessionId || "?"}`;
|
|
14712
|
+
const prevParked = coordinatorModalParkState.get(stateKey);
|
|
14713
|
+
if (prevParked !== modalParked) {
|
|
14714
|
+
coordinatorModalParkState.set(stateKey, modalParked);
|
|
14715
|
+
if (modalParked) {
|
|
14716
|
+
LOG.info("MeshReconcile", `Coordinator ${sessionId || "?"} (mesh ${meshId}) entered modal-park (status=${status}) \u2014 terminal events for it will be held until the modal is answered`);
|
|
14717
|
+
} else if (prevParked === true) {
|
|
14718
|
+
LOG.info("MeshReconcile", `Coordinator ${sessionId || "?"} (mesh ${meshId}) left modal-park (status=${status}) \u2014 held events will drain on this/next tick`);
|
|
14719
|
+
}
|
|
14720
|
+
}
|
|
14636
14721
|
out.push({ meshId, instance: inst, sessionId, idle: status === "idle", modalParked });
|
|
14637
14722
|
}
|
|
14638
14723
|
return out;
|
|
@@ -14826,7 +14911,55 @@ async function runMeshReconcileTick(components) {
|
|
|
14826
14911
|
const forceOnly = idleCoordinators.length === 0;
|
|
14827
14912
|
if (targetCoordinators.length === 0) {
|
|
14828
14913
|
if (modalParkedCoordinators.length > 0) {
|
|
14829
|
-
|
|
14914
|
+
const liveSessionIds = new Set(
|
|
14915
|
+
meshCoordinators.map((c) => readNonEmptyString2(c.sessionId)).filter(Boolean)
|
|
14916
|
+
);
|
|
14917
|
+
let orphanEscaped = 0;
|
|
14918
|
+
const hasPendingForOrphanPeek = !store || (() => {
|
|
14919
|
+
try {
|
|
14920
|
+
return store.pendingEventCount(meshId) > 0;
|
|
14921
|
+
} catch {
|
|
14922
|
+
return true;
|
|
14923
|
+
}
|
|
14924
|
+
})();
|
|
14925
|
+
if (hasPendingForOrphanPeek) {
|
|
14926
|
+
let peeked = [];
|
|
14927
|
+
try {
|
|
14928
|
+
peeked = getPendingMeshCoordinatorEvents(meshId, drainDaemonIds.length > 0 ? drainDaemonIds : void 0);
|
|
14929
|
+
} catch {
|
|
14930
|
+
peeked = [];
|
|
14931
|
+
}
|
|
14932
|
+
const isOrphan = (e) => {
|
|
14933
|
+
const want = readNonEmptyString2(e.targetCoordinatorSessionId);
|
|
14934
|
+
return !!want && !liveSessionIds.has(want);
|
|
14935
|
+
};
|
|
14936
|
+
const orphanEventNames = new Set(peeked.filter(isOrphan).map((e) => e.event));
|
|
14937
|
+
if (orphanEventNames.size > 0) {
|
|
14938
|
+
let drained = [];
|
|
14939
|
+
try {
|
|
14940
|
+
drained = drainPendingMeshCoordinatorEvents(
|
|
14941
|
+
meshId,
|
|
14942
|
+
drainDaemonIds.length > 0 ? drainDaemonIds : localDaemonId,
|
|
14943
|
+
{ onlyEvents: orphanEventNames }
|
|
14944
|
+
);
|
|
14945
|
+
} catch (e) {
|
|
14946
|
+
LOG.warn("MeshReconcile", `Orphan-escape drain failed for mesh ${meshId}: ${e?.message || e}`);
|
|
14947
|
+
drained = [];
|
|
14948
|
+
}
|
|
14949
|
+
for (const pending of drained) {
|
|
14950
|
+
if (isOrphan(pending)) {
|
|
14951
|
+
holdOrExpireStrictUnmatchedEvent(pending, readNonEmptyString2(pending.targetCoordinatorSessionId), meshId);
|
|
14952
|
+
orphanEscaped++;
|
|
14953
|
+
} else {
|
|
14954
|
+
try {
|
|
14955
|
+
queuePendingMeshCoordinatorEvent(pending);
|
|
14956
|
+
} catch {
|
|
14957
|
+
}
|
|
14958
|
+
}
|
|
14959
|
+
}
|
|
14960
|
+
}
|
|
14961
|
+
}
|
|
14962
|
+
LOG.info("MeshReconcile", `Reconcile skip \u2192 modal-parked: holding pending event(s) for mesh ${meshId} (${modalParkedCoordinators.length} coordinator(s) awaiting a modal answer; events left queued${orphanEscaped > 0 ? `; ${orphanEscaped} orphan-targeted event(s) routed to strict-route TTL` : ""})`);
|
|
14830
14963
|
let hasPending = true;
|
|
14831
14964
|
if (store) {
|
|
14832
14965
|
try {
|
|
@@ -15097,6 +15230,19 @@ async function reconcileUnterminatedDirectDispatches(components, mesh, selfIds,
|
|
|
15097
15230
|
const messages = Array.isArray(payload.messages) ? payload.messages : [];
|
|
15098
15231
|
const evidence = extractFinalAssistantSummaryEvidence(messages);
|
|
15099
15232
|
if (!evidence.finalSummary) continue;
|
|
15233
|
+
const dispatchedAtMs = Date.parse(readNonEmptyString2(dispatch.dispatchedAt));
|
|
15234
|
+
const transcriptAtMs = Date.parse(evidence.transcriptMessageAt ?? "");
|
|
15235
|
+
if (Number.isFinite(dispatchedAtMs) && Number.isFinite(transcriptAtMs) && transcriptAtMs < dispatchedAtMs) {
|
|
15236
|
+
LOG.info("MeshReconcile", `Stale-summary guard: skipping transcript reconcile for task ${taskId} on node ${nodeId} (mesh ${mesh.id}) \u2014 final assistant message (${evidence.transcriptMessageAt}) predates this task's dispatch (${dispatch.dispatchedAt}); it is a prior task's summary`);
|
|
15237
|
+
traceMeshEventDrop("reconcile_stale_summary_before_dispatch", {
|
|
15238
|
+
taskId,
|
|
15239
|
+
sessionId,
|
|
15240
|
+
nodeId,
|
|
15241
|
+
meshId: mesh.id,
|
|
15242
|
+
event: "agent:generating_completed"
|
|
15243
|
+
}, `transcriptAt=${evidence.transcriptMessageAt} < dispatchedAt=${dispatch.dispatchedAt}`);
|
|
15244
|
+
continue;
|
|
15245
|
+
}
|
|
15100
15246
|
const providerSessionId = readNonEmptyString2(payload.providerSessionId);
|
|
15101
15247
|
const coordinatorDaemonId = selfIds.find((id) => !!id);
|
|
15102
15248
|
try {
|
|
@@ -15224,7 +15370,7 @@ function setupMeshReconcileLoop(components) {
|
|
|
15224
15370
|
}
|
|
15225
15371
|
};
|
|
15226
15372
|
}
|
|
15227
|
-
var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_AUTO_PRUNE_MIN_AGE_MS, heldEventLedgerRecorded, ASSIGNED_STRANDED_DEADLINE_MS, STRICT_SESSION_MATCH_TTL_MS, unresolvedForwardRejectionCounts, MAX_FORWARD_REJECTIONS;
|
|
15373
|
+
var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_AUTO_PRUNE_MIN_AGE_MS, coordinatorModalParkState, heldEventLedgerRecorded, ASSIGNED_STRANDED_DEADLINE_MS, STRICT_SESSION_MATCH_TTL_MS, unresolvedForwardRejectionCounts, MAX_FORWARD_REJECTIONS;
|
|
15228
15374
|
var init_mesh_reconcile_loop = __esm({
|
|
15229
15375
|
"src/mesh/mesh-reconcile-loop.ts"() {
|
|
15230
15376
|
"use strict";
|
|
@@ -15246,6 +15392,7 @@ var init_mesh_reconcile_loop = __esm({
|
|
|
15246
15392
|
init_chat_message_normalization();
|
|
15247
15393
|
DEFAULT_RECONCILE_INTERVAL_MS = 4e3;
|
|
15248
15394
|
DEFAULT_AUTO_PRUNE_MIN_AGE_MS = 24 * 60 * 6e4;
|
|
15395
|
+
coordinatorModalParkState = /* @__PURE__ */ new Map();
|
|
15249
15396
|
heldEventLedgerRecorded = /* @__PURE__ */ new Set();
|
|
15250
15397
|
ASSIGNED_STRANDED_DEADLINE_MS = 5 * 6e4;
|
|
15251
15398
|
STRICT_SESSION_MATCH_TTL_MS = 6e4;
|
|
@@ -38184,6 +38331,8 @@ function getMessageTime(message) {
|
|
|
38184
38331
|
}
|
|
38185
38332
|
var COMPLETED_FINALIZATION_RETRY_MS = 1e3;
|
|
38186
38333
|
var COMPLETED_FINALIZATION_MAX_WAIT_MS = 3e4;
|
|
38334
|
+
var NATIVE_HISTORY_MESH_IDLE_SETTLE_MS = 1500;
|
|
38335
|
+
var USER_INPUT_ACK_DEDUP_WINDOW_MS = 6e4;
|
|
38187
38336
|
var TERMINAL_MESH_EVENTS = /* @__PURE__ */ new Set([
|
|
38188
38337
|
"agent:generating_completed",
|
|
38189
38338
|
"agent:stopped",
|
|
@@ -38455,6 +38604,14 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
38455
38604
|
runtimeMessages = [];
|
|
38456
38605
|
lastPersistedHistoryMessages = [];
|
|
38457
38606
|
lastAcknowledgedUserInputAt = 0;
|
|
38607
|
+
// TASKBUBBLE-DUP: per-content last-ack timestamps so the same dispatched
|
|
38608
|
+
// prompt acked twice in quick succession (the worker buffers the first
|
|
38609
|
+
// send during bootstrap/busy, then a redelivery — dispatch-confirm-timeout
|
|
38610
|
+
// requeue or a reconcile re-dispatch — fires a SECOND send_chat before the
|
|
38611
|
+
// outbound queue drains) collapses to ONE user bubble. Keyed on the trimmed
|
|
38612
|
+
// content; an entry older than USER_INPUT_ACK_DEDUP_WINDOW_MS is treated as
|
|
38613
|
+
// a fresh, intentional resend and is NOT suppressed.
|
|
38614
|
+
recentUserInputAcks = /* @__PURE__ */ new Map();
|
|
38458
38615
|
lastNativeSourceCanonicalCheckAt = 0;
|
|
38459
38616
|
lastNativeSourceCanonicalCacheKey = void 0;
|
|
38460
38617
|
cachedSqliteDb = null;
|
|
@@ -38889,6 +39046,15 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
38889
39046
|
const content = typeof input === "string" ? input.trim() : buildCliStructuredInputPrompt(input).trim();
|
|
38890
39047
|
if (!content) return;
|
|
38891
39048
|
const receivedAt = Date.now();
|
|
39049
|
+
const ackContentKey = shortHash(`${this.instanceId}:${content}`, 24);
|
|
39050
|
+
const lastAckAt = this.recentUserInputAcks.get(ackContentKey);
|
|
39051
|
+
if (lastAckAt !== void 0 && receivedAt - lastAckAt <= USER_INPUT_ACK_DEDUP_WINDOW_MS) {
|
|
39052
|
+
this.recentUserInputAcks.set(ackContentKey, receivedAt);
|
|
39053
|
+
this.pruneRecentUserInputAcks(receivedAt);
|
|
39054
|
+
return;
|
|
39055
|
+
}
|
|
39056
|
+
this.recentUserInputAcks.set(ackContentKey, receivedAt);
|
|
39057
|
+
this.pruneRecentUserInputAcks(receivedAt);
|
|
38892
39058
|
this.lastAcknowledgedUserInputAt = receivedAt;
|
|
38893
39059
|
const dedupKey = `user_input_ack:${shortHash(`${this.instanceId}:${content}:${receivedAt}`, 24)}`;
|
|
38894
39060
|
this.appendRuntimeMessage(buildChatMessage({
|
|
@@ -38906,6 +39072,13 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
38906
39072
|
}
|
|
38907
39073
|
}), dedupKey);
|
|
38908
39074
|
}
|
|
39075
|
+
/** Drop user-input ack entries older than the dedup window so the map can't grow unbounded. */
|
|
39076
|
+
pruneRecentUserInputAcks(now) {
|
|
39077
|
+
if (this.recentUserInputAcks.size <= 1) return;
|
|
39078
|
+
for (const [key, at] of this.recentUserInputAcks) {
|
|
39079
|
+
if (now - at > USER_INPUT_ACK_DEDUP_WINDOW_MS) this.recentUserInputAcks.delete(key);
|
|
39080
|
+
}
|
|
39081
|
+
}
|
|
38909
39082
|
dispose() {
|
|
38910
39083
|
this.adapter.shutdown();
|
|
38911
39084
|
this.monitor.reset();
|
|
@@ -39595,8 +39768,9 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
39595
39768
|
previousStatus: this.lastStatus
|
|
39596
39769
|
};
|
|
39597
39770
|
const ownsExternalHistory = !!this.adapter?.chatMessagesOwnedExternally;
|
|
39598
|
-
const
|
|
39599
|
-
|
|
39771
|
+
const meshWorkerSession = this.isMeshWorkerSession();
|
|
39772
|
+
const flushDelay = ownsExternalHistory ? meshWorkerSession ? NATIVE_HISTORY_MESH_IDLE_SETTLE_MS : 0 : 3e3;
|
|
39773
|
+
LOG.debug("CLI", `[${this.type}] set completedDebouncePending duration=${duration}s ownsExternalHistory=${ownsExternalHistory} meshWorker=${meshWorkerSession} flushDelay=${flushDelay}ms generatingStartedAt=${this.generatingStartedAt}`);
|
|
39600
39774
|
this.scheduleCompletedDebounceFlush(flushDelay);
|
|
39601
39775
|
}
|
|
39602
39776
|
} else if (newStatus === "idle" && this.lastStatus === "starting") {
|
|
@@ -42651,11 +42825,11 @@ var cliAgentHandlers = {
|
|
|
42651
42825
|
}
|
|
42652
42826
|
}
|
|
42653
42827
|
}
|
|
42654
|
-
const agentResult = await ctx.deps.cliManager.handleCliCommand("agent_command", args);
|
|
42655
42828
|
const meshCtx = args?.meshContext;
|
|
42656
42829
|
const dispatchNodeId = readStringValue(meshCtx?.nodeId);
|
|
42657
42830
|
const dispatchMeshId = readStringValue(meshCtx?.meshId);
|
|
42658
|
-
|
|
42831
|
+
const isSendChat = args?.action === "send_chat";
|
|
42832
|
+
if (isSendChat && dispatchNodeId && dispatchMeshId) {
|
|
42659
42833
|
try {
|
|
42660
42834
|
const { getMesh: getMesh2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
42661
42835
|
const meshObj = getMesh2(dispatchMeshId) ?? ctx.getCachedInlineMesh(dispatchMeshId);
|
|
@@ -42663,17 +42837,22 @@ var cliAgentHandlers = {
|
|
|
42663
42837
|
const bootstrapStatus = readStringValue(nodeObj?.worktreeBootstrap?.status);
|
|
42664
42838
|
if (bootstrapStatus === "running") {
|
|
42665
42839
|
return {
|
|
42666
|
-
success:
|
|
42667
|
-
|
|
42668
|
-
|
|
42669
|
-
|
|
42670
|
-
|
|
42840
|
+
success: false,
|
|
42841
|
+
recoverable: true,
|
|
42842
|
+
dispatched: false,
|
|
42843
|
+
code: "mesh_node_bootstrap_pending",
|
|
42844
|
+
reason: "bootstrap_still_running",
|
|
42845
|
+
nodeId: dispatchNodeId,
|
|
42846
|
+
meshId: dispatchMeshId,
|
|
42847
|
+
...readStringValue(meshCtx?.taskId) ? { taskId: readStringValue(meshCtx?.taskId) } : {},
|
|
42848
|
+
error: `Node '${dispatchNodeId}' worktree bootstrap is still running; a task injected now would land in the session input buffer before the provider is ready to consume it and be silently lost. Dispatch deferred.`,
|
|
42849
|
+
nextAction: "Wait for the worktree_bootstrap_complete event (or poll mesh_status until the node session is ready), then re-send the task with mesh_send_task. Alternatively use mesh_enqueue_task so the queue auto-assigns it once a ready session is available."
|
|
42671
42850
|
};
|
|
42672
42851
|
}
|
|
42673
42852
|
} catch {
|
|
42674
42853
|
}
|
|
42675
42854
|
}
|
|
42676
|
-
return
|
|
42855
|
+
return ctx.deps.cliManager.handleCliCommand("agent_command", args);
|
|
42677
42856
|
},
|
|
42678
42857
|
// ─── Logs ───
|
|
42679
42858
|
list_saved_sessions: async (ctx, args) => {
|
|
@@ -47795,49 +47974,72 @@ var fastForwardHandlers = {
|
|
|
47795
47974
|
};
|
|
47796
47975
|
},
|
|
47797
47976
|
fast_forward_mesh_node: async (ctx, args) => {
|
|
47798
|
-
const
|
|
47799
|
-
const
|
|
47800
|
-
|
|
47801
|
-
|
|
47802
|
-
|
|
47803
|
-
|
|
47804
|
-
|
|
47805
|
-
|
|
47806
|
-
|
|
47807
|
-
|
|
47808
|
-
if (
|
|
47809
|
-
|
|
47977
|
+
const workspaceForError = typeof args?.workspace === "string" ? args.workspace.trim() : "";
|
|
47978
|
+
const meshIdForError = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
47979
|
+
const nodeIdForError = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
|
|
47980
|
+
try {
|
|
47981
|
+
const meshId = meshIdForError;
|
|
47982
|
+
const nodeId = nodeIdForError;
|
|
47983
|
+
let workspace = workspaceForError;
|
|
47984
|
+
let submoduleIgnorePaths = Array.isArray(args?.submoduleIgnorePaths) ? args.submoduleIgnorePaths.filter((value) => typeof value === "string") : void 0;
|
|
47985
|
+
let nodeDaemonId;
|
|
47986
|
+
let allowAutoPublishSubmoduleMainCommits = false;
|
|
47987
|
+
if (meshId && nodeId) {
|
|
47988
|
+
const meshRecord = await ctx.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
|
|
47989
|
+
const mesh = meshRecord?.mesh;
|
|
47990
|
+
const node = mesh?.nodes?.find((n) => meshNodeIdMatches(n, nodeId));
|
|
47991
|
+
if (!workspace) {
|
|
47992
|
+
workspace = typeof node?.workspace === "string" ? node.workspace.trim() : "";
|
|
47993
|
+
}
|
|
47994
|
+
if (!submoduleIgnorePaths && Array.isArray(node?.policy?.submoduleIgnorePaths)) {
|
|
47995
|
+
submoduleIgnorePaths = node.policy.submoduleIgnorePaths.filter((value) => typeof value === "string");
|
|
47996
|
+
}
|
|
47997
|
+
allowAutoPublishSubmoduleMainCommits = mesh?.policy?.allowAutoPublishSubmoduleMainCommits === true;
|
|
47998
|
+
nodeDaemonId = typeof node?.daemonId === "string" ? node.daemonId.trim() : void 0;
|
|
47810
47999
|
}
|
|
47811
|
-
|
|
47812
|
-
|
|
48000
|
+
const selfDaemonId = ctx.deps.statusInstanceId;
|
|
48001
|
+
const isRemote = nodeDaemonId && selfDaemonId && !daemonIdsEquivalent(nodeDaemonId, selfDaemonId);
|
|
48002
|
+
if (isRemote && ctx.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
|
|
48003
|
+
const forwarded = await ctx.deps.dispatchMeshCommand(nodeDaemonId, "fast_forward_mesh_node", {
|
|
48004
|
+
...typeof args === "object" && args !== null ? args : {},
|
|
48005
|
+
workspace,
|
|
48006
|
+
_meshDirectDispatch: true
|
|
48007
|
+
});
|
|
48008
|
+
return forwarded ?? { success: false, error: "no response from remote node" };
|
|
47813
48009
|
}
|
|
47814
|
-
|
|
47815
|
-
|
|
47816
|
-
|
|
47817
|
-
const selfDaemonId = ctx.deps.statusInstanceId;
|
|
47818
|
-
const isRemote = nodeDaemonId && selfDaemonId && !daemonIdsEquivalent(nodeDaemonId, selfDaemonId);
|
|
47819
|
-
if (isRemote && ctx.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
|
|
47820
|
-
const forwarded = await ctx.deps.dispatchMeshCommand(nodeDaemonId, "fast_forward_mesh_node", {
|
|
47821
|
-
...typeof args === "object" && args !== null ? args : {},
|
|
48010
|
+
const result = await fastForwardMeshNode({
|
|
48011
|
+
meshId: meshId || void 0,
|
|
48012
|
+
nodeId: nodeId || void 0,
|
|
47822
48013
|
workspace,
|
|
47823
|
-
|
|
48014
|
+
branch: typeof args?.branch === "string" ? args.branch : void 0,
|
|
48015
|
+
execute: args?.execute === true,
|
|
48016
|
+
dryRun: args?.dryRun === true,
|
|
48017
|
+
updateSubmodules: args?.updateSubmodules === true,
|
|
48018
|
+
submoduleIgnorePaths,
|
|
48019
|
+
mode: args?.mode === "push" ? "push" : "merge",
|
|
48020
|
+
pushSubmodules: args?.pushSubmodules === true,
|
|
48021
|
+
allowAutoPublishSubmoduleMainCommits
|
|
47824
48022
|
});
|
|
47825
|
-
return
|
|
48023
|
+
return result;
|
|
48024
|
+
} catch (e) {
|
|
48025
|
+
const errorMessage = e?.message || String(e);
|
|
48026
|
+
return {
|
|
48027
|
+
success: false,
|
|
48028
|
+
code: "fast_forward_safety_gate_error",
|
|
48029
|
+
...meshIdForError ? { meshId: meshIdForError } : {},
|
|
48030
|
+
...nodeIdForError ? { nodeId: nodeIdForError } : {},
|
|
48031
|
+
workspace: workspaceForError,
|
|
48032
|
+
mode: args?.mode === "push" ? "push" : "merge",
|
|
48033
|
+
allowed: false,
|
|
48034
|
+
willRun: false,
|
|
48035
|
+
executed: false,
|
|
48036
|
+
// Surface the throw as a blocking reason instead of an opaque IPC crash
|
|
48037
|
+
// so the coordinator gets the same structured shape a clean node returns.
|
|
48038
|
+
blockingReasons: ["fast_forward_safety_gate_error"],
|
|
48039
|
+
operationError: errorMessage,
|
|
48040
|
+
error: errorMessage
|
|
48041
|
+
};
|
|
47826
48042
|
}
|
|
47827
|
-
const result = await fastForwardMeshNode({
|
|
47828
|
-
meshId: meshId || void 0,
|
|
47829
|
-
nodeId: nodeId || void 0,
|
|
47830
|
-
workspace,
|
|
47831
|
-
branch: typeof args?.branch === "string" ? args.branch : void 0,
|
|
47832
|
-
execute: args?.execute === true,
|
|
47833
|
-
dryRun: args?.dryRun === true,
|
|
47834
|
-
updateSubmodules: args?.updateSubmodules === true,
|
|
47835
|
-
submoduleIgnorePaths,
|
|
47836
|
-
mode: args?.mode === "push" ? "push" : "merge",
|
|
47837
|
-
pushSubmodules: args?.pushSubmodules === true,
|
|
47838
|
-
allowAutoPublishSubmoduleMainCommits
|
|
47839
|
-
});
|
|
47840
|
-
return result;
|
|
47841
48043
|
},
|
|
47842
48044
|
refine_mesh_node: async (ctx, args) => {
|
|
47843
48045
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
@@ -47994,6 +48196,56 @@ var meshCoordinatorLaunchHandlers = {
|
|
|
47994
48196
|
return "";
|
|
47995
48197
|
}
|
|
47996
48198
|
};
|
|
48199
|
+
const buildRecentActivityBestEffort = async (id) => {
|
|
48200
|
+
try {
|
|
48201
|
+
const { getLedgerSummary: getLedgerSummary2, readLedgerEntries: readLedgerEntries2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
|
|
48202
|
+
const { getMeshQueueStats: getMeshQueueStats2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
|
|
48203
|
+
const summary = getLedgerSummary2(id);
|
|
48204
|
+
const queue = getMeshQueueStats2(id);
|
|
48205
|
+
const failureEntries = readLedgerEntries2(id, { kind: ["task_failed"], tail: 5 });
|
|
48206
|
+
const recentFailures = failureEntries.map((e) => {
|
|
48207
|
+
const p = e.payload || {};
|
|
48208
|
+
const raw = typeof p.taskSummary === "string" ? p.taskSummary : typeof p.message === "string" ? p.message : typeof p.error === "string" ? p.error : "";
|
|
48209
|
+
const summaryText = raw.length > 160 ? `${raw.slice(0, 160)}\u2026` : raw;
|
|
48210
|
+
return {
|
|
48211
|
+
timestamp: e.timestamp,
|
|
48212
|
+
nodeId: e.nodeId,
|
|
48213
|
+
summary: summaryText
|
|
48214
|
+
};
|
|
48215
|
+
});
|
|
48216
|
+
return {
|
|
48217
|
+
recentFailures,
|
|
48218
|
+
recentFailureCount: summary.recentFailures,
|
|
48219
|
+
pendingTasks: queue.pending,
|
|
48220
|
+
assignedTasks: queue.assigned,
|
|
48221
|
+
stalledTasks: summary.taskStalled,
|
|
48222
|
+
lastActivityAt: summary.lastActivityAt
|
|
48223
|
+
};
|
|
48224
|
+
} catch {
|
|
48225
|
+
return void 0;
|
|
48226
|
+
}
|
|
48227
|
+
};
|
|
48228
|
+
const buildOperatingNotesBestEffort = async (id) => {
|
|
48229
|
+
try {
|
|
48230
|
+
const { readLedgerEntries: readLedgerEntries2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
|
|
48231
|
+
const noteEntries = readLedgerEntries2(id, { kind: ["coordinator_operating_note"], tail: 20 });
|
|
48232
|
+
const notes = noteEntries.map((e) => {
|
|
48233
|
+
const p = e.payload || {};
|
|
48234
|
+
const text = typeof p.text === "string" ? p.text.trim() : "";
|
|
48235
|
+
if (!text) return null;
|
|
48236
|
+
const category = p.category === "provider_quirk" || p.category === "pattern_to_avoid" || p.category === "recovery_lesson" ? p.category : void 0;
|
|
48237
|
+
return {
|
|
48238
|
+
text,
|
|
48239
|
+
category,
|
|
48240
|
+
createdAt: typeof p.createdAt === "string" ? p.createdAt : e.timestamp,
|
|
48241
|
+
sourceCoordinator: typeof p.sourceCoordinator === "string" ? p.sourceCoordinator : void 0
|
|
48242
|
+
};
|
|
48243
|
+
}).filter((n) => n !== null);
|
|
48244
|
+
return notes.length ? notes : void 0;
|
|
48245
|
+
} catch {
|
|
48246
|
+
return void 0;
|
|
48247
|
+
}
|
|
48248
|
+
};
|
|
47997
48249
|
let mesh;
|
|
47998
48250
|
if (args?.inlineMesh && typeof args.inlineMesh === "object") {
|
|
47999
48251
|
mesh = args.inlineMesh;
|
|
@@ -48084,7 +48336,7 @@ var meshCoordinatorLaunchHandlers = {
|
|
|
48084
48336
|
if (coordinatorSetup.kind === "cli_command") {
|
|
48085
48337
|
let cliCmdSystemPrompt = "";
|
|
48086
48338
|
try {
|
|
48087
|
-
cliCmdSystemPrompt = buildCoordinatorSystemPrompt2({ mesh, coordinatorCliType: cliType, userInstruction: extraSystemPrompt || void 0, missionSection: buildMissionSectionBestEffort(mesh.id) });
|
|
48339
|
+
cliCmdSystemPrompt = buildCoordinatorSystemPrompt2({ mesh, coordinatorCliType: cliType, userInstruction: extraSystemPrompt || void 0, missionSection: buildMissionSectionBestEffort(mesh.id), recentActivity: await buildRecentActivityBestEffort(mesh.id), operatingNotes: await buildOperatingNotesBestEffort(mesh.id) });
|
|
48088
48340
|
} catch (error) {
|
|
48089
48341
|
const message = error?.message || String(error);
|
|
48090
48342
|
LOG.error("MeshCoordinator", `Failed to build coordinator prompt: ${message}`);
|
|
@@ -48261,7 +48513,7 @@ ${ptyResult.output.slice(-2e3)}`);
|
|
|
48261
48513
|
}
|
|
48262
48514
|
let systemPrompt = "";
|
|
48263
48515
|
try {
|
|
48264
|
-
systemPrompt = buildCoordinatorSystemPrompt2({ mesh, coordinatorCliType: cliType, userInstruction: extraSystemPrompt || void 0, missionSection: buildMissionSectionBestEffort(mesh.id) });
|
|
48516
|
+
systemPrompt = buildCoordinatorSystemPrompt2({ mesh, coordinatorCliType: cliType, userInstruction: extraSystemPrompt || void 0, missionSection: buildMissionSectionBestEffort(mesh.id), recentActivity: await buildRecentActivityBestEffort(mesh.id), operatingNotes: await buildOperatingNotesBestEffort(mesh.id) });
|
|
48265
48517
|
} catch (error) {
|
|
48266
48518
|
const message = error?.message || String(error);
|
|
48267
48519
|
LOG.error("MeshCoordinator", `Failed to build coordinator prompt: ${message}`);
|