@adhdev/daemon-standalone 0.9.77-rc.41 → 0.9.77-rc.43

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 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, "--force"], {
22204
+ await execFileAsync2("git", ["worktree", "remove", worktreePath], {
22193
22205
  cwd: repoRoot,
22194
22206
  encoding: "utf8",
22195
22207
  timeout: GIT_TIMEOUT_MS,
@@ -22642,6 +22654,7 @@ var require_dist2 = __commonJS({
22642
22654
  workspace: opts.workspace.trim(),
22643
22655
  repoRoot: opts.repoRoot,
22644
22656
  daemonId: opts.daemonId,
22657
+ machineId: opts.machineId,
22645
22658
  userOverrides: opts.userOverrides || {},
22646
22659
  policy: opts.policy || {},
22647
22660
  isLocalWorktree: opts.isLocalWorktree,
@@ -22783,6 +22796,7 @@ ${rules.join("\n")}`;
22783
22796
  - **Respect node capabilities.** Don't send build tasks to read-only nodes. Don't push from nodes that aren't allowed to.
22784
22797
  - **Never fabricate tool results.** Always call the actual tool; never pretend you did.
22785
22798
  - **Clean up worktree nodes.** After a worktree task completes and its changes are merged or checkpointed, call \`mesh_remove_node\` to free resources.
22799
+ - **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
22800
  - **Name worktree branches meaningfully.** Use descriptive names like \`feat/auth-refactor\` or \`fix/build-123\`.${coordinatorNote}`;
22787
22801
  }
22788
22802
  var TOOLS_SECTION;
@@ -22806,6 +22820,7 @@ ${rules.join("\n")}`;
22806
22820
  | \`mesh_checkpoint\` | Create a git checkpoint on a node |
22807
22821
  | \`mesh_approve\` | Approve/reject a pending agent action |
22808
22822
  | \`mesh_clone_node\` | Create a worktree node for isolated parallel branch work |
22823
+ | \`mesh_refine_node\` | Validate and merge a completed worktree node back into its base branch |
22809
22824
  | \`mesh_remove_node\` | Remove a node (cleans up worktree if applicable) |`;
22810
22825
  TOOL_EXPOSURE_PREFLIGHT_SECTION = `## Tool Exposure Preflight
22811
22826
 
@@ -22822,8 +22837,9 @@ Before doing any coordinator work, confirm that the actual callable tool list in
22822
22837
  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
22838
  5. **Verify** \u2014 When a task reports completion or git work is visible, call \`mesh_git_status\` to verify changes were made.
22824
22839
  6. **Checkpoint** \u2014 Call \`mesh_checkpoint\` to save the work.
22825
- 7. **Clean up** \u2014 Remove worktree nodes via \`mesh_remove_node\` after their work is merged or no longer needed.
22826
- 8. **Report** \u2014 Summarize what was done, what changed, and any issues.
22840
+ 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.
22841
+ 8. **Clean up** \u2014 Remove worktree nodes via \`mesh_remove_node\` after their work is merged or no longer needed.
22842
+ 9. **Report** \u2014 Summarize what was done, what changed, any issues, and the branch convergence state.
22827
22843
 
22828
22844
  ## Failure Recovery
22829
22845
 
@@ -24891,6 +24907,7 @@ Do NOT retry on this node. Consider reassigning to a different node or asking th
24891
24907
  this.requirePromptEchoBeforeSubmit = resolvedConfig.requirePromptEchoBeforeSubmit;
24892
24908
  this.providerResolutionMeta = resolvedConfig.providerResolutionMeta;
24893
24909
  this.cliScripts = provider.scripts || {};
24910
+ this.scriptState = typeof this.cliScripts.createState === "function" ? this.cliScripts.createState() ?? null : null;
24894
24911
  const scriptNames = listCliScriptNames(this.cliScripts);
24895
24912
  if (scriptNames.length > 0) {
24896
24913
  LOG2.info("CLI", `[${this.cliType}] CLI scripts: [${scriptNames.join(", ")}]`);
@@ -25160,7 +25177,7 @@ ${lastSnapshot}`;
25160
25177
  this.cliScripts = scripts;
25161
25178
  this.parsedStatusCache = null;
25162
25179
  this.parseErrorMessage = null;
25163
- this.scriptState = typeof scripts.createState === "function" ? scripts.createState() : null;
25180
+ this.scriptState = typeof scripts.createState === "function" ? scripts.createState() ?? null : null;
25164
25181
  const scriptNames = listCliScriptNames(scripts);
25165
25182
  LOG2.info("CLI", `[${this.cliType}] CLI scripts injected: [${scriptNames.join(", ")}]`);
25166
25183
  }
@@ -26203,7 +26220,7 @@ ${lastSnapshot}`;
26203
26220
  scope: this.currentTurnScope,
26204
26221
  runtimeSettings: this.runtimeSettings
26205
26222
  });
26206
- return await Promise.resolve(fn(this.scriptState, {
26223
+ return await Promise.resolve(this.invokeCliScript(fn, {
26207
26224
  ...input,
26208
26225
  args: args && typeof args === "object" ? { ...args } : {}
26209
26226
  }));
@@ -38359,6 +38376,8 @@ ${effect.notification.body || ""}`.trim();
38359
38376
  }
38360
38377
  return normalizedId;
38361
38378
  }
38379
+ var COMPLETED_FINALIZATION_RETRY_MS = 1e3;
38380
+ var COMPLETED_FINALIZATION_MAX_WAIT_MS = 3e4;
38362
38381
  var IMAGE_MIME_EXTENSIONS = {
38363
38382
  "image/png": ".png",
38364
38383
  "image/jpeg": ".jpg",
@@ -38422,6 +38441,13 @@ ${effect.notification.body || ""}`.trim();
38422
38441
  } catch {
38423
38442
  }
38424
38443
  }
38444
+ function hasNonEmptyCliModalButtons(activeModal) {
38445
+ const buttons = activeModal?.buttons;
38446
+ return Array.isArray(buttons) && buttons.some((button) => String(button || "").trim().length > 0);
38447
+ }
38448
+ function isCliGeneratingLikeStatus(status) {
38449
+ return status === "generating" || status === "streaming" || status === "long_generating" || status === "starting";
38450
+ }
38425
38451
  function buildCliStructuredInputPrompt(input, options = {}) {
38426
38452
  const promptParts = [];
38427
38453
  const imageRefs = [];
@@ -38706,10 +38732,12 @@ ${effect.notification.body || ""}`.trim();
38706
38732
  const mergedMessages = this.mergeConversationMessages(parsedMessages);
38707
38733
  const canonicalBackedHistory = this.syncCanonicalSavedHistoryIfNeeded();
38708
38734
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
38735
+ const parsedChatStatus = typeof parsedStatus?.status === "string" && parsedStatus.status.trim() ? parsedStatus.status.trim() : void 0;
38736
+ const suppressStaleParsedBusyStatus = this.shouldSuppressStaleParsedBusyStatus(parsedStatus, adapterStatus);
38709
38737
  if (parsedMessages.length > 0) {
38710
38738
  const shouldSkipReplayPersist = this.suppressIdleHistoryReplay && adapterStatus.status === "idle" && parsedStatus?.status === "idle";
38711
38739
  let messagesToSave = parsedMessages;
38712
- if (parsedStatus?.status === "generating" || parsedStatus?.status === "long_generating") {
38740
+ if (!suppressStaleParsedBusyStatus && (parsedChatStatus === "generating" || parsedChatStatus === "long_generating")) {
38713
38741
  const lastIdx = messagesToSave.length - 1;
38714
38742
  if (lastIdx >= 0 && messagesToSave[lastIdx]?.role === "assistant") {
38715
38743
  messagesToSave = messagesToSave.slice(0, lastIdx);
@@ -38743,6 +38771,7 @@ ${effect.notification.body || ""}`.trim();
38743
38771
  summaryMetadata: this.summaryMetadata,
38744
38772
  controlValues: this.controlValues
38745
38773
  });
38774
+ const activeChatStatus = parseErrorMessage ? "error" : autoApproveActive && parsedStatus?.status === "waiting_approval" ? "generating" : adapterStatus.status !== "idle" ? visibleStatus : suppressStaleParsedBusyStatus ? visibleStatus : parsedChatStatus || visibleStatus;
38746
38775
  return {
38747
38776
  type: this.type,
38748
38777
  name: this.provider.name,
@@ -38752,7 +38781,7 @@ ${effect.notification.body || ""}`.trim();
38752
38781
  activeChat: {
38753
38782
  id: `${this.type}_${this.workingDir}`,
38754
38783
  title: parsedStatus?.title || dirName,
38755
- status: parseErrorMessage ? "error" : autoApproveActive && parsedStatus?.status === "waiting_approval" ? "generating" : adapterStatus.status !== "idle" ? visibleStatus : parsedStatus?.status || visibleStatus,
38784
+ status: activeChatStatus,
38756
38785
  messages: mergedMessages,
38757
38786
  activeModal: autoApproveActive ? null : parsedStatus?.activeModal ?? adapterStatus.activeModal,
38758
38787
  inputContent: ""
@@ -38882,6 +38911,102 @@ ${effect.notification.body || ""}`.trim();
38882
38911
  }
38883
38912
  this.applyProviderResponse(parsed.payload, { phase: "immediate" });
38884
38913
  }
38914
+ completionHasFinalAssistantMessage(messages) {
38915
+ const visibleMessages = (Array.isArray(messages) ? messages : []).filter((message) => isUserFacingChatMessage(message));
38916
+ const lastVisible = visibleMessages[visibleMessages.length - 1];
38917
+ const role = typeof lastVisible?.role === "string" ? lastVisible.role.trim().toLowerCase() : "";
38918
+ const content = lastVisible ? flattenContent(lastVisible.content).trim() : "";
38919
+ return role === "assistant" && !!content;
38920
+ }
38921
+ hasAdapterPendingResponse() {
38922
+ const adapterAny = this.adapter;
38923
+ if (adapterAny?.isWaitingForResponse === true) return true;
38924
+ if (adapterAny?.currentTurnScope) return true;
38925
+ try {
38926
+ if (typeof this.adapter.isProcessing === "function" && this.adapter.isProcessing()) return true;
38927
+ } catch {
38928
+ }
38929
+ try {
38930
+ const partial2 = typeof this.adapter.getPartialResponse === "function" ? this.adapter.getPartialResponse() : "";
38931
+ if (typeof partial2 === "string" && partial2.trim()) return true;
38932
+ } catch {
38933
+ }
38934
+ return false;
38935
+ }
38936
+ shouldSuppressStaleParsedBusyStatus(parsedStatus, adapterStatus) {
38937
+ const parsedRawStatus = typeof parsedStatus?.status === "string" ? parsedStatus.status.trim() : "";
38938
+ const adapterRawStatus = typeof adapterStatus?.status === "string" ? adapterStatus.status.trim() : "";
38939
+ if (!isCliGeneratingLikeStatus(parsedRawStatus)) return false;
38940
+ if (adapterRawStatus !== "idle") return false;
38941
+ if (hasNonEmptyCliModalButtons(parsedStatus?.activeModal ?? parsedStatus?.modal)) return false;
38942
+ return !this.hasAdapterPendingResponse();
38943
+ }
38944
+ getCompletedFinalizationBlockReason(latestVisibleStatus) {
38945
+ if (latestVisibleStatus !== "idle") return `status:${latestVisibleStatus}`;
38946
+ const adapterAny = this.adapter;
38947
+ if (adapterAny?.isWaitingForResponse === true) return "adapter_waiting_for_response";
38948
+ if (adapterAny?.currentTurnScope) return "adapter_turn_scope_active";
38949
+ const partial2 = typeof this.adapter.getPartialResponse === "function" ? this.adapter.getPartialResponse() : "";
38950
+ if (typeof partial2 === "string" && partial2.trim()) return "partial_response_pending";
38951
+ let parsed;
38952
+ try {
38953
+ parsed = this.adapter.getScriptParsedStatus();
38954
+ } catch (error48) {
38955
+ return `parse_error:${error48?.message || String(error48)}`;
38956
+ }
38957
+ const parsedStatus = typeof parsed?.status === "string" ? parsed.status : "unknown";
38958
+ if (parsedStatus !== "idle") return `parsed_status:${parsedStatus}`;
38959
+ if (parsed?.activeModal || parsed?.modal) return "parsed_modal_active";
38960
+ if (!this.completionHasFinalAssistantMessage(parsed?.messages)) return "missing_final_assistant";
38961
+ return null;
38962
+ }
38963
+ scheduleCompletedDebounceFlush(delayMs) {
38964
+ if (this.completedDebounceTimer) clearTimeout(this.completedDebounceTimer);
38965
+ this.completedDebounceTimer = setTimeout(() => this.flushCompletedDebounceIfFinalized(), delayMs);
38966
+ }
38967
+ flushCompletedDebounceIfFinalized() {
38968
+ const pending = this.completedDebouncePending;
38969
+ if (!pending) {
38970
+ this.completedDebounceTimer = null;
38971
+ return;
38972
+ }
38973
+ const latestStatus = this.adapter.getStatus({ allowParse: false });
38974
+ const latestAutoApproveActive = latestStatus.status === "waiting_approval" && this.shouldAutoApprove();
38975
+ const latestVisibleStatus = latestAutoApproveActive ? "generating" : latestStatus.status;
38976
+ if (latestVisibleStatus !== "idle") {
38977
+ LOG2.info("CLI", `[${this.type}] cancelled pending completed (resumed ${latestVisibleStatus})`);
38978
+ this.completedDebouncePending = null;
38979
+ this.completedDebounceTimer = null;
38980
+ return;
38981
+ }
38982
+ const blockReason = this.getCompletedFinalizationBlockReason(latestVisibleStatus);
38983
+ if (blockReason) {
38984
+ const waitedMs = Date.now() - pending.firstObservedAt;
38985
+ if (waitedMs < COMPLETED_FINALIZATION_MAX_WAIT_MS) {
38986
+ if (pending.loggedBlockReason !== blockReason) {
38987
+ LOG2.info("CLI", `[${this.type}] waiting to emit completed until transcript finalizes (${blockReason})`);
38988
+ pending.loggedBlockReason = blockReason;
38989
+ }
38990
+ this.scheduleCompletedDebounceFlush(COMPLETED_FINALIZATION_RETRY_MS);
38991
+ return;
38992
+ }
38993
+ LOG2.warn("CLI", `[${this.type}] suppressed completed event after ${waitedMs}ms without finalized assistant turn (${blockReason})`);
38994
+ this.completedDebouncePending = null;
38995
+ this.completedDebounceTimer = null;
38996
+ this.generatingStartedAt = 0;
38997
+ return;
38998
+ }
38999
+ LOG2.info("CLI", `[${this.type}] completed in ${pending.duration}s`);
39000
+ this.pushEvent({
39001
+ event: "agent:generating_completed",
39002
+ chatTitle: pending.chatTitle,
39003
+ duration: pending.duration,
39004
+ timestamp: pending.timestamp
39005
+ });
39006
+ this.completedDebouncePending = null;
39007
+ this.completedDebounceTimer = null;
39008
+ this.generatingStartedAt = 0;
39009
+ }
38885
39010
  maybeAutoApproveStatus(adapterStatus, now = Date.now()) {
38886
39011
  const autoApproveActive = adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove();
38887
39012
  if (autoApproveActive && !this.autoApproveBusy) {
@@ -38979,26 +39104,8 @@ ${effect.notification.body || ""}`.trim();
38979
39104
  this.generatingDebouncePending = null;
38980
39105
  this.generatingStartedAt = 0;
38981
39106
  } else {
38982
- if (this.completedDebounceTimer) clearTimeout(this.completedDebounceTimer);
38983
- this.completedDebouncePending = { chatTitle, duration: duration3, timestamp: now };
38984
- this.completedDebounceTimer = setTimeout(() => {
38985
- if (this.completedDebouncePending) {
38986
- const latestStatus = this.adapter.getStatus({ allowParse: false });
38987
- const latestAutoApproveActive = latestStatus.status === "waiting_approval" && this.shouldAutoApprove();
38988
- const latestVisibleStatus = latestAutoApproveActive ? "generating" : latestStatus.status;
38989
- if (latestVisibleStatus !== "idle") {
38990
- LOG2.info("CLI", `[${this.type}] cancelled pending completed (resumed ${latestVisibleStatus})`);
38991
- this.completedDebouncePending = null;
38992
- this.completedDebounceTimer = null;
38993
- return;
38994
- }
38995
- LOG2.info("CLI", `[${this.type}] completed in ${this.completedDebouncePending.duration}s`);
38996
- this.pushEvent({ event: "agent:generating_completed", ...this.completedDebouncePending });
38997
- this.completedDebouncePending = null;
38998
- this.generatingStartedAt = 0;
38999
- }
39000
- this.completedDebounceTimer = null;
39001
- }, 3e3);
39107
+ this.completedDebouncePending = { chatTitle, duration: duration3, timestamp: now, firstObservedAt: now };
39108
+ this.scheduleCompletedDebounceFlush(3e3);
39002
39109
  }
39003
39110
  } else if (newStatus === "idle" && this.lastStatus === "starting") {
39004
39111
  this.pushEvent({ event: "agent:ready", chatTitle, timestamp: now });
@@ -45145,6 +45252,89 @@ ${(0, import_node_path.resolve)(workspace || os17.tmpdir())}`;
45145
45252
  if (record2?.meta?.meshNodeId === nodeId) return true;
45146
45253
  return false;
45147
45254
  }
45255
+ async cleanupLocalWorktreeNode(args) {
45256
+ const workspace = typeof args.node?.workspace === "string" ? args.node.workspace.trim() : "";
45257
+ if (!workspace) {
45258
+ return {
45259
+ success: false,
45260
+ code: "mesh_worktree_cleanup_missing_workspace",
45261
+ error: `Worktree node '${args.nodeId}' is missing workspace metadata`,
45262
+ recoveryHint: "Inspect the mesh node record before removing it, or remove stale metadata manually only after confirming no managed worktree remains."
45263
+ };
45264
+ }
45265
+ const worktreeExists = fs10.existsSync(workspace);
45266
+ 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);
45267
+ const repoRoot = typeof sourceNode?.repoRoot === "string" && sourceNode.repoRoot.trim() ? sourceNode.repoRoot.trim() : typeof sourceNode?.workspace === "string" && sourceNode.workspace.trim() ? sourceNode.workspace.trim() : "";
45268
+ if (!worktreeExists) {
45269
+ return { success: true, skipped: true, removedPath: workspace, repoRoot: repoRoot || void 0, reason: "worktree_path_missing" };
45270
+ }
45271
+ if (!repoRoot || !fs10.existsSync(repoRoot)) {
45272
+ return {
45273
+ success: false,
45274
+ code: "mesh_worktree_cleanup_missing_source_repo",
45275
+ error: `Refusing to remove worktree '${workspace}' because the source repo root is unavailable`,
45276
+ recoveryHint: "Run mesh_remove_node from the machine that owns the source repo, or verify the source node metadata before retrying."
45277
+ };
45278
+ }
45279
+ if (typeof args.node?.worktreeBranch !== "string" || !args.node.worktreeBranch.trim()) {
45280
+ return {
45281
+ success: false,
45282
+ code: "mesh_worktree_cleanup_missing_branch",
45283
+ error: `Refusing to remove worktree '${workspace}' because worktreeBranch metadata is missing`,
45284
+ recoveryHint: "Confirm this is an ADHDev-managed worktree before removing it manually; managed worktree nodes include worktreeBranch metadata."
45285
+ };
45286
+ }
45287
+ const { resolveWorktreePath: resolveWorktreePath2, listWorktrees: listWorktrees2, removeWorktree: removeWorktree2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
45288
+ const normalizePath = (value) => {
45289
+ const resolved = (0, import_path6.resolve)(value);
45290
+ try {
45291
+ return fs10.realpathSync(resolved);
45292
+ } catch {
45293
+ return resolved;
45294
+ }
45295
+ };
45296
+ const expectedPath = normalizePath(resolveWorktreePath2(repoRoot, String(args.mesh?.name || args.mesh?.id || "mesh"), args.node.worktreeBranch));
45297
+ const actualPath = normalizePath(workspace);
45298
+ if (actualPath !== expectedPath) {
45299
+ return {
45300
+ success: false,
45301
+ code: "mesh_worktree_cleanup_unexpected_path",
45302
+ error: `Refusing to remove worktree '${workspace}' because it is not at the expected managed path '${expectedPath}'`,
45303
+ recoveryHint: "Use git worktree list/status to inspect the path. Retry only after confirming the mesh node metadata points to an ADHDev-managed worktree."
45304
+ };
45305
+ }
45306
+ const entries = await listWorktrees2(repoRoot);
45307
+ const managedEntry = entries.find((entry) => normalizePath(entry.path) === actualPath);
45308
+ if (!managedEntry) {
45309
+ return {
45310
+ success: false,
45311
+ code: "mesh_worktree_cleanup_not_registered",
45312
+ error: `Refusing to remove '${workspace}' because it is not registered in git worktree list for '${repoRoot}'`,
45313
+ recoveryHint: "Inspect git worktree list --porcelain from the source repo. If the path was already removed, prune git worktrees before retrying."
45314
+ };
45315
+ }
45316
+ if (managedEntry.branch && managedEntry.branch !== args.node.worktreeBranch) {
45317
+ return {
45318
+ success: false,
45319
+ code: "mesh_worktree_cleanup_branch_mismatch",
45320
+ error: `Refusing to remove '${workspace}' because git reports branch '${managedEntry.branch}', expected '${args.node.worktreeBranch}'`,
45321
+ recoveryHint: "Inspect the worktree branch and mesh metadata before retrying cleanup."
45322
+ };
45323
+ }
45324
+ try {
45325
+ const result = await removeWorktree2(repoRoot, workspace, { requireClean: true });
45326
+ return { success: true, removedPath: result.removedPath, repoRoot };
45327
+ } catch (e) {
45328
+ const message = String(e?.message || e || "worktree cleanup failed");
45329
+ const dirty = message.includes("dirty worktree") || message.includes("local changes");
45330
+ return {
45331
+ success: false,
45332
+ code: dirty ? "mesh_worktree_cleanup_dirty" : "mesh_worktree_cleanup_failed",
45333
+ error: message,
45334
+ 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."
45335
+ };
45336
+ }
45337
+ }
45148
45338
  isCompletedHostedSession(record2) {
45149
45339
  return record2?.lifecycle === "stopped" || record2?.lifecycle === "failed" || record2?.lifecycle === "interrupted";
45150
45340
  }
@@ -46145,17 +46335,21 @@ ${(0, import_node_path.resolve)(workspace || os17.tmpdir())}`;
46145
46335
  sessionCleanup = await this.cleanupMeshSessions({ meshId, nodeId, node, mode: sessionCleanupMode });
46146
46336
  if (sessionCleanup.success === false) return { success: false, removed: false, sessionCleanup };
46147
46337
  }
46148
- if (node?.isLocalWorktree && node.workspace) {
46149
- try {
46150
- const sourceNode = node.clonedFromNodeId ? mesh?.nodes.find((n) => n.id === node.clonedFromNodeId || n.nodeId === node.clonedFromNodeId) : mesh?.nodes.find((n) => !n.isLocalWorktree);
46151
- const repoRoot = sourceNode?.repoRoot || sourceNode?.workspace;
46152
- if (repoRoot) {
46153
- const { removeWorktree: removeWorktree2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
46154
- await removeWorktree2(repoRoot, node.workspace);
46155
- }
46156
- } catch (e) {
46157
- LOG2.warn("MeshNode", `Worktree cleanup failed for ${nodeId}: ${e.message}`);
46338
+ let worktreeCleanup;
46339
+ if (node?.isLocalWorktree) {
46340
+ const cleanupResult = await this.cleanupLocalWorktreeNode({ mesh, node, nodeId });
46341
+ if (cleanupResult.success === false) {
46342
+ return {
46343
+ success: false,
46344
+ removed: false,
46345
+ code: cleanupResult.code,
46346
+ error: cleanupResult.error,
46347
+ recoveryHint: cleanupResult.recoveryHint,
46348
+ ...sessionCleanup ? { sessionCleanup } : {},
46349
+ worktreeCleanup: cleanupResult
46350
+ };
46158
46351
  }
46352
+ worktreeCleanup = cleanupResult;
46159
46353
  }
46160
46354
  let removed = false;
46161
46355
  if (meshRecord?.inline) {
@@ -46181,7 +46375,7 @@ ${(0, import_node_path.resolve)(workspace || os17.tmpdir())}`;
46181
46375
  } catch {
46182
46376
  }
46183
46377
  }
46184
- return { success: true, removed, ...sessionCleanup ? { sessionCleanup } : {} };
46378
+ return { success: true, removed, ...sessionCleanup ? { sessionCleanup } : {}, ...worktreeCleanup ? { worktreeCleanup } : {} };
46185
46379
  } catch (e) {
46186
46380
  return { success: false, error: e.message };
46187
46381
  }
@@ -46216,6 +46410,7 @@ ${(0, import_node_path.resolve)(workspace || os17.tmpdir())}`;
46216
46410
  workspace: result.worktreePath,
46217
46411
  repoRoot: result.worktreePath,
46218
46412
  daemonId: sourceNode.daemonId,
46413
+ machineId: sourceNode.machineId ?? sourceNode.machine_id,
46219
46414
  userOverrides: { ...sourceNode.userOverrides || {} },
46220
46415
  policy: { ...sourceNode.policy || {} },
46221
46416
  isLocalWorktree: true,
@@ -46229,6 +46424,7 @@ ${(0, import_node_path.resolve)(workspace || os17.tmpdir())}`;
46229
46424
  workspace: result.worktreePath,
46230
46425
  repoRoot: result.worktreePath,
46231
46426
  daemonId: sourceNode.daemonId,
46427
+ machineId: sourceNode.machineId ?? sourceNode.machine_id,
46232
46428
  userOverrides: { ...sourceNode.userOverrides || {} },
46233
46429
  isLocalWorktree: true,
46234
46430
  worktreeBranch: result.branch,