@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.mjs
CHANGED
|
@@ -311,10 +311,10 @@ function readInjected(value) {
|
|
|
311
311
|
}
|
|
312
312
|
function getDaemonBuildInfo() {
|
|
313
313
|
if (cached) return cached;
|
|
314
|
-
const commit = readInjected(true ? "
|
|
315
|
-
const commitShort = readInjected(true ? "
|
|
316
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
317
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
314
|
+
const commit = readInjected(true ? "b17fb9165bd52c9e2ab8cccf2ce80734e2165680" : void 0) ?? "unknown";
|
|
315
|
+
const commitShort = readInjected(true ? "b17fb916" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
316
|
+
const version = readInjected(true ? "0.9.82-rc.375" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
317
|
+
const builtAt = readInjected(true ? "2026-06-25T02:21:59.111Z" : void 0);
|
|
318
318
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
319
319
|
return cached;
|
|
320
320
|
}
|
|
@@ -3039,6 +3039,10 @@ Default branch: \`${mesh.defaultBranch}\`` : ""}`);
|
|
|
3039
3039
|
if (ctx.missionSection?.trim()) {
|
|
3040
3040
|
sections.push(ctx.missionSection.trim());
|
|
3041
3041
|
}
|
|
3042
|
+
const recentActivity = buildRecentActivitySection(ctx.recentActivity);
|
|
3043
|
+
if (recentActivity) sections.push(recentActivity);
|
|
3044
|
+
const operatingNotes = buildOperatingNotesSection(ctx.operatingNotes);
|
|
3045
|
+
if (operatingNotes) sections.push(operatingNotes);
|
|
3042
3046
|
sections.push(buildPolicySection({ ...DEFAULT_MESH_POLICY, ...mesh.policy || {} }));
|
|
3043
3047
|
sections.push(TOOLS_SECTION);
|
|
3044
3048
|
sections.push(TOOL_EXPOSURE_PREFLIGHT_SECTION);
|
|
@@ -3070,6 +3074,8 @@ function expandPromptPlaceholders(template, ctx) {
|
|
|
3070
3074
|
cliType: coordinatorCliType || "",
|
|
3071
3075
|
nodes: nodesSection,
|
|
3072
3076
|
mission: ctx.missionSection?.trim() || "",
|
|
3077
|
+
recentActivity: buildRecentActivitySection(ctx.recentActivity) || "",
|
|
3078
|
+
operatingNotes: buildOperatingNotesSection(ctx.operatingNotes) || "",
|
|
3073
3079
|
policy: buildPolicySection({ ...DEFAULT_MESH_POLICY, ...mesh.policy || {} }),
|
|
3074
3080
|
tools: TOOLS_SECTION,
|
|
3075
3081
|
workflow: WORKFLOW_SECTION,
|
|
@@ -3141,6 +3147,56 @@ function indentFollowing(text, pad) {
|
|
|
3141
3147
|
if (lines.length === 1) return lines[0];
|
|
3142
3148
|
return [lines[0], ...lines.slice(1).map((l) => pad + l)].join("\n");
|
|
3143
3149
|
}
|
|
3150
|
+
function buildRecentActivitySection(activity) {
|
|
3151
|
+
if (!activity) return "";
|
|
3152
|
+
const failures = Array.isArray(activity.recentFailures) ? activity.recentFailures : [];
|
|
3153
|
+
const pending = Number.isFinite(activity.pendingTasks) ? Number(activity.pendingTasks) : 0;
|
|
3154
|
+
const assigned = Number.isFinite(activity.assignedTasks) ? Number(activity.assignedTasks) : 0;
|
|
3155
|
+
const stalled = Number.isFinite(activity.stalledTasks) ? Number(activity.stalledTasks) : 0;
|
|
3156
|
+
const recentFailureCount = Number.isFinite(activity.recentFailureCount) ? Number(activity.recentFailureCount) : failures.length;
|
|
3157
|
+
if (failures.length === 0 && pending === 0 && assigned === 0 && stalled === 0 && recentFailureCount === 0) {
|
|
3158
|
+
return "";
|
|
3159
|
+
}
|
|
3160
|
+
const lines = ["## Recent Activity", ""];
|
|
3161
|
+
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.");
|
|
3162
|
+
lines.push("");
|
|
3163
|
+
const counts = [];
|
|
3164
|
+
if (pending > 0) counts.push(`**${pending}** pending`);
|
|
3165
|
+
if (assigned > 0) counts.push(`**${assigned}** assigned`);
|
|
3166
|
+
if (stalled > 0) counts.push(`**${stalled}** stalled`);
|
|
3167
|
+
if (recentFailureCount > 0) counts.push(`**${recentFailureCount}** failed in the last 30 min`);
|
|
3168
|
+
if (counts.length) lines.push(`- Queue/ledger: ${counts.join(", ")}.`);
|
|
3169
|
+
if (activity.lastActivityAt) lines.push(`- Last ledger activity: ${activity.lastActivityAt}.`);
|
|
3170
|
+
if (failures.length > 0) {
|
|
3171
|
+
const recent = failures.slice(-5).reverse();
|
|
3172
|
+
lines.push("", "Recent failures (newest first):");
|
|
3173
|
+
for (const f of recent) {
|
|
3174
|
+
const when = f.timestamp ? `${f.timestamp} ` : "";
|
|
3175
|
+
const node = f.nodeId ? `node \`${f.nodeId}\`` : "unknown node";
|
|
3176
|
+
const summary = (f.summary || "").trim();
|
|
3177
|
+
lines.push(`- ${when}${node}${summary ? ` \u2014 ${summary}` : ""}`);
|
|
3178
|
+
}
|
|
3179
|
+
lines.push("", "_Check `mesh_task_history` before retrying; repeated failures on the same node mean reassign or escalate, not retry._");
|
|
3180
|
+
}
|
|
3181
|
+
return lines.join("\n");
|
|
3182
|
+
}
|
|
3183
|
+
function buildOperatingNotesSection(notes) {
|
|
3184
|
+
const valid = Array.isArray(notes) ? notes.filter((n) => n && typeof n.text === "string" && n.text.trim()) : [];
|
|
3185
|
+
if (valid.length === 0) return "";
|
|
3186
|
+
const categoryLabel = {
|
|
3187
|
+
provider_quirk: "provider quirk",
|
|
3188
|
+
pattern_to_avoid: "pattern to avoid",
|
|
3189
|
+
recovery_lesson: "recovery lesson"
|
|
3190
|
+
};
|
|
3191
|
+
const lines = ["## Operating Notes", ""];
|
|
3192
|
+
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.");
|
|
3193
|
+
lines.push("");
|
|
3194
|
+
for (const n of valid) {
|
|
3195
|
+
const cat = n.category && categoryLabel[n.category] ? `[${categoryLabel[n.category]}] ` : "";
|
|
3196
|
+
lines.push(`- ${cat}${n.text.trim()}`);
|
|
3197
|
+
}
|
|
3198
|
+
return lines.join("\n");
|
|
3199
|
+
}
|
|
3144
3200
|
function buildPolicySection(policy) {
|
|
3145
3201
|
const rules = [];
|
|
3146
3202
|
if (policy.requirePreTaskCheckpoint) rules.push("- Create a git checkpoint **before** starting each task");
|
|
@@ -3201,6 +3257,7 @@ var init_coordinator_prompt = __esm({
|
|
|
3201
3257
|
| \`mesh_read_chat\` | Read recent chat messages from a delegated agent session |
|
|
3202
3258
|
| \`mesh_read_debug\` | Collect a daemon-side chat/parser debug bundle for a session |
|
|
3203
3259
|
| \`mesh_task_history\` | Read the task ledger \u2014 dispatches, completions, failures. Use to understand what has been done before deciding next steps |
|
|
3260
|
+
| \`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 |
|
|
3204
3261
|
| \`mesh_git_status\` | Check git status on a specific node |
|
|
3205
3262
|
| \`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 |
|
|
3206
3263
|
| \`mesh_fast_forward_node\` | Safely dry-run or explicitly execute an obvious clean fast-forward without launching an agent session |
|
|
@@ -12936,6 +12993,18 @@ function isWeakTerminalLedgerPayload(payload) {
|
|
|
12936
12993
|
const diag = readRecord4(payload.completionDiagnostic);
|
|
12937
12994
|
return diag?.finalAssistantPresent === false || diag?.blockReason === "missing_final_assistant";
|
|
12938
12995
|
}
|
|
12996
|
+
function supersedesTruncatedTerminalSummary(args) {
|
|
12997
|
+
if (!args.terminalTaskId || !args.eventTaskId || args.terminalTaskId !== args.eventTaskId) return false;
|
|
12998
|
+
if (!isGenuineCompletionEvidence(args.metadataEvent)) return false;
|
|
12999
|
+
const terminalSummary = readNonEmptyString2(args.terminalPayload.finalSummary);
|
|
13000
|
+
const eventSummary = readNonEmptyString2(args.metadataEvent.finalSummary);
|
|
13001
|
+
if (!eventSummary) return false;
|
|
13002
|
+
if (terminalSummary === eventSummary) return false;
|
|
13003
|
+
if (isWeakTerminalLedgerPayload(args.terminalPayload)) return false;
|
|
13004
|
+
if (!terminalSummary) return true;
|
|
13005
|
+
if (eventSummary.startsWith(terminalSummary)) return true;
|
|
13006
|
+
return eventSummary.length > terminalSummary.length + 32;
|
|
13007
|
+
}
|
|
12939
13008
|
function resolveActiveDirectDispatchTaskId(meshId, sessionId) {
|
|
12940
13009
|
try {
|
|
12941
13010
|
const matches = getActiveDirectDispatches(meshId).filter((d) => d.sessionId === sessionId);
|
|
@@ -13852,7 +13921,13 @@ function evaluateMeshEventSuppression(args, ctx) {
|
|
|
13852
13921
|
const terminalTaskId = readNonEmptyString2(terminal.payload.taskId);
|
|
13853
13922
|
const eventTaskId = readNonEmptyString2(args.metadataEvent.taskId);
|
|
13854
13923
|
const distinctTaskCompletion = !!eventTaskId && !!terminalTaskId && eventTaskId !== terminalTaskId;
|
|
13855
|
-
|
|
13924
|
+
const supersedesTruncatedTerminal = supersedesTruncatedTerminalSummary({
|
|
13925
|
+
terminalPayload: terminal.payload,
|
|
13926
|
+
metadataEvent: args.metadataEvent,
|
|
13927
|
+
terminalTaskId,
|
|
13928
|
+
eventTaskId
|
|
13929
|
+
});
|
|
13930
|
+
if (!newDispatchAfterTerminal && !supersedesWeakTerminal && !distinctTaskCompletion && !supersedesTruncatedTerminal) {
|
|
13856
13931
|
const terminalProviderSessionId = readNonEmptyString2(terminal.payload.providerSessionId);
|
|
13857
13932
|
const terminalFinalSummary = readNonEmptyString2(terminal.payload.finalSummary);
|
|
13858
13933
|
const eventProviderSessionId = readNonEmptyString2(args.metadataEvent.providerSessionId);
|
|
@@ -14628,6 +14703,16 @@ function findLiveCoordinators(components) {
|
|
|
14628
14703
|
const status = readNonEmptyString2(state.status).toLowerCase();
|
|
14629
14704
|
const modalParked = status === "waiting_choice" || status === "waiting_approval";
|
|
14630
14705
|
const sessionId = readNonEmptyString2(state.instanceId);
|
|
14706
|
+
const stateKey = `${meshId}::${sessionId || "?"}`;
|
|
14707
|
+
const prevParked = coordinatorModalParkState.get(stateKey);
|
|
14708
|
+
if (prevParked !== modalParked) {
|
|
14709
|
+
coordinatorModalParkState.set(stateKey, modalParked);
|
|
14710
|
+
if (modalParked) {
|
|
14711
|
+
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`);
|
|
14712
|
+
} else if (prevParked === true) {
|
|
14713
|
+
LOG.info("MeshReconcile", `Coordinator ${sessionId || "?"} (mesh ${meshId}) left modal-park (status=${status}) \u2014 held events will drain on this/next tick`);
|
|
14714
|
+
}
|
|
14715
|
+
}
|
|
14631
14716
|
out.push({ meshId, instance: inst, sessionId, idle: status === "idle", modalParked });
|
|
14632
14717
|
}
|
|
14633
14718
|
return out;
|
|
@@ -14821,7 +14906,55 @@ async function runMeshReconcileTick(components) {
|
|
|
14821
14906
|
const forceOnly = idleCoordinators.length === 0;
|
|
14822
14907
|
if (targetCoordinators.length === 0) {
|
|
14823
14908
|
if (modalParkedCoordinators.length > 0) {
|
|
14824
|
-
|
|
14909
|
+
const liveSessionIds = new Set(
|
|
14910
|
+
meshCoordinators.map((c) => readNonEmptyString2(c.sessionId)).filter(Boolean)
|
|
14911
|
+
);
|
|
14912
|
+
let orphanEscaped = 0;
|
|
14913
|
+
const hasPendingForOrphanPeek = !store || (() => {
|
|
14914
|
+
try {
|
|
14915
|
+
return store.pendingEventCount(meshId) > 0;
|
|
14916
|
+
} catch {
|
|
14917
|
+
return true;
|
|
14918
|
+
}
|
|
14919
|
+
})();
|
|
14920
|
+
if (hasPendingForOrphanPeek) {
|
|
14921
|
+
let peeked = [];
|
|
14922
|
+
try {
|
|
14923
|
+
peeked = getPendingMeshCoordinatorEvents(meshId, drainDaemonIds.length > 0 ? drainDaemonIds : void 0);
|
|
14924
|
+
} catch {
|
|
14925
|
+
peeked = [];
|
|
14926
|
+
}
|
|
14927
|
+
const isOrphan = (e) => {
|
|
14928
|
+
const want = readNonEmptyString2(e.targetCoordinatorSessionId);
|
|
14929
|
+
return !!want && !liveSessionIds.has(want);
|
|
14930
|
+
};
|
|
14931
|
+
const orphanEventNames = new Set(peeked.filter(isOrphan).map((e) => e.event));
|
|
14932
|
+
if (orphanEventNames.size > 0) {
|
|
14933
|
+
let drained = [];
|
|
14934
|
+
try {
|
|
14935
|
+
drained = drainPendingMeshCoordinatorEvents(
|
|
14936
|
+
meshId,
|
|
14937
|
+
drainDaemonIds.length > 0 ? drainDaemonIds : localDaemonId,
|
|
14938
|
+
{ onlyEvents: orphanEventNames }
|
|
14939
|
+
);
|
|
14940
|
+
} catch (e) {
|
|
14941
|
+
LOG.warn("MeshReconcile", `Orphan-escape drain failed for mesh ${meshId}: ${e?.message || e}`);
|
|
14942
|
+
drained = [];
|
|
14943
|
+
}
|
|
14944
|
+
for (const pending of drained) {
|
|
14945
|
+
if (isOrphan(pending)) {
|
|
14946
|
+
holdOrExpireStrictUnmatchedEvent(pending, readNonEmptyString2(pending.targetCoordinatorSessionId), meshId);
|
|
14947
|
+
orphanEscaped++;
|
|
14948
|
+
} else {
|
|
14949
|
+
try {
|
|
14950
|
+
queuePendingMeshCoordinatorEvent(pending);
|
|
14951
|
+
} catch {
|
|
14952
|
+
}
|
|
14953
|
+
}
|
|
14954
|
+
}
|
|
14955
|
+
}
|
|
14956
|
+
}
|
|
14957
|
+
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` : ""})`);
|
|
14825
14958
|
let hasPending = true;
|
|
14826
14959
|
if (store) {
|
|
14827
14960
|
try {
|
|
@@ -15092,6 +15225,19 @@ async function reconcileUnterminatedDirectDispatches(components, mesh, selfIds,
|
|
|
15092
15225
|
const messages = Array.isArray(payload.messages) ? payload.messages : [];
|
|
15093
15226
|
const evidence = extractFinalAssistantSummaryEvidence(messages);
|
|
15094
15227
|
if (!evidence.finalSummary) continue;
|
|
15228
|
+
const dispatchedAtMs = Date.parse(readNonEmptyString2(dispatch.dispatchedAt));
|
|
15229
|
+
const transcriptAtMs = Date.parse(evidence.transcriptMessageAt ?? "");
|
|
15230
|
+
if (Number.isFinite(dispatchedAtMs) && Number.isFinite(transcriptAtMs) && transcriptAtMs < dispatchedAtMs) {
|
|
15231
|
+
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`);
|
|
15232
|
+
traceMeshEventDrop("reconcile_stale_summary_before_dispatch", {
|
|
15233
|
+
taskId,
|
|
15234
|
+
sessionId,
|
|
15235
|
+
nodeId,
|
|
15236
|
+
meshId: mesh.id,
|
|
15237
|
+
event: "agent:generating_completed"
|
|
15238
|
+
}, `transcriptAt=${evidence.transcriptMessageAt} < dispatchedAt=${dispatch.dispatchedAt}`);
|
|
15239
|
+
continue;
|
|
15240
|
+
}
|
|
15095
15241
|
const providerSessionId = readNonEmptyString2(payload.providerSessionId);
|
|
15096
15242
|
const coordinatorDaemonId = selfIds.find((id) => !!id);
|
|
15097
15243
|
try {
|
|
@@ -15219,7 +15365,7 @@ function setupMeshReconcileLoop(components) {
|
|
|
15219
15365
|
}
|
|
15220
15366
|
};
|
|
15221
15367
|
}
|
|
15222
|
-
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;
|
|
15368
|
+
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;
|
|
15223
15369
|
var init_mesh_reconcile_loop = __esm({
|
|
15224
15370
|
"src/mesh/mesh-reconcile-loop.ts"() {
|
|
15225
15371
|
"use strict";
|
|
@@ -15241,6 +15387,7 @@ var init_mesh_reconcile_loop = __esm({
|
|
|
15241
15387
|
init_chat_message_normalization();
|
|
15242
15388
|
DEFAULT_RECONCILE_INTERVAL_MS = 4e3;
|
|
15243
15389
|
DEFAULT_AUTO_PRUNE_MIN_AGE_MS = 24 * 60 * 6e4;
|
|
15390
|
+
coordinatorModalParkState = /* @__PURE__ */ new Map();
|
|
15244
15391
|
heldEventLedgerRecorded = /* @__PURE__ */ new Set();
|
|
15245
15392
|
ASSIGNED_STRANDED_DEADLINE_MS = 5 * 6e4;
|
|
15246
15393
|
STRICT_SESSION_MATCH_TTL_MS = 6e4;
|
|
@@ -37810,6 +37957,8 @@ function getMessageTime(message) {
|
|
|
37810
37957
|
}
|
|
37811
37958
|
var COMPLETED_FINALIZATION_RETRY_MS = 1e3;
|
|
37812
37959
|
var COMPLETED_FINALIZATION_MAX_WAIT_MS = 3e4;
|
|
37960
|
+
var NATIVE_HISTORY_MESH_IDLE_SETTLE_MS = 1500;
|
|
37961
|
+
var USER_INPUT_ACK_DEDUP_WINDOW_MS = 6e4;
|
|
37813
37962
|
var TERMINAL_MESH_EVENTS = /* @__PURE__ */ new Set([
|
|
37814
37963
|
"agent:generating_completed",
|
|
37815
37964
|
"agent:stopped",
|
|
@@ -38081,6 +38230,14 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
38081
38230
|
runtimeMessages = [];
|
|
38082
38231
|
lastPersistedHistoryMessages = [];
|
|
38083
38232
|
lastAcknowledgedUserInputAt = 0;
|
|
38233
|
+
// TASKBUBBLE-DUP: per-content last-ack timestamps so the same dispatched
|
|
38234
|
+
// prompt acked twice in quick succession (the worker buffers the first
|
|
38235
|
+
// send during bootstrap/busy, then a redelivery — dispatch-confirm-timeout
|
|
38236
|
+
// requeue or a reconcile re-dispatch — fires a SECOND send_chat before the
|
|
38237
|
+
// outbound queue drains) collapses to ONE user bubble. Keyed on the trimmed
|
|
38238
|
+
// content; an entry older than USER_INPUT_ACK_DEDUP_WINDOW_MS is treated as
|
|
38239
|
+
// a fresh, intentional resend and is NOT suppressed.
|
|
38240
|
+
recentUserInputAcks = /* @__PURE__ */ new Map();
|
|
38084
38241
|
lastNativeSourceCanonicalCheckAt = 0;
|
|
38085
38242
|
lastNativeSourceCanonicalCacheKey = void 0;
|
|
38086
38243
|
cachedSqliteDb = null;
|
|
@@ -38515,6 +38672,15 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
38515
38672
|
const content = typeof input === "string" ? input.trim() : buildCliStructuredInputPrompt(input).trim();
|
|
38516
38673
|
if (!content) return;
|
|
38517
38674
|
const receivedAt = Date.now();
|
|
38675
|
+
const ackContentKey = shortHash(`${this.instanceId}:${content}`, 24);
|
|
38676
|
+
const lastAckAt = this.recentUserInputAcks.get(ackContentKey);
|
|
38677
|
+
if (lastAckAt !== void 0 && receivedAt - lastAckAt <= USER_INPUT_ACK_DEDUP_WINDOW_MS) {
|
|
38678
|
+
this.recentUserInputAcks.set(ackContentKey, receivedAt);
|
|
38679
|
+
this.pruneRecentUserInputAcks(receivedAt);
|
|
38680
|
+
return;
|
|
38681
|
+
}
|
|
38682
|
+
this.recentUserInputAcks.set(ackContentKey, receivedAt);
|
|
38683
|
+
this.pruneRecentUserInputAcks(receivedAt);
|
|
38518
38684
|
this.lastAcknowledgedUserInputAt = receivedAt;
|
|
38519
38685
|
const dedupKey = `user_input_ack:${shortHash(`${this.instanceId}:${content}:${receivedAt}`, 24)}`;
|
|
38520
38686
|
this.appendRuntimeMessage(buildChatMessage({
|
|
@@ -38532,6 +38698,13 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
38532
38698
|
}
|
|
38533
38699
|
}), dedupKey);
|
|
38534
38700
|
}
|
|
38701
|
+
/** Drop user-input ack entries older than the dedup window so the map can't grow unbounded. */
|
|
38702
|
+
pruneRecentUserInputAcks(now) {
|
|
38703
|
+
if (this.recentUserInputAcks.size <= 1) return;
|
|
38704
|
+
for (const [key, at] of this.recentUserInputAcks) {
|
|
38705
|
+
if (now - at > USER_INPUT_ACK_DEDUP_WINDOW_MS) this.recentUserInputAcks.delete(key);
|
|
38706
|
+
}
|
|
38707
|
+
}
|
|
38535
38708
|
dispose() {
|
|
38536
38709
|
this.adapter.shutdown();
|
|
38537
38710
|
this.monitor.reset();
|
|
@@ -39221,8 +39394,9 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
39221
39394
|
previousStatus: this.lastStatus
|
|
39222
39395
|
};
|
|
39223
39396
|
const ownsExternalHistory = !!this.adapter?.chatMessagesOwnedExternally;
|
|
39224
|
-
const
|
|
39225
|
-
|
|
39397
|
+
const meshWorkerSession = this.isMeshWorkerSession();
|
|
39398
|
+
const flushDelay = ownsExternalHistory ? meshWorkerSession ? NATIVE_HISTORY_MESH_IDLE_SETTLE_MS : 0 : 3e3;
|
|
39399
|
+
LOG.debug("CLI", `[${this.type}] set completedDebouncePending duration=${duration}s ownsExternalHistory=${ownsExternalHistory} meshWorker=${meshWorkerSession} flushDelay=${flushDelay}ms generatingStartedAt=${this.generatingStartedAt}`);
|
|
39226
39400
|
this.scheduleCompletedDebounceFlush(flushDelay);
|
|
39227
39401
|
}
|
|
39228
39402
|
} else if (newStatus === "idle" && this.lastStatus === "starting") {
|
|
@@ -42282,11 +42456,11 @@ var cliAgentHandlers = {
|
|
|
42282
42456
|
}
|
|
42283
42457
|
}
|
|
42284
42458
|
}
|
|
42285
|
-
const agentResult = await ctx.deps.cliManager.handleCliCommand("agent_command", args);
|
|
42286
42459
|
const meshCtx = args?.meshContext;
|
|
42287
42460
|
const dispatchNodeId = readStringValue(meshCtx?.nodeId);
|
|
42288
42461
|
const dispatchMeshId = readStringValue(meshCtx?.meshId);
|
|
42289
|
-
|
|
42462
|
+
const isSendChat = args?.action === "send_chat";
|
|
42463
|
+
if (isSendChat && dispatchNodeId && dispatchMeshId) {
|
|
42290
42464
|
try {
|
|
42291
42465
|
const { getMesh: getMesh2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
42292
42466
|
const meshObj = getMesh2(dispatchMeshId) ?? ctx.getCachedInlineMesh(dispatchMeshId);
|
|
@@ -42294,17 +42468,22 @@ var cliAgentHandlers = {
|
|
|
42294
42468
|
const bootstrapStatus = readStringValue(nodeObj?.worktreeBootstrap?.status);
|
|
42295
42469
|
if (bootstrapStatus === "running") {
|
|
42296
42470
|
return {
|
|
42297
|
-
success:
|
|
42298
|
-
|
|
42299
|
-
|
|
42300
|
-
|
|
42301
|
-
|
|
42471
|
+
success: false,
|
|
42472
|
+
recoverable: true,
|
|
42473
|
+
dispatched: false,
|
|
42474
|
+
code: "mesh_node_bootstrap_pending",
|
|
42475
|
+
reason: "bootstrap_still_running",
|
|
42476
|
+
nodeId: dispatchNodeId,
|
|
42477
|
+
meshId: dispatchMeshId,
|
|
42478
|
+
...readStringValue(meshCtx?.taskId) ? { taskId: readStringValue(meshCtx?.taskId) } : {},
|
|
42479
|
+
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.`,
|
|
42480
|
+
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."
|
|
42302
42481
|
};
|
|
42303
42482
|
}
|
|
42304
42483
|
} catch {
|
|
42305
42484
|
}
|
|
42306
42485
|
}
|
|
42307
|
-
return
|
|
42486
|
+
return ctx.deps.cliManager.handleCliCommand("agent_command", args);
|
|
42308
42487
|
},
|
|
42309
42488
|
// ─── Logs ───
|
|
42310
42489
|
list_saved_sessions: async (ctx, args) => {
|
|
@@ -47426,49 +47605,72 @@ var fastForwardHandlers = {
|
|
|
47426
47605
|
};
|
|
47427
47606
|
},
|
|
47428
47607
|
fast_forward_mesh_node: async (ctx, args) => {
|
|
47429
|
-
const
|
|
47430
|
-
const
|
|
47431
|
-
|
|
47432
|
-
|
|
47433
|
-
|
|
47434
|
-
|
|
47435
|
-
|
|
47436
|
-
|
|
47437
|
-
|
|
47438
|
-
|
|
47439
|
-
if (
|
|
47440
|
-
|
|
47608
|
+
const workspaceForError = typeof args?.workspace === "string" ? args.workspace.trim() : "";
|
|
47609
|
+
const meshIdForError = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
47610
|
+
const nodeIdForError = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
|
|
47611
|
+
try {
|
|
47612
|
+
const meshId = meshIdForError;
|
|
47613
|
+
const nodeId = nodeIdForError;
|
|
47614
|
+
let workspace = workspaceForError;
|
|
47615
|
+
let submoduleIgnorePaths = Array.isArray(args?.submoduleIgnorePaths) ? args.submoduleIgnorePaths.filter((value) => typeof value === "string") : void 0;
|
|
47616
|
+
let nodeDaemonId;
|
|
47617
|
+
let allowAutoPublishSubmoduleMainCommits = false;
|
|
47618
|
+
if (meshId && nodeId) {
|
|
47619
|
+
const meshRecord = await ctx.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
|
|
47620
|
+
const mesh = meshRecord?.mesh;
|
|
47621
|
+
const node = mesh?.nodes?.find((n) => meshNodeIdMatches(n, nodeId));
|
|
47622
|
+
if (!workspace) {
|
|
47623
|
+
workspace = typeof node?.workspace === "string" ? node.workspace.trim() : "";
|
|
47624
|
+
}
|
|
47625
|
+
if (!submoduleIgnorePaths && Array.isArray(node?.policy?.submoduleIgnorePaths)) {
|
|
47626
|
+
submoduleIgnorePaths = node.policy.submoduleIgnorePaths.filter((value) => typeof value === "string");
|
|
47627
|
+
}
|
|
47628
|
+
allowAutoPublishSubmoduleMainCommits = mesh?.policy?.allowAutoPublishSubmoduleMainCommits === true;
|
|
47629
|
+
nodeDaemonId = typeof node?.daemonId === "string" ? node.daemonId.trim() : void 0;
|
|
47441
47630
|
}
|
|
47442
|
-
|
|
47443
|
-
|
|
47631
|
+
const selfDaemonId = ctx.deps.statusInstanceId;
|
|
47632
|
+
const isRemote = nodeDaemonId && selfDaemonId && !daemonIdsEquivalent(nodeDaemonId, selfDaemonId);
|
|
47633
|
+
if (isRemote && ctx.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
|
|
47634
|
+
const forwarded = await ctx.deps.dispatchMeshCommand(nodeDaemonId, "fast_forward_mesh_node", {
|
|
47635
|
+
...typeof args === "object" && args !== null ? args : {},
|
|
47636
|
+
workspace,
|
|
47637
|
+
_meshDirectDispatch: true
|
|
47638
|
+
});
|
|
47639
|
+
return forwarded ?? { success: false, error: "no response from remote node" };
|
|
47444
47640
|
}
|
|
47445
|
-
|
|
47446
|
-
|
|
47447
|
-
|
|
47448
|
-
const selfDaemonId = ctx.deps.statusInstanceId;
|
|
47449
|
-
const isRemote = nodeDaemonId && selfDaemonId && !daemonIdsEquivalent(nodeDaemonId, selfDaemonId);
|
|
47450
|
-
if (isRemote && ctx.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
|
|
47451
|
-
const forwarded = await ctx.deps.dispatchMeshCommand(nodeDaemonId, "fast_forward_mesh_node", {
|
|
47452
|
-
...typeof args === "object" && args !== null ? args : {},
|
|
47641
|
+
const result = await fastForwardMeshNode({
|
|
47642
|
+
meshId: meshId || void 0,
|
|
47643
|
+
nodeId: nodeId || void 0,
|
|
47453
47644
|
workspace,
|
|
47454
|
-
|
|
47645
|
+
branch: typeof args?.branch === "string" ? args.branch : void 0,
|
|
47646
|
+
execute: args?.execute === true,
|
|
47647
|
+
dryRun: args?.dryRun === true,
|
|
47648
|
+
updateSubmodules: args?.updateSubmodules === true,
|
|
47649
|
+
submoduleIgnorePaths,
|
|
47650
|
+
mode: args?.mode === "push" ? "push" : "merge",
|
|
47651
|
+
pushSubmodules: args?.pushSubmodules === true,
|
|
47652
|
+
allowAutoPublishSubmoduleMainCommits
|
|
47455
47653
|
});
|
|
47456
|
-
return
|
|
47654
|
+
return result;
|
|
47655
|
+
} catch (e) {
|
|
47656
|
+
const errorMessage = e?.message || String(e);
|
|
47657
|
+
return {
|
|
47658
|
+
success: false,
|
|
47659
|
+
code: "fast_forward_safety_gate_error",
|
|
47660
|
+
...meshIdForError ? { meshId: meshIdForError } : {},
|
|
47661
|
+
...nodeIdForError ? { nodeId: nodeIdForError } : {},
|
|
47662
|
+
workspace: workspaceForError,
|
|
47663
|
+
mode: args?.mode === "push" ? "push" : "merge",
|
|
47664
|
+
allowed: false,
|
|
47665
|
+
willRun: false,
|
|
47666
|
+
executed: false,
|
|
47667
|
+
// Surface the throw as a blocking reason instead of an opaque IPC crash
|
|
47668
|
+
// so the coordinator gets the same structured shape a clean node returns.
|
|
47669
|
+
blockingReasons: ["fast_forward_safety_gate_error"],
|
|
47670
|
+
operationError: errorMessage,
|
|
47671
|
+
error: errorMessage
|
|
47672
|
+
};
|
|
47457
47673
|
}
|
|
47458
|
-
const result = await fastForwardMeshNode({
|
|
47459
|
-
meshId: meshId || void 0,
|
|
47460
|
-
nodeId: nodeId || void 0,
|
|
47461
|
-
workspace,
|
|
47462
|
-
branch: typeof args?.branch === "string" ? args.branch : void 0,
|
|
47463
|
-
execute: args?.execute === true,
|
|
47464
|
-
dryRun: args?.dryRun === true,
|
|
47465
|
-
updateSubmodules: args?.updateSubmodules === true,
|
|
47466
|
-
submoduleIgnorePaths,
|
|
47467
|
-
mode: args?.mode === "push" ? "push" : "merge",
|
|
47468
|
-
pushSubmodules: args?.pushSubmodules === true,
|
|
47469
|
-
allowAutoPublishSubmoduleMainCommits
|
|
47470
|
-
});
|
|
47471
|
-
return result;
|
|
47472
47674
|
},
|
|
47473
47675
|
refine_mesh_node: async (ctx, args) => {
|
|
47474
47676
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
@@ -47625,6 +47827,56 @@ var meshCoordinatorLaunchHandlers = {
|
|
|
47625
47827
|
return "";
|
|
47626
47828
|
}
|
|
47627
47829
|
};
|
|
47830
|
+
const buildRecentActivityBestEffort = async (id) => {
|
|
47831
|
+
try {
|
|
47832
|
+
const { getLedgerSummary: getLedgerSummary2, readLedgerEntries: readLedgerEntries2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
|
|
47833
|
+
const { getMeshQueueStats: getMeshQueueStats2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
|
|
47834
|
+
const summary = getLedgerSummary2(id);
|
|
47835
|
+
const queue = getMeshQueueStats2(id);
|
|
47836
|
+
const failureEntries = readLedgerEntries2(id, { kind: ["task_failed"], tail: 5 });
|
|
47837
|
+
const recentFailures = failureEntries.map((e) => {
|
|
47838
|
+
const p = e.payload || {};
|
|
47839
|
+
const raw = typeof p.taskSummary === "string" ? p.taskSummary : typeof p.message === "string" ? p.message : typeof p.error === "string" ? p.error : "";
|
|
47840
|
+
const summaryText = raw.length > 160 ? `${raw.slice(0, 160)}\u2026` : raw;
|
|
47841
|
+
return {
|
|
47842
|
+
timestamp: e.timestamp,
|
|
47843
|
+
nodeId: e.nodeId,
|
|
47844
|
+
summary: summaryText
|
|
47845
|
+
};
|
|
47846
|
+
});
|
|
47847
|
+
return {
|
|
47848
|
+
recentFailures,
|
|
47849
|
+
recentFailureCount: summary.recentFailures,
|
|
47850
|
+
pendingTasks: queue.pending,
|
|
47851
|
+
assignedTasks: queue.assigned,
|
|
47852
|
+
stalledTasks: summary.taskStalled,
|
|
47853
|
+
lastActivityAt: summary.lastActivityAt
|
|
47854
|
+
};
|
|
47855
|
+
} catch {
|
|
47856
|
+
return void 0;
|
|
47857
|
+
}
|
|
47858
|
+
};
|
|
47859
|
+
const buildOperatingNotesBestEffort = async (id) => {
|
|
47860
|
+
try {
|
|
47861
|
+
const { readLedgerEntries: readLedgerEntries2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
|
|
47862
|
+
const noteEntries = readLedgerEntries2(id, { kind: ["coordinator_operating_note"], tail: 20 });
|
|
47863
|
+
const notes = noteEntries.map((e) => {
|
|
47864
|
+
const p = e.payload || {};
|
|
47865
|
+
const text = typeof p.text === "string" ? p.text.trim() : "";
|
|
47866
|
+
if (!text) return null;
|
|
47867
|
+
const category = p.category === "provider_quirk" || p.category === "pattern_to_avoid" || p.category === "recovery_lesson" ? p.category : void 0;
|
|
47868
|
+
return {
|
|
47869
|
+
text,
|
|
47870
|
+
category,
|
|
47871
|
+
createdAt: typeof p.createdAt === "string" ? p.createdAt : e.timestamp,
|
|
47872
|
+
sourceCoordinator: typeof p.sourceCoordinator === "string" ? p.sourceCoordinator : void 0
|
|
47873
|
+
};
|
|
47874
|
+
}).filter((n) => n !== null);
|
|
47875
|
+
return notes.length ? notes : void 0;
|
|
47876
|
+
} catch {
|
|
47877
|
+
return void 0;
|
|
47878
|
+
}
|
|
47879
|
+
};
|
|
47628
47880
|
let mesh;
|
|
47629
47881
|
if (args?.inlineMesh && typeof args.inlineMesh === "object") {
|
|
47630
47882
|
mesh = args.inlineMesh;
|
|
@@ -47715,7 +47967,7 @@ var meshCoordinatorLaunchHandlers = {
|
|
|
47715
47967
|
if (coordinatorSetup.kind === "cli_command") {
|
|
47716
47968
|
let cliCmdSystemPrompt = "";
|
|
47717
47969
|
try {
|
|
47718
|
-
cliCmdSystemPrompt = buildCoordinatorSystemPrompt2({ mesh, coordinatorCliType: cliType, userInstruction: extraSystemPrompt || void 0, missionSection: buildMissionSectionBestEffort(mesh.id) });
|
|
47970
|
+
cliCmdSystemPrompt = buildCoordinatorSystemPrompt2({ mesh, coordinatorCliType: cliType, userInstruction: extraSystemPrompt || void 0, missionSection: buildMissionSectionBestEffort(mesh.id), recentActivity: await buildRecentActivityBestEffort(mesh.id), operatingNotes: await buildOperatingNotesBestEffort(mesh.id) });
|
|
47719
47971
|
} catch (error) {
|
|
47720
47972
|
const message = error?.message || String(error);
|
|
47721
47973
|
LOG.error("MeshCoordinator", `Failed to build coordinator prompt: ${message}`);
|
|
@@ -47892,7 +48144,7 @@ ${ptyResult.output.slice(-2e3)}`);
|
|
|
47892
48144
|
}
|
|
47893
48145
|
let systemPrompt = "";
|
|
47894
48146
|
try {
|
|
47895
|
-
systemPrompt = buildCoordinatorSystemPrompt2({ mesh, coordinatorCliType: cliType, userInstruction: extraSystemPrompt || void 0, missionSection: buildMissionSectionBestEffort(mesh.id) });
|
|
48147
|
+
systemPrompt = buildCoordinatorSystemPrompt2({ mesh, coordinatorCliType: cliType, userInstruction: extraSystemPrompt || void 0, missionSection: buildMissionSectionBestEffort(mesh.id), recentActivity: await buildRecentActivityBestEffort(mesh.id), operatingNotes: await buildOperatingNotesBestEffort(mesh.id) });
|
|
47896
48148
|
} catch (error) {
|
|
47897
48149
|
const message = error?.message || String(error);
|
|
47898
48150
|
LOG.error("MeshCoordinator", `Failed to build coordinator prompt: ${message}`);
|