@adhdev/daemon-core 0.9.82-rc.77 → 0.9.82-rc.79
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 +231 -83
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +233 -85
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-active-work.d.ts +3 -0
- package/dist/mesh/mesh-events.d.ts +1 -0
- package/dist/providers/cli-provider-instance.d.ts +1 -1
- package/package.json +1 -1
- package/src/commands/cli-manager.ts +45 -1
- package/src/commands/router.ts +75 -34
- package/src/mesh/coordinator-prompt.ts +24 -7
- package/src/mesh/mesh-active-work.ts +32 -15
- package/src/mesh/mesh-events.ts +54 -12
- package/src/providers/cli-provider-instance.ts +23 -12
package/dist/index.js
CHANGED
|
@@ -1133,25 +1133,42 @@ ${userInstruction}`);
|
|
|
1133
1133
|
return sections.join("\n\n");
|
|
1134
1134
|
}
|
|
1135
1135
|
function buildNodeStatusSection(nodes) {
|
|
1136
|
-
const lines = [
|
|
1136
|
+
const lines = [
|
|
1137
|
+
"## Current Node Status",
|
|
1138
|
+
"",
|
|
1139
|
+
"Node labels are display context, not aliases. Use exact `nodeId` values in mesh tool calls; do not invent shorthand names such as M1/M2 unless they are explicitly configured labels.",
|
|
1140
|
+
""
|
|
1141
|
+
];
|
|
1137
1142
|
for (const n of nodes) {
|
|
1138
1143
|
const healthIcon = n.health === "online" ? "\u{1F7E2}" : n.health === "dirty" ? "\u{1F7E1}" : n.health === "offline" ? "\u26AB" : "\u{1F534}";
|
|
1139
1144
|
const sessions = n.activeSessions.length > 0 ? `sessions: ${n.activeSessions.join(", ")}` : "no active sessions";
|
|
1140
1145
|
const branch = n.git?.branch ? `branch: \`${n.git.branch}\`` : "";
|
|
1141
|
-
|
|
1142
|
-
|
|
1146
|
+
const context = [
|
|
1147
|
+
n.daemonId ? `daemon: \`${n.daemonId}\`` : "",
|
|
1148
|
+
n.providers?.length ? `providers: ${n.providers.join(", ")}` : ""
|
|
1149
|
+
].filter(Boolean).join(" | ");
|
|
1150
|
+
lines.push(`- ${healthIcon} **${n.machineLabel}** (nodeId: \`${n.nodeId}\`)`);
|
|
1151
|
+
lines.push(` workspace: \`${n.workspace}\`${context ? ` | ${context}` : ""} | ${branch} | ${sessions}`);
|
|
1143
1152
|
if (n.error) lines.push(` \u26A0\uFE0F ${n.error}`);
|
|
1144
1153
|
}
|
|
1145
1154
|
return lines.join("\n");
|
|
1146
1155
|
}
|
|
1147
1156
|
function buildNodeConfigSection(mesh) {
|
|
1148
|
-
const lines = [
|
|
1157
|
+
const lines = [
|
|
1158
|
+
"## Configured Nodes",
|
|
1159
|
+
"",
|
|
1160
|
+
"Node labels are display context, not aliases. Use exact `nodeId` values in mesh tool calls; do not invent shorthand names such as M1/M2 unless they are explicitly configured labels.",
|
|
1161
|
+
""
|
|
1162
|
+
];
|
|
1149
1163
|
for (const n of mesh.nodes) {
|
|
1150
1164
|
const labels = [];
|
|
1151
1165
|
if (n.isLocalWorktree) labels.push("worktree");
|
|
1152
1166
|
if (n.policy?.readOnly) labels.push("read-only");
|
|
1153
1167
|
const suffix = labels.length ? ` [${labels.join(", ")}]` : "";
|
|
1154
|
-
|
|
1168
|
+
const explicitMachineLabel = typeof n.machineLabel === "string" ? n.machineLabel : "";
|
|
1169
|
+
const explicitLabel = explicitMachineLabel ? ` label: **${explicitMachineLabel}** |` : "";
|
|
1170
|
+
const providerPriority = n.policy?.providerPriority?.length ? ` | providers: ${n.policy.providerPriority.join(", ")}` : "";
|
|
1171
|
+
lines.push(`- ${explicitLabel} nodeId: \`${n.id}\` | workspace: \`${n.workspace}\`${n.daemonId ? ` | daemon: \`${n.daemonId}\`` : ""}${providerPriority}${suffix}`);
|
|
1155
1172
|
}
|
|
1156
1173
|
lines.push("", "_Use `mesh_status` to probe live health before delegating work._");
|
|
1157
1174
|
return lines.join("\n");
|
|
@@ -1192,7 +1209,7 @@ function buildRulesSection(coordinatorCliType) {
|
|
|
1192
1209
|
- **Clean up worktree nodes.** After a worktree task completes and its changes are merged or checkpointed, call \`mesh_remove_node\` to free resources.
|
|
1193
1210
|
- **Do not strand completed branches.** A checkpointed or clean feature/worktree branch is not done by itself. Merge/refine it to the mesh default branch, fast-forward obvious clean behind-only branches with \`mesh_fast_forward_node\`, or explicitly report one of \`pushed_feature_branch_needs_merge\`, \`blocked_review\`, \`cleanup_candidate\`, or \`not_mergeable\` with the next action.
|
|
1194
1211
|
- **Keep Refinery validation project-configurable.** \`mesh_refine_node\` must execute validation from repo mesh/refine config (for example \`.adhdev/refine.{json,yaml,yml}\`, \`.adhdev/repo-mesh-refine.*\`, or \`repo-mesh.refine.*\`). Heuristics are suggestions/scaffolding only, not the execution path.
|
|
1195
|
-
- **Treat submodule reachability as publish-needed.** A \`submodule_reachability_failed\` refine result means the root gitlink points at a submodule commit that is not reachable from the configured submodule remote. Do not retry validation blindly or start code review first. Classify it as \`blocked_review\`, request user approval to push/publish the submodule commit, then rerun \`mesh_refine_node\`.
|
|
1212
|
+
- **Treat submodule main reachability as publish-needed.** A \`submodule_reachability_failed\` refine result means the root gitlink points at a submodule commit that is not reachable from the configured submodule remote main branch. Do not treat feature-branch reachability as complete, retry validation blindly, or start code review first. Classify it as \`blocked_review\`, request user approval to push/publish the submodule commit to submodule main, then rerun \`mesh_refine_node\`.
|
|
1196
1213
|
- **Name worktree branches meaningfully.** Use descriptive names like \`feat/auth-refactor\` or \`fix/build-123\`.${coordinatorNote}`;
|
|
1197
1214
|
}
|
|
1198
1215
|
var TOOLS_SECTION, TOOL_EXPOSURE_PREFLIGHT_SECTION, WORKFLOW_SECTION;
|
|
@@ -1239,7 +1256,7 @@ Before doing any coordinator work, confirm that the actual callable tool list in
|
|
|
1239
1256
|
4. **Monitor** \u2014 Prefer event-driven completion/status notifications. Do **not** poll \`mesh_read_chat\` repeatedly. Use \`mesh_view_queue\` to see the status of all pending, assigned, completed, and failed tasks. Do not call \`mesh_read_chat\` again within a few seconds for the same generating session. Use at most one compact \`mesh_read_chat\` check after a completion/approval signal. Handle approvals via \`mesh_approve\`.
|
|
1240
1257
|
5. **Verify** \u2014 When a task reports completion or git work is visible, call \`mesh_git_status\` to verify changes were made.
|
|
1241
1258
|
6. **Checkpoint** \u2014 Call \`mesh_checkpoint\` to save the work.
|
|
1242
|
-
7. **Converge branches** \u2014 Before marking any task complete, classify every touched node/branch into exactly one final state: \`merged_to_main\`, \`pushed_feature_branch_needs_merge\`, \`blocked_review\`, \`cleanup_candidate\`, or \`not_mergeable\`. Use \`mesh_status\` branchConvergenceSummary. For obvious clean branch catch-up (ahead 0, behind > 0, upstream fresh, no dirty/stash/submodule issues), use \`mesh_fast_forward_node\` dry-run first and execute only when explicitly safe/approved; this avoids consuming an agent session. Use \`mesh_refine_node\` for clean worktree branches when safe. Before/refine merging root commits that contain submodule gitlink changes, require each submodule commit to be reachable from
|
|
1259
|
+
7. **Converge branches** \u2014 Before marking any task complete, classify every touched node/branch into exactly one final state: \`merged_to_main\`, \`pushed_feature_branch_needs_merge\`, \`blocked_review\`, \`cleanup_candidate\`, or \`not_mergeable\`. Use \`mesh_status\` branchConvergenceSummary. For obvious clean branch catch-up (ahead 0, behind > 0, upstream fresh, no dirty/stash/submodule issues), use \`mesh_fast_forward_node\` dry-run first and execute only when explicitly safe/approved; this avoids consuming an agent session. Use \`mesh_refine_node\` for clean worktree branches when safe. Before/refine merging root commits that contain submodule gitlink changes, require each submodule commit to be reachable from the configured submodule remote main branch, not merely present on a feature ref or local checkout. If \`mesh_refine_node\` returns \`submodule_reachability_failed\` or publish-required evidence, keep the public convergence bucket as \`blocked_review\`, ask the user for explicit approval to push/publish the unreachable submodule commit(s) to submodule main, then rerun \`mesh_refine_node\`; do not merge the root branch until the submodule commit(s) are reachable from submodule origin/main. A task that remains on a non-main branch is not fully complete unless the final report names the follow-up state and next step.
|
|
1243
1260
|
8. **Clean up** \u2014 Remove worktree nodes via \`mesh_remove_node\` after their work is merged or no longer needed.
|
|
1244
1261
|
9. **Report** \u2014 Summarize what was done, what changed, any issues, and the branch convergence state.
|
|
1245
1262
|
|
|
@@ -3051,23 +3068,54 @@ Do NOT retry on this node. Consider reassigning to a different node or asking th
|
|
|
3051
3068
|
const jobId = readRefineJobId({ metadataEvent: args.metadataEvent });
|
|
3052
3069
|
const result = readRecord(args.metadataEvent.result);
|
|
3053
3070
|
const validationSummary = readRecord(result?.validationSummary);
|
|
3071
|
+
const patchEquivalence = readRecord(result?.patchEquivalence);
|
|
3072
|
+
const finalConvergence = readRecord(result?.finalBranchConvergenceState);
|
|
3054
3073
|
const validationStatus = readNonEmptyString2(validationSummary?.status);
|
|
3074
|
+
const patchStatus = readNonEmptyString2(patchEquivalence?.status) || (patchEquivalence?.equivalent === true ? "passed" : "");
|
|
3055
3075
|
const into = readNonEmptyString2(result?.into);
|
|
3056
3076
|
const branch = readNonEmptyString2(result?.branch);
|
|
3077
|
+
const mergeStatus = result?.merged === true ? "merged" : readNonEmptyString2(finalConvergence?.status);
|
|
3078
|
+
const convergenceStatus = readNonEmptyString2(finalConvergence?.status);
|
|
3079
|
+
const nextStep = readNonEmptyString2(result?.nextStep) || readNonEmptyString2(finalConvergence?.nextStep) || "Continue from the updated mesh state.";
|
|
3057
3080
|
const details = [
|
|
3058
3081
|
jobId ? `job_id=${jobId}` : "",
|
|
3059
3082
|
branch && into ? `${branch}\u2192${into}` : "",
|
|
3060
|
-
validationStatus ? `validation=${validationStatus}` : ""
|
|
3083
|
+
validationStatus ? `validation=${validationStatus}` : "",
|
|
3084
|
+
patchStatus ? `patch_equivalence=${patchStatus}` : "",
|
|
3085
|
+
mergeStatus ? `merge=${mergeStatus}` : "",
|
|
3086
|
+
convergenceStatus ? `final_convergence=${convergenceStatus}` : ""
|
|
3061
3087
|
].filter(Boolean).join("; ");
|
|
3062
|
-
return `[System] Refinery async job for ${args.nodeLabel} completed successfully${details ? ` (${details})` : ""}.
|
|
3088
|
+
return `[System] Refinery async job for ${args.nodeLabel} completed successfully${details ? ` (${details})` : ""}.
|
|
3089
|
+
Next step: ${nextStep}`;
|
|
3063
3090
|
}
|
|
3064
3091
|
if (args.event === "refine:failed") {
|
|
3065
3092
|
const jobId = readRefineJobId({ metadataEvent: args.metadataEvent });
|
|
3066
3093
|
const result = readRecord(args.metadataEvent.result);
|
|
3094
|
+
const validationSummary = readRecord(result?.validationSummary);
|
|
3095
|
+
const patchEquivalence = readRecord(result?.patchEquivalence);
|
|
3096
|
+
const finalConvergence = readRecord(result?.finalBranchConvergenceState);
|
|
3067
3097
|
const code = readNonEmptyString2(result?.code);
|
|
3068
3098
|
const error = readNonEmptyString2(result?.error);
|
|
3069
|
-
const
|
|
3070
|
-
|
|
3099
|
+
const validationStatus = readNonEmptyString2(validationSummary?.status);
|
|
3100
|
+
const patchStatus = readNonEmptyString2(patchEquivalence?.status) || (patchEquivalence?.equivalent === true ? "passed" : "");
|
|
3101
|
+
const mergeStatus = result?.merged === true ? "merged" : finalConvergence?.merged === false ? "not_merged" : "";
|
|
3102
|
+
const convergenceStatus = readNonEmptyString2(result?.convergenceStatus) || readNonEmptyString2(finalConvergence?.status);
|
|
3103
|
+
const blockedReason = readNonEmptyString2(result?.blockedReason);
|
|
3104
|
+
const nextStep = readNonEmptyString2(result?.nextStep) || readNonEmptyString2(finalConvergence?.nextStep);
|
|
3105
|
+
const details = [
|
|
3106
|
+
jobId ? `job_id=${jobId}` : "",
|
|
3107
|
+
code ? `code=${code}` : "",
|
|
3108
|
+
validationStatus ? `validation=${validationStatus}` : "",
|
|
3109
|
+
patchStatus ? `patch_equivalence=${patchStatus}` : "",
|
|
3110
|
+
mergeStatus ? `merge=${mergeStatus}` : "",
|
|
3111
|
+
convergenceStatus ? `convergence=${convergenceStatus}` : "",
|
|
3112
|
+
blockedReason ? `reason=${blockedReason}` : ""
|
|
3113
|
+
].filter(Boolean).join("; ");
|
|
3114
|
+
const parts = [
|
|
3115
|
+
`[System] Refinery async job for ${args.nodeLabel} failed${details ? ` (${details})` : ""}${error ? `: ${error}` : "."}`,
|
|
3116
|
+
nextStep ? `Next step: ${nextStep}` : "Review the terminal refine event/ledger before retrying."
|
|
3117
|
+
];
|
|
3118
|
+
return parts.join("\n");
|
|
3071
3119
|
}
|
|
3072
3120
|
return "";
|
|
3073
3121
|
}
|
|
@@ -3189,7 +3237,8 @@ function injectMeshSystemMessage(components, args) {
|
|
|
3189
3237
|
remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
|
|
3190
3238
|
}
|
|
3191
3239
|
if (sessionId) {
|
|
3192
|
-
updateSessionTaskStatus(args.meshId, sessionId, "failed");
|
|
3240
|
+
const failedTask = updateSessionTaskStatus(args.meshId, sessionId, "failed");
|
|
3241
|
+
completedTaskForLedger = failedTask ? { id: failedTask.id } : null;
|
|
3193
3242
|
}
|
|
3194
3243
|
}
|
|
3195
3244
|
const ledgerKind = EVENT_TO_LEDGER_KIND[args.event];
|
|
@@ -3284,6 +3333,13 @@ function injectMeshSystemMessage(components, args) {
|
|
|
3284
3333
|
LOG.warn("MeshRecovery", `Failed to build recovery context: ${e?.message || e}`);
|
|
3285
3334
|
}
|
|
3286
3335
|
}
|
|
3336
|
+
const messageText = buildMeshSystemMessage({
|
|
3337
|
+
event: args.event,
|
|
3338
|
+
nodeLabel: args.nodeLabel,
|
|
3339
|
+
metadataEvent: args.metadataEvent,
|
|
3340
|
+
recoveryContext
|
|
3341
|
+
});
|
|
3342
|
+
if (!messageText) return { success: false, error: "unsupported mesh event" };
|
|
3287
3343
|
const coordinatorInstances = components.instanceManager.getByCategory("cli").filter((inst) => {
|
|
3288
3344
|
const instState = inst.getState();
|
|
3289
3345
|
if (instState.settings?.meshCoordinatorFor !== args.meshId) return false;
|
|
@@ -3301,19 +3357,13 @@ function injectMeshSystemMessage(components, args) {
|
|
|
3301
3357
|
...args.metadataEvent,
|
|
3302
3358
|
...recoveryContext ? { recoveryContext } : {}
|
|
3303
3359
|
},
|
|
3360
|
+
coordinatorMessage: messageText,
|
|
3304
3361
|
queuedAt: Date.now()
|
|
3305
3362
|
})) {
|
|
3306
3363
|
LOG.info("MeshEvents", `Queued ${args.event} for MCP coordinator (mesh ${args.meshId})`);
|
|
3307
3364
|
}
|
|
3308
3365
|
return { success: true, forwarded: 0 };
|
|
3309
3366
|
}
|
|
3310
|
-
const messageText = buildMeshSystemMessage({
|
|
3311
|
-
event: args.event,
|
|
3312
|
-
nodeLabel: args.nodeLabel,
|
|
3313
|
-
metadataEvent: args.metadataEvent,
|
|
3314
|
-
recoveryContext
|
|
3315
|
-
});
|
|
3316
|
-
if (!messageText) return { success: false, error: "unsupported mesh event" };
|
|
3317
3367
|
for (const coord of coordinatorInstances) {
|
|
3318
3368
|
const coordState = coord.getState();
|
|
3319
3369
|
LOG.info("MeshEvents", `Forwarding mesh event to coordinator ${coordState.instanceId}`);
|
|
@@ -9279,9 +9329,11 @@ function elapsedSince(value, now) {
|
|
|
9279
9329
|
return Number.isFinite(started) ? Math.max(0, now - started) : 0;
|
|
9280
9330
|
}
|
|
9281
9331
|
function sessionStatusFromNodes(nodes, nodeId, sessionId) {
|
|
9282
|
-
if (!
|
|
9332
|
+
if (!Array.isArray(nodes)) return {};
|
|
9333
|
+
if (!nodeId) return { staleReason: "direct task has no node id" };
|
|
9283
9334
|
const node = nodes.find((item) => readString2(item?.id) === nodeId || readString2(item?.nodeId) === nodeId || readString2(item?.node_id) === nodeId);
|
|
9284
|
-
if (!node) return
|
|
9335
|
+
if (!node) return { staleReason: "direct task node is no longer in the live mesh" };
|
|
9336
|
+
if (!sessionId) return {};
|
|
9285
9337
|
const candidates = [];
|
|
9286
9338
|
for (const value of [node.sessions, node.activeSessions, node.active_sessions, node.lastProbe?.sessions, node.last_probe?.sessions, node.lastProbe?.status?.sessions, node.last_probe?.status?.sessions]) {
|
|
9287
9339
|
if (Array.isArray(value)) candidates.push(...value);
|
|
@@ -9290,16 +9342,18 @@ function sessionStatusFromNodes(nodes, nodeId, sessionId) {
|
|
|
9290
9342
|
if (value && typeof value === "object") candidates.push(value);
|
|
9291
9343
|
}
|
|
9292
9344
|
const session = candidates.find((item) => {
|
|
9345
|
+
if (typeof item === "string") return item === sessionId;
|
|
9293
9346
|
const id = readString2(item?.id) || readString2(item?.sessionId) || readString2(item?.session_id) || readString2(item?.runtimeSessionId) || readString2(item?.instanceId);
|
|
9294
9347
|
return id === sessionId;
|
|
9295
9348
|
});
|
|
9296
|
-
if (!session) return
|
|
9349
|
+
if (!session) return { staleReason: "direct task session is not present in live session records" };
|
|
9350
|
+
if (typeof session === "string") return {};
|
|
9297
9351
|
const raw = `${readString2(session.status) || ""} ${readString2(session.lifecycle) || ""} ${readString2(session.state) || ""} ${readString2(session.activeChat?.status) || ""}`.toLowerCase();
|
|
9298
|
-
if (raw.includes("approval")) return "awaiting_approval";
|
|
9299
|
-
if (raw.includes("generating") || raw.includes("running") || raw.includes("busy")) return "generating";
|
|
9300
|
-
if (raw.includes("failed") || raw.includes("stopped") || raw.includes("terminated") || raw.includes("exited")) return "failed";
|
|
9301
|
-
if (raw.includes("idle") || raw.includes("waiting_input") || raw.includes("ready")) return "idle";
|
|
9302
|
-
return
|
|
9352
|
+
if (raw.includes("approval")) return { status: "awaiting_approval" };
|
|
9353
|
+
if (raw.includes("generating") || raw.includes("running") || raw.includes("busy")) return { status: "generating" };
|
|
9354
|
+
if (raw.includes("failed") || raw.includes("stopped") || raw.includes("terminated") || raw.includes("exited")) return { status: "failed" };
|
|
9355
|
+
if (raw.includes("idle") || raw.includes("waiting_input") || raw.includes("ready")) return { status: "idle" };
|
|
9356
|
+
return {};
|
|
9303
9357
|
}
|
|
9304
9358
|
function isDirectDispatch(entry) {
|
|
9305
9359
|
if (entry.kind !== "task_dispatched") return false;
|
|
@@ -9346,15 +9400,17 @@ function buildMeshActiveWorkSummary(activeWork) {
|
|
|
9346
9400
|
failedCount: statusCounts.failed,
|
|
9347
9401
|
idleCount: statusCounts.idle,
|
|
9348
9402
|
sourceCounts,
|
|
9349
|
-
statusCounts
|
|
9403
|
+
statusCounts,
|
|
9404
|
+
staleDirectCount: activeWork.filter((item) => item.source === "direct" && item.staleReason).length
|
|
9350
9405
|
};
|
|
9351
9406
|
}
|
|
9352
9407
|
function buildMeshActiveWork(opts) {
|
|
9353
9408
|
const now = opts.now ?? Date.now();
|
|
9354
9409
|
const records = [];
|
|
9410
|
+
const staleDirectWork = [];
|
|
9355
9411
|
for (const task of opts.queue || []) {
|
|
9356
9412
|
if (task.status !== "pending" && task.status !== "assigned") continue;
|
|
9357
|
-
const { title, summary } = summarizeMessage(task.message || "");
|
|
9413
|
+
const { title, summary: summary2 } = summarizeMessage(task.message || "");
|
|
9358
9414
|
records.push({
|
|
9359
9415
|
taskId: task.id,
|
|
9360
9416
|
source: "queue",
|
|
@@ -9362,7 +9418,7 @@ function buildMeshActiveWork(opts) {
|
|
|
9362
9418
|
nodeId: task.assignedNodeId || task.targetNodeId,
|
|
9363
9419
|
sessionId: task.assignedSessionId || task.targetSessionId,
|
|
9364
9420
|
taskTitle: title,
|
|
9365
|
-
taskSummary:
|
|
9421
|
+
taskSummary: summary2,
|
|
9366
9422
|
message: task.message,
|
|
9367
9423
|
taskMode: task.taskMode,
|
|
9368
9424
|
createdAt: task.createdAt,
|
|
@@ -9377,13 +9433,13 @@ function buildMeshActiveWork(opts) {
|
|
|
9377
9433
|
const taskId = directDispatchTaskId(dispatch);
|
|
9378
9434
|
const terminal = terminals.filter((entry) => new Date(entry.timestamp).getTime() >= new Date(dispatch.timestamp).getTime()).find((entry) => terminalMatchesDispatch(entry, dispatch, taskId));
|
|
9379
9435
|
const terminalStatus = terminal ? statusFromTerminal(terminal) : void 0;
|
|
9380
|
-
const
|
|
9381
|
-
const status = terminalStatus ||
|
|
9436
|
+
const live = sessionStatusFromNodes(opts.nodes, dispatch.nodeId, dispatch.sessionId);
|
|
9437
|
+
const status = terminalStatus || live.status || "assigned";
|
|
9382
9438
|
const terminalRow = Boolean(terminal && terminal.kind !== "task_approval_needed");
|
|
9383
9439
|
if (terminalRow && opts.includeTerminalDirect !== true) continue;
|
|
9384
9440
|
const message = readString2(dispatch.payload?.message) || readString2(dispatch.payload?.summary) || "";
|
|
9385
|
-
const { title, summary } = summarizeMessage(message);
|
|
9386
|
-
|
|
9441
|
+
const { title, summary: summary2 } = summarizeMessage(message);
|
|
9442
|
+
const record = {
|
|
9387
9443
|
taskId,
|
|
9388
9444
|
source: "direct",
|
|
9389
9445
|
status,
|
|
@@ -9391,7 +9447,7 @@ function buildMeshActiveWork(opts) {
|
|
|
9391
9447
|
sessionId: dispatch.sessionId,
|
|
9392
9448
|
providerType: dispatch.providerType || readString2(dispatch.payload?.providerType),
|
|
9393
9449
|
taskTitle: readString2(dispatch.payload?.taskTitle) || title,
|
|
9394
|
-
taskSummary: readString2(dispatch.payload?.taskSummary) ||
|
|
9450
|
+
taskSummary: readString2(dispatch.payload?.taskSummary) || summary2,
|
|
9395
9451
|
message,
|
|
9396
9452
|
taskMode: readString2(dispatch.payload?.taskMode),
|
|
9397
9453
|
createdAt: dispatch.timestamp,
|
|
@@ -9400,11 +9456,20 @@ function buildMeshActiveWork(opts) {
|
|
|
9400
9456
|
elapsedMs: elapsedSince(dispatch.timestamp, now),
|
|
9401
9457
|
terminal: terminalRow,
|
|
9402
9458
|
terminalKind: terminal?.kind,
|
|
9403
|
-
terminalAt: terminal?.timestamp
|
|
9404
|
-
|
|
9459
|
+
terminalAt: terminal?.timestamp,
|
|
9460
|
+
staleReason: live.staleReason
|
|
9461
|
+
};
|
|
9462
|
+
if (live.staleReason && !terminalRow) {
|
|
9463
|
+
staleDirectWork.push(record);
|
|
9464
|
+
continue;
|
|
9465
|
+
}
|
|
9466
|
+
records.push(record);
|
|
9405
9467
|
}
|
|
9406
9468
|
records.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
|
|
9407
|
-
|
|
9469
|
+
staleDirectWork.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
|
|
9470
|
+
const summary = buildMeshActiveWorkSummary(records);
|
|
9471
|
+
summary.staleDirectCount = staleDirectWork.length;
|
|
9472
|
+
return { activeWork: records, staleDirectWork, summary };
|
|
9408
9473
|
}
|
|
9409
9474
|
|
|
9410
9475
|
// src/index.ts
|
|
@@ -19691,23 +19756,28 @@ var CliProviderInstance = class {
|
|
|
19691
19756
|
if (hasNonEmptyCliModalButtons(parsedStatus?.activeModal ?? parsedStatus?.modal)) return false;
|
|
19692
19757
|
return !this.hasAdapterPendingResponse();
|
|
19693
19758
|
}
|
|
19694
|
-
|
|
19695
|
-
if (latestVisibleStatus !== "idle") return `status:${latestVisibleStatus}
|
|
19759
|
+
getCompletedFinalizationBlock(latestVisibleStatus) {
|
|
19760
|
+
if (latestVisibleStatus !== "idle") return { reason: `status:${latestVisibleStatus}`, terminal: true };
|
|
19696
19761
|
const adapterAny = this.adapter;
|
|
19697
|
-
if (adapterAny?.isWaitingForResponse === true) return "adapter_waiting_for_response";
|
|
19698
|
-
if (adapterAny?.currentTurnScope) return "adapter_turn_scope_active";
|
|
19762
|
+
if (adapterAny?.isWaitingForResponse === true) return { reason: "adapter_waiting_for_response", terminal: true };
|
|
19763
|
+
if (adapterAny?.currentTurnScope) return { reason: "adapter_turn_scope_active", terminal: true };
|
|
19764
|
+
if (this.hasAdapterPendingResponse()) return { reason: "adapter_pending_response", terminal: true };
|
|
19699
19765
|
const partial = typeof this.adapter.getPartialResponse === "function" ? this.adapter.getPartialResponse() : "";
|
|
19700
|
-
if (typeof partial === "string" && partial.trim()) return "partial_response_pending";
|
|
19766
|
+
if (typeof partial === "string" && partial.trim()) return { reason: "partial_response_pending", terminal: true };
|
|
19701
19767
|
let parsed;
|
|
19702
19768
|
try {
|
|
19703
19769
|
parsed = this.adapter.getScriptParsedStatus();
|
|
19704
19770
|
} catch (error) {
|
|
19705
|
-
return `parse_error:${error?.message || String(error)}
|
|
19771
|
+
return { reason: `parse_error:${error?.message || String(error)}` };
|
|
19706
19772
|
}
|
|
19707
19773
|
const parsedStatus = typeof parsed?.status === "string" ? parsed.status : "unknown";
|
|
19708
|
-
if (parsedStatus !== "idle")
|
|
19709
|
-
|
|
19710
|
-
|
|
19774
|
+
if (parsedStatus !== "idle") {
|
|
19775
|
+
const adapterStatus = this.adapter.getStatus({ allowParse: false });
|
|
19776
|
+
if (this.shouldSuppressStaleParsedBusyStatus(parsed, adapterStatus)) return null;
|
|
19777
|
+
return { reason: `parsed_status:${parsedStatus}`, terminal: isCliGeneratingLikeStatus(parsedStatus) };
|
|
19778
|
+
}
|
|
19779
|
+
if (parsed?.activeModal || parsed?.modal) return { reason: "parsed_modal_active", terminal: true };
|
|
19780
|
+
if (!this.completionHasFinalAssistantMessage(parsed?.messages)) return { reason: "missing_final_assistant" };
|
|
19711
19781
|
return null;
|
|
19712
19782
|
}
|
|
19713
19783
|
scheduleCompletedDebounceFlush(delayMs) {
|
|
@@ -19729,10 +19799,11 @@ var CliProviderInstance = class {
|
|
|
19729
19799
|
this.completedDebounceTimer = null;
|
|
19730
19800
|
return;
|
|
19731
19801
|
}
|
|
19732
|
-
const
|
|
19733
|
-
if (
|
|
19802
|
+
const block2 = this.getCompletedFinalizationBlock(latestVisibleStatus);
|
|
19803
|
+
if (block2) {
|
|
19804
|
+
const blockReason = block2.reason;
|
|
19734
19805
|
const waitedMs = Date.now() - pending.firstObservedAt;
|
|
19735
|
-
if (waitedMs < COMPLETED_FINALIZATION_MAX_WAIT_MS) {
|
|
19806
|
+
if (block2.terminal || waitedMs < COMPLETED_FINALIZATION_MAX_WAIT_MS) {
|
|
19736
19807
|
if (pending.loggedBlockReason !== blockReason) {
|
|
19737
19808
|
LOG.info("CLI", `[${this.type}] waiting to emit completed until transcript finalizes (${blockReason})`);
|
|
19738
19809
|
pending.loggedBlockReason = blockReason;
|
|
@@ -21596,6 +21667,47 @@ var BUSY_AGENT_STATUSES = /* @__PURE__ */ new Set(["generating", "running", "str
|
|
|
21596
21667
|
function normalizeAgentStatus(value) {
|
|
21597
21668
|
return typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
21598
21669
|
}
|
|
21670
|
+
function hasNonEmptyModalButtons2(activeModal) {
|
|
21671
|
+
const buttons = activeModal?.buttons;
|
|
21672
|
+
return Array.isArray(buttons) && buttons.some((button) => String(button || "").trim().length > 0);
|
|
21673
|
+
}
|
|
21674
|
+
function hasAdapterPendingResponse(adapter) {
|
|
21675
|
+
if (adapter?.isWaitingForResponse === true) return true;
|
|
21676
|
+
if (adapter?.currentTurnScope) return true;
|
|
21677
|
+
try {
|
|
21678
|
+
if (typeof adapter?.isProcessing === "function" && adapter.isProcessing()) return true;
|
|
21679
|
+
} catch {
|
|
21680
|
+
}
|
|
21681
|
+
try {
|
|
21682
|
+
const partial = typeof adapter?.getPartialResponse === "function" ? adapter.getPartialResponse() : "";
|
|
21683
|
+
if (typeof partial === "string" && partial.trim()) return true;
|
|
21684
|
+
} catch {
|
|
21685
|
+
}
|
|
21686
|
+
return false;
|
|
21687
|
+
}
|
|
21688
|
+
function shouldSuppressStaleParsedBusyStatus(adapterStatus, parsedStatus, adapter) {
|
|
21689
|
+
const parsedRawStatus = normalizeAgentStatus(parsedStatus?.status);
|
|
21690
|
+
if (!BUSY_AGENT_STATUSES.has(parsedRawStatus)) return false;
|
|
21691
|
+
if (adapterStatus !== "idle") return false;
|
|
21692
|
+
if (hasNonEmptyModalButtons2(parsedStatus?.activeModal ?? parsedStatus?.modal)) return false;
|
|
21693
|
+
return !hasAdapterPendingResponse(adapter);
|
|
21694
|
+
}
|
|
21695
|
+
function getEffectiveAgentSendStatus(adapter) {
|
|
21696
|
+
const adapterStatus = normalizeAgentStatus(adapter?.getStatus?.({ allowParse: false })?.status ?? adapter?.getStatus?.()?.status);
|
|
21697
|
+
if (adapterStatus && adapterStatus !== "idle") return adapterStatus;
|
|
21698
|
+
if (adapterStatus !== "idle") return adapterStatus;
|
|
21699
|
+
if (typeof adapter?.getScriptParsedStatus !== "function") return adapterStatus;
|
|
21700
|
+
try {
|
|
21701
|
+
const parsedStatus = adapter.getScriptParsedStatus();
|
|
21702
|
+
const parsedRawStatus = normalizeAgentStatus(parsedStatus?.status);
|
|
21703
|
+
if (BUSY_AGENT_STATUSES.has(parsedRawStatus) && !shouldSuppressStaleParsedBusyStatus(adapterStatus, parsedStatus, adapter)) {
|
|
21704
|
+
return parsedRawStatus;
|
|
21705
|
+
}
|
|
21706
|
+
} catch {
|
|
21707
|
+
return adapterStatus;
|
|
21708
|
+
}
|
|
21709
|
+
return adapterStatus;
|
|
21710
|
+
}
|
|
21599
21711
|
var chalkModule = import_chalk.default;
|
|
21600
21712
|
var chalkApi = typeof chalkModule.yellow === "function" ? chalkModule : chalkModule.default || null;
|
|
21601
21713
|
function colorize(color, text) {
|
|
@@ -22370,7 +22482,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
22370
22482
|
if (!found) throw new Error(`CLI agent not running: ${agentType}`);
|
|
22371
22483
|
const { adapter, key } = found;
|
|
22372
22484
|
if (action === "send_chat") {
|
|
22373
|
-
const currentStatus =
|
|
22485
|
+
const currentStatus = getEffectiveAgentSendStatus(adapter);
|
|
22374
22486
|
if (BUSY_AGENT_STATUSES.has(currentStatus)) {
|
|
22375
22487
|
return {
|
|
22376
22488
|
success: false,
|
|
@@ -25859,6 +25971,17 @@ function readGitSubmodules(value, parentRepoRoot) {
|
|
|
25859
25971
|
}).filter((entry) => entry !== null);
|
|
25860
25972
|
return submodules.length > 0 ? submodules : void 0;
|
|
25861
25973
|
}
|
|
25974
|
+
function buildMeshNodeDisplayLabel(node, nodeId, providerPriority) {
|
|
25975
|
+
const explicit = readStringValue(node.machineLabel, node.machine_label, node.machineNickname, node.machine_nickname, node.alias);
|
|
25976
|
+
if (explicit) return explicit;
|
|
25977
|
+
const workspace = readStringValue(node.workspace, node.repoRoot, node.repo_root);
|
|
25978
|
+
const workspaceName = workspace ? (0, import_path8.basename)(workspace) : void 0;
|
|
25979
|
+
const host = readStringValue(node.hostname, node.host, node.daemonId, node.daemon_id, node.machineId, node.machine_id);
|
|
25980
|
+
const provider = providerPriority[0] || (Array.isArray(node.providers) ? readStringValue(...node.providers) : void 0);
|
|
25981
|
+
const parts = [workspaceName, host, provider].filter(Boolean);
|
|
25982
|
+
if (parts.length > 0) return parts.join(" \xB7 ");
|
|
25983
|
+
return nodeId || "unidentified mesh node";
|
|
25984
|
+
}
|
|
25862
25985
|
function normalizeInlineMeshGitStatus(status, node, options) {
|
|
25863
25986
|
const isGitRepo = readBooleanValue(status.isGitRepo);
|
|
25864
25987
|
if (!Object.keys(status).length || isGitRepo === void 0) return void 0;
|
|
@@ -26529,7 +26652,7 @@ function recordMeshRefineStage(stages, stage, status, startedAt, details) {
|
|
|
26529
26652
|
}
|
|
26530
26653
|
function buildSubmodulePublishRequiredNextStep(entries) {
|
|
26531
26654
|
const refs = entries.map((entry) => `${entry.path}@${entry.commit}`).join(", ");
|
|
26532
|
-
return `Ask the user for explicit approval to push/publish the unreachable submodule commit(s) (${refs}) to
|
|
26655
|
+
return `Ask the user for explicit approval to push/publish the unreachable submodule commit(s) (${refs}) to the configured submodule remote main branch, then rerun mesh_refine_node. Do not merge the root branch until every submodule gitlink commit is reachable from submodule origin/main.`;
|
|
26533
26656
|
}
|
|
26534
26657
|
async function computeGitPatchId(cwd, fromRef, toRef) {
|
|
26535
26658
|
const { execFileSync: execFileSync3 } = await import("child_process");
|
|
@@ -26616,15 +26739,9 @@ async function runMeshRefineSubmoduleReachabilityGate(repoRoot, mergedTree) {
|
|
|
26616
26739
|
});
|
|
26617
26740
|
return String(stdout || "");
|
|
26618
26741
|
};
|
|
26619
|
-
const
|
|
26620
|
-
|
|
26621
|
-
|
|
26622
|
-
await runGit2(probeDir, ["init", "-q"]);
|
|
26623
|
-
await runGit2(probeDir, ["-c", "protocol.file.allow=always", "fetch", "--depth=1", remoteUrl, commit]);
|
|
26624
|
-
await runGit2(probeDir, ["cat-file", "-e", `${commit}^{commit}`]);
|
|
26625
|
-
} finally {
|
|
26626
|
-
fs11.rmSync(probeDir, { recursive: true, force: true });
|
|
26627
|
-
}
|
|
26742
|
+
const verifyRemoteMainContainsCommit = async (submodulePath, commit, branch = "main") => {
|
|
26743
|
+
await runGit2(submodulePath, ["-c", "protocol.file.allow=always", "fetch", "origin", `refs/heads/${branch}:refs/remotes/origin/${branch}`]);
|
|
26744
|
+
await runGit2(submodulePath, ["merge-base", "--is-ancestor", commit, `refs/remotes/origin/${branch}`]);
|
|
26628
26745
|
};
|
|
26629
26746
|
const treeOutput = await runGit2(repoRoot, ["ls-tree", "-r", "-z", mergedTree]);
|
|
26630
26747
|
const gitlinks = treeOutput.split("\0").filter(Boolean).map((record) => {
|
|
@@ -26665,15 +26782,18 @@ async function runMeshRefineSubmoduleReachabilityGate(repoRoot, mergedTree) {
|
|
|
26665
26782
|
entries.push(entry);
|
|
26666
26783
|
continue;
|
|
26667
26784
|
}
|
|
26668
|
-
|
|
26785
|
+
entry.remoteMainBranch = "main";
|
|
26786
|
+
await verifyRemoteMainContainsCommit(submodulePath, gitlink.commit, "main");
|
|
26669
26787
|
entry.fetchedFromOrigin = true;
|
|
26670
26788
|
entry.remoteReachable = true;
|
|
26789
|
+
entry.remoteMainReachable = true;
|
|
26671
26790
|
entry.reachable = true;
|
|
26672
26791
|
} catch (e) {
|
|
26673
26792
|
entry.remoteReachable = false;
|
|
26793
|
+
entry.remoteMainReachable = false;
|
|
26674
26794
|
entry.publishRequired = true;
|
|
26675
26795
|
const details = truncateValidationOutput(e?.stderr || e?.message || String(e));
|
|
26676
|
-
entry.error = `Submodule remote reachability check failed for origin: ${details}`;
|
|
26796
|
+
entry.error = `Submodule remote main reachability check failed for origin/main: ${details}`;
|
|
26677
26797
|
}
|
|
26678
26798
|
} catch (e) {
|
|
26679
26799
|
entry.error = truncateValidationOutput(e?.message || String(e));
|
|
@@ -27648,28 +27768,51 @@ var DaemonCommandRouter = class {
|
|
|
27648
27768
|
};
|
|
27649
27769
|
}
|
|
27650
27770
|
queueRefineJobEvent(event, handle, result) {
|
|
27651
|
-
|
|
27771
|
+
const metadataEvent = {
|
|
27772
|
+
source: "refine_mesh_node_async_job",
|
|
27773
|
+
jobId: handle.jobId,
|
|
27774
|
+
interactionId: handle.interactionId,
|
|
27775
|
+
meshId: handle.meshId,
|
|
27776
|
+
nodeId: handle.targetNodeId,
|
|
27777
|
+
targetDaemonId: handle.targetDaemonId,
|
|
27778
|
+
workspace: handle.workspace,
|
|
27779
|
+
status: handle.status,
|
|
27780
|
+
startedAt: handle.startedAt,
|
|
27781
|
+
completedAt: handle.completedAt,
|
|
27782
|
+
retryOfJobId: handle.retryOfJobId,
|
|
27783
|
+
...result ? { result } : {}
|
|
27784
|
+
};
|
|
27785
|
+
const eventPayload = {
|
|
27652
27786
|
event,
|
|
27653
27787
|
meshId: handle.meshId,
|
|
27654
27788
|
nodeLabel: handle.targetNodeId,
|
|
27655
27789
|
nodeId: handle.targetNodeId,
|
|
27656
27790
|
workspace: handle.workspace,
|
|
27657
|
-
metadataEvent
|
|
27658
|
-
source: "refine_mesh_node_async_job",
|
|
27659
|
-
jobId: handle.jobId,
|
|
27660
|
-
interactionId: handle.interactionId,
|
|
27661
|
-
meshId: handle.meshId,
|
|
27662
|
-
nodeId: handle.targetNodeId,
|
|
27663
|
-
targetDaemonId: handle.targetDaemonId,
|
|
27664
|
-
workspace: handle.workspace,
|
|
27665
|
-
status: handle.status,
|
|
27666
|
-
startedAt: handle.startedAt,
|
|
27667
|
-
completedAt: handle.completedAt,
|
|
27668
|
-
retryOfJobId: handle.retryOfJobId,
|
|
27669
|
-
...result ? { result } : {}
|
|
27670
|
-
},
|
|
27791
|
+
metadataEvent,
|
|
27671
27792
|
queuedAt: Date.now()
|
|
27672
|
-
}
|
|
27793
|
+
};
|
|
27794
|
+
if (typeof this.deps.instanceManager?.getByCategory === "function") {
|
|
27795
|
+
const forwarded = handleMeshForwardEvent(
|
|
27796
|
+
{ instanceManager: this.deps.instanceManager },
|
|
27797
|
+
{
|
|
27798
|
+
event,
|
|
27799
|
+
meshId: handle.meshId,
|
|
27800
|
+
nodeId: handle.targetNodeId,
|
|
27801
|
+
workspace: handle.workspace,
|
|
27802
|
+
jobId: handle.jobId,
|
|
27803
|
+
interactionId: handle.interactionId,
|
|
27804
|
+
status: handle.status,
|
|
27805
|
+
targetDaemonId: handle.targetDaemonId,
|
|
27806
|
+
startedAt: handle.startedAt,
|
|
27807
|
+
completedAt: handle.completedAt,
|
|
27808
|
+
retryOfJobId: handle.retryOfJobId,
|
|
27809
|
+
...result ? { result } : {}
|
|
27810
|
+
}
|
|
27811
|
+
);
|
|
27812
|
+
if (forwarded?.success === true) return;
|
|
27813
|
+
LOG.warn("Mesh", `[Refinery] Failed to forward async refine event ${event}: ${forwarded?.error || "unknown error"}`);
|
|
27814
|
+
}
|
|
27815
|
+
queuePendingMeshCoordinatorEvent(eventPayload);
|
|
27673
27816
|
}
|
|
27674
27817
|
async appendRefineJobLedger(kind, handle, result) {
|
|
27675
27818
|
try {
|
|
@@ -27821,6 +27964,8 @@ var DaemonCommandRouter = class {
|
|
|
27821
27964
|
remote: entry.remote,
|
|
27822
27965
|
remoteUrl: entry.remoteUrl,
|
|
27823
27966
|
remoteReachable: entry.remoteReachable,
|
|
27967
|
+
remoteMainBranch: entry.remoteMainBranch,
|
|
27968
|
+
remoteMainReachable: entry.remoteMainReachable,
|
|
27824
27969
|
error: entry.error
|
|
27825
27970
|
})),
|
|
27826
27971
|
error: submoduleReachability.error
|
|
@@ -27833,13 +27978,13 @@ var DaemonCommandRouter = class {
|
|
|
27833
27978
|
convergenceStatus: "blocked_review",
|
|
27834
27979
|
publishRequired: true,
|
|
27835
27980
|
blockedReason: "submodule_publish_required",
|
|
27836
|
-
error: "Refinery submodule reachability preflight failed because one or more submodule gitlink commits are not reachable from their configured remote; merge/refine cleanup was not attempted.",
|
|
27981
|
+
error: "Refinery submodule reachability preflight failed because one or more submodule gitlink commits are not reachable from their configured remote main branch; merge/refine cleanup was not attempted.",
|
|
27837
27982
|
nextStep,
|
|
27838
27983
|
nextSteps: [
|
|
27839
27984
|
"Ask the user for explicit approval before pushing or publishing any submodule commit.",
|
|
27840
|
-
"Push/publish each unreachable submodule commit to the configured submodule remote shown in the evidence.",
|
|
27985
|
+
"Push/publish each unreachable submodule commit to the configured submodule remote main branch shown in the evidence.",
|
|
27841
27986
|
"Rerun mesh_refine_node after remote reachability is confirmed.",
|
|
27842
|
-
"Do not merge the root branch until every submodule gitlink commit is reachable from
|
|
27987
|
+
"Do not merge the root branch until every submodule gitlink commit is reachable from submodule origin/main."
|
|
27843
27988
|
],
|
|
27844
27989
|
unreachableSubmoduleCommits: submoduleReachability.unreachable.map((entry) => ({
|
|
27845
27990
|
path: entry.path,
|
|
@@ -27847,6 +27992,8 @@ var DaemonCommandRouter = class {
|
|
|
27847
27992
|
remote: entry.remote,
|
|
27848
27993
|
remoteUrl: entry.remoteUrl,
|
|
27849
27994
|
remoteReachable: entry.remoteReachable,
|
|
27995
|
+
remoteMainBranch: entry.remoteMainBranch,
|
|
27996
|
+
remoteMainReachable: entry.remoteMainReachable,
|
|
27850
27997
|
error: entry.error
|
|
27851
27998
|
})),
|
|
27852
27999
|
branch,
|
|
@@ -29721,7 +29868,8 @@ ${block2}`);
|
|
|
29721
29868
|
) || Boolean(meshRecord?.inline && nodeIndex === 0);
|
|
29722
29869
|
const status = {
|
|
29723
29870
|
nodeId,
|
|
29724
|
-
machineLabel: node
|
|
29871
|
+
machineLabel: buildMeshNodeDisplayLabel(node, nodeId, providerPriority),
|
|
29872
|
+
labelSource: readStringValue(node.machineLabel, node.machine_label, node.machineNickname, node.machine_nickname, node.alias) ? "explicit_metadata" : "workspace_host_provider_context",
|
|
29725
29873
|
workspace: node.workspace,
|
|
29726
29874
|
repoRoot: node.repoRoot,
|
|
29727
29875
|
isLocalWorktree: node.isLocalWorktree,
|