@adhdev/daemon-core 0.9.82-rc.262 → 0.9.82-rc.264

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
@@ -256,6 +256,29 @@ var init_git_executor = __esm({
256
256
  }
257
257
  });
258
258
 
259
+ // src/build-info.ts
260
+ function readInjected(value) {
261
+ if (typeof value !== "string") return void 0;
262
+ const trimmed = value.trim();
263
+ if (!trimmed || trimmed === "unknown") return void 0;
264
+ return trimmed;
265
+ }
266
+ function getDaemonBuildInfo() {
267
+ if (cached) return cached;
268
+ const commit = readInjected(true ? "751232919b0935654ee3d75d7cee4607594d6836" : void 0) ?? "unknown";
269
+ const commitShort = readInjected(true ? "7512329" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
270
+ const version = readInjected(true ? "0.9.82-rc.264" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
271
+ const builtAt = readInjected(true ? "2026-06-14T15:24:06.476Z" : void 0);
272
+ cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
273
+ return cached;
274
+ }
275
+ var cached;
276
+ var init_build_info = __esm({
277
+ "src/build-info.ts"() {
278
+ "use strict";
279
+ }
280
+ });
281
+
259
282
  // src/git/git-status.ts
260
283
  async function getGitRepoStatus(workspace, options = {}) {
261
284
  const lastCheckedAt = Date.now();
@@ -278,6 +301,7 @@ async function getGitRepoStatus(workspace, options = {}) {
278
301
  }
279
302
  const submoduleDirty = (submodules || []).some((submodule) => submodule.dirty || submodule.outOfSync || !!submodule.error);
280
303
  const dirty = parsed.staged + parsed.modified + parsed.untracked + parsed.deleted + parsed.renamed > 0 || parsed.conflictFiles.length > 0 || stashCount > 0 || submoduleDirty;
304
+ const daemonBuildBehind = await detectDaemonBuildBehind(repo, submodules, options);
281
305
  return {
282
306
  workspace: repo.workspace,
283
307
  repoRoot: repo.repoRoot,
@@ -301,7 +325,8 @@ async function getGitRepoStatus(workspace, options = {}) {
301
325
  conflictFiles: parsed.conflictFiles,
302
326
  stashCount,
303
327
  lastCheckedAt,
304
- submodules
328
+ submodules,
329
+ ...daemonBuildBehind ? { daemonBuildBehind } : {}
305
330
  };
306
331
  } catch (error) {
307
332
  if (error instanceof GitCommandError) {
@@ -314,6 +339,68 @@ async function getGitRepoStatus(workspace, options = {}) {
314
339
  );
315
340
  }
316
341
  }
342
+ async function classifyDaemonBuildChange(repoPath, buildCommit, options) {
343
+ try {
344
+ const diff = await runGit(repoPath, ["diff", "--name-only", `${buildCommit}..HEAD`], options);
345
+ const files = diff.stdout.split("\n").map((line) => line.trim()).filter(Boolean);
346
+ if (files.length === 0) {
347
+ return { isDaemonAffecting: true, affectedPackages: [] };
348
+ }
349
+ const pkgs = /* @__PURE__ */ new Set();
350
+ let sawNonPackageOrUnknown = false;
351
+ for (const file of files) {
352
+ const match = file.match(/(?:^|\/)packages\/([^/]+)\//);
353
+ if (!match) {
354
+ sawNonPackageOrUnknown = true;
355
+ continue;
356
+ }
357
+ pkgs.add(match[1]);
358
+ }
359
+ const affectedPackages = [...pkgs].sort();
360
+ const allWebOnly = !sawNonPackageOrUnknown && affectedPackages.length > 0 && affectedPackages.every((p) => WEB_ONLY_PACKAGES.has(p) && !DAEMON_RUNTIME_PACKAGES.has(p));
361
+ return { isDaemonAffecting: !allWebOnly, affectedPackages };
362
+ } catch {
363
+ return { isDaemonAffecting: true, affectedPackages: [] };
364
+ }
365
+ }
366
+ async function detectDaemonBuildBehind(repo, submodules, options) {
367
+ const build = options.daemonBuildInfo ?? getDaemonBuildInfo();
368
+ if (!build.commit || build.commit === "unknown") return void 0;
369
+ const scopes = [
370
+ { scope: "root", repoPath: repo.repoRoot || repo.workspace }
371
+ ];
372
+ for (const sub of submodules || []) {
373
+ if (sub.repoPath && !sub.error) scopes.push({ scope: sub.path, repoPath: sub.repoPath });
374
+ }
375
+ for (const { scope, repoPath } of scopes) {
376
+ try {
377
+ await runGit(repoPath, ["cat-file", "-e", `${build.commit}^{commit}`], options);
378
+ const headResult = await runGit(repoPath, ["rev-parse", "HEAD"], options);
379
+ const head = headResult.stdout.trim();
380
+ if (!head || head === build.commit) continue;
381
+ await runGit(repoPath, ["merge-base", "--is-ancestor", build.commit, "HEAD"], options);
382
+ const { isDaemonAffecting, affectedPackages } = await classifyDaemonBuildChange(
383
+ repoPath,
384
+ build.commit,
385
+ options
386
+ );
387
+ const scopeLabel = scope === "root" ? "workspace" : scope;
388
+ const warning = isDaemonAffecting ? `Live daemon was built from ${build.commitShort} which is behind ${scopeLabel} HEAD ${head.slice(0, 7)}. Merged code is NOT live until the daemon is rebuilt/redeployed and restarted \u2014 a local dist rebuild alone does not update a cloud daemon.` : `Live daemon was built from ${build.commitShort} which is behind ${scopeLabel} HEAD ${head.slice(0, 7)}, but only web packages changed (${(affectedPackages || []).join(", ") || "web"}). Daemon restart NOT required \u2014 redeploy the web app to reflect the change.`;
389
+ return {
390
+ buildCommit: build.commit,
391
+ buildCommitShort: build.commitShort,
392
+ head,
393
+ scope,
394
+ isDaemonAffecting,
395
+ ...affectedPackages && affectedPackages.length > 0 ? { affectedPackages } : {},
396
+ warning
397
+ };
398
+ } catch {
399
+ continue;
400
+ }
401
+ }
402
+ return void 0;
403
+ }
317
404
  async function readPorcelainStatus(repo, options) {
318
405
  const statusOutput = await runGit(repo, ["status", "--porcelain=v2", "--branch"], options);
319
406
  return parsePorcelainV2Status(statusOutput.stdout);
@@ -526,10 +613,29 @@ function parseSubmoduleStatusOutput(output, repoRoot, ignorePaths) {
526
613
  }
527
614
  return submodules;
528
615
  }
616
+ var DAEMON_RUNTIME_PACKAGES, WEB_ONLY_PACKAGES;
529
617
  var init_git_status = __esm({
530
618
  "src/git/git-status.ts"() {
531
619
  "use strict";
532
620
  init_git_executor();
621
+ init_build_info();
622
+ DAEMON_RUNTIME_PACKAGES = /* @__PURE__ */ new Set([
623
+ "daemon-core",
624
+ "daemon-standalone",
625
+ "session-host-core",
626
+ "session-host-daemon",
627
+ "terminal-mux-core",
628
+ "terminal-mux-control",
629
+ "terminal-mux-cli",
630
+ "ghostty-vt-node",
631
+ "mcp-server"
632
+ ]);
633
+ WEB_ONLY_PACKAGES = /* @__PURE__ */ new Set([
634
+ "web-core",
635
+ "web-standalone",
636
+ "web-devconsole",
637
+ "terminal-render-web"
638
+ ]);
533
639
  }
534
640
  });
535
641
 
@@ -2386,8 +2492,8 @@ function readLedgerFromStore(meshId) {
2386
2492
  }
2387
2493
  function getCachedRawEntries(meshId) {
2388
2494
  const now = Date.now();
2389
- const cached = ledgerReadCache.get(meshId);
2390
- if (cached && now - cached.cachedAt < LEDGER_CACHE_TTL_MS) return cached.entries;
2495
+ const cached2 = ledgerReadCache.get(meshId);
2496
+ if (cached2 && now - cached2.cachedAt < LEDGER_CACHE_TTL_MS) return cached2.entries;
2391
2497
  let entries;
2392
2498
  try {
2393
2499
  entries = readLedgerFromStore(meshId);
@@ -2650,6 +2756,7 @@ __export(mesh_work_queue_exports, {
2650
2756
  cancelTask: () => cancelTask,
2651
2757
  claimNextTask: () => claimNextTask,
2652
2758
  cleanupTerminalDirectDispatches: () => cleanupTerminalDirectDispatches,
2759
+ deleteDirectDispatchesByTaskId: () => deleteDirectDispatchesByTaskId,
2653
2760
  describeTaskDependencyState: () => describeTaskDependencyState,
2654
2761
  enqueueTask: () => enqueueTask,
2655
2762
  getActiveDirectDispatches: () => getActiveDirectDispatches,
@@ -3058,6 +3165,13 @@ function markStaleDirectDispatches(meshId, olderThanMs = 60 * 6e4) {
3058
3165
  } catch {
3059
3166
  }
3060
3167
  }
3168
+ function deleteDirectDispatchesByTaskId(meshId, taskIds) {
3169
+ try {
3170
+ return MeshRuntimeStore.getInstance().deleteDirectDispatchesByTaskId(meshId, taskIds);
3171
+ } catch {
3172
+ return 0;
3173
+ }
3174
+ }
3061
3175
  function recordMeshToolCall(opts) {
3062
3176
  try {
3063
3177
  return MeshRuntimeStore.getInstance().recordMeshToolCall(opts);
@@ -3691,6 +3805,24 @@ var init_mesh_runtime_store = __esm({
3691
3805
  deleteDirectDispatches(meshId) {
3692
3806
  this.db.prepare(`DELETE FROM mesh_direct_dispatches WHERE mesh_id = ?`).run(meshId);
3693
3807
  }
3808
+ /**
3809
+ * Delete specific direct dispatch rows by taskId for a mesh. Used by the staleDirect prune
3810
+ * path to remove orphaned/terminal dispatch records whose node/session is no longer in the
3811
+ * live mesh. Returns the number of rows actually deleted. No-op for an empty taskId list.
3812
+ */
3813
+ deleteDirectDispatchesByTaskId(meshId, taskIds) {
3814
+ const ids = (taskIds || []).map((id) => typeof id === "string" ? id.trim() : "").filter(Boolean);
3815
+ if (!ids.length) return 0;
3816
+ const stmt = this.db.prepare(`DELETE FROM mesh_direct_dispatches WHERE mesh_id = ? AND task_id = ?`);
3817
+ let deleted = 0;
3818
+ const run = this.db.transaction((rows) => {
3819
+ for (const taskId of rows) {
3820
+ deleted += stmt.run(meshId, taskId).changes;
3821
+ }
3822
+ });
3823
+ run(ids);
3824
+ return deleted;
3825
+ }
3694
3826
  markStaleDirectDispatches(meshId, olderThanMs) {
3695
3827
  const cutoff = new Date(Date.now() - olderThanMs).toISOString();
3696
3828
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -5359,11 +5491,14 @@ async function fastForwardMeshNode(args) {
5359
5491
  const trigger = normalizeOptionalString(args.trigger) || "manual";
5360
5492
  const updateSubmodules = args.updateSubmodules === true;
5361
5493
  const dryRun = args.dryRun === true || args.execute !== true;
5362
- const plannedSteps = buildPlannedSteps(updateSubmodules);
5494
+ const mode = args.mode === "push" ? "push" : "merge";
5495
+ const pushSubmodules = mode === "push" && args.pushSubmodules === true;
5496
+ const plannedSteps = buildPlannedSteps(mode, updateSubmodules, pushSubmodules);
5363
5497
  const base = {
5364
5498
  ...nodeId ? { nodeId } : {},
5365
5499
  ...meshId ? { meshId } : {},
5366
5500
  workspace,
5501
+ mode,
5367
5502
  dryRun,
5368
5503
  updateSubmodules,
5369
5504
  plannedSteps,
@@ -5377,13 +5512,24 @@ async function fastForwardMeshNode(args) {
5377
5512
  submoduleIgnorePaths: args.submoduleIgnorePaths,
5378
5513
  timeoutMs: args.timeoutMs ?? STATUS_OPTIONS.timeoutMs
5379
5514
  });
5515
+ if (mode === "push") {
5516
+ return pushMeshNode(base, args, current, {
5517
+ pushSubmodules,
5518
+ allowAutoPublishSubmoduleMainCommits: args.allowAutoPublishSubmoduleMainCommits === true
5519
+ });
5520
+ }
5380
5521
  const earlyBlockers = collectPreflightBlockers(current, requestedBranch);
5381
5522
  if (earlyBlockers.length > 0) {
5523
+ const blockCode = chooseBlockCode(current, earlyBlockers);
5382
5524
  const result2 = {
5383
- ...block(base, chooseBlockCode(current, earlyBlockers), earlyBlockers),
5525
+ ...block(base, blockCode, earlyBlockers),
5384
5526
  current,
5385
- finalBranchConvergenceState: buildConvergenceState(current, codeToConvergenceStatus(chooseBlockCode(current, earlyBlockers)))
5527
+ finalBranchConvergenceState: buildConvergenceState(current, codeToConvergenceStatus(blockCode))
5386
5528
  };
5529
+ if (blockCode === "branch_ahead" && current.ahead > 0 && current.behind === 0 && otherBlockersAreOnlyAhead(earlyBlockers)) {
5530
+ result2.code = "ahead_needs_push";
5531
+ result2.nextStep = 'Local branch is ahead of origin with nothing to merge. Re-run mesh_fast_forward_node with mode="push" (execute=true) to ff-only push the local commits to origin.';
5532
+ }
5387
5533
  await appendFastForwardLedger(result2, "blocked");
5388
5534
  return result2;
5389
5535
  }
@@ -5494,7 +5640,246 @@ async function fastForwardMeshNode(args) {
5494
5640
  await appendFastForwardLedger(result, success ? "executed" : "failed");
5495
5641
  return result;
5496
5642
  }
5497
- function buildPlannedSteps(updateSubmodules) {
5643
+ async function pushMeshNode(base, args, current, options) {
5644
+ const workspace = base.workspace;
5645
+ const requestedBranch = normalizeOptionalString(args.branch);
5646
+ const dryRun = base.dryRun;
5647
+ const blockers = collectPushPreflightBlockers(current, requestedBranch);
5648
+ if (blockers.length > 0) {
5649
+ const code2 = choosePushBlockCode(current, blockers);
5650
+ const result2 = {
5651
+ ...block(base, code2, blockers),
5652
+ current,
5653
+ preStatus: current,
5654
+ finalBranchConvergenceState: buildConvergenceState(current, codeToConvergenceStatus(code2))
5655
+ };
5656
+ await appendFastForwardLedger(result2, "blocked");
5657
+ return result2;
5658
+ }
5659
+ const target = parseUpstreamTarget(current.upstream || "");
5660
+ if (!target) {
5661
+ const result2 = {
5662
+ ...block(base, "upstream_unparseable", ["upstream_unparseable"]),
5663
+ current,
5664
+ preStatus: current,
5665
+ finalBranchConvergenceState: buildConvergenceState(current, "blocked")
5666
+ };
5667
+ await appendFastForwardLedger(result2, "blocked");
5668
+ return result2;
5669
+ }
5670
+ const refspec = `HEAD:refs/heads/${target.remoteBranch}`;
5671
+ const pushTarget = { remote: target.remote, remoteBranch: target.remoteBranch, refspec };
5672
+ if (current.ahead <= 0) {
5673
+ const result2 = {
5674
+ ...base,
5675
+ success: true,
5676
+ code: "nothing_to_push",
5677
+ allowed: true,
5678
+ willRun: false,
5679
+ executed: false,
5680
+ blockingReasons: [],
5681
+ current,
5682
+ preStatus: current,
5683
+ postStatus: current,
5684
+ pushTarget,
5685
+ finalBranchConvergenceState: buildConvergenceState(current, "up_to_date")
5686
+ };
5687
+ await appendFastForwardLedger(result2, "noop");
5688
+ return result2;
5689
+ }
5690
+ const descendant = await verifyUpstreamIsAncestorOfHead(workspace, current.upstream || "", args.timeoutMs);
5691
+ if (!descendant.ok) {
5692
+ const result2 = {
5693
+ ...block(base, "non_fast_forward_push", ["head_is_not_descendant_of_upstream"]),
5694
+ current,
5695
+ preStatus: current,
5696
+ pushTarget,
5697
+ operationError: descendant.error,
5698
+ nextStep: "origin/<branch> has commits not in local HEAD; a ff-only push would lose them. Converge by rebasing onto origin first, then re-run. This operation never force-pushes.",
5699
+ finalBranchConvergenceState: buildConvergenceState(current, "not_mergeable")
5700
+ };
5701
+ await appendFastForwardLedger(result2, "blocked");
5702
+ return result2;
5703
+ }
5704
+ if (dryRun) {
5705
+ const result2 = {
5706
+ ...base,
5707
+ success: true,
5708
+ code: "push_available",
5709
+ allowed: true,
5710
+ willRun: false,
5711
+ executed: false,
5712
+ blockingReasons: [],
5713
+ current,
5714
+ preStatus: current,
5715
+ pushTarget,
5716
+ ...options.pushSubmodules ? { submodulePushes: await planSubmodulePushes(current, options, args.timeoutMs) } : {},
5717
+ finalBranchConvergenceState: buildConvergenceState(current, "push_available")
5718
+ };
5719
+ await appendFastForwardLedger(result2, "dry_run");
5720
+ return result2;
5721
+ }
5722
+ try {
5723
+ await runGit(workspace, ["push", target.remote, refspec], { timeoutMs: args.timeoutMs ?? 3e4 });
5724
+ } catch (error) {
5725
+ const result2 = {
5726
+ ...block(base, "push_ff_only_failed", ["push_ff_only_failed"]),
5727
+ current,
5728
+ preStatus: current,
5729
+ pushTarget,
5730
+ operationError: formatGitError2(error),
5731
+ finalBranchConvergenceState: buildConvergenceState(current, "not_mergeable")
5732
+ };
5733
+ await appendFastForwardLedger(result2, "failed");
5734
+ return result2;
5735
+ }
5736
+ let submodulePushes;
5737
+ if (options.pushSubmodules) {
5738
+ submodulePushes = await executeSubmodulePushes(current, options, args.timeoutMs);
5739
+ }
5740
+ const postStatus = await getGitRepoStatus(workspace, {
5741
+ ...STATUS_OPTIONS,
5742
+ submoduleIgnorePaths: args.submoduleIgnorePaths,
5743
+ timeoutMs: args.timeoutMs ?? STATUS_OPTIONS.timeoutMs
5744
+ });
5745
+ const submodulePushFailed = (submodulePushes || []).some((entry) => !entry.pushed && !entry.skipped);
5746
+ const blockingReasons = [];
5747
+ if (postStatus.ahead !== 0) blockingReasons.push("post_branch_ahead");
5748
+ if (submodulePushFailed) blockingReasons.push("submodule_push_failed");
5749
+ const success = blockingReasons.length === 0;
5750
+ const code = success ? "push_applied" : submodulePushFailed && postStatus.ahead === 0 ? "push_applied_submodule_push_failed" : "post_push_verify_failed";
5751
+ const result = {
5752
+ ...base,
5753
+ success,
5754
+ code,
5755
+ allowed: true,
5756
+ willRun: true,
5757
+ executed: true,
5758
+ blockingReasons,
5759
+ current,
5760
+ preStatus: current,
5761
+ postStatus,
5762
+ pushTarget,
5763
+ ...submodulePushes ? { submodulePushes } : {},
5764
+ finalBranchConvergenceState: buildConvergenceState(postStatus, success ? "pushed" : "post_verify_failed")
5765
+ };
5766
+ await appendFastForwardLedger(result, success ? "executed" : "failed");
5767
+ return result;
5768
+ }
5769
+ function collectPushPreflightBlockers(status, requestedBranch) {
5770
+ const blockers = [];
5771
+ if (!status.isGitRepo) blockers.push("not_git_repo");
5772
+ if (!status.branch) blockers.push("detached_head_or_unknown_branch");
5773
+ if (requestedBranch && status.branch !== requestedBranch) blockers.push("branch_mismatch");
5774
+ if (!status.upstream) blockers.push("upstream_missing");
5775
+ if (status.upstreamStatus !== "fresh") blockers.push("upstream_not_fresh");
5776
+ if (status.hasConflicts) blockers.push("conflicts_present");
5777
+ if (status.staged > 0) blockers.push("staged_changes_present");
5778
+ if (status.modified > 0) blockers.push("modified_changes_present");
5779
+ if (status.untracked > 0) blockers.push("untracked_changes_present");
5780
+ if (status.deleted > 0) blockers.push("deleted_changes_present");
5781
+ if (status.renamed > 0) blockers.push("renamed_changes_present");
5782
+ if (status.stashCount > 0) blockers.push("stash_entries_present");
5783
+ if (status.ahead > 0 && status.behind > 0) blockers.push("branch_diverged_from_upstream");
5784
+ else if (status.behind > 0) blockers.push("branch_behind_upstream");
5785
+ return blockers;
5786
+ }
5787
+ function choosePushBlockCode(status, blockers) {
5788
+ if (blockers.includes("not_git_repo")) return "not_git_repo";
5789
+ if (blockers.includes("branch_mismatch")) return "branch_mismatch";
5790
+ if (blockers.includes("upstream_missing")) return "upstream_missing";
5791
+ if (blockers.includes("upstream_not_fresh")) return "upstream_not_fresh";
5792
+ if (blockers.includes("branch_diverged_from_upstream")) return "branch_diverged";
5793
+ if (blockers.includes("branch_behind_upstream")) return "non_fast_forward_push";
5794
+ if (blockers.some((reason) => reason.includes("changes") || reason.includes("conflicts") || reason.includes("stash"))) return "dirty_worktree";
5795
+ return "preflight_blocked";
5796
+ }
5797
+ function parseUpstreamTarget(upstream) {
5798
+ const trimmed = upstream.trim();
5799
+ const slash = trimmed.indexOf("/");
5800
+ if (slash <= 0 || slash >= trimmed.length - 1) return null;
5801
+ return { remote: trimmed.slice(0, slash), remoteBranch: trimmed.slice(slash + 1) };
5802
+ }
5803
+ async function verifyUpstreamIsAncestorOfHead(workspace, upstream, timeoutMs) {
5804
+ if (!upstream) return { ok: false, error: "missing upstream" };
5805
+ try {
5806
+ await runGit(workspace, ["merge-base", "--is-ancestor", upstream, "HEAD"], { timeoutMs: timeoutMs ?? 15e3 });
5807
+ return { ok: true };
5808
+ } catch (error) {
5809
+ return { ok: false, error: formatGitError2(error) };
5810
+ }
5811
+ }
5812
+ async function planSubmodulePushes(status, options, timeoutMs) {
5813
+ return resolveSubmodulePushes(status, options, false, timeoutMs);
5814
+ }
5815
+ async function executeSubmodulePushes(status, options, timeoutMs) {
5816
+ return resolveSubmodulePushes(status, options, true, timeoutMs);
5817
+ }
5818
+ async function resolveSubmodulePushes(status, options, execute, timeoutMs) {
5819
+ const submodules = Array.isArray(status.submodules) ? status.submodules : [];
5820
+ const results = [];
5821
+ for (const submodule of submodules) {
5822
+ const base = {
5823
+ path: submodule.path,
5824
+ commit: submodule.commit,
5825
+ remote: "origin",
5826
+ remoteBranch: "main",
5827
+ pushed: false,
5828
+ skipped: true,
5829
+ code: "submodule_push_skipped"
5830
+ };
5831
+ if (!options.allowAutoPublishSubmoduleMainCommits) {
5832
+ results.push({ ...base, code: "submodule_push_policy_disabled", error: "allowAutoPublishSubmoduleMainCommits is not enabled" });
5833
+ continue;
5834
+ }
5835
+ if (submodule.error || submodule.dirty) {
5836
+ results.push({ ...base, code: "submodule_not_clean", error: submodule.error || "submodule worktree is dirty" });
5837
+ continue;
5838
+ }
5839
+ const repoPath = submodule.repoPath;
5840
+ if (!repoPath || !submodule.commit) {
5841
+ results.push({ ...base, code: "submodule_status_incomplete" });
5842
+ continue;
5843
+ }
5844
+ try {
5845
+ await runGit(repoPath, ["-c", "protocol.file.allow=always", "fetch", "origin", "refs/heads/main:refs/remotes/origin/main"], { timeoutMs: timeoutMs ?? 3e4 });
5846
+ } catch (error) {
5847
+ results.push({ ...base, code: "submodule_fetch_failed", error: formatGitError2(error) });
5848
+ continue;
5849
+ }
5850
+ let alreadyReachable = false;
5851
+ try {
5852
+ await runGit(repoPath, ["merge-base", "--is-ancestor", submodule.commit, "refs/remotes/origin/main"], { timeoutMs: timeoutMs ?? 15e3 });
5853
+ alreadyReachable = true;
5854
+ } catch {
5855
+ }
5856
+ if (alreadyReachable) {
5857
+ results.push({ ...base, pushed: false, skipped: true, code: "submodule_already_reachable" });
5858
+ continue;
5859
+ }
5860
+ try {
5861
+ await runGit(repoPath, ["merge-base", "--is-ancestor", "refs/remotes/origin/main", submodule.commit], { timeoutMs: timeoutMs ?? 15e3 });
5862
+ } catch (error) {
5863
+ results.push({ ...base, pushed: false, skipped: false, code: "submodule_non_fast_forward", error: formatGitError2(error) });
5864
+ continue;
5865
+ }
5866
+ const refspec = `${submodule.commit}:refs/heads/main`;
5867
+ if (!execute) {
5868
+ results.push({ ...base, pushed: false, skipped: false, code: "submodule_push_available", refspec });
5869
+ continue;
5870
+ }
5871
+ try {
5872
+ await runGit(repoPath, ["push", "origin", refspec], { timeoutMs: timeoutMs ?? 3e4 });
5873
+ await runGit(repoPath, ["-c", "protocol.file.allow=always", "fetch", "origin", "refs/heads/main:refs/remotes/origin/main"], { timeoutMs: timeoutMs ?? 3e4 });
5874
+ await runGit(repoPath, ["merge-base", "--is-ancestor", submodule.commit, "refs/remotes/origin/main"], { timeoutMs: timeoutMs ?? 15e3 });
5875
+ results.push({ ...base, pushed: true, skipped: false, code: "submodule_pushed", refspec });
5876
+ } catch (error) {
5877
+ results.push({ ...base, pushed: false, skipped: false, code: "submodule_push_failed", refspec, error: formatGitError2(error) });
5878
+ }
5879
+ }
5880
+ return results;
5881
+ }
5882
+ function buildPlannedSteps(mode, updateSubmodules, pushSubmodules) {
5498
5883
  const steps = [
5499
5884
  {
5500
5885
  operation: "refresh_upstream",
@@ -5507,20 +5892,49 @@ function buildPlannedSteps(updateSubmodules) {
5507
5892
  description: "Require clean staged/modified/untracked/deleted/renamed/conflict/stash/submodule state.",
5508
5893
  safe: true,
5509
5894
  willMutateWorktree: false
5510
- },
5511
- {
5512
- operation: "verify_fast_forward",
5513
- description: "Require ahead=0, behind>0, and HEAD to be an ancestor of the upstream ref.",
5895
+ }
5896
+ ];
5897
+ if (mode === "push") {
5898
+ steps.push({
5899
+ operation: "verify_push_descendant",
5900
+ description: "Require HEAD to be a descendant of origin/<branch> (origin/<branch> is an ancestor of HEAD); refuse any non-fast-forward push.",
5514
5901
  safe: true,
5515
5902
  willMutateWorktree: false
5516
- },
5517
- {
5518
- operation: "merge_ff_only",
5519
- description: "Apply git merge --ff-only against the tracked upstream; no force, reset, rebase, push, or deploy.",
5903
+ });
5904
+ steps.push({
5905
+ operation: "push_ff_only",
5906
+ description: "Run git push origin HEAD:<branch> as a strict ff-only push; never --force, --force-with-lease, reset, or rebase. Does not mutate the worktree.",
5520
5907
  safe: true,
5521
- willMutateWorktree: true
5908
+ willMutateWorktree: false
5909
+ });
5910
+ if (pushSubmodules) {
5911
+ steps.push({
5912
+ operation: "push_submodules_ff_only",
5913
+ description: "For each submodule, if allowAutoPublishSubmoduleMainCommits is enabled and the submodule HEAD is a descendant of its origin main, ff-only push it to submodule origin main; otherwise skip.",
5914
+ safe: true,
5915
+ willMutateWorktree: false
5916
+ });
5522
5917
  }
5523
- ];
5918
+ steps.push({
5919
+ operation: "verify_post_status",
5920
+ description: "Re-read daemon-owned git status and report final branch convergence state.",
5921
+ safe: true,
5922
+ willMutateWorktree: false
5923
+ });
5924
+ return steps;
5925
+ }
5926
+ steps.push({
5927
+ operation: "verify_fast_forward",
5928
+ description: "Require ahead=0, behind>0, and HEAD to be an ancestor of the upstream ref.",
5929
+ safe: true,
5930
+ willMutateWorktree: false
5931
+ });
5932
+ steps.push({
5933
+ operation: "merge_ff_only",
5934
+ description: "Apply git merge --ff-only against the tracked upstream; no force, reset, rebase, push, or deploy.",
5935
+ safe: true,
5936
+ willMutateWorktree: true
5937
+ });
5524
5938
  if (updateSubmodules) {
5525
5939
  steps.push({
5526
5940
  operation: "submodule_update",
@@ -5537,6 +5951,10 @@ function buildPlannedSteps(updateSubmodules) {
5537
5951
  });
5538
5952
  return steps;
5539
5953
  }
5954
+ function otherBlockersAreOnlyAhead(blockers) {
5955
+ const aheadOnly = /* @__PURE__ */ new Set(["branch_has_local_commits"]);
5956
+ return blockers.every((reason) => aheadOnly.has(reason));
5957
+ }
5540
5958
  function collectPreflightBlockers(status, requestedBranch) {
5541
5959
  const blockers = [];
5542
5960
  if (!status.isGitRepo) blockers.push("not_git_repo");
@@ -5593,7 +6011,7 @@ function chooseBlockCode(status, blockers) {
5593
6011
  return "preflight_blocked";
5594
6012
  }
5595
6013
  function codeToConvergenceStatus(code) {
5596
- if (code === "branch_diverged" || code === "branch_ahead" || code === "non_fast_forward") return "not_mergeable";
6014
+ if (code === "branch_diverged" || code === "branch_ahead" || code === "non_fast_forward" || code === "non_fast_forward_push" || code === "upstream_unparseable") return "not_mergeable";
5597
6015
  if (code === "dirty_worktree" || code === "submodule_not_clean") return "blocked_review";
5598
6016
  return "blocked";
5599
6017
  }
@@ -5676,6 +6094,7 @@ async function appendFastForwardLedger(result, outcome) {
5676
6094
  ...result.nodeId ? { nodeId: result.nodeId } : {},
5677
6095
  payload: {
5678
6096
  operation: "mesh_fast_forward_node",
6097
+ mode: result.mode,
5679
6098
  trigger: result.trigger || "manual",
5680
6099
  outcome,
5681
6100
  code: result.code,
@@ -5686,6 +6105,7 @@ async function appendFastForwardLedger(result, outcome) {
5686
6105
  executed: result.executed,
5687
6106
  branch: result.postStatus?.branch ?? result.current?.branch,
5688
6107
  upstream: result.postStatus?.upstream ?? result.current?.upstream,
6108
+ ...result.pushTarget ? { pushTarget: result.pushTarget } : {},
5689
6109
  before: result.current ? {
5690
6110
  headCommit: result.current.headCommit,
5691
6111
  ahead: result.current.ahead,
@@ -5696,6 +6116,16 @@ async function appendFastForwardLedger(result, outcome) {
5696
6116
  ahead: result.postStatus.ahead,
5697
6117
  behind: result.postStatus.behind
5698
6118
  } : void 0,
6119
+ ...result.submodulePushes ? {
6120
+ submodulePushes: result.submodulePushes.map((entry) => ({
6121
+ path: entry.path,
6122
+ commit: entry.commit,
6123
+ pushed: entry.pushed,
6124
+ skipped: entry.skipped,
6125
+ code: entry.code,
6126
+ ...entry.refspec ? { refspec: entry.refspec } : {}
6127
+ }))
6128
+ } : {},
5699
6129
  blockingReasons: result.blockingReasons
5700
6130
  }
5701
6131
  });
@@ -6866,8 +7296,8 @@ var init_mesh_routing = __esm({
6866
7296
  // src/mesh/mesh-events-coordinator.ts
6867
7297
  function getCachedMeshByWorkspace(workspace) {
6868
7298
  const now = Date.now();
6869
- const cached = meshByWorkspaceCache.get(workspace);
6870
- if (cached && now - cached.cachedAt < MESH_WORKSPACE_CACHE_TTL_MS) return cached.mesh;
7299
+ const cached2 = meshByWorkspaceCache.get(workspace);
7300
+ if (cached2 && now - cached2.cachedAt < MESH_WORKSPACE_CACHE_TTL_MS) return cached2.mesh;
6871
7301
  const mesh = getMeshByRepo(workspace);
6872
7302
  meshByWorkspaceCache.set(workspace, { mesh, cachedAt: now });
6873
7303
  return mesh;
@@ -11759,10 +12189,10 @@ ${lastSnapshot}`;
11759
12189
  return this.accumulatedRawBuffer.replace(/\x1B\][^\x07]*(?:\x07|\x1B\\)/g, "").replace(/\x1B[P^_X][\s\S]*?(?:\x07|\x1B\\)/g, "").replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
11760
12190
  }
11761
12191
  getFreshParsedStatusCache() {
11762
- const cached = this.parsedStatusCache;
12192
+ const cached2 = this.parsedStatusCache;
11763
12193
  const accumulatedRawBufferKey = this.getAccumulatedRawBufferCacheKey();
11764
- if (cached && cached.responseBuffer === this.responseBuffer && cached.currentTurnScope === this.engine.currentTurnScope && cached.recentOutputBuffer === this.recentOutputBuffer && cached.accumulatedBuffer === this.accumulatedBuffer && cached.accumulatedRawBufferKey === accumulatedRawBufferKey && cached.screenText === this.lastScreenText && cached.currentStatus === this.engine.currentStatus && cached.activeModal === this.engine.activeModal && cached.cliName === this.cliName) {
11765
- return cached.result;
12194
+ if (cached2 && cached2.responseBuffer === this.responseBuffer && cached2.currentTurnScope === this.engine.currentTurnScope && cached2.recentOutputBuffer === this.recentOutputBuffer && cached2.accumulatedBuffer === this.accumulatedBuffer && cached2.accumulatedRawBufferKey === accumulatedRawBufferKey && cached2.screenText === this.lastScreenText && cached2.currentStatus === this.engine.currentStatus && cached2.activeModal === this.engine.activeModal && cached2.cliName === this.cliName) {
12195
+ return cached2.result;
11766
12196
  }
11767
12197
  return null;
11768
12198
  }
@@ -12222,10 +12652,10 @@ ${lastSnapshot}`;
12222
12652
  getScriptParsedStatus() {
12223
12653
  const screenText = this.readTerminalScreenText();
12224
12654
  const parseScreenText = this.getParseScreenText(screenText);
12225
- const cached = this.parsedStatusCache;
12655
+ const cached2 = this.parsedStatusCache;
12226
12656
  const accumulatedRawBufferKey = this.getAccumulatedRawBufferCacheKey();
12227
- if (!this.providerOwnsTranscript() && cached && cached.responseBuffer === this.responseBuffer && cached.currentTurnScope === this.engine.currentTurnScope && cached.recentOutputBuffer === this.recentOutputBuffer && cached.accumulatedBuffer === this.accumulatedBuffer && cached.accumulatedRawBufferKey === accumulatedRawBufferKey && cached.screenText === parseScreenText && cached.currentStatus === this.engine.currentStatus && cached.activeModal === this.engine.activeModal && cached.cliName === this.cliName) {
12228
- return cached.result;
12657
+ if (!this.providerOwnsTranscript() && cached2 && cached2.responseBuffer === this.responseBuffer && cached2.currentTurnScope === this.engine.currentTurnScope && cached2.recentOutputBuffer === this.recentOutputBuffer && cached2.accumulatedBuffer === this.accumulatedBuffer && cached2.accumulatedRawBufferKey === accumulatedRawBufferKey && cached2.screenText === parseScreenText && cached2.currentStatus === this.engine.currentStatus && cached2.activeModal === this.engine.activeModal && cached2.cliName === this.cliName) {
12658
+ return cached2.result;
12229
12659
  }
12230
12660
  const parsed = this.runParseSession();
12231
12661
  if (!parsed || !Array.isArray(parsed.messages)) {
@@ -14091,6 +14521,7 @@ __export(index_exports, {
14091
14521
  MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS: () => MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS,
14092
14522
  NodePtyTransportFactory: () => NodePtyTransportFactory,
14093
14523
  P2pRelayFailureError: () => P2pRelayFailureError,
14524
+ PRUNABLE_ORPHAN_STALE_REASONS: () => PRUNABLE_ORPHAN_STALE_REASONS,
14094
14525
  ProviderCliAdapter: () => ProviderCliAdapter,
14095
14526
  ProviderInstanceManager: () => ProviderInstanceManager,
14096
14527
  ProviderLoader: () => ProviderLoader,
@@ -14144,6 +14575,7 @@ __export(index_exports, {
14144
14575
  classifyChatMessageVisibility: () => classifyChatMessageVisibility,
14145
14576
  classifyHotChatSessionsForSubscriptionFlush: () => classifyHotChatSessionsForSubscriptionFlush,
14146
14577
  classifyP2pRelayFailure: () => classifyP2pRelayFailure,
14578
+ classifyStaleDirectForPrune: () => classifyStaleDirectForPrune,
14147
14579
  cleanupTerminalDirectDispatches: () => cleanupTerminalDirectDispatches,
14148
14580
  clearDebugTrace: () => clearDebugTrace,
14149
14581
  clearPendingMeshCoordinatorEvents: () => clearPendingMeshCoordinatorEvents,
@@ -14163,6 +14595,7 @@ __export(index_exports, {
14163
14595
  createNativeHistoryDispatcher: () => createNativeHistoryDispatcher,
14164
14596
  createSessionDelivery: () => createSessionDelivery,
14165
14597
  createWorktree: () => createWorktree,
14598
+ deleteDirectDispatchesByTaskId: () => deleteDirectDispatchesByTaskId,
14166
14599
  deleteMesh: () => deleteMesh,
14167
14600
  deriveMeshReviewInboxItems: () => deriveMeshReviewInboxItems,
14168
14601
  describeTaskDependencyState: () => describeTaskDependencyState,
@@ -14191,6 +14624,7 @@ __export(index_exports, {
14191
14624
  getAvailableIdeIds: () => getAvailableIdeIds,
14192
14625
  getCoordinatorForSession: () => getCoordinatorForSession,
14193
14626
  getCurrentDaemonLogPath: () => getCurrentDaemonLogPath,
14627
+ getDaemonBuildInfo: () => getDaemonBuildInfo,
14194
14628
  getDaemonDataDir: () => getDaemonDataDir,
14195
14629
  getDaemonLogDir: () => getDaemonLogDir,
14196
14630
  getDebugRuntimeConfig: () => getDebugRuntimeConfig,
@@ -17037,6 +17471,17 @@ function buildMeshActiveWork(opts) {
17037
17471
  }
17038
17472
  return { activeWork: records, staleDirectWork, staleDirectWorkNote, terminalDirectWork, summary };
17039
17473
  }
17474
+ var PRUNABLE_ORPHAN_STALE_REASONS = /* @__PURE__ */ new Set([
17475
+ "direct task node is no longer in the live mesh",
17476
+ "direct task session is not present in live session records",
17477
+ "direct task has no node id"
17478
+ ]);
17479
+ function classifyStaleDirectForPrune(record, opts = {}) {
17480
+ if (record.staleDispatchUnacknowledged === true) return "preserve_unacknowledged";
17481
+ if (record.terminal === true) return opts.includeTerminal ? "prunable_terminal" : "preserve_active";
17482
+ if (record.staleReason && PRUNABLE_ORPHAN_STALE_REASONS.has(record.staleReason)) return "prunable_orphan";
17483
+ return "preserve_active";
17484
+ }
17040
17485
  function buildCompactStaleDirectWorkSummary(staleDirectWork, opts = {}) {
17041
17486
  const sampleLimit = Math.max(0, Math.min(10, Math.floor(opts.sampleLimit ?? 3)));
17042
17487
  const reasonCounts = {};
@@ -20260,9 +20705,9 @@ function computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatur
20260
20705
  for (const file of files.slice().sort()) {
20261
20706
  const filePath = path12.join(dir, file);
20262
20707
  const signature = fileSignatures.get(file) || `${file}:missing`;
20263
- const cached = savedHistoryFileSummaryCache.get(filePath);
20708
+ const cached2 = savedHistoryFileSummaryCache.get(filePath);
20264
20709
  const persisted = persistedEntries.get(file);
20265
- const reusableEntry = cached?.signature === signature ? cached : persisted?.signature === signature ? persisted : null;
20710
+ const reusableEntry = cached2?.signature === signature ? cached2 : persisted?.signature === signature ? persisted : null;
20266
20711
  const fileSummary = reusableEntry?.summary || computeSavedHistoryFileSummary(dir, file);
20267
20712
  const nextEntry = reusableEntry || {
20268
20713
  signature,
@@ -20714,23 +21159,23 @@ function listSavedHistorySessions(agentType, options = {}, historyBehavior) {
20714
21159
  savedHistorySessionCache.delete(sanitized);
20715
21160
  return { sessions: [], hasMore: false };
20716
21161
  }
20717
- const cached = savedHistorySessionCache.get(sanitized);
21162
+ const cached2 = savedHistorySessionCache.get(sanitized);
20718
21163
  const offset = Math.max(0, options.offset || 0);
20719
21164
  const limit = Math.max(1, options.limit || 30);
20720
21165
  const indexSignature = buildSavedHistoryIndexFileSignature(dir);
20721
21166
  let cacheWasInvalidated = false;
20722
- if (cached) {
20723
- const cacheLooksPersisted = cached.signature.startsWith("index:");
20724
- const cacheStillValid = cacheLooksPersisted ? cached.signature === indexSignature : (() => {
21167
+ if (cached2) {
21168
+ const cacheLooksPersisted = cached2.signature.startsWith("index:");
21169
+ const cacheStillValid = cacheLooksPersisted ? cached2.signature === indexSignature : (() => {
20725
21170
  const files2 = listHistoryFiles(dir);
20726
21171
  const fileSignatures2 = buildSavedHistoryFileSignatureMap(dir, files2);
20727
- return cached.signature === buildSavedHistoryCacheSignature(files2, fileSignatures2);
21172
+ return cached2.signature === buildSavedHistoryCacheSignature(files2, fileSignatures2);
20728
21173
  })();
20729
21174
  if (cacheStillValid) {
20730
- const sliced2 = cached.summaries.slice(offset, offset + limit);
21175
+ const sliced2 = cached2.summaries.slice(offset, offset + limit);
20731
21176
  return {
20732
21177
  sessions: sliced2,
20733
- hasMore: cached.summaries.length > offset + limit
21178
+ hasMore: cached2.summaries.length > offset + limit
20734
21179
  };
20735
21180
  }
20736
21181
  cacheWasInvalidated = true;
@@ -37319,8 +37764,8 @@ var ProviderLoader = class _ProviderLoader {
37319
37764
  return null;
37320
37765
  }
37321
37766
  registerProviderScriptRootSafely(path30.dirname(path30.dirname(providerDir)));
37322
- const cached = this.scriptsCache.get(dir);
37323
- if (cached) return cached;
37767
+ const cached2 = this.scriptsCache.get(dir);
37768
+ if (cached2) return cached2;
37324
37769
  const scriptsJs = path30.join(dir, "scripts.js");
37325
37770
  if (fs20.existsSync(scriptsJs)) {
37326
37771
  try {
@@ -39318,6 +39763,9 @@ function buildStatusSnapshot(options) {
39318
39763
  };
39319
39764
  }
39320
39765
 
39766
+ // src/commands/router.ts
39767
+ init_build_info();
39768
+
39321
39769
  // src/commands/upgrade-helper.ts
39322
39770
  var import_child_process7 = require("child_process");
39323
39771
  var import_child_process8 = require("child_process");
@@ -40103,13 +40551,13 @@ function sanitizeInlineMesh(inlineMesh) {
40103
40551
  nodes
40104
40552
  };
40105
40553
  }
40106
- function reconcileInlineMeshCache(cached, incoming) {
40107
- if (!cached || typeof cached !== "object" || Array.isArray(cached)) return incoming;
40108
- if (!incoming || typeof incoming !== "object" || Array.isArray(incoming)) return cached;
40109
- const cachedNodes = Array.isArray(cached.nodes) ? cached.nodes : [];
40554
+ function reconcileInlineMeshCache(cached2, incoming) {
40555
+ if (!cached2 || typeof cached2 !== "object" || Array.isArray(cached2)) return incoming;
40556
+ if (!incoming || typeof incoming !== "object" || Array.isArray(incoming)) return cached2;
40557
+ const cachedNodes = Array.isArray(cached2.nodes) ? cached2.nodes : [];
40110
40558
  const incomingNodes = Array.isArray(incoming.nodes) ? incoming.nodes : [];
40111
- if (!cachedNodes.length || !incomingNodes.length) return { ...cached, ...incoming };
40112
- const cachedUpdatedAt = Date.parse(readStringValue(cached.updatedAt, cached.updated_at) || "");
40559
+ if (!cachedNodes.length || !incomingNodes.length) return { ...cached2, ...incoming };
40560
+ const cachedUpdatedAt = Date.parse(readStringValue(cached2.updatedAt, cached2.updated_at) || "");
40113
40561
  const incomingUpdatedAt = Date.parse(readStringValue(incoming.updatedAt, incoming.updated_at) || "");
40114
40562
  const preserveCachedMembership = Number.isFinite(cachedUpdatedAt) && (!Number.isFinite(incomingUpdatedAt) || cachedUpdatedAt > incomingUpdatedAt);
40115
40563
  const cachedById = /* @__PURE__ */ new Map();
@@ -40138,7 +40586,7 @@ function reconcileInlineMeshCache(cached, incoming) {
40138
40586
  }
40139
40587
  }
40140
40588
  return {
40141
- ...cached,
40589
+ ...cached2,
40142
40590
  ...incoming,
40143
40591
  nodes
40144
40592
  };
@@ -41822,13 +42270,13 @@ var DaemonCommandRouter = class {
41822
42270
  };
41823
42271
  }
41824
42272
  getCachedAggregateMeshStatus(meshId, mesh, options) {
41825
- const cached = this.aggregateMeshStatusCache.get(meshId);
41826
- if (!cached?.snapshot || cached.snapshot.success !== true || !Array.isArray(cached.snapshot.nodes)) return null;
41827
- if (cached.queueRevision !== getMeshQueueRevision(meshId)) return null;
41828
- let snapshot = this.cloneJsonValue(cached.snapshot);
42273
+ const cached2 = this.aggregateMeshStatusCache.get(meshId);
42274
+ if (!cached2?.snapshot || cached2.snapshot.success !== true || !Array.isArray(cached2.snapshot.nodes)) return null;
42275
+ if (cached2.queueRevision !== getMeshQueueRevision(meshId)) return null;
42276
+ let snapshot = this.cloneJsonValue(cached2.snapshot);
41829
42277
  snapshot = this.hydrateCachedAggregateMeshStatusFromInline(snapshot, mesh, options);
41830
42278
  if (shouldRefreshStalePendingAggregate(snapshot, options)) return null;
41831
- const ageMs = Math.max(0, Date.now() - cached.builtAt);
42279
+ const ageMs = Math.max(0, Date.now() - cached2.builtAt);
41832
42280
  const sourceOfTruth = snapshot.sourceOfTruth && typeof snapshot.sourceOfTruth === "object" ? snapshot.sourceOfTruth : {};
41833
42281
  snapshot.sourceOfTruth = {
41834
42282
  ...sourceOfTruth,
@@ -41839,7 +42287,7 @@ var DaemonCommandRouter = class {
41839
42287
  source: "memory",
41840
42288
  refreshReason: "memory_cache_hit",
41841
42289
  ageMs,
41842
- cachedAt: new Date(cached.builtAt).toISOString(),
42290
+ cachedAt: new Date(cached2.builtAt).toISOString(),
41843
42291
  returnedAt: (/* @__PURE__ */ new Date()).toISOString()
41844
42292
  }
41845
42293
  };
@@ -41883,9 +42331,9 @@ var DaemonCommandRouter = class {
41883
42331
  warmInlineMeshCache(meshId, inlineMesh) {
41884
42332
  if (!inlineMesh || typeof inlineMesh !== "object") return void 0;
41885
42333
  const sanitizedInlineMesh = sanitizeInlineMesh(inlineMesh);
41886
- const cached = this.inlineMeshCache.get(meshId);
41887
- if (cached) {
41888
- const merged = reconcileInlineMeshCache(cached, sanitizedInlineMesh);
42334
+ const cached2 = this.inlineMeshCache.get(meshId);
42335
+ if (cached2) {
42336
+ const merged = reconcileInlineMeshCache(cached2, sanitizedInlineMesh);
41889
42337
  this.inlineMeshCache.set(meshId, merged);
41890
42338
  return merged;
41891
42339
  }
@@ -41895,14 +42343,14 @@ var DaemonCommandRouter = class {
41895
42343
  async getMeshForCommand(meshId, inlineMesh, options) {
41896
42344
  const preferInline = options?.preferInline === true;
41897
42345
  if (preferInline) {
41898
- const cached2 = this.getCachedInlineMesh(meshId);
41899
- if (cached2) {
42346
+ const cached3 = this.getCachedInlineMesh(meshId);
42347
+ if (cached3) {
41900
42348
  if (inlineMeshCarriesTransientNodeTruth(inlineMesh)) {
41901
- const merged = reconcileInlineMeshCache(cached2, inlineMesh);
42349
+ const merged = reconcileInlineMeshCache(cached3, inlineMesh);
41902
42350
  this.inlineMeshCache.set(meshId, sanitizeInlineMesh(merged));
41903
42351
  return { mesh: merged, inline: true, source: "inline_cache" };
41904
42352
  }
41905
- return { mesh: cached2, inline: true, source: "inline_cache" };
42353
+ return { mesh: cached3, inline: true, source: "inline_cache" };
41906
42354
  }
41907
42355
  if (inlineMeshCarriesTransientNodeTruth(inlineMesh)) {
41908
42356
  this.warmInlineMeshCache(meshId, inlineMesh);
@@ -41915,8 +42363,8 @@ var DaemonCommandRouter = class {
41915
42363
  if (mesh) return { mesh, inline: false, source: "local_config" };
41916
42364
  } catch {
41917
42365
  }
41918
- const cached = this.getCachedInlineMesh(meshId);
41919
- if (cached) return { mesh: cached, inline: true, source: "inline_cache" };
42366
+ const cached2 = this.getCachedInlineMesh(meshId);
42367
+ if (cached2) return { mesh: cached2, inline: true, source: "inline_cache" };
41920
42368
  const warmedInline = this.warmInlineMeshCache(meshId, inlineMesh);
41921
42369
  return warmedInline ? { mesh: warmedInline, inline: true, source: "inline_bootstrap" } : null;
41922
42370
  }
@@ -42223,6 +42671,8 @@ var DaemonCommandRouter = class {
42223
42671
  const skippedSessionIds = [];
42224
42672
  const skippedLiveSessionIds = [];
42225
42673
  const skippedCoordinatorSessionIds = [];
42674
+ const skippedLiveSessionReasons = [];
42675
+ const actedLiveDelegateSessionIds = [];
42226
42676
  const deleteUnsupportedSessionIds = [];
42227
42677
  const recordsRemainSessionIds = [];
42228
42678
  const errors = [];
@@ -42256,16 +42706,31 @@ var DaemonCommandRouter = class {
42256
42706
  const surfaceKind = getSessionHostSurfaceKind(record);
42257
42707
  const liveRuntime = surfaceKind === "live_runtime";
42258
42708
  const coordinatorSession = readStringValue(record?.meta?.meshCoordinatorFor) === args.meshId;
42709
+ const recordNodeId = readStringValue(record?.meta?.meshNodeId);
42710
+ const recordMeshNodeFor = readStringValue(record?.meta?.meshNodeFor);
42711
+ const delegateBoundToThisNode = !!recordNodeId && recordNodeId === args.nodeId && (!recordMeshNodeFor || recordMeshNodeFor === args.meshId);
42259
42712
  if (!hasExplicitSessionIds && coordinatorSession) {
42260
42713
  skippedSessionIds.push(sessionId);
42261
42714
  skippedCoordinatorSessionIds.push(sessionId);
42262
42715
  continue;
42263
42716
  }
42264
- if (!hasExplicitSessionIds && liveRuntime) {
42717
+ if (!hasExplicitSessionIds && liveRuntime && !delegateBoundToThisNode) {
42265
42718
  skippedSessionIds.push(sessionId);
42266
42719
  skippedLiveSessionIds.push(sessionId);
42720
+ const matchedByWorkspaceOnly = !recordNodeId;
42721
+ const reason = recordNodeId && recordNodeId !== args.nodeId ? `live_delegate_bound_to_other_node:${recordNodeId}` : matchedByWorkspaceOnly ? "live_session_matched_by_workspace_only_no_node_binding" : "live_session_not_bound_to_this_node";
42722
+ skippedLiveSessionReasons.push({ sessionId, reason });
42267
42723
  continue;
42268
42724
  }
42725
+ if (!hasExplicitSessionIds && liveRuntime && delegateBoundToThisNode && args.mode === "delete_stopped") {
42726
+ skippedSessionIds.push(sessionId);
42727
+ skippedLiveSessionIds.push(sessionId);
42728
+ skippedLiveSessionReasons.push({ sessionId, reason: "live_delegate_preserved_by_delete_stopped_mode_use_stop_or_stop_and_delete" });
42729
+ continue;
42730
+ }
42731
+ if (!hasExplicitSessionIds && liveRuntime && delegateBoundToThisNode) {
42732
+ actedLiveDelegateSessionIds.push(sessionId);
42733
+ }
42269
42734
  try {
42270
42735
  if (args.mode === "stop") {
42271
42736
  if (!completed) {
@@ -42327,6 +42792,8 @@ var DaemonCommandRouter = class {
42327
42792
  skippedSessionIds,
42328
42793
  skippedLiveSessionIds,
42329
42794
  skippedCoordinatorSessionIds,
42795
+ ...actedLiveDelegateSessionIds.length ? { actedLiveDelegateSessionIds } : {},
42796
+ ...skippedLiveSessionReasons.length ? { skippedLiveSessionReasons } : {},
42330
42797
  ...deleteUnsupported ? {
42331
42798
  deleteUnsupported: true,
42332
42799
  effectiveCleanup: args.mode === "stop_and_delete" ? "stopped_only_records_remain" : "delete_unsupported_records_remain",
@@ -43093,10 +43560,31 @@ ${hintLines.join("\n")}` : "",
43093
43560
  };
43094
43561
  }
43095
43562
  const cleanupStarted = Date.now();
43563
+ const refineSessionCleanupMode = this.normalizeMeshSessionCleanupMode(
43564
+ mesh?.policy?.sessionCleanupOnNodeRemove
43565
+ );
43566
+ let refineSessionIds;
43567
+ if (refineSessionCleanupMode !== "preserve" && this.deps.sessionHostControl) {
43568
+ try {
43569
+ const liveSessions = await this.deps.sessionHostControl.listSessions();
43570
+ const workspace = typeof node.workspace === "string" ? node.workspace : "";
43571
+ refineSessionIds = liveSessions.filter((record) => {
43572
+ const sid = typeof record?.sessionId === "string" ? record.sessionId : "";
43573
+ if (!sid) return false;
43574
+ if (readStringValue(record?.meta?.meshCoordinatorFor) === meshId) return false;
43575
+ const boundToNode = readStringValue(record?.meta?.meshNodeId) === nodeId;
43576
+ const matchedByWorkspace = !!workspace && record?.workspace === workspace;
43577
+ return boundToNode || matchedByWorkspace;
43578
+ }).map((record) => String(record.sessionId));
43579
+ } catch {
43580
+ refineSessionIds = void 0;
43581
+ }
43582
+ }
43096
43583
  const removeResult = await this.execute("remove_mesh_node", {
43097
43584
  meshId,
43098
43585
  nodeId,
43099
- sessionCleanupMode: "preserve",
43586
+ sessionCleanupMode: refineSessionCleanupMode,
43587
+ ...refineSessionIds && refineSessionIds.length > 0 ? { sessionIds: refineSessionIds } : {},
43100
43588
  inlineMesh: args?.inlineMesh
43101
43589
  });
43102
43590
  recordMeshRefineStage(refineStages, "cleanup", removeResult?.success === false ? "failed" : "passed", cleanupStarted, {
@@ -43254,8 +43742,8 @@ ${hintLines.join("\n")}` : "",
43254
43742
  const repoRootBaseRef = /* @__PURE__ */ new Map();
43255
43743
  const submodulePathsByRepoRoot = /* @__PURE__ */ new Map();
43256
43744
  const resolveBaseRef = async (repoRoot) => {
43257
- const cached = repoRootBaseRef.get(repoRoot);
43258
- if (cached) return cached;
43745
+ const cached2 = repoRootBaseRef.get(repoRoot);
43746
+ if (cached2) return cached2;
43259
43747
  let baseBranch = "main";
43260
43748
  try {
43261
43749
  const { stdout } = await execFileAsync4("git", ["branch", "--show-current"], { cwd: repoRoot, encoding: "utf8" });
@@ -44153,7 +44641,7 @@ ${hintLines.join("\n")}` : "",
44153
44641
  version: this.deps.statusVersion || "unknown",
44154
44642
  profile: "metadata"
44155
44643
  });
44156
- return { success: true, status: snapshot };
44644
+ return { success: true, status: snapshot, daemonBuild: getDaemonBuildInfo() };
44157
44645
  }
44158
44646
  case "get_machine_runtime_stats": {
44159
44647
  return {
@@ -45123,6 +45611,7 @@ ${hintLines.join("\n")}` : "",
45123
45611
  let workspace = typeof args?.workspace === "string" ? args.workspace.trim() : "";
45124
45612
  let submoduleIgnorePaths = Array.isArray(args?.submoduleIgnorePaths) ? args.submoduleIgnorePaths.filter((value) => typeof value === "string") : void 0;
45125
45613
  let nodeDaemonId;
45614
+ let allowAutoPublishSubmoduleMainCommits = false;
45126
45615
  if (meshId && nodeId) {
45127
45616
  const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
45128
45617
  const mesh = meshRecord?.mesh;
@@ -45133,6 +45622,7 @@ ${hintLines.join("\n")}` : "",
45133
45622
  if (!submoduleIgnorePaths && Array.isArray(node?.policy?.submoduleIgnorePaths)) {
45134
45623
  submoduleIgnorePaths = node.policy.submoduleIgnorePaths.filter((value) => typeof value === "string");
45135
45624
  }
45625
+ allowAutoPublishSubmoduleMainCommits = mesh?.policy?.allowAutoPublishSubmoduleMainCommits === true;
45136
45626
  nodeDaemonId = typeof node?.daemonId === "string" ? node.daemonId.trim() : void 0;
45137
45627
  }
45138
45628
  const selfDaemonId = this.deps.statusInstanceId;
@@ -45153,7 +45643,10 @@ ${hintLines.join("\n")}` : "",
45153
45643
  execute: args?.execute === true,
45154
45644
  dryRun: args?.dryRun === true,
45155
45645
  updateSubmodules: args?.updateSubmodules === true,
45156
- submoduleIgnorePaths
45646
+ submoduleIgnorePaths,
45647
+ mode: args?.mode === "push" ? "push" : "merge",
45648
+ pushSubmodules: args?.pushSubmodules === true,
45649
+ allowAutoPublishSubmoduleMainCommits
45157
45650
  });
45158
45651
  return result;
45159
45652
  }
@@ -45161,6 +45654,23 @@ ${hintLines.join("\n")}` : "",
45161
45654
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
45162
45655
  const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
45163
45656
  if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
45657
+ const isDryRun = args?.dryRun !== false && args?.execute !== true;
45658
+ if (isDryRun) {
45659
+ const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
45660
+ const mesh = meshRecord?.mesh;
45661
+ const node = mesh?.nodes?.find((n) => n.id === nodeId || n.nodeId === nodeId);
45662
+ if (!node?.workspace) return { success: false, error: `Node '${nodeId}' workspace not found` };
45663
+ return {
45664
+ success: true,
45665
+ dryRun: true,
45666
+ nodeId,
45667
+ workspace: node.workspace,
45668
+ validationPlan: buildMeshRefineValidationPlan(mesh, node.workspace),
45669
+ mergeWillRun: false,
45670
+ cleanupWillRun: false,
45671
+ hint: "Dry-run only \u2014 no merge/push/cleanup performed. Re-invoke with execute:true to converge this node."
45672
+ };
45673
+ }
45164
45674
  return this.startMeshRefineJob(meshId, nodeId, args);
45165
45675
  }
45166
45676
  case "batch_refine_mesh_nodes": {
@@ -45182,9 +45692,17 @@ ${hintLines.join("\n")}` : "",
45182
45692
  const sessionCleanupMode = this.normalizeMeshSessionCleanupMode(
45183
45693
  args?.sessionCleanupMode ?? args?.session_cleanup_mode ?? mesh?.policy?.sessionCleanupOnNodeRemove
45184
45694
  );
45695
+ const explicitSessionIds = Array.isArray(args?.sessionIds) ? args.sessionIds.filter((v) => typeof v === "string" && v.trim().length > 0).map((v) => v.trim()) : void 0;
45185
45696
  let sessionCleanup;
45186
45697
  if (node && sessionCleanupMode !== "preserve") {
45187
- sessionCleanup = await this.cleanupMeshSessions({ meshId, nodeId, node, mode: sessionCleanupMode, source: "mesh_remove_node" });
45698
+ sessionCleanup = await this.cleanupMeshSessions({
45699
+ meshId,
45700
+ nodeId,
45701
+ node,
45702
+ mode: sessionCleanupMode,
45703
+ ...explicitSessionIds && explicitSessionIds.length > 0 ? { sessionIds: explicitSessionIds } : {},
45704
+ source: "mesh_remove_node"
45705
+ });
45188
45706
  if (sessionCleanup.success === false) return { success: false, removed: false, sessionCleanup };
45189
45707
  }
45190
45708
  let worktreeCleanup;
@@ -46853,6 +47371,7 @@ var DaemonStatusReporter = class {
46853
47371
  };
46854
47372
 
46855
47373
  // src/index.ts
47374
+ init_build_info();
46856
47375
  init_logger();
46857
47376
  init_debug_config();
46858
47377
 
@@ -55124,6 +55643,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
55124
55643
  MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS,
55125
55644
  NodePtyTransportFactory,
55126
55645
  P2pRelayFailureError,
55646
+ PRUNABLE_ORPHAN_STALE_REASONS,
55127
55647
  ProviderCliAdapter,
55128
55648
  ProviderInstanceManager,
55129
55649
  ProviderLoader,
@@ -55177,6 +55697,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
55177
55697
  classifyChatMessageVisibility,
55178
55698
  classifyHotChatSessionsForSubscriptionFlush,
55179
55699
  classifyP2pRelayFailure,
55700
+ classifyStaleDirectForPrune,
55180
55701
  cleanupTerminalDirectDispatches,
55181
55702
  clearDebugTrace,
55182
55703
  clearPendingMeshCoordinatorEvents,
@@ -55196,6 +55717,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
55196
55717
  createNativeHistoryDispatcher,
55197
55718
  createSessionDelivery,
55198
55719
  createWorktree,
55720
+ deleteDirectDispatchesByTaskId,
55199
55721
  deleteMesh,
55200
55722
  deriveMeshReviewInboxItems,
55201
55723
  describeTaskDependencyState,
@@ -55224,6 +55746,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
55224
55746
  getAvailableIdeIds,
55225
55747
  getCoordinatorForSession,
55226
55748
  getCurrentDaemonLogPath,
55749
+ getDaemonBuildInfo,
55227
55750
  getDaemonDataDir,
55228
55751
  getDaemonLogDir,
55229
55752
  getDebugRuntimeConfig,