@adhdev/daemon-standalone 0.9.77-rc.41 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-standalone",
3
- "version": "0.9.77-rc.41",
3
+ "version": "0.9.77-rc.42",
4
4
  "description": "ADHDev standalone daemon — embedded HTTP/WS server for local dashboard",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -25304,13 +25304,25 @@ async function createWorktree(opts) {
25304
25304
  branch
25305
25305
  };
25306
25306
  }
25307
- async function removeWorktree(repoRoot, worktreePath) {
25307
+ async function removeWorktree(repoRoot, worktreePath, opts = {}) {
25308
25308
  if (!(0, import_fs3.existsSync)(worktreePath)) {
25309
25309
  await pruneWorktrees(repoRoot);
25310
25310
  return { success: true, removedPath: worktreePath };
25311
25311
  }
25312
+ if (opts.requireClean) {
25313
+ const { stdout } = await execFileAsync2("git", ["status", "--porcelain"], {
25314
+ cwd: worktreePath,
25315
+ encoding: "utf8",
25316
+ timeout: GIT_TIMEOUT_MS,
25317
+ maxBuffer: GIT_MAX_BUFFER,
25318
+ windowsHide: true
25319
+ });
25320
+ if (stdout.trim()) {
25321
+ throw new Error(`Refusing to remove dirty worktree: ${worktreePath}`);
25322
+ }
25323
+ }
25312
25324
  try {
25313
- await execFileAsync2("git", ["worktree", "remove", worktreePath, "--force"], {
25325
+ await execFileAsync2("git", ["worktree", "remove", worktreePath], {
25314
25326
  cwd: repoRoot,
25315
25327
  encoding: "utf8",
25316
25328
  timeout: GIT_TIMEOUT_MS,
@@ -25794,6 +25806,7 @@ function buildRulesSection(coordinatorCliType) {
25794
25806
  - **Respect node capabilities.** Don't send build tasks to read-only nodes. Don't push from nodes that aren't allowed to.
25795
25807
  - **Never fabricate tool results.** Always call the actual tool; never pretend you did.
25796
25808
  - **Clean up worktree nodes.** After a worktree task completes and its changes are merged or checkpointed, call \`mesh_remove_node\` to free resources.
25809
+ - **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.
25797
25810
  - **Name worktree branches meaningfully.** Use descriptive names like \`feat/auth-refactor\` or \`fix/build-123\`.${coordinatorNote}`;
25798
25811
  }
25799
25812
  function getLedgerDir() {
@@ -34389,6 +34402,13 @@ function cleanupStaleMaterializedImages(dir) {
34389
34402
  } catch {
34390
34403
  }
34391
34404
  }
34405
+ function hasNonEmptyCliModalButtons(activeModal) {
34406
+ const buttons = activeModal?.buttons;
34407
+ return Array.isArray(buttons) && buttons.some((button) => String(button || "").trim().length > 0);
34408
+ }
34409
+ function isCliGeneratingLikeStatus(status) {
34410
+ return status === "generating" || status === "streaming" || status === "long_generating" || status === "starting";
34411
+ }
34392
34412
  function buildCliStructuredInputPrompt(input, options = {}) {
34393
34413
  const promptParts = [];
34394
34414
  const imageRefs = [];
@@ -41114,7 +41134,7 @@ async function shutdownDaemonComponents(components) {
41114
41134
  }
41115
41135
  cdpManagers.clear();
41116
41136
  }
41117
- var path4, import_promises4, import_fs3, import_child_process, import_util3, import_os2, import_path, import_fs4, import_crypto2, import_fs5, import_path2, import_crypto3, import_fs6, import_path3, import_crypto4, import_events2, import_fs7, import_path4, import_crypto5, fs2, path8, os22, os8, os9, path14, import_child_process2, os10, path15, os11, import_child_process3, import_fs8, import_promises5, path, import_util4, import_promises6, path22, path32, fs, os4, path5, import_crypto6, path6, path7, import_fs9, import_path5, import_child_process4, import_fs10, import_os3, path9, import_child_process5, os32, path10, import_fs11, os42, import_child_process6, http, crypto2, fs3, path11, os5, fs4, os6, path12, import_crypto7, fs5, path13, os7, os13, path18, crypto4, import_fs12, import_child_process7, os12, path16, crypto3, fs6, import_module, path17, import_stream2, import_child_process8, import_child_process9, net2, os15, path20, fs7, path19, os14, fs8, path21, os16, import_child_process10, import_crypto8, import_fs13, import_module2, os17, import_path6, os18, import_child_process11, import_child_process12, fs9, os19, path222, import_os4, import_path7, fs10, fs11, path23, os20, import_child_process13, import_os5, http2, fs15, path27, fs12, path24, fs13, path25, fs14, path26, os21, import_child_process14, __defProp2, __getOwnPropDesc2, __getOwnPropNames2, __hasOwnProp2, __require2, __esm2, __export2, __copyProps2, __toCommonJS2, DEFAULT_MESH_POLICY, init_repo_mesh_types, git_worktree_exports, execFileAsync2, WORKTREE_DIR_NAME, GIT_TIMEOUT_MS, GIT_MAX_BUFFER, init_git_worktree, config_exports, DEFAULT_CONFIG, MACHINE_ID_PREFIX, init_config, mesh_config_exports, SESSION_CLEANUP_MODES, SPAWNED_SESSION_VISIBILITY_MODES, init_mesh_config, coordinator_prompt_exports, TOOLS_SECTION, TOOL_EXPOSURE_PREFLIGHT_SECTION, WORKFLOW_SECTION, init_coordinator_prompt, mesh_ledger_exports, LEDGER_DIR_NAME, MAX_FILE_SIZE_BYTES, RECENT_FAILURE_WINDOW_MS, meshLedgerEvents, init_mesh_ledger, mesh_work_queue_exports, init_mesh_work_queue, LEVEL_NUM, LEVEL_LABEL, currentLevel, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH, init_logger, mesh_events_exports, remoteIdleSessions, MAX_PENDING_EVENTS, pendingMeshCoordinatorEvents, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, init_mesh_events, NORMAL_TRACE_BUFFER_SIZE, DEV_TRACE_BUFFER_SIZE, DEFAULT_CONFIG2, currentConfig, init_debug_config, DEFAULT_BINDING_CANDIDATES, cachedBinding, cachedBindingError, GhosttyVtTerminalBackend, init_ghostty_vt_backend, TerminalCtor, XtermTerminalBackend, init_xterm_backend, DEFAULT_SCROLLBACK, loggedTerminalBackends, TerminalScreen, init_terminal_screen, init_spawn_env, cachedPty, NodePtyRuntimeTransport, NodePtyTransportFactory, init_pty_transport, TerminalTranscriptAccumulator, buildCliSpawnEnv, init_provider_cli_shared, init_provider_cli_parse, init_provider_cli_config, init_provider_cli_runtime, provider_cli_adapter_exports, ProviderCliAdapter, init_provider_cli_adapter, execFileAsync, DEFAULT_TIMEOUT_MS, DEFAULT_MAX_BUFFER, GitCommandError, DEFAULT_MAX_FILES, DEFAULT_MAX_BYTES, summarizeGitStatus, InMemoryGitSnapshotStore, DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS, MIN_GIT_WORKSPACE_POLL_INTERVAL_MS, GitWorkspaceMonitor, GIT_COMMAND_NAMES, SNAPSHOT_REASONS, FAILURE_REASONS, defaultSnapshotStore, defaultGitCommandServices, BUSY_STATUSES, TERMINAL_STATUSES, TurnSnapshotTracker, MAX_WORKSPACES, MAX_ACTIVITY, MAX_SAVED_SESSIONS, DEFAULT_STATE, BUILTIN_IDE_DEFINITIONS, registeredIDEs, LIVE_LIFECYCLES, DEFAULT_ACTIVE_CHAT_POLL_STATUSES, DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS, LIVE_RUNTIME_LIFECYCLES, DaemonCdpManager, CdpDomHandlers, DEFAULT_MONITOR_CONFIG, StatusMonitor, BUILTIN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_VISIBILITIES, CHAT_MESSAGE_TRANSCRIPT_VISIBILITIES, CHAT_MESSAGE_AUDIENCES, CHAT_MESSAGE_SOURCES, CHAT_MESSAGE_ACTIVITY_SOURCES, CHAT_MESSAGE_INTERNAL_SOURCES, KNOWN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_KIND_ALIASES, EXPLICIT_HIDDEN_VISIBILITIES, EXPLICIT_VISIBLE_VISIBILITIES, HIDDEN_AUDIENCES, ACTIVITY_SOURCE_SET, INTERNAL_SOURCE_SET, HISTORY_DIR, RETAIN_DAYS, SAVED_HISTORY_INDEX_VERSION, SAVED_HISTORY_INDEX_FILE, SAVED_HISTORY_INDEX_LOCK_SUFFIX, SAVED_HISTORY_INDEX_LOCK_WAIT_MS, SAVED_HISTORY_INDEX_LOCK_STALE_MS, SAVED_HISTORY_INDEX_LOCK_POLL_MS, SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES, savedHistorySessionCache, savedHistoryFileSummaryCache, savedHistoryBackgroundRefresh, savedHistoryRollupInFlight, ChatHistoryWriter, IDE_PROVIDER_SESSION_CAPABILITIES_BASE, EXTENSION_PROVIDER_SESSION_CAPABILITIES_BASE, ExtensionProviderInstance, VALID_STATUSES, VALID_ROLES, VALID_BUBBLE_STATES, VALID_TURN_STATUSES, DEFAULT_APPROVAL_POSITIVE_HINTS, IdeProviderInstance, DEFAULT_CDP_SCAN_INTERVAL_MS, DEFAULT_CDP_DISCOVERY_INTERVAL_MS, DEFAULT_STATUS_INITIAL_REPORT_DELAY_MS, DEFAULT_STATUS_SERVER_REPORT_INTERVAL_MS, DEFAULT_STATUS_P2P_REPORT_INTERVAL_MS, MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS, DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS, MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS, DEFAULT_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS, DEFAULT_SESSION_HOST_READY_TIMEOUT_MS, STANDALONE_CDP_SCAN_INTERVAL_MS, DaemonCdpScanner, DaemonCdpInitializer, WORKING_STATUSES, FULL_STATUS_ACTIVE_CHAT_OPTIONS, LIVE_STATUS_ACTIVE_CHAT_OPTIONS, STATUS_MODAL_MESSAGE_LIMIT, STATUS_MODAL_BUTTON_LIMIT, VALID_INPUT_MEDIA_TYPES, VALID_INPUT_STRATEGIES, TEXT_ONLY_MESSAGE_INPUT_SUPPORT, IDE_SESSION_CAPABILITIES, EXTENSION_SESSION_CAPABILITIES, PTY_SESSION_CAPABILITIES, CLI_CHAT_SESSION_CAPABILITIES, ACP_SESSION_CAPABILITIES, globalStore, RECENT_SEND_WINDOW_MS, READ_CHAT_PROVIDER_EVAL_TIMEOUT_MS, HERMES_CLI_STARTING_SEND_SETTLE_MS, recentSendByTarget, DEFAULT_DEBUG_SANITIZE_OPTIONS, SECRET_KEY_PATTERN, KEY_TO_VK, COMMAND_DEBUG_LEVELS, DaemonCommandHandler, IMAGE_MIME_EXTENSIONS, MATERIALIZED_IMAGE_MAX_AGE_MS, MATERIALIZED_IMAGE_CLEANUP_INTERVAL_MS, lastMaterializedImageCleanupAt, CachedDatabaseSync, CliProviderInstance, AcpProviderInstance, chalkModule, chalkApi, COORDINATOR_DELEGATED_ENV_UNSETS, DaemonCliManager, VALID_CAPABILITY_MEDIA_TYPES, VALID_INPUT_STRATEGIES2, KNOWN_PROVIDER_FIELDS, VALUE_CONTROL_TYPES, ProviderLoader, _providerLoader, LOG_DIR2, MAX_FILE_SIZE, MAX_DAYS, SENSITIVE_KEYS, currentDate2, currentFile, writeCount2, SKIP_COMMANDS, DEFAULT_SERVER_NAME, DEFAULT_ADHDEV_MCP_COMMAND, HERMES_CLI_TYPE, HERMES_MCP_CONFIG_PATH, READ_DEBUG_ENABLED, recentReadDebugSignatureBySession, UPGRADE_HELPER_ENV, CHANNEL_NPM_TAG, CHANNEL_SERVER_URL, CHAT_COMMANDS, READ_DEBUG_ENABLED2, DaemonCommandRouter, DaemonStatusReporter, DEFAULT_DAEMON_PORT, DAEMON_WS_PATH, ProviderStreamAdapter, DaemonAgentStreamManager, AgentStreamPoller, ProviderInstanceManager, ARCHIVE_PATH, MAX_ENTRIES_PER_PROVIDER, VersionArchive, DEV_SERVER_PORT, DevServer, SessionHostRuntimeTransport, SessionHostPtyTransportFactory, DEFAULT_SESSION_HOST_APP_NAME, DEFAULT_STANDALONE_SESSION_HOST_APP_NAME, STARTUP_TIMEOUT_MS, STARTUP_POLL_MS, SessionHostCompatibilityError, EXTENSION_CATALOG, SessionRegistry;
41137
+ var path4, import_promises4, import_fs3, import_child_process, import_util3, import_os2, import_path, import_fs4, import_crypto2, import_fs5, import_path2, import_crypto3, import_fs6, import_path3, import_crypto4, import_events2, import_fs7, import_path4, import_crypto5, fs2, path8, os22, os8, os9, path14, import_child_process2, os10, path15, os11, import_child_process3, import_fs8, import_promises5, path, import_util4, import_promises6, path22, path32, fs, os4, path5, import_crypto6, path6, path7, import_fs9, import_path5, import_child_process4, import_fs10, import_os3, path9, import_child_process5, os32, path10, import_fs11, os42, import_child_process6, http, crypto2, fs3, path11, os5, fs4, os6, path12, import_crypto7, fs5, path13, os7, os13, path18, crypto4, import_fs12, import_child_process7, os12, path16, crypto3, fs6, import_module, path17, import_stream2, import_child_process8, import_child_process9, net2, os15, path20, fs7, path19, os14, fs8, path21, os16, import_child_process10, import_crypto8, import_fs13, import_module2, os17, import_path6, os18, import_child_process11, import_child_process12, fs9, os19, path222, import_os4, import_path7, fs10, fs11, path23, os20, import_child_process13, import_os5, http2, fs15, path27, fs12, path24, fs13, path25, fs14, path26, os21, import_child_process14, __defProp2, __getOwnPropDesc2, __getOwnPropNames2, __hasOwnProp2, __require2, __esm2, __export2, __copyProps2, __toCommonJS2, DEFAULT_MESH_POLICY, init_repo_mesh_types, git_worktree_exports, execFileAsync2, WORKTREE_DIR_NAME, GIT_TIMEOUT_MS, GIT_MAX_BUFFER, init_git_worktree, config_exports, DEFAULT_CONFIG, MACHINE_ID_PREFIX, init_config, mesh_config_exports, SESSION_CLEANUP_MODES, SPAWNED_SESSION_VISIBILITY_MODES, init_mesh_config, coordinator_prompt_exports, TOOLS_SECTION, TOOL_EXPOSURE_PREFLIGHT_SECTION, WORKFLOW_SECTION, init_coordinator_prompt, mesh_ledger_exports, LEDGER_DIR_NAME, MAX_FILE_SIZE_BYTES, RECENT_FAILURE_WINDOW_MS, meshLedgerEvents, init_mesh_ledger, mesh_work_queue_exports, init_mesh_work_queue, LEVEL_NUM, LEVEL_LABEL, currentLevel, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH, init_logger, mesh_events_exports, remoteIdleSessions, MAX_PENDING_EVENTS, pendingMeshCoordinatorEvents, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, init_mesh_events, NORMAL_TRACE_BUFFER_SIZE, DEV_TRACE_BUFFER_SIZE, DEFAULT_CONFIG2, currentConfig, init_debug_config, DEFAULT_BINDING_CANDIDATES, cachedBinding, cachedBindingError, GhosttyVtTerminalBackend, init_ghostty_vt_backend, TerminalCtor, XtermTerminalBackend, init_xterm_backend, DEFAULT_SCROLLBACK, loggedTerminalBackends, TerminalScreen, init_terminal_screen, init_spawn_env, cachedPty, NodePtyRuntimeTransport, NodePtyTransportFactory, init_pty_transport, TerminalTranscriptAccumulator, buildCliSpawnEnv, init_provider_cli_shared, init_provider_cli_parse, init_provider_cli_config, init_provider_cli_runtime, provider_cli_adapter_exports, ProviderCliAdapter, init_provider_cli_adapter, execFileAsync, DEFAULT_TIMEOUT_MS, DEFAULT_MAX_BUFFER, GitCommandError, DEFAULT_MAX_FILES, DEFAULT_MAX_BYTES, summarizeGitStatus, InMemoryGitSnapshotStore, DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS, MIN_GIT_WORKSPACE_POLL_INTERVAL_MS, GitWorkspaceMonitor, GIT_COMMAND_NAMES, SNAPSHOT_REASONS, FAILURE_REASONS, defaultSnapshotStore, defaultGitCommandServices, BUSY_STATUSES, TERMINAL_STATUSES, TurnSnapshotTracker, MAX_WORKSPACES, MAX_ACTIVITY, MAX_SAVED_SESSIONS, DEFAULT_STATE, BUILTIN_IDE_DEFINITIONS, registeredIDEs, LIVE_LIFECYCLES, DEFAULT_ACTIVE_CHAT_POLL_STATUSES, DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS, LIVE_RUNTIME_LIFECYCLES, DaemonCdpManager, CdpDomHandlers, DEFAULT_MONITOR_CONFIG, StatusMonitor, BUILTIN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_VISIBILITIES, CHAT_MESSAGE_TRANSCRIPT_VISIBILITIES, CHAT_MESSAGE_AUDIENCES, CHAT_MESSAGE_SOURCES, CHAT_MESSAGE_ACTIVITY_SOURCES, CHAT_MESSAGE_INTERNAL_SOURCES, KNOWN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_KIND_ALIASES, EXPLICIT_HIDDEN_VISIBILITIES, EXPLICIT_VISIBLE_VISIBILITIES, HIDDEN_AUDIENCES, ACTIVITY_SOURCE_SET, INTERNAL_SOURCE_SET, HISTORY_DIR, RETAIN_DAYS, SAVED_HISTORY_INDEX_VERSION, SAVED_HISTORY_INDEX_FILE, SAVED_HISTORY_INDEX_LOCK_SUFFIX, SAVED_HISTORY_INDEX_LOCK_WAIT_MS, SAVED_HISTORY_INDEX_LOCK_STALE_MS, SAVED_HISTORY_INDEX_LOCK_POLL_MS, SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES, savedHistorySessionCache, savedHistoryFileSummaryCache, savedHistoryBackgroundRefresh, savedHistoryRollupInFlight, ChatHistoryWriter, IDE_PROVIDER_SESSION_CAPABILITIES_BASE, EXTENSION_PROVIDER_SESSION_CAPABILITIES_BASE, ExtensionProviderInstance, VALID_STATUSES, VALID_ROLES, VALID_BUBBLE_STATES, VALID_TURN_STATUSES, DEFAULT_APPROVAL_POSITIVE_HINTS, IdeProviderInstance, DEFAULT_CDP_SCAN_INTERVAL_MS, DEFAULT_CDP_DISCOVERY_INTERVAL_MS, DEFAULT_STATUS_INITIAL_REPORT_DELAY_MS, DEFAULT_STATUS_SERVER_REPORT_INTERVAL_MS, DEFAULT_STATUS_P2P_REPORT_INTERVAL_MS, MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS, DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS, MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS, DEFAULT_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS, DEFAULT_SESSION_HOST_READY_TIMEOUT_MS, STANDALONE_CDP_SCAN_INTERVAL_MS, DaemonCdpScanner, DaemonCdpInitializer, WORKING_STATUSES, FULL_STATUS_ACTIVE_CHAT_OPTIONS, LIVE_STATUS_ACTIVE_CHAT_OPTIONS, STATUS_MODAL_MESSAGE_LIMIT, STATUS_MODAL_BUTTON_LIMIT, VALID_INPUT_MEDIA_TYPES, VALID_INPUT_STRATEGIES, TEXT_ONLY_MESSAGE_INPUT_SUPPORT, IDE_SESSION_CAPABILITIES, EXTENSION_SESSION_CAPABILITIES, PTY_SESSION_CAPABILITIES, CLI_CHAT_SESSION_CAPABILITIES, ACP_SESSION_CAPABILITIES, globalStore, RECENT_SEND_WINDOW_MS, READ_CHAT_PROVIDER_EVAL_TIMEOUT_MS, HERMES_CLI_STARTING_SEND_SETTLE_MS, recentSendByTarget, DEFAULT_DEBUG_SANITIZE_OPTIONS, SECRET_KEY_PATTERN, KEY_TO_VK, COMMAND_DEBUG_LEVELS, DaemonCommandHandler, COMPLETED_FINALIZATION_RETRY_MS, COMPLETED_FINALIZATION_MAX_WAIT_MS, IMAGE_MIME_EXTENSIONS, MATERIALIZED_IMAGE_MAX_AGE_MS, MATERIALIZED_IMAGE_CLEANUP_INTERVAL_MS, lastMaterializedImageCleanupAt, CachedDatabaseSync, CliProviderInstance, AcpProviderInstance, chalkModule, chalkApi, COORDINATOR_DELEGATED_ENV_UNSETS, DaemonCliManager, VALID_CAPABILITY_MEDIA_TYPES, VALID_INPUT_STRATEGIES2, KNOWN_PROVIDER_FIELDS, VALUE_CONTROL_TYPES, ProviderLoader, _providerLoader, LOG_DIR2, MAX_FILE_SIZE, MAX_DAYS, SENSITIVE_KEYS, currentDate2, currentFile, writeCount2, SKIP_COMMANDS, DEFAULT_SERVER_NAME, DEFAULT_ADHDEV_MCP_COMMAND, HERMES_CLI_TYPE, HERMES_MCP_CONFIG_PATH, READ_DEBUG_ENABLED, recentReadDebugSignatureBySession, UPGRADE_HELPER_ENV, CHANNEL_NPM_TAG, CHANNEL_SERVER_URL, CHAT_COMMANDS, READ_DEBUG_ENABLED2, DaemonCommandRouter, DaemonStatusReporter, DEFAULT_DAEMON_PORT, DAEMON_WS_PATH, ProviderStreamAdapter, DaemonAgentStreamManager, AgentStreamPoller, ProviderInstanceManager, ARCHIVE_PATH, MAX_ENTRIES_PER_PROVIDER, VersionArchive, DEV_SERVER_PORT, DevServer, SessionHostRuntimeTransport, SessionHostPtyTransportFactory, DEFAULT_SESSION_HOST_APP_NAME, DEFAULT_STANDALONE_SESSION_HOST_APP_NAME, STARTUP_TIMEOUT_MS, STARTUP_POLL_MS, SessionHostCompatibilityError, EXTENSION_CATALOG, SessionRegistry;
41118
41138
  var init_dist2 = __esm({
41119
41139
  "../daemon-core/dist/index.mjs"() {
41120
41140
  "use strict";
@@ -41394,6 +41414,7 @@ var init_dist2 = __esm({
41394
41414
  | \`mesh_checkpoint\` | Create a git checkpoint on a node |
41395
41415
  | \`mesh_approve\` | Approve/reject a pending agent action |
41396
41416
  | \`mesh_clone_node\` | Create a worktree node for isolated parallel branch work |
41417
+ | \`mesh_refine_node\` | Validate and merge a completed worktree node back into its base branch |
41397
41418
  | \`mesh_remove_node\` | Remove a node (cleans up worktree if applicable) |`;
41398
41419
  TOOL_EXPOSURE_PREFLIGHT_SECTION = `## Tool Exposure Preflight
41399
41420
 
@@ -41410,8 +41431,9 @@ Before doing any coordinator work, confirm that the actual callable tool list in
41410
41431
  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\`.
41411
41432
  5. **Verify** \u2014 When a task reports completion or git work is visible, call \`mesh_git_status\` to verify changes were made.
41412
41433
  6. **Checkpoint** \u2014 Call \`mesh_checkpoint\` to save the work.
41413
- 7. **Clean up** \u2014 Remove worktree nodes via \`mesh_remove_node\` after their work is merged or no longer needed.
41414
- 8. **Report** \u2014 Summarize what was done, what changed, and any issues.
41434
+ 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.
41435
+ 8. **Clean up** \u2014 Remove worktree nodes via \`mesh_remove_node\` after their work is merged or no longer needed.
41436
+ 9. **Report** \u2014 Summarize what was done, what changed, any issues, and the branch convergence state.
41415
41437
 
41416
41438
  ## Failure Recovery
41417
41439
 
@@ -41998,6 +42020,7 @@ Follow these recovery rules:
41998
42020
  this.requirePromptEchoBeforeSubmit = resolvedConfig.requirePromptEchoBeforeSubmit;
41999
42021
  this.providerResolutionMeta = resolvedConfig.providerResolutionMeta;
42000
42022
  this.cliScripts = provider.scripts || {};
42023
+ this.scriptState = typeof this.cliScripts.createState === "function" ? this.cliScripts.createState() ?? null : null;
42001
42024
  const scriptNames = listCliScriptNames(this.cliScripts);
42002
42025
  if (scriptNames.length > 0) {
42003
42026
  LOG.info("CLI", `[${this.cliType}] CLI scripts: [${scriptNames.join(", ")}]`);
@@ -42267,7 +42290,7 @@ ${lastSnapshot}`;
42267
42290
  this.cliScripts = scripts;
42268
42291
  this.parsedStatusCache = null;
42269
42292
  this.parseErrorMessage = null;
42270
- this.scriptState = typeof scripts.createState === "function" ? scripts.createState() : null;
42293
+ this.scriptState = typeof scripts.createState === "function" ? scripts.createState() ?? null : null;
42271
42294
  const scriptNames = listCliScriptNames(scripts);
42272
42295
  LOG.info("CLI", `[${this.cliType}] CLI scripts injected: [${scriptNames.join(", ")}]`);
42273
42296
  }
@@ -43310,7 +43333,7 @@ ${lastSnapshot}`;
43310
43333
  scope: this.currentTurnScope,
43311
43334
  runtimeSettings: this.runtimeSettings
43312
43335
  });
43313
- return await Promise.resolve(fn(this.scriptState, {
43336
+ return await Promise.resolve(this.invokeCliScript(fn, {
43314
43337
  ...input,
43315
43338
  args: args && typeof args === "object" ? { ...args } : {}
43316
43339
  }));
@@ -48050,6 +48073,8 @@ ${effect.notification.body || ""}`.trim();
48050
48073
  init_config();
48051
48074
  init_provider_cli_adapter();
48052
48075
  init_logger();
48076
+ COMPLETED_FINALIZATION_RETRY_MS = 1e3;
48077
+ COMPLETED_FINALIZATION_MAX_WAIT_MS = 3e4;
48053
48078
  IMAGE_MIME_EXTENSIONS = {
48054
48079
  "image/png": ".png",
48055
48080
  "image/jpeg": ".jpg",
@@ -48235,10 +48260,12 @@ ${effect.notification.body || ""}`.trim();
48235
48260
  const mergedMessages = this.mergeConversationMessages(parsedMessages);
48236
48261
  const canonicalBackedHistory = this.syncCanonicalSavedHistoryIfNeeded();
48237
48262
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
48263
+ const parsedChatStatus = typeof parsedStatus?.status === "string" && parsedStatus.status.trim() ? parsedStatus.status.trim() : void 0;
48264
+ const suppressStaleParsedBusyStatus = this.shouldSuppressStaleParsedBusyStatus(parsedStatus, adapterStatus);
48238
48265
  if (parsedMessages.length > 0) {
48239
48266
  const shouldSkipReplayPersist = this.suppressIdleHistoryReplay && adapterStatus.status === "idle" && parsedStatus?.status === "idle";
48240
48267
  let messagesToSave = parsedMessages;
48241
- if (parsedStatus?.status === "generating" || parsedStatus?.status === "long_generating") {
48268
+ if (!suppressStaleParsedBusyStatus && (parsedChatStatus === "generating" || parsedChatStatus === "long_generating")) {
48242
48269
  const lastIdx = messagesToSave.length - 1;
48243
48270
  if (lastIdx >= 0 && messagesToSave[lastIdx]?.role === "assistant") {
48244
48271
  messagesToSave = messagesToSave.slice(0, lastIdx);
@@ -48272,6 +48299,7 @@ ${effect.notification.body || ""}`.trim();
48272
48299
  summaryMetadata: this.summaryMetadata,
48273
48300
  controlValues: this.controlValues
48274
48301
  });
48302
+ const activeChatStatus = parseErrorMessage ? "error" : autoApproveActive && parsedStatus?.status === "waiting_approval" ? "generating" : adapterStatus.status !== "idle" ? visibleStatus : suppressStaleParsedBusyStatus ? visibleStatus : parsedChatStatus || visibleStatus;
48275
48303
  return {
48276
48304
  type: this.type,
48277
48305
  name: this.provider.name,
@@ -48281,7 +48309,7 @@ ${effect.notification.body || ""}`.trim();
48281
48309
  activeChat: {
48282
48310
  id: `${this.type}_${this.workingDir}`,
48283
48311
  title: parsedStatus?.title || dirName,
48284
- status: parseErrorMessage ? "error" : autoApproveActive && parsedStatus?.status === "waiting_approval" ? "generating" : adapterStatus.status !== "idle" ? visibleStatus : parsedStatus?.status || visibleStatus,
48312
+ status: activeChatStatus,
48285
48313
  messages: mergedMessages,
48286
48314
  activeModal: autoApproveActive ? null : parsedStatus?.activeModal ?? adapterStatus.activeModal,
48287
48315
  inputContent: ""
@@ -48411,6 +48439,102 @@ ${effect.notification.body || ""}`.trim();
48411
48439
  }
48412
48440
  this.applyProviderResponse(parsed.payload, { phase: "immediate" });
48413
48441
  }
48442
+ completionHasFinalAssistantMessage(messages) {
48443
+ const visibleMessages = (Array.isArray(messages) ? messages : []).filter((message) => isUserFacingChatMessage(message));
48444
+ const lastVisible = visibleMessages[visibleMessages.length - 1];
48445
+ const role = typeof lastVisible?.role === "string" ? lastVisible.role.trim().toLowerCase() : "";
48446
+ const content = lastVisible ? flattenContent(lastVisible.content).trim() : "";
48447
+ return role === "assistant" && !!content;
48448
+ }
48449
+ hasAdapterPendingResponse() {
48450
+ const adapterAny = this.adapter;
48451
+ if (adapterAny?.isWaitingForResponse === true) return true;
48452
+ if (adapterAny?.currentTurnScope) return true;
48453
+ try {
48454
+ if (typeof this.adapter.isProcessing === "function" && this.adapter.isProcessing()) return true;
48455
+ } catch {
48456
+ }
48457
+ try {
48458
+ const partial2 = typeof this.adapter.getPartialResponse === "function" ? this.adapter.getPartialResponse() : "";
48459
+ if (typeof partial2 === "string" && partial2.trim()) return true;
48460
+ } catch {
48461
+ }
48462
+ return false;
48463
+ }
48464
+ shouldSuppressStaleParsedBusyStatus(parsedStatus, adapterStatus) {
48465
+ const parsedRawStatus = typeof parsedStatus?.status === "string" ? parsedStatus.status.trim() : "";
48466
+ const adapterRawStatus = typeof adapterStatus?.status === "string" ? adapterStatus.status.trim() : "";
48467
+ if (!isCliGeneratingLikeStatus(parsedRawStatus)) return false;
48468
+ if (adapterRawStatus !== "idle") return false;
48469
+ if (hasNonEmptyCliModalButtons(parsedStatus?.activeModal ?? parsedStatus?.modal)) return false;
48470
+ return !this.hasAdapterPendingResponse();
48471
+ }
48472
+ getCompletedFinalizationBlockReason(latestVisibleStatus) {
48473
+ if (latestVisibleStatus !== "idle") return `status:${latestVisibleStatus}`;
48474
+ const adapterAny = this.adapter;
48475
+ if (adapterAny?.isWaitingForResponse === true) return "adapter_waiting_for_response";
48476
+ if (adapterAny?.currentTurnScope) return "adapter_turn_scope_active";
48477
+ const partial2 = typeof this.adapter.getPartialResponse === "function" ? this.adapter.getPartialResponse() : "";
48478
+ if (typeof partial2 === "string" && partial2.trim()) return "partial_response_pending";
48479
+ let parsed;
48480
+ try {
48481
+ parsed = this.adapter.getScriptParsedStatus();
48482
+ } catch (error48) {
48483
+ return `parse_error:${error48?.message || String(error48)}`;
48484
+ }
48485
+ const parsedStatus = typeof parsed?.status === "string" ? parsed.status : "unknown";
48486
+ if (parsedStatus !== "idle") return `parsed_status:${parsedStatus}`;
48487
+ if (parsed?.activeModal || parsed?.modal) return "parsed_modal_active";
48488
+ if (!this.completionHasFinalAssistantMessage(parsed?.messages)) return "missing_final_assistant";
48489
+ return null;
48490
+ }
48491
+ scheduleCompletedDebounceFlush(delayMs) {
48492
+ if (this.completedDebounceTimer) clearTimeout(this.completedDebounceTimer);
48493
+ this.completedDebounceTimer = setTimeout(() => this.flushCompletedDebounceIfFinalized(), delayMs);
48494
+ }
48495
+ flushCompletedDebounceIfFinalized() {
48496
+ const pending = this.completedDebouncePending;
48497
+ if (!pending) {
48498
+ this.completedDebounceTimer = null;
48499
+ return;
48500
+ }
48501
+ const latestStatus = this.adapter.getStatus({ allowParse: false });
48502
+ const latestAutoApproveActive = latestStatus.status === "waiting_approval" && this.shouldAutoApprove();
48503
+ const latestVisibleStatus = latestAutoApproveActive ? "generating" : latestStatus.status;
48504
+ if (latestVisibleStatus !== "idle") {
48505
+ LOG.info("CLI", `[${this.type}] cancelled pending completed (resumed ${latestVisibleStatus})`);
48506
+ this.completedDebouncePending = null;
48507
+ this.completedDebounceTimer = null;
48508
+ return;
48509
+ }
48510
+ const blockReason = this.getCompletedFinalizationBlockReason(latestVisibleStatus);
48511
+ if (blockReason) {
48512
+ const waitedMs = Date.now() - pending.firstObservedAt;
48513
+ if (waitedMs < COMPLETED_FINALIZATION_MAX_WAIT_MS) {
48514
+ if (pending.loggedBlockReason !== blockReason) {
48515
+ LOG.info("CLI", `[${this.type}] waiting to emit completed until transcript finalizes (${blockReason})`);
48516
+ pending.loggedBlockReason = blockReason;
48517
+ }
48518
+ this.scheduleCompletedDebounceFlush(COMPLETED_FINALIZATION_RETRY_MS);
48519
+ return;
48520
+ }
48521
+ LOG.warn("CLI", `[${this.type}] suppressed completed event after ${waitedMs}ms without finalized assistant turn (${blockReason})`);
48522
+ this.completedDebouncePending = null;
48523
+ this.completedDebounceTimer = null;
48524
+ this.generatingStartedAt = 0;
48525
+ return;
48526
+ }
48527
+ LOG.info("CLI", `[${this.type}] completed in ${pending.duration}s`);
48528
+ this.pushEvent({
48529
+ event: "agent:generating_completed",
48530
+ chatTitle: pending.chatTitle,
48531
+ duration: pending.duration,
48532
+ timestamp: pending.timestamp
48533
+ });
48534
+ this.completedDebouncePending = null;
48535
+ this.completedDebounceTimer = null;
48536
+ this.generatingStartedAt = 0;
48537
+ }
48414
48538
  maybeAutoApproveStatus(adapterStatus, now = Date.now()) {
48415
48539
  const autoApproveActive = adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove();
48416
48540
  if (autoApproveActive && !this.autoApproveBusy) {
@@ -48508,26 +48632,8 @@ ${effect.notification.body || ""}`.trim();
48508
48632
  this.generatingDebouncePending = null;
48509
48633
  this.generatingStartedAt = 0;
48510
48634
  } else {
48511
- if (this.completedDebounceTimer) clearTimeout(this.completedDebounceTimer);
48512
- this.completedDebouncePending = { chatTitle, duration: duration3, timestamp: now };
48513
- this.completedDebounceTimer = setTimeout(() => {
48514
- if (this.completedDebouncePending) {
48515
- const latestStatus = this.adapter.getStatus({ allowParse: false });
48516
- const latestAutoApproveActive = latestStatus.status === "waiting_approval" && this.shouldAutoApprove();
48517
- const latestVisibleStatus = latestAutoApproveActive ? "generating" : latestStatus.status;
48518
- if (latestVisibleStatus !== "idle") {
48519
- LOG.info("CLI", `[${this.type}] cancelled pending completed (resumed ${latestVisibleStatus})`);
48520
- this.completedDebouncePending = null;
48521
- this.completedDebounceTimer = null;
48522
- return;
48523
- }
48524
- LOG.info("CLI", `[${this.type}] completed in ${this.completedDebouncePending.duration}s`);
48525
- this.pushEvent({ event: "agent:generating_completed", ...this.completedDebouncePending });
48526
- this.completedDebouncePending = null;
48527
- this.generatingStartedAt = 0;
48528
- }
48529
- this.completedDebounceTimer = null;
48530
- }, 3e3);
48635
+ this.completedDebouncePending = { chatTitle, duration: duration3, timestamp: now, firstObservedAt: now };
48636
+ this.scheduleCompletedDebounceFlush(3e3);
48531
48637
  }
48532
48638
  } else if (newStatus === "idle" && this.lastStatus === "starting") {
48533
48639
  this.pushEvent({ event: "agent:ready", chatTitle, timestamp: now });
@@ -52426,6 +52532,89 @@ Run 'adhdev doctor' for detailed diagnostics.`
52426
52532
  if (record2?.meta?.meshNodeId === nodeId) return true;
52427
52533
  return false;
52428
52534
  }
52535
+ async cleanupLocalWorktreeNode(args) {
52536
+ const workspace = typeof args.node?.workspace === "string" ? args.node.workspace.trim() : "";
52537
+ if (!workspace) {
52538
+ return {
52539
+ success: false,
52540
+ code: "mesh_worktree_cleanup_missing_workspace",
52541
+ error: `Worktree node '${args.nodeId}' is missing workspace metadata`,
52542
+ recoveryHint: "Inspect the mesh node record before removing it, or remove stale metadata manually only after confirming no managed worktree remains."
52543
+ };
52544
+ }
52545
+ const worktreeExists = fs10.existsSync(workspace);
52546
+ 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);
52547
+ const repoRoot = typeof sourceNode?.repoRoot === "string" && sourceNode.repoRoot.trim() ? sourceNode.repoRoot.trim() : typeof sourceNode?.workspace === "string" && sourceNode.workspace.trim() ? sourceNode.workspace.trim() : "";
52548
+ if (!worktreeExists) {
52549
+ return { success: true, skipped: true, removedPath: workspace, repoRoot: repoRoot || void 0, reason: "worktree_path_missing" };
52550
+ }
52551
+ if (!repoRoot || !fs10.existsSync(repoRoot)) {
52552
+ return {
52553
+ success: false,
52554
+ code: "mesh_worktree_cleanup_missing_source_repo",
52555
+ error: `Refusing to remove worktree '${workspace}' because the source repo root is unavailable`,
52556
+ recoveryHint: "Run mesh_remove_node from the machine that owns the source repo, or verify the source node metadata before retrying."
52557
+ };
52558
+ }
52559
+ if (typeof args.node?.worktreeBranch !== "string" || !args.node.worktreeBranch.trim()) {
52560
+ return {
52561
+ success: false,
52562
+ code: "mesh_worktree_cleanup_missing_branch",
52563
+ error: `Refusing to remove worktree '${workspace}' because worktreeBranch metadata is missing`,
52564
+ recoveryHint: "Confirm this is an ADHDev-managed worktree before removing it manually; managed worktree nodes include worktreeBranch metadata."
52565
+ };
52566
+ }
52567
+ const { resolveWorktreePath: resolveWorktreePath2, listWorktrees: listWorktrees2, removeWorktree: removeWorktree2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
52568
+ const normalizePath2 = (value) => {
52569
+ const resolved = (0, import_path7.resolve)(value);
52570
+ try {
52571
+ return fs10.realpathSync(resolved);
52572
+ } catch {
52573
+ return resolved;
52574
+ }
52575
+ };
52576
+ const expectedPath = normalizePath2(resolveWorktreePath2(repoRoot, String(args.mesh?.name || args.mesh?.id || "mesh"), args.node.worktreeBranch));
52577
+ const actualPath = normalizePath2(workspace);
52578
+ if (actualPath !== expectedPath) {
52579
+ return {
52580
+ success: false,
52581
+ code: "mesh_worktree_cleanup_unexpected_path",
52582
+ error: `Refusing to remove worktree '${workspace}' because it is not at the expected managed path '${expectedPath}'`,
52583
+ recoveryHint: "Use git worktree list/status to inspect the path. Retry only after confirming the mesh node metadata points to an ADHDev-managed worktree."
52584
+ };
52585
+ }
52586
+ const entries = await listWorktrees2(repoRoot);
52587
+ const managedEntry = entries.find((entry) => normalizePath2(entry.path) === actualPath);
52588
+ if (!managedEntry) {
52589
+ return {
52590
+ success: false,
52591
+ code: "mesh_worktree_cleanup_not_registered",
52592
+ error: `Refusing to remove '${workspace}' because it is not registered in git worktree list for '${repoRoot}'`,
52593
+ recoveryHint: "Inspect git worktree list --porcelain from the source repo. If the path was already removed, prune git worktrees before retrying."
52594
+ };
52595
+ }
52596
+ if (managedEntry.branch && managedEntry.branch !== args.node.worktreeBranch) {
52597
+ return {
52598
+ success: false,
52599
+ code: "mesh_worktree_cleanup_branch_mismatch",
52600
+ error: `Refusing to remove '${workspace}' because git reports branch '${managedEntry.branch}', expected '${args.node.worktreeBranch}'`,
52601
+ recoveryHint: "Inspect the worktree branch and mesh metadata before retrying cleanup."
52602
+ };
52603
+ }
52604
+ try {
52605
+ const result = await removeWorktree2(repoRoot, workspace, { requireClean: true });
52606
+ return { success: true, removedPath: result.removedPath, repoRoot };
52607
+ } catch (e) {
52608
+ const message = String(e?.message || e || "worktree cleanup failed");
52609
+ const dirty = message.includes("dirty worktree") || message.includes("local changes");
52610
+ return {
52611
+ success: false,
52612
+ code: dirty ? "mesh_worktree_cleanup_dirty" : "mesh_worktree_cleanup_failed",
52613
+ error: message,
52614
+ 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."
52615
+ };
52616
+ }
52617
+ }
52429
52618
  isCompletedHostedSession(record2) {
52430
52619
  return record2?.lifecycle === "stopped" || record2?.lifecycle === "failed" || record2?.lifecycle === "interrupted";
52431
52620
  }
@@ -53426,17 +53615,21 @@ Run 'adhdev doctor' for detailed diagnostics.`
53426
53615
  sessionCleanup = await this.cleanupMeshSessions({ meshId, nodeId, node, mode: sessionCleanupMode });
53427
53616
  if (sessionCleanup.success === false) return { success: false, removed: false, sessionCleanup };
53428
53617
  }
53429
- if (node?.isLocalWorktree && node.workspace) {
53430
- try {
53431
- const sourceNode = node.clonedFromNodeId ? mesh?.nodes.find((n) => n.id === node.clonedFromNodeId || n.nodeId === node.clonedFromNodeId) : mesh?.nodes.find((n) => !n.isLocalWorktree);
53432
- const repoRoot = sourceNode?.repoRoot || sourceNode?.workspace;
53433
- if (repoRoot) {
53434
- const { removeWorktree: removeWorktree2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
53435
- await removeWorktree2(repoRoot, node.workspace);
53436
- }
53437
- } catch (e) {
53438
- LOG.warn("MeshNode", `Worktree cleanup failed for ${nodeId}: ${e.message}`);
53618
+ let worktreeCleanup;
53619
+ if (node?.isLocalWorktree) {
53620
+ const cleanupResult = await this.cleanupLocalWorktreeNode({ mesh, node, nodeId });
53621
+ if (cleanupResult.success === false) {
53622
+ return {
53623
+ success: false,
53624
+ removed: false,
53625
+ code: cleanupResult.code,
53626
+ error: cleanupResult.error,
53627
+ recoveryHint: cleanupResult.recoveryHint,
53628
+ ...sessionCleanup ? { sessionCleanup } : {},
53629
+ worktreeCleanup: cleanupResult
53630
+ };
53439
53631
  }
53632
+ worktreeCleanup = cleanupResult;
53440
53633
  }
53441
53634
  let removed = false;
53442
53635
  if (meshRecord?.inline) {
@@ -53462,7 +53655,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
53462
53655
  } catch {
53463
53656
  }
53464
53657
  }
53465
- return { success: true, removed, ...sessionCleanup ? { sessionCleanup } : {} };
53658
+ return { success: true, removed, ...sessionCleanup ? { sessionCleanup } : {}, ...worktreeCleanup ? { worktreeCleanup } : {} };
53466
53659
  } catch (e) {
53467
53660
  return { success: false, error: e.message };
53468
53661
  }
@@ -58051,6 +58244,113 @@ function getNodeLaunchReadiness(node) {
58051
58244
  launchBlockedMessage: missingProviderPriorityMessage(node.id)
58052
58245
  };
58053
58246
  }
58247
+ function readNumeric(value, fallback = 0) {
58248
+ const parsed = Number(value);
58249
+ return Number.isFinite(parsed) ? parsed : fallback;
58250
+ }
58251
+ function buildBranchConvergence(mesh, node, status, dirty, uncommittedChanges) {
58252
+ const defaultBranch = readString(mesh.defaultBranch) ?? "main";
58253
+ const branch = readString(status?.branch) ?? readString(node.worktreeBranch) ?? null;
58254
+ const ahead = readNumeric(status?.ahead);
58255
+ const behind = readNumeric(status?.behind);
58256
+ const upstream = readString(status?.upstream) ?? null;
58257
+ const hasConflicts = status?.hasConflicts === true || Array.isArray(status?.conflictFiles) && status.conflictFiles.length > 0;
58258
+ const base = {
58259
+ defaultBranch,
58260
+ branch,
58261
+ upstream,
58262
+ ahead,
58263
+ behind,
58264
+ isWorktree: node.isLocalWorktree === true,
58265
+ isDefaultBranch: branch === defaultBranch
58266
+ };
58267
+ if (status?.isGitRepo !== true) {
58268
+ return {
58269
+ ...base,
58270
+ status: "blocked_review",
58271
+ needsConvergence: true,
58272
+ reason: "git_status_unavailable",
58273
+ nextStep: `Resolve git status for node '${node.id}' before marking the task complete.`
58274
+ };
58275
+ }
58276
+ if (!branch) {
58277
+ return {
58278
+ ...base,
58279
+ status: "blocked_review",
58280
+ needsConvergence: true,
58281
+ reason: "branch_unknown",
58282
+ nextStep: `Inspect node '${node.id}' git branch before deciding whether it is merged to ${defaultBranch}.`
58283
+ };
58284
+ }
58285
+ if (hasConflicts || dirty || uncommittedChanges > 0) {
58286
+ return {
58287
+ ...base,
58288
+ status: "not_mergeable",
58289
+ needsConvergence: true,
58290
+ reason: hasConflicts ? "conflicts_present" : "dirty_workspace",
58291
+ nextStep: `Commit, checkpoint, or resolve node '${node.id}' before any main convergence step.`
58292
+ };
58293
+ }
58294
+ if (branch === defaultBranch) {
58295
+ if (ahead > 0 || behind > 0) {
58296
+ return {
58297
+ ...base,
58298
+ status: "blocked_review",
58299
+ needsConvergence: true,
58300
+ reason: "default_branch_not_even_with_upstream",
58301
+ nextStep: `Bring ${defaultBranch} even with its upstream before declaring convergence complete.`
58302
+ };
58303
+ }
58304
+ return {
58305
+ ...base,
58306
+ status: "merged_to_main",
58307
+ needsConvergence: false,
58308
+ reason: "clean_default_branch",
58309
+ nextStep: null
58310
+ };
58311
+ }
58312
+ if (node.isLocalWorktree) {
58313
+ return {
58314
+ ...base,
58315
+ status: "cleanup_candidate",
58316
+ needsConvergence: true,
58317
+ reason: "clean_non_default_worktree_branch",
58318
+ nextStep: `Run mesh_refine_node(node_id: "${node.id}") or explicitly classify this worktree as blocked_review/not_mergeable before ending the task.`
58319
+ };
58320
+ }
58321
+ if (!upstream || ahead > 0 || behind > 0) {
58322
+ return {
58323
+ ...base,
58324
+ status: "blocked_review",
58325
+ needsConvergence: true,
58326
+ reason: !upstream ? "feature_branch_missing_upstream" : "feature_branch_not_even_with_upstream",
58327
+ nextStep: `Push or reconcile branch '${branch}', then merge it into ${defaultBranch} or mark it not_mergeable with a reason.`
58328
+ };
58329
+ }
58330
+ return {
58331
+ ...base,
58332
+ status: "pushed_feature_branch_needs_merge",
58333
+ needsConvergence: true,
58334
+ reason: "clean_non_default_branch",
58335
+ nextStep: `Review and merge branch '${branch}' into ${defaultBranch}; do not report the task as fully complete while it remains off main.`
58336
+ };
58337
+ }
58338
+ function summarizeBranchConvergence(nodes) {
58339
+ const followUps = nodes.filter((node) => node?.branchConvergence?.needsConvergence === true).map((node) => ({
58340
+ nodeId: node.nodeId,
58341
+ workspace: node.workspace,
58342
+ branch: node.branchConvergence.branch,
58343
+ status: node.branchConvergence.status,
58344
+ reason: node.branchConvergence.reason,
58345
+ nextStep: node.branchConvergence.nextStep
58346
+ }));
58347
+ return {
58348
+ needsFollowUp: followUps.length > 0,
58349
+ unresolvedCount: followUps.length,
58350
+ requiredFinalStates: ["merged_to_main", "pushed_feature_branch_needs_merge", "blocked_review", "cleanup_candidate", "not_mergeable"],
58351
+ followUps
58352
+ };
58353
+ }
58054
58354
  async function commandForNode(ctx, node, command, args = {}) {
58055
58355
  const isLocalNode = ctx.localMachineId && node.machineId === ctx.localMachineId || ctx.localDaemonId && node.daemonId === ctx.localDaemonId;
58056
58356
  if (ctx.transport instanceof IpcTransport && node.daemonId && !isLocalNode) {
@@ -58061,6 +58361,18 @@ async function commandForNode(ctx, node, command, args = {}) {
58061
58361
  }
58062
58362
  throw new Error(`Command '${command}' requires daemon IPC/local transport for node '${node.id}'`);
58063
58363
  }
58364
+ function isP2pTransportUnavailableError(error48) {
58365
+ const message = error48 instanceof Error ? error48.message : String(error48 || "");
58366
+ return /p2p|datachannel|mesh_relay_command|daemon_mesh_p2p_transport_unavailable/i.test(message) && /unavailable|failed|timeout|timed out|not connected|closed/i.test(message);
58367
+ }
58368
+ function buildRemoveNodeArgs(ctx, nodeId, sessionCleanupMode) {
58369
+ return {
58370
+ meshId: ctx.mesh.id,
58371
+ nodeId,
58372
+ ...sessionCleanupMode ? { sessionCleanupMode } : {},
58373
+ inlineMesh: ctx.mesh
58374
+ };
58375
+ }
58064
58376
  var MESH_STATUS_TOOL = {
58065
58377
  name: "mesh_status",
58066
58378
  description: "Get the current status of all nodes in the repo mesh \u2014 health, git state, active sessions, recovery hints, and recommended next steps. Use this to decide which node to send work to or how to recover from failures.",
@@ -58339,6 +58651,7 @@ async function meshStatus(ctx) {
58339
58651
  entry.branch = status?.branch;
58340
58652
  entry.isDirty = dirty;
58341
58653
  entry.uncommittedChanges = uncommittedChanges;
58654
+ entry.branchConvergence = buildBranchConvergence(mesh, node, status, dirty, uncommittedChanges);
58342
58655
  } else if (isLocalTransport(transport)) {
58343
58656
  const statusResult = await commandForNode(ctx, node, "git_status", { workspace: node.workspace });
58344
58657
  const status = extractGitStatus(statusResult);
@@ -58348,6 +58661,7 @@ async function meshStatus(ctx) {
58348
58661
  entry.branch = status?.branch;
58349
58662
  entry.isDirty = dirty;
58350
58663
  entry.uncommittedChanges = uncommittedChanges;
58664
+ entry.branchConvergence = buildBranchConvergence(mesh, node, status, dirty, uncommittedChanges);
58351
58665
  } else {
58352
58666
  entry.health = "unknown";
58353
58667
  entry.note = "No daemonId available for cloud status probe";
@@ -58385,6 +58699,9 @@ async function meshStatus(ctx) {
58385
58699
  } else if (entry.health === "degraded" && entry.error?.includes("git")) {
58386
58700
  nextStepHints.push("Initialize git repository or check workspace path.");
58387
58701
  }
58702
+ if (entry.branchConvergence?.needsConvergence === true && entry.branchConvergence.nextStep) {
58703
+ nextStepHints.push(String(entry.branchConvergence.nextStep));
58704
+ }
58388
58705
  if (recoveryContext.consecutiveNodeFailures > 0) {
58389
58706
  if (recoveryContext.retryRecommended) {
58390
58707
  nextStepHints.push(`Retry task on this node or launch a fresh session.`);
@@ -58405,7 +58722,8 @@ async function meshStatus(ctx) {
58405
58722
  repoIdentity: mesh.repoIdentity,
58406
58723
  policy: mesh.policy,
58407
58724
  refreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
58408
- nodes: results
58725
+ nodes: results,
58726
+ branchConvergenceSummary: summarizeBranchConvergence(results)
58409
58727
  };
58410
58728
  try {
58411
58729
  response.ledgerSummary = ledgerSummary;
@@ -59018,12 +59336,28 @@ async function meshCleanupSessions(ctx, args) {
59018
59336
  async function meshRemoveNode(ctx, args) {
59019
59337
  const node = await findNodeWithRefresh(ctx, args.node_id);
59020
59338
  if (isLocalTransport(ctx.transport)) {
59021
- const result = await commandForNode(ctx, node, "remove_mesh_node", {
59022
- meshId: ctx.mesh.id,
59023
- nodeId: args.node_id,
59024
- ...args.session_cleanup_mode ? { sessionCleanupMode: args.session_cleanup_mode } : {},
59025
- inlineMesh: ctx.mesh
59026
- });
59339
+ const removeArgs = buildRemoveNodeArgs(ctx, args.node_id, args.session_cleanup_mode);
59340
+ let result;
59341
+ let transportFallback;
59342
+ try {
59343
+ result = await commandForNode(ctx, node, "remove_mesh_node", removeArgs);
59344
+ } catch (e) {
59345
+ if (ctx.transport instanceof IpcTransport && node.isLocalWorktree && isP2pTransportUnavailableError(e)) {
59346
+ result = await ctx.transport.command("remove_mesh_node", removeArgs);
59347
+ transportFallback = {
59348
+ from: "p2p_mesh_relay",
59349
+ to: "local_control_plane",
59350
+ reason: e?.message || String(e)
59351
+ };
59352
+ } else {
59353
+ return JSON.stringify({
59354
+ success: false,
59355
+ code: isP2pTransportUnavailableError(e) ? "p2p_unavailable" : "mesh_remove_node_failed",
59356
+ error: e?.message || String(e),
59357
+ recoveryHint: isP2pTransportUnavailableError(e) ? "If this is an ADHDev-managed local worktree, retry from a coordinator connected to the daemon that owns the worktree; dashboard command/data-plane traffic still requires P2P." : "Inspect mesh_status and retry after resolving the reported failure."
59358
+ }, null, 2);
59359
+ }
59360
+ }
59027
59361
  if (result?.success && result.removed !== false) {
59028
59362
  const idx = ctx.mesh.nodes.findIndex((n) => n.id === args.node_id);
59029
59363
  if (idx >= 0) {
@@ -59031,7 +59365,7 @@ async function meshRemoveNode(ctx, args) {
59031
59365
  ctx.mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
59032
59366
  }
59033
59367
  }
59034
- return JSON.stringify(result, null, 2);
59368
+ return JSON.stringify({ ...result || {}, ...transportFallback ? { transportFallback } : {} }, null, 2);
59035
59369
  } else if (!isLocalTransport(ctx.transport) && node.daemonId) {
59036
59370
  try {
59037
59371
  const res = await ctx.transport.meshRemoveNode(node.daemonId, {