@adhdev/daemon-standalone 0.9.77-rc.40 → 0.9.77-rc.42
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 +270 -43
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/vendor/mcp-server/index.js +420 -52
- package/vendor/mcp-server/index.js.map +1 -1
package/dist/index.js
CHANGED
|
@@ -22183,13 +22183,25 @@ var require_dist2 = __commonJS({
|
|
|
22183
22183
|
branch
|
|
22184
22184
|
};
|
|
22185
22185
|
}
|
|
22186
|
-
async function removeWorktree(repoRoot, worktreePath) {
|
|
22186
|
+
async function removeWorktree(repoRoot, worktreePath, opts = {}) {
|
|
22187
22187
|
if (!(0, import_node_fs2.existsSync)(worktreePath)) {
|
|
22188
22188
|
await pruneWorktrees(repoRoot);
|
|
22189
22189
|
return { success: true, removedPath: worktreePath };
|
|
22190
22190
|
}
|
|
22191
|
+
if (opts.requireClean) {
|
|
22192
|
+
const { stdout } = await execFileAsync2("git", ["status", "--porcelain"], {
|
|
22193
|
+
cwd: worktreePath,
|
|
22194
|
+
encoding: "utf8",
|
|
22195
|
+
timeout: GIT_TIMEOUT_MS,
|
|
22196
|
+
maxBuffer: GIT_MAX_BUFFER,
|
|
22197
|
+
windowsHide: true
|
|
22198
|
+
});
|
|
22199
|
+
if (stdout.trim()) {
|
|
22200
|
+
throw new Error(`Refusing to remove dirty worktree: ${worktreePath}`);
|
|
22201
|
+
}
|
|
22202
|
+
}
|
|
22191
22203
|
try {
|
|
22192
|
-
await execFileAsync2("git", ["worktree", "remove", worktreePath
|
|
22204
|
+
await execFileAsync2("git", ["worktree", "remove", worktreePath], {
|
|
22193
22205
|
cwd: repoRoot,
|
|
22194
22206
|
encoding: "utf8",
|
|
22195
22207
|
timeout: GIT_TIMEOUT_MS,
|
|
@@ -22783,6 +22795,7 @@ ${rules.join("\n")}`;
|
|
|
22783
22795
|
- **Respect node capabilities.** Don't send build tasks to read-only nodes. Don't push from nodes that aren't allowed to.
|
|
22784
22796
|
- **Never fabricate tool results.** Always call the actual tool; never pretend you did.
|
|
22785
22797
|
- **Clean up worktree nodes.** After a worktree task completes and its changes are merged or checkpointed, call \`mesh_remove_node\` to free resources.
|
|
22798
|
+
- **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, or explicitly report one of \`pushed_feature_branch_needs_merge\`, \`blocked_review\`, \`cleanup_candidate\`, or \`not_mergeable\` with the next action.
|
|
22786
22799
|
- **Name worktree branches meaningfully.** Use descriptive names like \`feat/auth-refactor\` or \`fix/build-123\`.${coordinatorNote}`;
|
|
22787
22800
|
}
|
|
22788
22801
|
var TOOLS_SECTION;
|
|
@@ -22806,6 +22819,7 @@ ${rules.join("\n")}`;
|
|
|
22806
22819
|
| \`mesh_checkpoint\` | Create a git checkpoint on a node |
|
|
22807
22820
|
| \`mesh_approve\` | Approve/reject a pending agent action |
|
|
22808
22821
|
| \`mesh_clone_node\` | Create a worktree node for isolated parallel branch work |
|
|
22822
|
+
| \`mesh_refine_node\` | Validate and merge a completed worktree node back into its base branch |
|
|
22809
22823
|
| \`mesh_remove_node\` | Remove a node (cleans up worktree if applicable) |`;
|
|
22810
22824
|
TOOL_EXPOSURE_PREFLIGHT_SECTION = `## Tool Exposure Preflight
|
|
22811
22825
|
|
|
@@ -22822,8 +22836,9 @@ Before doing any coordinator work, confirm that the actual callable tool list in
|
|
|
22822
22836
|
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\`.
|
|
22823
22837
|
5. **Verify** \u2014 When a task reports completion or git work is visible, call \`mesh_git_status\` to verify changes were made.
|
|
22824
22838
|
6. **Checkpoint** \u2014 Call \`mesh_checkpoint\` to save the work.
|
|
22825
|
-
7. **
|
|
22826
|
-
8. **
|
|
22839
|
+
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 and \`mesh_refine_node\` for clean worktree branches when safe. 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.
|
|
22840
|
+
8. **Clean up** \u2014 Remove worktree nodes via \`mesh_remove_node\` after their work is merged or no longer needed.
|
|
22841
|
+
9. **Report** \u2014 Summarize what was done, what changed, any issues, and the branch convergence state.
|
|
22827
22842
|
|
|
22828
22843
|
## Failure Recovery
|
|
22829
22844
|
|
|
@@ -24891,6 +24906,7 @@ Do NOT retry on this node. Consider reassigning to a different node or asking th
|
|
|
24891
24906
|
this.requirePromptEchoBeforeSubmit = resolvedConfig.requirePromptEchoBeforeSubmit;
|
|
24892
24907
|
this.providerResolutionMeta = resolvedConfig.providerResolutionMeta;
|
|
24893
24908
|
this.cliScripts = provider.scripts || {};
|
|
24909
|
+
this.scriptState = typeof this.cliScripts.createState === "function" ? this.cliScripts.createState() ?? null : null;
|
|
24894
24910
|
const scriptNames = listCliScriptNames(this.cliScripts);
|
|
24895
24911
|
if (scriptNames.length > 0) {
|
|
24896
24912
|
LOG2.info("CLI", `[${this.cliType}] CLI scripts: [${scriptNames.join(", ")}]`);
|
|
@@ -25051,9 +25067,13 @@ ${lastSnapshot}`;
|
|
|
25051
25067
|
this.lastScreenChangeAt = 0;
|
|
25052
25068
|
this.lastScreenSnapshotReadAt = Number.NEGATIVE_INFINITY;
|
|
25053
25069
|
}
|
|
25070
|
+
getAccumulatedRawBufferCacheKey() {
|
|
25071
|
+
return this.accumulatedRawBuffer.replace(/\x1B\][^\x07]*(?:\x07|\x1B\\)/g, "").replace(/\x1B[P^_X][\s\S]*?(?:\x07|\x1B\\)/g, "").replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
|
|
25072
|
+
}
|
|
25054
25073
|
getFreshParsedStatusCache() {
|
|
25055
25074
|
const cached2 = this.parsedStatusCache;
|
|
25056
|
-
|
|
25075
|
+
const accumulatedRawBufferKey = this.getAccumulatedRawBufferCacheKey();
|
|
25076
|
+
if (cached2 && cached2.responseBuffer === this.responseBuffer && cached2.currentTurnScope === this.currentTurnScope && cached2.recentOutputBuffer === this.recentOutputBuffer && cached2.accumulatedBuffer === this.accumulatedBuffer && cached2.accumulatedRawBufferKey === accumulatedRawBufferKey && cached2.screenText === this.lastScreenText && cached2.currentStatus === this.currentStatus && cached2.activeModal === this.activeModal && cached2.cliName === this.cliName) {
|
|
25057
25077
|
return cached2.result;
|
|
25058
25078
|
}
|
|
25059
25079
|
return null;
|
|
@@ -25156,7 +25176,7 @@ ${lastSnapshot}`;
|
|
|
25156
25176
|
this.cliScripts = scripts;
|
|
25157
25177
|
this.parsedStatusCache = null;
|
|
25158
25178
|
this.parseErrorMessage = null;
|
|
25159
|
-
this.scriptState = typeof scripts.createState === "function" ? scripts.createState() : null;
|
|
25179
|
+
this.scriptState = typeof scripts.createState === "function" ? scripts.createState() ?? null : null;
|
|
25160
25180
|
const scriptNames = listCliScriptNames(scripts);
|
|
25161
25181
|
LOG2.info("CLI", `[${this.cliType}] CLI scripts injected: [${scriptNames.join(", ")}]`);
|
|
25162
25182
|
}
|
|
@@ -26145,7 +26165,8 @@ ${lastSnapshot}`;
|
|
|
26145
26165
|
const screenText = this.readTerminalScreenText();
|
|
26146
26166
|
const parseScreenText = this.getParseScreenText(screenText);
|
|
26147
26167
|
const cached2 = this.parsedStatusCache;
|
|
26148
|
-
|
|
26168
|
+
const accumulatedRawBufferKey = this.getAccumulatedRawBufferCacheKey();
|
|
26169
|
+
if (cached2 && cached2.responseBuffer === this.responseBuffer && cached2.currentTurnScope === this.currentTurnScope && cached2.recentOutputBuffer === this.recentOutputBuffer && cached2.accumulatedBuffer === this.accumulatedBuffer && cached2.accumulatedRawBufferKey === accumulatedRawBufferKey && cached2.screenText === parseScreenText && cached2.currentStatus === this.currentStatus && cached2.activeModal === this.activeModal && cached2.cliName === this.cliName) {
|
|
26149
26170
|
return cached2.result;
|
|
26150
26171
|
}
|
|
26151
26172
|
const parsed = this.runParseSession();
|
|
@@ -26173,7 +26194,7 @@ ${lastSnapshot}`;
|
|
|
26173
26194
|
currentTurnScope: this.currentTurnScope,
|
|
26174
26195
|
recentOutputBuffer: this.recentOutputBuffer,
|
|
26175
26196
|
accumulatedBuffer: this.accumulatedBuffer,
|
|
26176
|
-
|
|
26197
|
+
accumulatedRawBufferKey,
|
|
26177
26198
|
screenText: parseScreenText,
|
|
26178
26199
|
currentStatus: this.currentStatus,
|
|
26179
26200
|
activeModal: this.activeModal,
|
|
@@ -26198,7 +26219,7 @@ ${lastSnapshot}`;
|
|
|
26198
26219
|
scope: this.currentTurnScope,
|
|
26199
26220
|
runtimeSettings: this.runtimeSettings
|
|
26200
26221
|
});
|
|
26201
|
-
return await Promise.resolve(
|
|
26222
|
+
return await Promise.resolve(this.invokeCliScript(fn, {
|
|
26202
26223
|
...input,
|
|
26203
26224
|
args: args && typeof args === "object" ? { ...args } : {}
|
|
26204
26225
|
}));
|
|
@@ -38354,6 +38375,8 @@ ${effect.notification.body || ""}`.trim();
|
|
|
38354
38375
|
}
|
|
38355
38376
|
return normalizedId;
|
|
38356
38377
|
}
|
|
38378
|
+
var COMPLETED_FINALIZATION_RETRY_MS = 1e3;
|
|
38379
|
+
var COMPLETED_FINALIZATION_MAX_WAIT_MS = 3e4;
|
|
38357
38380
|
var IMAGE_MIME_EXTENSIONS = {
|
|
38358
38381
|
"image/png": ".png",
|
|
38359
38382
|
"image/jpeg": ".jpg",
|
|
@@ -38417,6 +38440,13 @@ ${effect.notification.body || ""}`.trim();
|
|
|
38417
38440
|
} catch {
|
|
38418
38441
|
}
|
|
38419
38442
|
}
|
|
38443
|
+
function hasNonEmptyCliModalButtons(activeModal) {
|
|
38444
|
+
const buttons = activeModal?.buttons;
|
|
38445
|
+
return Array.isArray(buttons) && buttons.some((button) => String(button || "").trim().length > 0);
|
|
38446
|
+
}
|
|
38447
|
+
function isCliGeneratingLikeStatus(status) {
|
|
38448
|
+
return status === "generating" || status === "streaming" || status === "long_generating" || status === "starting";
|
|
38449
|
+
}
|
|
38420
38450
|
function buildCliStructuredInputPrompt(input, options = {}) {
|
|
38421
38451
|
const promptParts = [];
|
|
38422
38452
|
const imageRefs = [];
|
|
@@ -38701,10 +38731,12 @@ ${effect.notification.body || ""}`.trim();
|
|
|
38701
38731
|
const mergedMessages = this.mergeConversationMessages(parsedMessages);
|
|
38702
38732
|
const canonicalBackedHistory = this.syncCanonicalSavedHistoryIfNeeded();
|
|
38703
38733
|
const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
|
|
38734
|
+
const parsedChatStatus = typeof parsedStatus?.status === "string" && parsedStatus.status.trim() ? parsedStatus.status.trim() : void 0;
|
|
38735
|
+
const suppressStaleParsedBusyStatus = this.shouldSuppressStaleParsedBusyStatus(parsedStatus, adapterStatus);
|
|
38704
38736
|
if (parsedMessages.length > 0) {
|
|
38705
38737
|
const shouldSkipReplayPersist = this.suppressIdleHistoryReplay && adapterStatus.status === "idle" && parsedStatus?.status === "idle";
|
|
38706
38738
|
let messagesToSave = parsedMessages;
|
|
38707
|
-
if (
|
|
38739
|
+
if (!suppressStaleParsedBusyStatus && (parsedChatStatus === "generating" || parsedChatStatus === "long_generating")) {
|
|
38708
38740
|
const lastIdx = messagesToSave.length - 1;
|
|
38709
38741
|
if (lastIdx >= 0 && messagesToSave[lastIdx]?.role === "assistant") {
|
|
38710
38742
|
messagesToSave = messagesToSave.slice(0, lastIdx);
|
|
@@ -38738,6 +38770,7 @@ ${effect.notification.body || ""}`.trim();
|
|
|
38738
38770
|
summaryMetadata: this.summaryMetadata,
|
|
38739
38771
|
controlValues: this.controlValues
|
|
38740
38772
|
});
|
|
38773
|
+
const activeChatStatus = parseErrorMessage ? "error" : autoApproveActive && parsedStatus?.status === "waiting_approval" ? "generating" : adapterStatus.status !== "idle" ? visibleStatus : suppressStaleParsedBusyStatus ? visibleStatus : parsedChatStatus || visibleStatus;
|
|
38741
38774
|
return {
|
|
38742
38775
|
type: this.type,
|
|
38743
38776
|
name: this.provider.name,
|
|
@@ -38747,7 +38780,7 @@ ${effect.notification.body || ""}`.trim();
|
|
|
38747
38780
|
activeChat: {
|
|
38748
38781
|
id: `${this.type}_${this.workingDir}`,
|
|
38749
38782
|
title: parsedStatus?.title || dirName,
|
|
38750
|
-
status:
|
|
38783
|
+
status: activeChatStatus,
|
|
38751
38784
|
messages: mergedMessages,
|
|
38752
38785
|
activeModal: autoApproveActive ? null : parsedStatus?.activeModal ?? adapterStatus.activeModal,
|
|
38753
38786
|
inputContent: ""
|
|
@@ -38877,6 +38910,102 @@ ${effect.notification.body || ""}`.trim();
|
|
|
38877
38910
|
}
|
|
38878
38911
|
this.applyProviderResponse(parsed.payload, { phase: "immediate" });
|
|
38879
38912
|
}
|
|
38913
|
+
completionHasFinalAssistantMessage(messages) {
|
|
38914
|
+
const visibleMessages = (Array.isArray(messages) ? messages : []).filter((message) => isUserFacingChatMessage(message));
|
|
38915
|
+
const lastVisible = visibleMessages[visibleMessages.length - 1];
|
|
38916
|
+
const role = typeof lastVisible?.role === "string" ? lastVisible.role.trim().toLowerCase() : "";
|
|
38917
|
+
const content = lastVisible ? flattenContent(lastVisible.content).trim() : "";
|
|
38918
|
+
return role === "assistant" && !!content;
|
|
38919
|
+
}
|
|
38920
|
+
hasAdapterPendingResponse() {
|
|
38921
|
+
const adapterAny = this.adapter;
|
|
38922
|
+
if (adapterAny?.isWaitingForResponse === true) return true;
|
|
38923
|
+
if (adapterAny?.currentTurnScope) return true;
|
|
38924
|
+
try {
|
|
38925
|
+
if (typeof this.adapter.isProcessing === "function" && this.adapter.isProcessing()) return true;
|
|
38926
|
+
} catch {
|
|
38927
|
+
}
|
|
38928
|
+
try {
|
|
38929
|
+
const partial2 = typeof this.adapter.getPartialResponse === "function" ? this.adapter.getPartialResponse() : "";
|
|
38930
|
+
if (typeof partial2 === "string" && partial2.trim()) return true;
|
|
38931
|
+
} catch {
|
|
38932
|
+
}
|
|
38933
|
+
return false;
|
|
38934
|
+
}
|
|
38935
|
+
shouldSuppressStaleParsedBusyStatus(parsedStatus, adapterStatus) {
|
|
38936
|
+
const parsedRawStatus = typeof parsedStatus?.status === "string" ? parsedStatus.status.trim() : "";
|
|
38937
|
+
const adapterRawStatus = typeof adapterStatus?.status === "string" ? adapterStatus.status.trim() : "";
|
|
38938
|
+
if (!isCliGeneratingLikeStatus(parsedRawStatus)) return false;
|
|
38939
|
+
if (adapterRawStatus !== "idle") return false;
|
|
38940
|
+
if (hasNonEmptyCliModalButtons(parsedStatus?.activeModal ?? parsedStatus?.modal)) return false;
|
|
38941
|
+
return !this.hasAdapterPendingResponse();
|
|
38942
|
+
}
|
|
38943
|
+
getCompletedFinalizationBlockReason(latestVisibleStatus) {
|
|
38944
|
+
if (latestVisibleStatus !== "idle") return `status:${latestVisibleStatus}`;
|
|
38945
|
+
const adapterAny = this.adapter;
|
|
38946
|
+
if (adapterAny?.isWaitingForResponse === true) return "adapter_waiting_for_response";
|
|
38947
|
+
if (adapterAny?.currentTurnScope) return "adapter_turn_scope_active";
|
|
38948
|
+
const partial2 = typeof this.adapter.getPartialResponse === "function" ? this.adapter.getPartialResponse() : "";
|
|
38949
|
+
if (typeof partial2 === "string" && partial2.trim()) return "partial_response_pending";
|
|
38950
|
+
let parsed;
|
|
38951
|
+
try {
|
|
38952
|
+
parsed = this.adapter.getScriptParsedStatus();
|
|
38953
|
+
} catch (error48) {
|
|
38954
|
+
return `parse_error:${error48?.message || String(error48)}`;
|
|
38955
|
+
}
|
|
38956
|
+
const parsedStatus = typeof parsed?.status === "string" ? parsed.status : "unknown";
|
|
38957
|
+
if (parsedStatus !== "idle") return `parsed_status:${parsedStatus}`;
|
|
38958
|
+
if (parsed?.activeModal || parsed?.modal) return "parsed_modal_active";
|
|
38959
|
+
if (!this.completionHasFinalAssistantMessage(parsed?.messages)) return "missing_final_assistant";
|
|
38960
|
+
return null;
|
|
38961
|
+
}
|
|
38962
|
+
scheduleCompletedDebounceFlush(delayMs) {
|
|
38963
|
+
if (this.completedDebounceTimer) clearTimeout(this.completedDebounceTimer);
|
|
38964
|
+
this.completedDebounceTimer = setTimeout(() => this.flushCompletedDebounceIfFinalized(), delayMs);
|
|
38965
|
+
}
|
|
38966
|
+
flushCompletedDebounceIfFinalized() {
|
|
38967
|
+
const pending = this.completedDebouncePending;
|
|
38968
|
+
if (!pending) {
|
|
38969
|
+
this.completedDebounceTimer = null;
|
|
38970
|
+
return;
|
|
38971
|
+
}
|
|
38972
|
+
const latestStatus = this.adapter.getStatus({ allowParse: false });
|
|
38973
|
+
const latestAutoApproveActive = latestStatus.status === "waiting_approval" && this.shouldAutoApprove();
|
|
38974
|
+
const latestVisibleStatus = latestAutoApproveActive ? "generating" : latestStatus.status;
|
|
38975
|
+
if (latestVisibleStatus !== "idle") {
|
|
38976
|
+
LOG2.info("CLI", `[${this.type}] cancelled pending completed (resumed ${latestVisibleStatus})`);
|
|
38977
|
+
this.completedDebouncePending = null;
|
|
38978
|
+
this.completedDebounceTimer = null;
|
|
38979
|
+
return;
|
|
38980
|
+
}
|
|
38981
|
+
const blockReason = this.getCompletedFinalizationBlockReason(latestVisibleStatus);
|
|
38982
|
+
if (blockReason) {
|
|
38983
|
+
const waitedMs = Date.now() - pending.firstObservedAt;
|
|
38984
|
+
if (waitedMs < COMPLETED_FINALIZATION_MAX_WAIT_MS) {
|
|
38985
|
+
if (pending.loggedBlockReason !== blockReason) {
|
|
38986
|
+
LOG2.info("CLI", `[${this.type}] waiting to emit completed until transcript finalizes (${blockReason})`);
|
|
38987
|
+
pending.loggedBlockReason = blockReason;
|
|
38988
|
+
}
|
|
38989
|
+
this.scheduleCompletedDebounceFlush(COMPLETED_FINALIZATION_RETRY_MS);
|
|
38990
|
+
return;
|
|
38991
|
+
}
|
|
38992
|
+
LOG2.warn("CLI", `[${this.type}] suppressed completed event after ${waitedMs}ms without finalized assistant turn (${blockReason})`);
|
|
38993
|
+
this.completedDebouncePending = null;
|
|
38994
|
+
this.completedDebounceTimer = null;
|
|
38995
|
+
this.generatingStartedAt = 0;
|
|
38996
|
+
return;
|
|
38997
|
+
}
|
|
38998
|
+
LOG2.info("CLI", `[${this.type}] completed in ${pending.duration}s`);
|
|
38999
|
+
this.pushEvent({
|
|
39000
|
+
event: "agent:generating_completed",
|
|
39001
|
+
chatTitle: pending.chatTitle,
|
|
39002
|
+
duration: pending.duration,
|
|
39003
|
+
timestamp: pending.timestamp
|
|
39004
|
+
});
|
|
39005
|
+
this.completedDebouncePending = null;
|
|
39006
|
+
this.completedDebounceTimer = null;
|
|
39007
|
+
this.generatingStartedAt = 0;
|
|
39008
|
+
}
|
|
38880
39009
|
maybeAutoApproveStatus(adapterStatus, now = Date.now()) {
|
|
38881
39010
|
const autoApproveActive = adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove();
|
|
38882
39011
|
if (autoApproveActive && !this.autoApproveBusy) {
|
|
@@ -38974,26 +39103,8 @@ ${effect.notification.body || ""}`.trim();
|
|
|
38974
39103
|
this.generatingDebouncePending = null;
|
|
38975
39104
|
this.generatingStartedAt = 0;
|
|
38976
39105
|
} else {
|
|
38977
|
-
|
|
38978
|
-
this.
|
|
38979
|
-
this.completedDebounceTimer = setTimeout(() => {
|
|
38980
|
-
if (this.completedDebouncePending) {
|
|
38981
|
-
const latestStatus = this.adapter.getStatus({ allowParse: false });
|
|
38982
|
-
const latestAutoApproveActive = latestStatus.status === "waiting_approval" && this.shouldAutoApprove();
|
|
38983
|
-
const latestVisibleStatus = latestAutoApproveActive ? "generating" : latestStatus.status;
|
|
38984
|
-
if (latestVisibleStatus !== "idle") {
|
|
38985
|
-
LOG2.info("CLI", `[${this.type}] cancelled pending completed (resumed ${latestVisibleStatus})`);
|
|
38986
|
-
this.completedDebouncePending = null;
|
|
38987
|
-
this.completedDebounceTimer = null;
|
|
38988
|
-
return;
|
|
38989
|
-
}
|
|
38990
|
-
LOG2.info("CLI", `[${this.type}] completed in ${this.completedDebouncePending.duration}s`);
|
|
38991
|
-
this.pushEvent({ event: "agent:generating_completed", ...this.completedDebouncePending });
|
|
38992
|
-
this.completedDebouncePending = null;
|
|
38993
|
-
this.generatingStartedAt = 0;
|
|
38994
|
-
}
|
|
38995
|
-
this.completedDebounceTimer = null;
|
|
38996
|
-
}, 3e3);
|
|
39106
|
+
this.completedDebouncePending = { chatTitle, duration: duration3, timestamp: now, firstObservedAt: now };
|
|
39107
|
+
this.scheduleCompletedDebounceFlush(3e3);
|
|
38997
39108
|
}
|
|
38998
39109
|
} else if (newStatus === "idle" && this.lastStatus === "starting") {
|
|
38999
39110
|
this.pushEvent({ event: "agent:ready", chatTitle, timestamp: now });
|
|
@@ -44955,6 +45066,34 @@ ${(0, import_node_path.resolve)(workspace || os17.tmpdir())}`;
|
|
|
44955
45066
|
const { mcp_servers: _mcpServers, ...baseConfig } = parsed;
|
|
44956
45067
|
return { config: baseConfig, sourceHome, sourceConfigPath };
|
|
44957
45068
|
}
|
|
45069
|
+
function stripHermesCoordinatorTempModelProviderOverrides(config2) {
|
|
45070
|
+
const {
|
|
45071
|
+
model: _model,
|
|
45072
|
+
provider: _provider,
|
|
45073
|
+
default_model: _defaultModel,
|
|
45074
|
+
defaultProvider: _defaultProvider,
|
|
45075
|
+
default_provider: _defaultProviderSnake,
|
|
45076
|
+
modelProvider: _modelProvider,
|
|
45077
|
+
model_provider: _modelProviderSnake,
|
|
45078
|
+
...sanitized
|
|
45079
|
+
} = config2;
|
|
45080
|
+
const delegation = sanitized.delegation;
|
|
45081
|
+
if (delegation && typeof delegation === "object" && !Array.isArray(delegation)) {
|
|
45082
|
+
const {
|
|
45083
|
+
model: _delegationModel,
|
|
45084
|
+
provider: _delegationProvider,
|
|
45085
|
+
modelProvider: _delegationModelProvider,
|
|
45086
|
+
model_provider: _delegationModelProviderSnake,
|
|
45087
|
+
...delegationRest
|
|
45088
|
+
} = delegation;
|
|
45089
|
+
if (Object.keys(delegationRest).length > 0) {
|
|
45090
|
+
sanitized.delegation = delegationRest;
|
|
45091
|
+
} else {
|
|
45092
|
+
delete sanitized.delegation;
|
|
45093
|
+
}
|
|
45094
|
+
}
|
|
45095
|
+
return sanitized;
|
|
45096
|
+
}
|
|
44958
45097
|
function copyHermesCoordinatorCredentialFiles(sourceHome, targetHome) {
|
|
44959
45098
|
if ((0, import_path6.resolve)(sourceHome) === (0, import_path6.resolve)(targetHome)) return;
|
|
44960
45099
|
for (const fileName of [".env", "auth.json"]) {
|
|
@@ -45112,6 +45251,89 @@ ${(0, import_node_path.resolve)(workspace || os17.tmpdir())}`;
|
|
|
45112
45251
|
if (record2?.meta?.meshNodeId === nodeId) return true;
|
|
45113
45252
|
return false;
|
|
45114
45253
|
}
|
|
45254
|
+
async cleanupLocalWorktreeNode(args) {
|
|
45255
|
+
const workspace = typeof args.node?.workspace === "string" ? args.node.workspace.trim() : "";
|
|
45256
|
+
if (!workspace) {
|
|
45257
|
+
return {
|
|
45258
|
+
success: false,
|
|
45259
|
+
code: "mesh_worktree_cleanup_missing_workspace",
|
|
45260
|
+
error: `Worktree node '${args.nodeId}' is missing workspace metadata`,
|
|
45261
|
+
recoveryHint: "Inspect the mesh node record before removing it, or remove stale metadata manually only after confirming no managed worktree remains."
|
|
45262
|
+
};
|
|
45263
|
+
}
|
|
45264
|
+
const worktreeExists = fs10.existsSync(workspace);
|
|
45265
|
+
const sourceNode = args.node?.clonedFromNodeId ? args.mesh?.nodes?.find((n) => n.id === args.node.clonedFromNodeId || n.nodeId === args.node.clonedFromNodeId) : args.mesh?.nodes?.find((n) => !n.isLocalWorktree);
|
|
45266
|
+
const repoRoot = typeof sourceNode?.repoRoot === "string" && sourceNode.repoRoot.trim() ? sourceNode.repoRoot.trim() : typeof sourceNode?.workspace === "string" && sourceNode.workspace.trim() ? sourceNode.workspace.trim() : "";
|
|
45267
|
+
if (!worktreeExists) {
|
|
45268
|
+
return { success: true, skipped: true, removedPath: workspace, repoRoot: repoRoot || void 0, reason: "worktree_path_missing" };
|
|
45269
|
+
}
|
|
45270
|
+
if (!repoRoot || !fs10.existsSync(repoRoot)) {
|
|
45271
|
+
return {
|
|
45272
|
+
success: false,
|
|
45273
|
+
code: "mesh_worktree_cleanup_missing_source_repo",
|
|
45274
|
+
error: `Refusing to remove worktree '${workspace}' because the source repo root is unavailable`,
|
|
45275
|
+
recoveryHint: "Run mesh_remove_node from the machine that owns the source repo, or verify the source node metadata before retrying."
|
|
45276
|
+
};
|
|
45277
|
+
}
|
|
45278
|
+
if (typeof args.node?.worktreeBranch !== "string" || !args.node.worktreeBranch.trim()) {
|
|
45279
|
+
return {
|
|
45280
|
+
success: false,
|
|
45281
|
+
code: "mesh_worktree_cleanup_missing_branch",
|
|
45282
|
+
error: `Refusing to remove worktree '${workspace}' because worktreeBranch metadata is missing`,
|
|
45283
|
+
recoveryHint: "Confirm this is an ADHDev-managed worktree before removing it manually; managed worktree nodes include worktreeBranch metadata."
|
|
45284
|
+
};
|
|
45285
|
+
}
|
|
45286
|
+
const { resolveWorktreePath: resolveWorktreePath2, listWorktrees: listWorktrees2, removeWorktree: removeWorktree2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
|
|
45287
|
+
const normalizePath = (value) => {
|
|
45288
|
+
const resolved = (0, import_path6.resolve)(value);
|
|
45289
|
+
try {
|
|
45290
|
+
return fs10.realpathSync(resolved);
|
|
45291
|
+
} catch {
|
|
45292
|
+
return resolved;
|
|
45293
|
+
}
|
|
45294
|
+
};
|
|
45295
|
+
const expectedPath = normalizePath(resolveWorktreePath2(repoRoot, String(args.mesh?.name || args.mesh?.id || "mesh"), args.node.worktreeBranch));
|
|
45296
|
+
const actualPath = normalizePath(workspace);
|
|
45297
|
+
if (actualPath !== expectedPath) {
|
|
45298
|
+
return {
|
|
45299
|
+
success: false,
|
|
45300
|
+
code: "mesh_worktree_cleanup_unexpected_path",
|
|
45301
|
+
error: `Refusing to remove worktree '${workspace}' because it is not at the expected managed path '${expectedPath}'`,
|
|
45302
|
+
recoveryHint: "Use git worktree list/status to inspect the path. Retry only after confirming the mesh node metadata points to an ADHDev-managed worktree."
|
|
45303
|
+
};
|
|
45304
|
+
}
|
|
45305
|
+
const entries = await listWorktrees2(repoRoot);
|
|
45306
|
+
const managedEntry = entries.find((entry) => normalizePath(entry.path) === actualPath);
|
|
45307
|
+
if (!managedEntry) {
|
|
45308
|
+
return {
|
|
45309
|
+
success: false,
|
|
45310
|
+
code: "mesh_worktree_cleanup_not_registered",
|
|
45311
|
+
error: `Refusing to remove '${workspace}' because it is not registered in git worktree list for '${repoRoot}'`,
|
|
45312
|
+
recoveryHint: "Inspect git worktree list --porcelain from the source repo. If the path was already removed, prune git worktrees before retrying."
|
|
45313
|
+
};
|
|
45314
|
+
}
|
|
45315
|
+
if (managedEntry.branch && managedEntry.branch !== args.node.worktreeBranch) {
|
|
45316
|
+
return {
|
|
45317
|
+
success: false,
|
|
45318
|
+
code: "mesh_worktree_cleanup_branch_mismatch",
|
|
45319
|
+
error: `Refusing to remove '${workspace}' because git reports branch '${managedEntry.branch}', expected '${args.node.worktreeBranch}'`,
|
|
45320
|
+
recoveryHint: "Inspect the worktree branch and mesh metadata before retrying cleanup."
|
|
45321
|
+
};
|
|
45322
|
+
}
|
|
45323
|
+
try {
|
|
45324
|
+
const result = await removeWorktree2(repoRoot, workspace, { requireClean: true });
|
|
45325
|
+
return { success: true, removedPath: result.removedPath, repoRoot };
|
|
45326
|
+
} catch (e) {
|
|
45327
|
+
const message = String(e?.message || e || "worktree cleanup failed");
|
|
45328
|
+
const dirty = message.includes("dirty worktree") || message.includes("local changes");
|
|
45329
|
+
return {
|
|
45330
|
+
success: false,
|
|
45331
|
+
code: dirty ? "mesh_worktree_cleanup_dirty" : "mesh_worktree_cleanup_failed",
|
|
45332
|
+
error: message,
|
|
45333
|
+
recoveryHint: dirty ? "Commit, stash, or intentionally discard the worktree changes before retrying mesh_remove_node. The mesh registry entry is preserved until cleanup is safe." : "Inspect git worktree status/list from the source repo and retry after resolving the reported cleanup failure."
|
|
45334
|
+
};
|
|
45335
|
+
}
|
|
45336
|
+
}
|
|
45115
45337
|
isCompletedHostedSession(record2) {
|
|
45116
45338
|
return record2?.lifecycle === "stopped" || record2?.lifecycle === "failed" || record2?.lifecycle === "interrupted";
|
|
45117
45339
|
}
|
|
@@ -46112,17 +46334,21 @@ ${(0, import_node_path.resolve)(workspace || os17.tmpdir())}`;
|
|
|
46112
46334
|
sessionCleanup = await this.cleanupMeshSessions({ meshId, nodeId, node, mode: sessionCleanupMode });
|
|
46113
46335
|
if (sessionCleanup.success === false) return { success: false, removed: false, sessionCleanup };
|
|
46114
46336
|
}
|
|
46115
|
-
|
|
46116
|
-
|
|
46117
|
-
|
|
46118
|
-
|
|
46119
|
-
|
|
46120
|
-
|
|
46121
|
-
|
|
46122
|
-
|
|
46123
|
-
|
|
46124
|
-
|
|
46337
|
+
let worktreeCleanup;
|
|
46338
|
+
if (node?.isLocalWorktree) {
|
|
46339
|
+
const cleanupResult = await this.cleanupLocalWorktreeNode({ mesh, node, nodeId });
|
|
46340
|
+
if (cleanupResult.success === false) {
|
|
46341
|
+
return {
|
|
46342
|
+
success: false,
|
|
46343
|
+
removed: false,
|
|
46344
|
+
code: cleanupResult.code,
|
|
46345
|
+
error: cleanupResult.error,
|
|
46346
|
+
recoveryHint: cleanupResult.recoveryHint,
|
|
46347
|
+
...sessionCleanup ? { sessionCleanup } : {},
|
|
46348
|
+
worktreeCleanup: cleanupResult
|
|
46349
|
+
};
|
|
46125
46350
|
}
|
|
46351
|
+
worktreeCleanup = cleanupResult;
|
|
46126
46352
|
}
|
|
46127
46353
|
let removed = false;
|
|
46128
46354
|
if (meshRecord?.inline) {
|
|
@@ -46148,7 +46374,7 @@ ${(0, import_node_path.resolve)(workspace || os17.tmpdir())}`;
|
|
|
46148
46374
|
} catch {
|
|
46149
46375
|
}
|
|
46150
46376
|
}
|
|
46151
|
-
return { success: true, removed, ...sessionCleanup ? { sessionCleanup } : {} };
|
|
46377
|
+
return { success: true, removed, ...sessionCleanup ? { sessionCleanup } : {}, ...worktreeCleanup ? { worktreeCleanup } : {} };
|
|
46152
46378
|
} catch (e) {
|
|
46153
46379
|
return { success: false, error: e.message };
|
|
46154
46380
|
}
|
|
@@ -46478,7 +46704,8 @@ ${block}`);
|
|
|
46478
46704
|
if (hadExistingMcpConfig) {
|
|
46479
46705
|
try {
|
|
46480
46706
|
const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(readFileSync17(mcpConfigPath, "utf-8"), configFormat);
|
|
46481
|
-
|
|
46707
|
+
const existingCoordinatorConfig = hermesManualFallback ? stripHermesCoordinatorTempModelProviderOverrides(parsedExistingMcpConfig) : parsedExistingMcpConfig;
|
|
46708
|
+
existingMcpConfig = { ...existingMcpConfig, ...existingCoordinatorConfig };
|
|
46482
46709
|
copyFileSync4(mcpConfigPath, mcpConfigPath + ".backup");
|
|
46483
46710
|
} catch (error48) {
|
|
46484
46711
|
LOG2.error("MeshCoordinator", `Failed to parse existing MCP config ${mcpConfigPath}: ${error48?.message || error48}`);
|