@adhdev/daemon-standalone 0.9.82-rc.172 → 0.9.82-rc.173

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
@@ -35008,6 +35008,261 @@ var require_dist3 = __commonJS({
35008
35008
  };
35009
35009
  }
35010
35010
  });
35011
+ async function getGitRepoStatus(workspace, options = {}) {
35012
+ const lastCheckedAt = Date.now();
35013
+ const includeSubmodules = options.includeSubmodules !== false;
35014
+ try {
35015
+ const repo = await resolveGitRepository(workspace, options);
35016
+ let parsed = await readPorcelainStatus(repo, options);
35017
+ let upstreamProbe = getInitialUpstreamProbe(parsed);
35018
+ if (options.refreshUpstream) {
35019
+ upstreamProbe = await refreshTrackedUpstream(repo, parsed, options);
35020
+ if (upstreamProbe.upstreamStatus === "fresh") {
35021
+ parsed = await readPorcelainStatus(repo, options);
35022
+ }
35023
+ }
35024
+ const head = await readHead(repo, options);
35025
+ const stashCount = await readStashCount(repo, options);
35026
+ let submodules;
35027
+ if (includeSubmodules) {
35028
+ submodules = await getSubmoduleStatuses(repo, options);
35029
+ }
35030
+ return {
35031
+ workspace: repo.workspace,
35032
+ repoRoot: repo.repoRoot,
35033
+ isGitRepo: true,
35034
+ branch: parsed.branch,
35035
+ headCommit: head.commit,
35036
+ headMessage: head.message,
35037
+ upstream: parsed.upstream,
35038
+ upstreamStatus: parsed.upstream ? upstreamProbe.upstreamStatus : "no_upstream",
35039
+ upstreamFetchedAt: upstreamProbe.upstreamFetchedAt,
35040
+ upstreamFetchError: upstreamProbe.upstreamFetchError,
35041
+ ahead: parsed.ahead,
35042
+ behind: parsed.behind,
35043
+ staged: parsed.staged,
35044
+ modified: parsed.modified,
35045
+ untracked: parsed.untracked,
35046
+ deleted: parsed.deleted,
35047
+ renamed: parsed.renamed,
35048
+ hasConflicts: parsed.conflictFiles.length > 0,
35049
+ conflictFiles: parsed.conflictFiles,
35050
+ stashCount,
35051
+ lastCheckedAt,
35052
+ submodules
35053
+ };
35054
+ } catch (error48) {
35055
+ if (error48 instanceof GitCommandError) {
35056
+ return emptyStatus(workspace, lastCheckedAt, error48);
35057
+ }
35058
+ return emptyStatus(
35059
+ workspace,
35060
+ lastCheckedAt,
35061
+ new GitCommandError("git_command_failed", "Failed to read Git status", { cause: error48 })
35062
+ );
35063
+ }
35064
+ }
35065
+ async function readPorcelainStatus(repo, options) {
35066
+ const statusOutput = await runGit(repo, ["status", "--porcelain=v2", "--branch"], options);
35067
+ return parsePorcelainV2Status(statusOutput.stdout);
35068
+ }
35069
+ function getInitialUpstreamProbe(parsed) {
35070
+ return {
35071
+ upstreamStatus: parsed.upstream ? "unchecked" : "no_upstream"
35072
+ };
35073
+ }
35074
+ async function refreshTrackedUpstream(repo, parsed, options) {
35075
+ if (!parsed.upstream || !parsed.branch) {
35076
+ return { upstreamStatus: "no_upstream" };
35077
+ }
35078
+ const remoteName = await readBranchRemote(repo, parsed.branch, options) ?? inferRemoteName(parsed.upstream);
35079
+ if (!remoteName) {
35080
+ return {
35081
+ upstreamStatus: "stale",
35082
+ upstreamFetchError: `Unable to resolve remote for upstream '${parsed.upstream}'`
35083
+ };
35084
+ }
35085
+ try {
35086
+ await runGit(repo, ["fetch", "--quiet", "--prune", "--no-tags", remoteName], options);
35087
+ return {
35088
+ upstreamStatus: "fresh",
35089
+ upstreamFetchedAt: Date.now()
35090
+ };
35091
+ } catch (error48) {
35092
+ return {
35093
+ upstreamStatus: "stale",
35094
+ upstreamFetchError: formatGitError(error48)
35095
+ };
35096
+ }
35097
+ }
35098
+ async function readBranchRemote(repo, branch, options) {
35099
+ try {
35100
+ const result = await runGit(repo, ["config", "--get", `branch.${branch}.remote`], options);
35101
+ return result.stdout.trim() || null;
35102
+ } catch {
35103
+ return null;
35104
+ }
35105
+ }
35106
+ function inferRemoteName(upstream) {
35107
+ const [remoteName] = upstream.split("/");
35108
+ return remoteName?.trim() || null;
35109
+ }
35110
+ function formatGitError(error48) {
35111
+ if (error48 instanceof GitCommandError) {
35112
+ return error48.stderr || error48.message;
35113
+ }
35114
+ if (error48 instanceof Error) {
35115
+ return error48.message;
35116
+ }
35117
+ return String(error48);
35118
+ }
35119
+ function parsePorcelainV2Status(output) {
35120
+ const parsed = {
35121
+ branch: null,
35122
+ upstream: null,
35123
+ ahead: 0,
35124
+ behind: 0,
35125
+ staged: 0,
35126
+ modified: 0,
35127
+ untracked: 0,
35128
+ deleted: 0,
35129
+ renamed: 0,
35130
+ conflictFiles: []
35131
+ };
35132
+ for (const line of output.split("\n")) {
35133
+ if (!line) continue;
35134
+ if (line.startsWith("# branch.head ")) {
35135
+ const branch = line.slice("# branch.head ".length).trim();
35136
+ parsed.branch = branch && branch !== "(detached)" ? branch : null;
35137
+ continue;
35138
+ }
35139
+ if (line.startsWith("# branch.upstream ")) {
35140
+ parsed.upstream = line.slice("# branch.upstream ".length).trim() || null;
35141
+ continue;
35142
+ }
35143
+ if (line.startsWith("# branch.ab ")) {
35144
+ const match = line.match(/\+(-?\d+)\s+-(-?\d+)/);
35145
+ if (match) {
35146
+ parsed.ahead = Number.parseInt(match[1] ?? "0", 10) || 0;
35147
+ parsed.behind = Number.parseInt(match[2] ?? "0", 10) || 0;
35148
+ }
35149
+ continue;
35150
+ }
35151
+ if (line.startsWith("? ")) {
35152
+ parsed.untracked += 1;
35153
+ continue;
35154
+ }
35155
+ if (line.startsWith("u ")) {
35156
+ const fields = line.split(" ");
35157
+ const filePath = fields.slice(10).join(" ");
35158
+ if (filePath) parsed.conflictFiles.push(filePath);
35159
+ continue;
35160
+ }
35161
+ if (line.startsWith("1 ") || line.startsWith("2 ")) {
35162
+ const fields = line.split(" ");
35163
+ const xy = fields[1] ?? "..";
35164
+ const indexStatus = xy[0] ?? ".";
35165
+ const worktreeStatus = xy[1] ?? ".";
35166
+ if (isStagedStatus(indexStatus)) parsed.staged += 1;
35167
+ if (worktreeStatus === "M" || worktreeStatus === "T") parsed.modified += 1;
35168
+ if (indexStatus === "D" || worktreeStatus === "D") parsed.deleted += 1;
35169
+ if (indexStatus === "R" || worktreeStatus === "R") parsed.renamed += 1;
35170
+ if (xy.includes("U")) {
35171
+ const filePath = fields.slice(line.startsWith("2 ") ? 9 : 8).join(" ").split(" ")[0] ?? "";
35172
+ if (filePath) parsed.conflictFiles.push(filePath);
35173
+ }
35174
+ }
35175
+ }
35176
+ parsed.conflictFiles = Array.from(new Set(parsed.conflictFiles));
35177
+ return parsed;
35178
+ }
35179
+ async function readHead(repo, options) {
35180
+ try {
35181
+ const result = await runGit(repo, ["log", "-1", "--pretty=%h%x00%s"], options);
35182
+ const text = result.stdout.trimEnd();
35183
+ if (!text) return { commit: null, message: null };
35184
+ const [commit, ...messageParts] = text.split("\0");
35185
+ return {
35186
+ commit: commit || null,
35187
+ message: messageParts.join("\0") || null
35188
+ };
35189
+ } catch {
35190
+ return { commit: null, message: null };
35191
+ }
35192
+ }
35193
+ async function readStashCount(repo, options) {
35194
+ try {
35195
+ const result = await runGit(repo, ["stash", "list", "--format=%gd"], options);
35196
+ return result.stdout.split("\n").filter((line) => line.trim().length > 0).length;
35197
+ } catch {
35198
+ return 0;
35199
+ }
35200
+ }
35201
+ function isStagedStatus(status) {
35202
+ return status !== "." && status !== "?" && status !== "U";
35203
+ }
35204
+ function emptyStatus(workspace, lastCheckedAt, error48) {
35205
+ return {
35206
+ workspace,
35207
+ repoRoot: null,
35208
+ isGitRepo: false,
35209
+ branch: null,
35210
+ headCommit: null,
35211
+ headMessage: null,
35212
+ upstream: null,
35213
+ upstreamStatus: "unavailable",
35214
+ ahead: 0,
35215
+ behind: 0,
35216
+ staged: 0,
35217
+ modified: 0,
35218
+ untracked: 0,
35219
+ deleted: 0,
35220
+ renamed: 0,
35221
+ hasConflicts: false,
35222
+ conflictFiles: [],
35223
+ stashCount: 0,
35224
+ lastCheckedAt,
35225
+ error: error48.stderr || error48.message,
35226
+ reason: error48.reason
35227
+ };
35228
+ }
35229
+ async function getSubmoduleStatuses(repo, options) {
35230
+ if (!repo.repoRoot) return [];
35231
+ try {
35232
+ const result = await runGit(repo, ["submodule", "status", "--recursive"], options);
35233
+ return parseSubmoduleStatusOutput(result.stdout, repo.repoRoot, options.submoduleIgnorePaths);
35234
+ } catch {
35235
+ return [];
35236
+ }
35237
+ }
35238
+ function parseSubmoduleStatusOutput(output, repoRoot, ignorePaths) {
35239
+ const submodules = [];
35240
+ const ignoreSet = new Set(ignorePaths || []);
35241
+ for (const line of output.split("\n")) {
35242
+ if (!line.trim()) continue;
35243
+ const match = line.match(/^([\-+\s])([0-9a-f]{40})\s+(\S+)(?:\s+\(([^)]+)\))?/);
35244
+ if (!match) continue;
35245
+ const prefix = match[1];
35246
+ const commit = match[2];
35247
+ const path40 = match[3];
35248
+ if (ignoreSet.has(path40)) continue;
35249
+ submodules.push({
35250
+ path: path40,
35251
+ commit,
35252
+ repoPath: repoRoot + "/" + path40,
35253
+ dirty: prefix === "+",
35254
+ outOfSync: prefix === "-",
35255
+ lastCheckedAt: Date.now()
35256
+ });
35257
+ }
35258
+ return submodules;
35259
+ }
35260
+ var init_git_status = __esm2({
35261
+ "src/git/git-status.ts"() {
35262
+ "use strict";
35263
+ init_git_executor();
35264
+ }
35265
+ });
35011
35266
  var git_worktree_exports = {};
35012
35267
  __export2(git_worktree_exports, {
35013
35268
  createWorktree: () => createWorktree,
@@ -35540,6 +35795,16 @@ ${error48.message || ""}`;
35540
35795
  return { meshes: [] };
35541
35796
  }
35542
35797
  }
35798
+ function normalizeCapabilityTags(value) {
35799
+ if (!Array.isArray(value)) return void 0;
35800
+ const seen = /* @__PURE__ */ new Set();
35801
+ const tags = value.map((tag) => typeof tag === "string" ? tag.trim() : "").filter(Boolean).filter((tag) => {
35802
+ if (seen.has(tag)) return false;
35803
+ seen.add(tag);
35804
+ return true;
35805
+ });
35806
+ return tags.length ? tags : void 0;
35807
+ }
35543
35808
  function saveMeshConfig(config2) {
35544
35809
  const path40 = getMeshConfigPath();
35545
35810
  (0, import_fs2.writeFileSync)(path40, JSON.stringify(config2, null, 2), { encoding: "utf-8", mode: 384 });
@@ -35825,6 +36090,7 @@ ${error48.message || ""}`;
35825
36090
  repoRoot: opts.repoRoot,
35826
36091
  daemonId: opts.daemonId,
35827
36092
  machineId: opts.machineId,
36093
+ capabilities: normalizeCapabilityTags(opts.capabilities),
35828
36094
  userOverrides: opts.userOverrides || {},
35829
36095
  policy: opts.policy || {},
35830
36096
  isLocalWorktree: opts.isLocalWorktree,
@@ -37349,6 +37615,367 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
37349
37615
  LEDGER_CACHE_TTL_MS = 100;
37350
37616
  }
37351
37617
  });
37618
+ async function fastForwardMeshNode(args) {
37619
+ const workspace = typeof args.workspace === "string" ? args.workspace.trim() : "";
37620
+ const nodeId = normalizeOptionalString(args.nodeId);
37621
+ const meshId = normalizeOptionalString(args.meshId);
37622
+ const requestedBranch = normalizeOptionalString(args.branch);
37623
+ const trigger = normalizeOptionalString(args.trigger) || "manual";
37624
+ const updateSubmodules = args.updateSubmodules === true;
37625
+ const dryRun = args.dryRun === true || args.execute !== true;
37626
+ const plannedSteps = buildPlannedSteps(updateSubmodules);
37627
+ const base = {
37628
+ ...nodeId ? { nodeId } : {},
37629
+ ...meshId ? { meshId } : {},
37630
+ workspace,
37631
+ dryRun,
37632
+ updateSubmodules,
37633
+ plannedSteps,
37634
+ trigger
37635
+ };
37636
+ if (!workspace) {
37637
+ return block(base, "invalid_workspace", ["workspace_required"]);
37638
+ }
37639
+ const current = await getGitRepoStatus(workspace, {
37640
+ ...STATUS_OPTIONS,
37641
+ submoduleIgnorePaths: args.submoduleIgnorePaths,
37642
+ timeoutMs: args.timeoutMs ?? STATUS_OPTIONS.timeoutMs
37643
+ });
37644
+ const earlyBlockers = collectPreflightBlockers(current, requestedBranch);
37645
+ if (earlyBlockers.length > 0) {
37646
+ const result2 = {
37647
+ ...block(base, chooseBlockCode(current, earlyBlockers), earlyBlockers),
37648
+ current,
37649
+ finalBranchConvergenceState: buildConvergenceState(current, codeToConvergenceStatus(chooseBlockCode(current, earlyBlockers)))
37650
+ };
37651
+ await appendFastForwardLedger(result2, "blocked");
37652
+ return result2;
37653
+ }
37654
+ if (current.behind === 0) {
37655
+ const result2 = {
37656
+ ...base,
37657
+ success: true,
37658
+ code: "already_up_to_date",
37659
+ allowed: true,
37660
+ willRun: false,
37661
+ executed: false,
37662
+ blockingReasons: [],
37663
+ current,
37664
+ preStatus: current,
37665
+ postStatus: current,
37666
+ finalBranchConvergenceState: buildConvergenceState(current, "up_to_date")
37667
+ };
37668
+ await appendFastForwardLedger(result2, "noop");
37669
+ return result2;
37670
+ }
37671
+ const ancestorCheck = await verifyHeadIsAncestorOfUpstream(workspace, current.upstream || "", args.timeoutMs);
37672
+ if (!ancestorCheck.ok) {
37673
+ const result2 = {
37674
+ ...block(base, "non_fast_forward", ["head_is_not_ancestor_of_upstream"]),
37675
+ current,
37676
+ preStatus: current,
37677
+ operationError: ancestorCheck.error,
37678
+ finalBranchConvergenceState: buildConvergenceState(current, "not_mergeable")
37679
+ };
37680
+ await appendFastForwardLedger(result2, "blocked");
37681
+ return result2;
37682
+ }
37683
+ if (dryRun) {
37684
+ const result2 = {
37685
+ ...base,
37686
+ success: true,
37687
+ code: "fast_forward_available",
37688
+ allowed: true,
37689
+ willRun: false,
37690
+ executed: false,
37691
+ blockingReasons: [],
37692
+ current,
37693
+ preStatus: current,
37694
+ finalBranchConvergenceState: buildConvergenceState(current, "fast_forward_available")
37695
+ };
37696
+ await appendFastForwardLedger(result2, "dry_run");
37697
+ return result2;
37698
+ }
37699
+ try {
37700
+ await runGit(workspace, ["merge", "--ff-only", current.upstream || ""], { timeoutMs: args.timeoutMs ?? 3e4 });
37701
+ } catch (error48) {
37702
+ const result2 = {
37703
+ ...block(base, "merge_ff_only_failed", ["merge_ff_only_failed"]),
37704
+ current,
37705
+ preStatus: current,
37706
+ operationError: formatGitError2(error48),
37707
+ finalBranchConvergenceState: buildConvergenceState(current, "not_mergeable")
37708
+ };
37709
+ await appendFastForwardLedger(result2, "failed");
37710
+ return result2;
37711
+ }
37712
+ let postStatus = await getGitRepoStatus(workspace, {
37713
+ ...STATUS_OPTIONS,
37714
+ submoduleIgnorePaths: args.submoduleIgnorePaths,
37715
+ timeoutMs: args.timeoutMs ?? STATUS_OPTIONS.timeoutMs
37716
+ });
37717
+ const submoduleIssues = collectSubmoduleBlockers(postStatus, "post");
37718
+ let submoduleFollowUpRequired = false;
37719
+ let operationError;
37720
+ if (submoduleIssues.length > 0) {
37721
+ if (updateSubmodules) {
37722
+ try {
37723
+ await runGit(workspace, ["submodule", "update", "--init", "--recursive"], { timeoutMs: args.timeoutMs ?? 6e4 });
37724
+ postStatus = await getGitRepoStatus(workspace, {
37725
+ ...STATUS_OPTIONS,
37726
+ submoduleIgnorePaths: args.submoduleIgnorePaths,
37727
+ timeoutMs: args.timeoutMs ?? STATUS_OPTIONS.timeoutMs
37728
+ });
37729
+ } catch (error48) {
37730
+ operationError = formatGitError2(error48);
37731
+ }
37732
+ } else {
37733
+ submoduleFollowUpRequired = true;
37734
+ }
37735
+ }
37736
+ const postBlockers = collectPostExecutionBlockers(postStatus);
37737
+ if (operationError) postBlockers.push("submodule_update_failed");
37738
+ if (submoduleFollowUpRequired) postBlockers.push("submodule_update_required");
37739
+ const success2 = postBlockers.length === 0 || submoduleFollowUpRequired;
37740
+ const code = postBlockers.length === 0 ? "fast_forward_applied" : submoduleFollowUpRequired ? "fast_forward_applied_submodule_update_required" : "post_verify_failed";
37741
+ const result = {
37742
+ ...base,
37743
+ success: success2,
37744
+ code,
37745
+ allowed: true,
37746
+ willRun: true,
37747
+ executed: true,
37748
+ blockingReasons: postBlockers,
37749
+ current,
37750
+ preStatus: current,
37751
+ postStatus,
37752
+ ...operationError ? { operationError } : {},
37753
+ finalBranchConvergenceState: buildConvergenceState(
37754
+ postStatus,
37755
+ postBlockers.length === 0 ? "fast_forwarded" : submoduleFollowUpRequired ? "follow_up_required" : "post_verify_failed"
37756
+ )
37757
+ };
37758
+ await appendFastForwardLedger(result, success2 ? "executed" : "failed");
37759
+ return result;
37760
+ }
37761
+ function buildPlannedSteps(updateSubmodules) {
37762
+ const steps = [
37763
+ {
37764
+ operation: "refresh_upstream",
37765
+ description: "Refresh the tracked upstream remote ref before trusting ahead/behind state.",
37766
+ safe: true,
37767
+ willMutateWorktree: false
37768
+ },
37769
+ {
37770
+ operation: "verify_clean_worktree",
37771
+ description: "Require clean staged/modified/untracked/deleted/renamed/conflict/stash/submodule state.",
37772
+ safe: true,
37773
+ willMutateWorktree: false
37774
+ },
37775
+ {
37776
+ operation: "verify_fast_forward",
37777
+ description: "Require ahead=0, behind>0, and HEAD to be an ancestor of the upstream ref.",
37778
+ safe: true,
37779
+ willMutateWorktree: false
37780
+ },
37781
+ {
37782
+ operation: "merge_ff_only",
37783
+ description: "Apply git merge --ff-only against the tracked upstream; no force, reset, rebase, push, or deploy.",
37784
+ safe: true,
37785
+ willMutateWorktree: true
37786
+ }
37787
+ ];
37788
+ if (updateSubmodules) {
37789
+ steps.push({
37790
+ operation: "submodule_update",
37791
+ description: "If the fast-forward changes gitlinks, run git submodule update --init --recursive and re-verify submodules.",
37792
+ safe: true,
37793
+ willMutateWorktree: true
37794
+ });
37795
+ }
37796
+ steps.push({
37797
+ operation: "verify_post_status",
37798
+ description: "Re-read daemon-owned git status and report final branch convergence state.",
37799
+ safe: true,
37800
+ willMutateWorktree: false
37801
+ });
37802
+ return steps;
37803
+ }
37804
+ function collectPreflightBlockers(status, requestedBranch) {
37805
+ const blockers = [];
37806
+ if (!status.isGitRepo) blockers.push("not_git_repo");
37807
+ if (!status.branch) blockers.push("detached_head_or_unknown_branch");
37808
+ if (requestedBranch && status.branch !== requestedBranch) blockers.push("branch_mismatch");
37809
+ if (!status.upstream) blockers.push("upstream_missing");
37810
+ if (status.upstreamStatus !== "fresh") blockers.push("upstream_not_fresh");
37811
+ if (status.hasConflicts) blockers.push("conflicts_present");
37812
+ if (status.staged > 0) blockers.push("staged_changes_present");
37813
+ if (status.modified > 0) blockers.push("modified_changes_present");
37814
+ if (status.untracked > 0) blockers.push("untracked_changes_present");
37815
+ if (status.deleted > 0) blockers.push("deleted_changes_present");
37816
+ if (status.renamed > 0) blockers.push("renamed_changes_present");
37817
+ if (status.stashCount > 0) blockers.push("stash_entries_present");
37818
+ blockers.push(...collectSubmoduleBlockers(status, "pre"));
37819
+ if (status.ahead > 0 && status.behind > 0) {
37820
+ blockers.push("branch_diverged_from_upstream");
37821
+ blockers.push("branch_has_local_commits");
37822
+ } else if (status.ahead > 0) blockers.push("branch_has_local_commits");
37823
+ return blockers;
37824
+ }
37825
+ function collectPostExecutionBlockers(status) {
37826
+ const blockers = [];
37827
+ if (!status.isGitRepo) blockers.push("post_not_git_repo");
37828
+ if (status.hasConflicts) blockers.push("post_conflicts_present");
37829
+ if (status.ahead !== 0) blockers.push("post_branch_ahead");
37830
+ if (status.behind !== 0) blockers.push("post_branch_still_behind");
37831
+ if (status.staged > 0 || status.modified > 0 || status.untracked > 0 || status.deleted > 0 || status.renamed > 0) {
37832
+ blockers.push("post_working_tree_not_clean");
37833
+ }
37834
+ if (status.stashCount > 0) blockers.push("post_stash_entries_present");
37835
+ blockers.push(...collectSubmoduleBlockers(status, "post"));
37836
+ return blockers;
37837
+ }
37838
+ function collectSubmoduleBlockers(status, phase) {
37839
+ const submodules = Array.isArray(status.submodules) ? status.submodules : [];
37840
+ const blockers = [];
37841
+ for (const submodule of submodules) {
37842
+ if (submodule.error) blockers.push(`${phase}_submodule_status_error:${submodule.path}`);
37843
+ if (submodule.dirty) blockers.push(`${phase}_submodule_dirty:${submodule.path}`);
37844
+ if (submodule.outOfSync) blockers.push(`${phase}_submodule_out_of_sync:${submodule.path}`);
37845
+ }
37846
+ return blockers;
37847
+ }
37848
+ function chooseBlockCode(status, blockers) {
37849
+ if (blockers.includes("not_git_repo")) return "not_git_repo";
37850
+ if (blockers.includes("branch_mismatch")) return "branch_mismatch";
37851
+ if (blockers.includes("upstream_missing")) return "upstream_missing";
37852
+ if (blockers.includes("upstream_not_fresh")) return "upstream_not_fresh";
37853
+ if (blockers.some((reason) => reason.includes("submodule"))) return "submodule_not_clean";
37854
+ if (blockers.includes("branch_diverged_from_upstream")) return "branch_diverged";
37855
+ if (blockers.includes("branch_has_local_commits") || status.ahead > 0) return "branch_ahead";
37856
+ if (blockers.some((reason) => reason.includes("changes") || reason.includes("conflicts") || reason.includes("stash"))) return "dirty_worktree";
37857
+ return "preflight_blocked";
37858
+ }
37859
+ function codeToConvergenceStatus(code) {
37860
+ if (code === "branch_diverged" || code === "branch_ahead" || code === "non_fast_forward") return "not_mergeable";
37861
+ if (code === "dirty_worktree" || code === "submodule_not_clean") return "blocked_review";
37862
+ return "blocked";
37863
+ }
37864
+ async function verifyHeadIsAncestorOfUpstream(workspace, upstream, timeoutMs) {
37865
+ if (!upstream) return { ok: false, error: "missing upstream" };
37866
+ try {
37867
+ await runGit(workspace, ["merge-base", "--is-ancestor", "HEAD", upstream], { timeoutMs: timeoutMs ?? 15e3 });
37868
+ return { ok: true };
37869
+ } catch (error48) {
37870
+ return { ok: false, error: formatGitError2(error48) };
37871
+ }
37872
+ }
37873
+ function block(base, code, blockingReasons) {
37874
+ const normalizedReasons = normalizeBlockingReasons(blockingReasons);
37875
+ return {
37876
+ ...base,
37877
+ success: false,
37878
+ code,
37879
+ allowed: false,
37880
+ willRun: false,
37881
+ executed: false,
37882
+ blockingReasons: normalizedReasons
37883
+ };
37884
+ }
37885
+ function normalizeBlockingReasons(reasons) {
37886
+ const normalized = /* @__PURE__ */ new Set();
37887
+ for (const reason of reasons) {
37888
+ normalized.add(reason);
37889
+ }
37890
+ if ([
37891
+ "conflicts_present",
37892
+ "staged_changes_present",
37893
+ "modified_changes_present",
37894
+ "untracked_changes_present",
37895
+ "deleted_changes_present",
37896
+ "renamed_changes_present"
37897
+ ].some((reason) => normalized.has(reason))) {
37898
+ normalized.add("working_tree_not_clean");
37899
+ }
37900
+ return Array.from(normalized);
37901
+ }
37902
+ function buildConvergenceState(status, convergenceStatus) {
37903
+ return {
37904
+ status: convergenceStatus,
37905
+ branch: status.branch,
37906
+ headCommit: status.headCommit,
37907
+ upstream: status.upstream,
37908
+ ahead: status.ahead,
37909
+ behind: status.behind,
37910
+ dirty: status.staged + status.modified + status.untracked + status.deleted + status.renamed > 0 || status.hasConflicts,
37911
+ stashCount: status.stashCount,
37912
+ submodules: summarizeSubmodules(status.submodules)
37913
+ };
37914
+ }
37915
+ function summarizeSubmodules(submodules) {
37916
+ return (submodules || []).map((submodule) => ({
37917
+ path: submodule.path,
37918
+ commit: submodule.commit,
37919
+ dirty: submodule.dirty,
37920
+ outOfSync: submodule.outOfSync,
37921
+ ...submodule.error ? { error: submodule.error } : {}
37922
+ }));
37923
+ }
37924
+ function normalizeOptionalString(value) {
37925
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
37926
+ }
37927
+ function formatGitError2(error48) {
37928
+ if (error48 instanceof GitCommandError) {
37929
+ return error48.stderr || error48.stdout || error48.message;
37930
+ }
37931
+ if (error48 instanceof Error) return error48.message;
37932
+ return String(error48);
37933
+ }
37934
+ async function appendFastForwardLedger(result, outcome) {
37935
+ if (!result.meshId) return;
37936
+ try {
37937
+ const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
37938
+ appendLedgerEntry2(result.meshId, {
37939
+ kind: "direct_fast_forward",
37940
+ ...result.nodeId ? { nodeId: result.nodeId } : {},
37941
+ payload: {
37942
+ operation: "mesh_fast_forward_node",
37943
+ trigger: result.trigger || "manual",
37944
+ outcome,
37945
+ code: result.code,
37946
+ workspace: result.workspace,
37947
+ allowed: result.allowed,
37948
+ dryRun: result.dryRun,
37949
+ willRun: result.willRun,
37950
+ executed: result.executed,
37951
+ branch: result.postStatus?.branch ?? result.current?.branch,
37952
+ upstream: result.postStatus?.upstream ?? result.current?.upstream,
37953
+ before: result.current ? {
37954
+ headCommit: result.current.headCommit,
37955
+ ahead: result.current.ahead,
37956
+ behind: result.current.behind
37957
+ } : void 0,
37958
+ after: result.postStatus ? {
37959
+ headCommit: result.postStatus.headCommit,
37960
+ ahead: result.postStatus.ahead,
37961
+ behind: result.postStatus.behind
37962
+ } : void 0,
37963
+ blockingReasons: result.blockingReasons
37964
+ }
37965
+ });
37966
+ } catch (error48) {
37967
+ result.ledgerError = error48 instanceof Error ? error48.message : String(error48);
37968
+ }
37969
+ }
37970
+ var STATUS_OPTIONS;
37971
+ var init_mesh_fast_forward = __esm2({
37972
+ "src/mesh/mesh-fast-forward.ts"() {
37973
+ "use strict";
37974
+ init_git_status();
37975
+ init_git_executor();
37976
+ STATUS_OPTIONS = { refreshUpstream: true, includeSubmodules: true, timeoutMs: 15e3 };
37977
+ }
37978
+ });
37352
37979
  function loadDatabaseCtor() {
37353
37980
  if (DatabaseCtor) return DatabaseCtor;
37354
37981
  const runtimeRequire = typeof require === "function" ? require : (0, import_module.createRequire)(import_meta.url);
@@ -37374,6 +38001,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
37374
38001
  import_path7 = require("path");
37375
38002
  import_module = require("module");
37376
38003
  init_mesh_ledger();
38004
+ init_mesh_work_queue();
37377
38005
  import_meta = {};
37378
38006
  BeadsDB = class _BeadsDB {
37379
38007
  static instance;
@@ -37595,25 +38223,29 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
37595
38223
  return row !== void 0;
37596
38224
  }
37597
38225
  // O(1) claim: transaction ensures only one session claims a pending task
37598
- claimNextQueueTask(meshId, nodeId, sessionId) {
38226
+ claimNextQueueTask(meshId, nodeId, sessionId, capabilityTags = []) {
37599
38227
  return this.transaction(() => {
37600
38228
  this.ensureLegacyQueueMigrated(meshId);
37601
38229
  if (this.hasActiveAssignment(meshId, sessionId, nodeId)) return null;
37602
- const row = this.db.prepare(`
38230
+ const rows = [
38231
+ ...this.db.prepare(`
37603
38232
  SELECT payload FROM mesh_queue
37604
38233
  WHERE mesh_id = ? AND status = 'pending' AND target_session_id = ?
37605
- ORDER BY created_at ASC LIMIT 1
37606
- `).get(meshId, sessionId) || this.db.prepare(`
38234
+ ORDER BY created_at ASC
38235
+ `).all(meshId, sessionId),
38236
+ ...this.db.prepare(`
37607
38237
  SELECT payload FROM mesh_queue
37608
38238
  WHERE mesh_id = ? AND status = 'pending' AND target_node_id = ? AND target_session_id IS NULL
37609
- ORDER BY created_at ASC LIMIT 1
37610
- `).get(meshId, nodeId) || this.db.prepare(`
38239
+ ORDER BY created_at ASC
38240
+ `).all(meshId, nodeId),
38241
+ ...this.db.prepare(`
37611
38242
  SELECT payload FROM mesh_queue
37612
38243
  WHERE mesh_id = ? AND status = 'pending' AND target_node_id IS NULL AND target_session_id IS NULL
37613
- ORDER BY created_at ASC LIMIT 1
37614
- `).get(meshId);
37615
- if (!row) return null;
37616
- const entry = JSON.parse(row.payload);
38244
+ ORDER BY created_at ASC
38245
+ `).all(meshId)
38246
+ ];
38247
+ const entry = rows.map((row) => JSON.parse(row.payload)).find((candidate) => nodeSatisfiesRequiredTags(candidate.requiredTags, capabilityTags));
38248
+ if (!entry) return null;
37617
38249
  const now = (/* @__PURE__ */ new Date()).toISOString();
37618
38250
  entry.status = "assigned";
37619
38251
  entry.assignedNodeId = nodeId;
@@ -37759,6 +38391,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
37759
38391
  __clearMeshQueueForTests: () => __clearMeshQueueForTests,
37760
38392
  __replaceMeshQueueForTests: () => __replaceMeshQueueForTests,
37761
38393
  __resetBeadsDBForTests: () => __resetBeadsDBForTests,
38394
+ buildMeshNodeCapabilityTags: () => buildMeshNodeCapabilityTags,
37762
38395
  cancelTask: () => cancelTask,
37763
38396
  claimNextTask: () => claimNextTask,
37764
38397
  cleanupTerminalDirectDispatches: () => cleanupTerminalDirectDispatches,
@@ -37769,6 +38402,8 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
37769
38402
  getQueue: () => getQueue,
37770
38403
  insertDirectDispatch: () => insertDirectDispatch,
37771
38404
  markStaleDirectDispatches: () => markStaleDirectDispatches,
38405
+ nodeSatisfiesRequiredTags: () => nodeSatisfiesRequiredTags,
38406
+ normalizeMeshCapabilityTags: () => normalizeMeshCapabilityTags,
37772
38407
  normalizeMeshTaskMode: () => normalizeMeshTaskMode,
37773
38408
  recordTaskAutoLaunch: () => recordTaskAutoLaunch,
37774
38409
  requeueTask: () => requeueTask,
@@ -37803,6 +38438,35 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
37803
38438
  ]
37804
38439
  };
37805
38440
  }
38441
+ function normalizeMeshCapabilityTags(value) {
38442
+ if (!Array.isArray(value)) return [];
38443
+ const seen = /* @__PURE__ */ new Set();
38444
+ return value.map((tag) => typeof tag === "string" ? tag.trim() : "").filter(Boolean).filter((tag) => {
38445
+ if (seen.has(tag)) return false;
38446
+ seen.add(tag);
38447
+ return true;
38448
+ });
38449
+ }
38450
+ function firstProviderPriority(policy) {
38451
+ const raw = policy && typeof policy === "object" && !Array.isArray(policy) ? policy.providerPriority : void 0;
38452
+ if (!Array.isArray(raw)) return void 0;
38453
+ return raw.find((type) => typeof type === "string" && type.trim())?.trim();
38454
+ }
38455
+ function buildMeshNodeCapabilityTags(node, providerType) {
38456
+ const provider = typeof providerType === "string" && providerType.trim() ? providerType.trim() : firstProviderPriority(node?.policy);
38457
+ return normalizeMeshCapabilityTags([
38458
+ ...Array.isArray(node?.capabilities) ? node.capabilities : [],
38459
+ `os=${process.platform}`,
38460
+ `arch=${process.arch}`,
38461
+ ...provider ? [`provider=${provider}`] : []
38462
+ ]);
38463
+ }
38464
+ function nodeSatisfiesRequiredTags(requiredTags, capabilityTags) {
38465
+ const required2 = normalizeMeshCapabilityTags(requiredTags);
38466
+ if (required2.length === 0) return true;
38467
+ const available = new Set(normalizeMeshCapabilityTags(capabilityTags));
38468
+ return required2.every((tag) => available.has(tag));
38469
+ }
37806
38470
  function withQueueLock(_meshId, fn) {
37807
38471
  return BeadsDB.getInstance().transaction(fn);
37808
38472
  }
@@ -37820,6 +38484,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
37820
38484
  taskMode: modeValidation.taskMode,
37821
38485
  targetNodeId: opts?.targetNodeId,
37822
38486
  targetSessionId: opts?.targetSessionId,
38487
+ requiredTags: normalizeMeshCapabilityTags(opts?.requiredTags),
37823
38488
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
37824
38489
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
37825
38490
  };
@@ -37832,8 +38497,8 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
37832
38497
  function getMeshQueueRevision(meshId) {
37833
38498
  return BeadsDB.getInstance().getQueueRevision(meshId);
37834
38499
  }
37835
- function claimNextTask(meshId, nodeId, sessionId) {
37836
- return BeadsDB.getInstance().claimNextQueueTask(meshId, nodeId, sessionId);
38500
+ function claimNextTask(meshId, nodeId, sessionId, capabilityTags) {
38501
+ return BeadsDB.getInstance().claimNextQueueTask(meshId, nodeId, sessionId, capabilityTags);
37837
38502
  }
37838
38503
  function updateTaskStatus(meshId, taskId, status, opts) {
37839
38504
  requireMeshHostQueueOwner(opts);
@@ -38129,6 +38794,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
38129
38794
  });
38130
38795
  var mesh_events_exports = {};
38131
38796
  __export2(mesh_events_exports, {
38797
+ __resetIdleAutoFastForwardForTests: () => __resetIdleAutoFastForwardForTests,
38132
38798
  clearPendingMeshCoordinatorEvents: () => clearPendingMeshCoordinatorEvents,
38133
38799
  drainPendingMeshCoordinatorEvents: () => drainPendingMeshCoordinatorEvents,
38134
38800
  getPendingMeshCoordinatorEvents: () => getPendingMeshCoordinatorEvents,
@@ -38149,6 +38815,9 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
38149
38815
  function readWorkerResultMetadata(event) {
38150
38816
  return readRecord2(event.workerResult) || readRecord2(event.meshWorkerResult) || readRecord2(event.structuredResult);
38151
38817
  }
38818
+ function __resetIdleAutoFastForwardForTests() {
38819
+ idleAutoFastForwardLastAttempt.clear();
38820
+ }
38152
38821
  function sweepExpiredRemoteIdleSessions() {
38153
38822
  const now = Date.now();
38154
38823
  for (const [key, session] of remoteIdleSessions) {
@@ -38574,13 +39243,14 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
38574
39243
  };
38575
39244
  }
38576
39245
  function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
38577
- const task = claimNextTask(meshId, nodeId, sessionId);
39246
+ const mesh = getMeshWithCache(components, meshId);
39247
+ const node = mesh?.nodes.find((n) => n.id === nodeId);
39248
+ const capabilityTags = buildMeshNodeCapabilityTags(node, providerType);
39249
+ const task = claimNextTask(meshId, nodeId, sessionId, capabilityTags);
38578
39250
  if (!task) {
38579
39251
  return false;
38580
39252
  }
38581
39253
  LOG2.info("MeshQueue", `Node ${nodeId} (${sessionId}) pulled task ${task.id}`);
38582
- const mesh = getMeshWithCache(components, meshId);
38583
- const node = mesh?.nodes.find((n) => n.id === nodeId);
38584
39254
  if (node?.daemonId && components.dispatchMeshCommand) {
38585
39255
  const isLocalNode = components.cliManager.adapters.has(sessionId);
38586
39256
  if (!isLocalNode) {
@@ -38886,6 +39556,55 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
38886
39556
  }
38887
39557
  await maybeAutoLaunchOneQueueSession(components, meshId, mesh);
38888
39558
  }
39559
+ async function maybeAutoFastForwardIdleNode(components, args) {
39560
+ const mesh = getMeshWithCache(components, args.meshId);
39561
+ const node = mesh?.nodes?.find((candidate) => candidate?.id === args.nodeId || candidate?.nodeId === args.nodeId);
39562
+ const workspace = readNonEmptyString2(node?.workspace);
39563
+ if (!workspace) return;
39564
+ if (!(0, import_fs9.existsSync)(workspace)) return;
39565
+ const throttleKey = `${args.meshId}:${args.nodeId}`;
39566
+ const now = Date.now();
39567
+ const lastAttempt = idleAutoFastForwardLastAttempt.get(throttleKey) || 0;
39568
+ if (now - lastAttempt < IDLE_AUTO_FAST_FORWARD_THROTTLE_MS) return;
39569
+ idleAutoFastForwardLastAttempt.set(throttleKey, now);
39570
+ const submoduleIgnorePaths = Array.isArray(node?.policy?.submoduleIgnorePaths) ? node.policy.submoduleIgnorePaths.filter((value) => typeof value === "string") : void 0;
39571
+ try {
39572
+ const dryRun = await fastForwardMeshNode({
39573
+ meshId: args.meshId,
39574
+ nodeId: args.nodeId,
39575
+ workspace,
39576
+ execute: false,
39577
+ dryRun: true,
39578
+ updateSubmodules: false,
39579
+ submoduleIgnorePaths,
39580
+ trigger: "idle_auto"
39581
+ });
39582
+ if (!dryRun || dryRun.code !== "fast_forward_available" || dryRun.allowed !== true) return;
39583
+ await fastForwardMeshNode({
39584
+ meshId: args.meshId,
39585
+ nodeId: args.nodeId,
39586
+ workspace,
39587
+ execute: true,
39588
+ dryRun: false,
39589
+ updateSubmodules: false,
39590
+ submoduleIgnorePaths,
39591
+ trigger: "idle_auto"
39592
+ });
39593
+ } catch (e) {
39594
+ LOG2.warn("MeshFastForward", `Idle auto fast-forward check failed for ${args.nodeId}: ${e?.message || e}`);
39595
+ }
39596
+ }
39597
+ function runIdleMaintenanceThenAssignQueue(components, args) {
39598
+ setImmediate(() => {
39599
+ maybeAutoFastForwardIdleNode(components, args).finally(() => {
39600
+ try {
39601
+ tryAssignQueueTask(components, args.meshId, args.nodeId, args.sessionId, args.providerType);
39602
+ } catch (e) {
39603
+ LOG2.warn("MeshQueue", `Failed to assign idle queue task after maintenance for ${args.nodeId}: ${e?.message || e}`);
39604
+ }
39605
+ });
39606
+ });
39607
+ }
38889
39608
  function buildMeshSystemMessage(args) {
38890
39609
  const metadata = formatCompletionMetadata(args.metadataEvent);
38891
39610
  if (args.event === "agent:generating_completed") {
@@ -39107,9 +39826,7 @@ Next step: ${nextStep}`;
39107
39826
  updateDirectDispatchStatus(args.meshId, sessionId, "completed");
39108
39827
  setImmediate(() => cleanupTerminalDirectDispatches());
39109
39828
  if (nodeId && providerType) {
39110
- setImmediate(() => {
39111
- tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
39112
- });
39829
+ runIdleMaintenanceThenAssignQueue(components, { meshId: args.meshId, nodeId, sessionId, providerType });
39113
39830
  }
39114
39831
  }
39115
39832
  } else if (args.event === "agent:ready") {
@@ -39163,8 +39880,14 @@ Next step: ${nextStep}`;
39163
39880
  expiresAt: Date.now() + REMOTE_IDLE_SESSION_TTL_MS
39164
39881
  });
39165
39882
  setImmediate(() => {
39166
- const assigned = tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
39167
- if (assigned) remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
39883
+ maybeAutoFastForwardIdleNode(components, { meshId: args.meshId, nodeId, sessionId, providerType }).finally(() => {
39884
+ try {
39885
+ const assigned = tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
39886
+ if (assigned) remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
39887
+ } catch (e) {
39888
+ LOG2.warn("MeshQueue", `Failed to assign idle queue task after maintenance for ${nodeId}: ${e?.message || e}`);
39889
+ }
39890
+ });
39168
39891
  });
39169
39892
  }
39170
39893
  } else if (args.event === "agent:generating_started") {
@@ -39446,6 +40169,8 @@ Next step: ${nextStep}`;
39446
40169
  var remoteIdleSessions;
39447
40170
  var meshByWorkspaceCache;
39448
40171
  var MESH_WORKSPACE_CACHE_TTL_MS;
40172
+ var IDLE_AUTO_FAST_FORWARD_THROTTLE_MS;
40173
+ var idleAutoFastForwardLastAttempt;
39449
40174
  var REFINE_TERMINAL_EVENTS;
39450
40175
  var MAX_PENDING_EVENTS_BYTES;
39451
40176
  var MAX_PENDING_EVENTS_KEEP;
@@ -39468,10 +40193,13 @@ Next step: ${nextStep}`;
39468
40193
  init_mesh_ledger();
39469
40194
  init_mesh_work_queue();
39470
40195
  init_beads_db();
40196
+ init_mesh_fast_forward();
39471
40197
  REMOTE_IDLE_SESSION_TTL_MS = 5 * 60 * 1e3;
39472
40198
  remoteIdleSessions = /* @__PURE__ */ new Map();
39473
40199
  meshByWorkspaceCache = /* @__PURE__ */ new Map();
39474
40200
  MESH_WORKSPACE_CACHE_TTL_MS = 5e3;
40201
+ IDLE_AUTO_FAST_FORWARD_THROTTLE_MS = 30 * 60 * 1e3;
40202
+ idleAutoFastForwardLastAttempt = /* @__PURE__ */ new Map();
39475
40203
  REFINE_TERMINAL_EVENTS = /* @__PURE__ */ new Set(["refine:completed", "refine:failed"]);
39476
40204
  MAX_PENDING_EVENTS_BYTES = 100 * 1024;
39477
40205
  MAX_PENDING_EVENTS_KEEP = 50;
@@ -46972,6 +47700,7 @@ ${lastSnapshot}`;
46972
47700
  buildMeshHostRequiredFailure: () => buildMeshHostRequiredFailure,
46973
47701
  buildMeshLedgerReconciliationEvidence: () => buildMeshLedgerReconciliationEvidence,
46974
47702
  buildMeshLedgerReplicaEvidence: () => buildMeshLedgerReplicaEvidence,
47703
+ buildMeshNodeCapabilityTags: () => buildMeshNodeCapabilityTags,
46975
47704
  buildP2pRelayFailurePayload: () => buildP2pRelayFailurePayload,
46976
47705
  buildPinnedGlobalInstallCommand: () => buildPinnedGlobalInstallCommand,
46977
47706
  buildRuntimeSystemChatMessage: () => buildRuntimeSystemChatMessage,
@@ -47099,6 +47828,7 @@ ${lastSnapshot}`;
47099
47828
  maybeRunDaemonUpgradeHelperFromEnv: () => maybeRunDaemonUpgradeHelperFromEnv2,
47100
47829
  namedKeyToAnsi: () => namedKeyToAnsi,
47101
47830
  namedKeysToAnsi: () => namedKeysToAnsi,
47831
+ nodeSatisfiesRequiredTags: () => nodeSatisfiesRequiredTags,
47102
47832
  normalizeActiveChatData: () => normalizeActiveChatData,
47103
47833
  normalizeChatMessage: () => normalizeChatMessage,
47104
47834
  normalizeChatMessageKind: () => normalizeChatMessageKind,
@@ -47110,6 +47840,7 @@ ${lastSnapshot}`;
47110
47840
  normalizeInteractivePrompt: () => normalizeInteractivePrompt,
47111
47841
  normalizeInteractivePromptResponse: () => normalizeInteractivePromptResponse2,
47112
47842
  normalizeManagedStatus: () => normalizeManagedStatus,
47843
+ normalizeMeshCapabilityTags: () => normalizeMeshCapabilityTags,
47113
47844
  normalizeMeshDaemonRole: () => normalizeMeshDaemonRole,
47114
47845
  normalizeMeshTaskMode: () => normalizeMeshTaskMode,
47115
47846
  normalizeMeshWorkerResult: () => normalizeMeshWorkerResult,
@@ -47168,7 +47899,6 @@ ${lastSnapshot}`;
47168
47899
  startLocalIpcServer: () => startLocalIpcServer2,
47169
47900
  suggestMeshRefineConfig: () => suggestMeshRefineConfig,
47170
47901
  summarizeGitStatus: () => summarizeGitStatus,
47171
- syncMeshes: () => syncMeshes,
47172
47902
  triggerMeshQueue: () => triggerMeshQueue,
47173
47903
  unregisterMeshCoordinator: () => unregisterMeshCoordinator,
47174
47904
  updateConfig: () => updateConfig,
@@ -47405,256 +48135,7 @@ ${lastSnapshot}`;
47405
48135
  }
47406
48136
  init_repo_mesh_types();
47407
48137
  init_git_executor();
47408
- init_git_executor();
47409
- async function getGitRepoStatus(workspace, options = {}) {
47410
- const lastCheckedAt = Date.now();
47411
- const includeSubmodules = options.includeSubmodules !== false;
47412
- try {
47413
- const repo = await resolveGitRepository(workspace, options);
47414
- let parsed = await readPorcelainStatus(repo, options);
47415
- let upstreamProbe = getInitialUpstreamProbe(parsed);
47416
- if (options.refreshUpstream) {
47417
- upstreamProbe = await refreshTrackedUpstream(repo, parsed, options);
47418
- if (upstreamProbe.upstreamStatus === "fresh") {
47419
- parsed = await readPorcelainStatus(repo, options);
47420
- }
47421
- }
47422
- const head = await readHead(repo, options);
47423
- const stashCount = await readStashCount(repo, options);
47424
- let submodules;
47425
- if (includeSubmodules) {
47426
- submodules = await getSubmoduleStatuses(repo, options);
47427
- }
47428
- return {
47429
- workspace: repo.workspace,
47430
- repoRoot: repo.repoRoot,
47431
- isGitRepo: true,
47432
- branch: parsed.branch,
47433
- headCommit: head.commit,
47434
- headMessage: head.message,
47435
- upstream: parsed.upstream,
47436
- upstreamStatus: parsed.upstream ? upstreamProbe.upstreamStatus : "no_upstream",
47437
- upstreamFetchedAt: upstreamProbe.upstreamFetchedAt,
47438
- upstreamFetchError: upstreamProbe.upstreamFetchError,
47439
- ahead: parsed.ahead,
47440
- behind: parsed.behind,
47441
- staged: parsed.staged,
47442
- modified: parsed.modified,
47443
- untracked: parsed.untracked,
47444
- deleted: parsed.deleted,
47445
- renamed: parsed.renamed,
47446
- hasConflicts: parsed.conflictFiles.length > 0,
47447
- conflictFiles: parsed.conflictFiles,
47448
- stashCount,
47449
- lastCheckedAt,
47450
- submodules
47451
- };
47452
- } catch (error48) {
47453
- if (error48 instanceof GitCommandError) {
47454
- return emptyStatus(workspace, lastCheckedAt, error48);
47455
- }
47456
- return emptyStatus(
47457
- workspace,
47458
- lastCheckedAt,
47459
- new GitCommandError("git_command_failed", "Failed to read Git status", { cause: error48 })
47460
- );
47461
- }
47462
- }
47463
- async function readPorcelainStatus(repo, options) {
47464
- const statusOutput = await runGit(repo, ["status", "--porcelain=v2", "--branch"], options);
47465
- return parsePorcelainV2Status(statusOutput.stdout);
47466
- }
47467
- function getInitialUpstreamProbe(parsed) {
47468
- return {
47469
- upstreamStatus: parsed.upstream ? "unchecked" : "no_upstream"
47470
- };
47471
- }
47472
- async function refreshTrackedUpstream(repo, parsed, options) {
47473
- if (!parsed.upstream || !parsed.branch) {
47474
- return { upstreamStatus: "no_upstream" };
47475
- }
47476
- const remoteName = await readBranchRemote(repo, parsed.branch, options) ?? inferRemoteName(parsed.upstream);
47477
- if (!remoteName) {
47478
- return {
47479
- upstreamStatus: "stale",
47480
- upstreamFetchError: `Unable to resolve remote for upstream '${parsed.upstream}'`
47481
- };
47482
- }
47483
- try {
47484
- await runGit(repo, ["fetch", "--quiet", "--prune", "--no-tags", remoteName], options);
47485
- return {
47486
- upstreamStatus: "fresh",
47487
- upstreamFetchedAt: Date.now()
47488
- };
47489
- } catch (error48) {
47490
- return {
47491
- upstreamStatus: "stale",
47492
- upstreamFetchError: formatGitError(error48)
47493
- };
47494
- }
47495
- }
47496
- async function readBranchRemote(repo, branch, options) {
47497
- try {
47498
- const result = await runGit(repo, ["config", "--get", `branch.${branch}.remote`], options);
47499
- return result.stdout.trim() || null;
47500
- } catch {
47501
- return null;
47502
- }
47503
- }
47504
- function inferRemoteName(upstream) {
47505
- const [remoteName] = upstream.split("/");
47506
- return remoteName?.trim() || null;
47507
- }
47508
- function formatGitError(error48) {
47509
- if (error48 instanceof GitCommandError) {
47510
- return error48.stderr || error48.message;
47511
- }
47512
- if (error48 instanceof Error) {
47513
- return error48.message;
47514
- }
47515
- return String(error48);
47516
- }
47517
- function parsePorcelainV2Status(output) {
47518
- const parsed = {
47519
- branch: null,
47520
- upstream: null,
47521
- ahead: 0,
47522
- behind: 0,
47523
- staged: 0,
47524
- modified: 0,
47525
- untracked: 0,
47526
- deleted: 0,
47527
- renamed: 0,
47528
- conflictFiles: []
47529
- };
47530
- for (const line of output.split("\n")) {
47531
- if (!line) continue;
47532
- if (line.startsWith("# branch.head ")) {
47533
- const branch = line.slice("# branch.head ".length).trim();
47534
- parsed.branch = branch && branch !== "(detached)" ? branch : null;
47535
- continue;
47536
- }
47537
- if (line.startsWith("# branch.upstream ")) {
47538
- parsed.upstream = line.slice("# branch.upstream ".length).trim() || null;
47539
- continue;
47540
- }
47541
- if (line.startsWith("# branch.ab ")) {
47542
- const match = line.match(/\+(-?\d+)\s+-(-?\d+)/);
47543
- if (match) {
47544
- parsed.ahead = Number.parseInt(match[1] ?? "0", 10) || 0;
47545
- parsed.behind = Number.parseInt(match[2] ?? "0", 10) || 0;
47546
- }
47547
- continue;
47548
- }
47549
- if (line.startsWith("? ")) {
47550
- parsed.untracked += 1;
47551
- continue;
47552
- }
47553
- if (line.startsWith("u ")) {
47554
- const fields = line.split(" ");
47555
- const filePath = fields.slice(10).join(" ");
47556
- if (filePath) parsed.conflictFiles.push(filePath);
47557
- continue;
47558
- }
47559
- if (line.startsWith("1 ") || line.startsWith("2 ")) {
47560
- const fields = line.split(" ");
47561
- const xy = fields[1] ?? "..";
47562
- const indexStatus = xy[0] ?? ".";
47563
- const worktreeStatus = xy[1] ?? ".";
47564
- if (isStagedStatus(indexStatus)) parsed.staged += 1;
47565
- if (worktreeStatus === "M" || worktreeStatus === "T") parsed.modified += 1;
47566
- if (indexStatus === "D" || worktreeStatus === "D") parsed.deleted += 1;
47567
- if (indexStatus === "R" || worktreeStatus === "R") parsed.renamed += 1;
47568
- if (xy.includes("U")) {
47569
- const filePath = fields.slice(line.startsWith("2 ") ? 9 : 8).join(" ").split(" ")[0] ?? "";
47570
- if (filePath) parsed.conflictFiles.push(filePath);
47571
- }
47572
- }
47573
- }
47574
- parsed.conflictFiles = Array.from(new Set(parsed.conflictFiles));
47575
- return parsed;
47576
- }
47577
- async function readHead(repo, options) {
47578
- try {
47579
- const result = await runGit(repo, ["log", "-1", "--pretty=%h%x00%s"], options);
47580
- const text = result.stdout.trimEnd();
47581
- if (!text) return { commit: null, message: null };
47582
- const [commit, ...messageParts] = text.split("\0");
47583
- return {
47584
- commit: commit || null,
47585
- message: messageParts.join("\0") || null
47586
- };
47587
- } catch {
47588
- return { commit: null, message: null };
47589
- }
47590
- }
47591
- async function readStashCount(repo, options) {
47592
- try {
47593
- const result = await runGit(repo, ["stash", "list", "--format=%gd"], options);
47594
- return result.stdout.split("\n").filter((line) => line.trim().length > 0).length;
47595
- } catch {
47596
- return 0;
47597
- }
47598
- }
47599
- function isStagedStatus(status) {
47600
- return status !== "." && status !== "?" && status !== "U";
47601
- }
47602
- function emptyStatus(workspace, lastCheckedAt, error48) {
47603
- return {
47604
- workspace,
47605
- repoRoot: null,
47606
- isGitRepo: false,
47607
- branch: null,
47608
- headCommit: null,
47609
- headMessage: null,
47610
- upstream: null,
47611
- upstreamStatus: "unavailable",
47612
- ahead: 0,
47613
- behind: 0,
47614
- staged: 0,
47615
- modified: 0,
47616
- untracked: 0,
47617
- deleted: 0,
47618
- renamed: 0,
47619
- hasConflicts: false,
47620
- conflictFiles: [],
47621
- stashCount: 0,
47622
- lastCheckedAt,
47623
- error: error48.stderr || error48.message,
47624
- reason: error48.reason
47625
- };
47626
- }
47627
- async function getSubmoduleStatuses(repo, options) {
47628
- if (!repo.repoRoot) return [];
47629
- try {
47630
- const result = await runGit(repo, ["submodule", "status", "--recursive"], options);
47631
- return parseSubmoduleStatusOutput(result.stdout, repo.repoRoot, options.submoduleIgnorePaths);
47632
- } catch {
47633
- return [];
47634
- }
47635
- }
47636
- function parseSubmoduleStatusOutput(output, repoRoot, ignorePaths) {
47637
- const submodules = [];
47638
- const ignoreSet = new Set(ignorePaths || []);
47639
- for (const line of output.split("\n")) {
47640
- if (!line.trim()) continue;
47641
- const match = line.match(/^([\-+\s])([0-9a-f]{40})\s+(\S+)(?:\s+\(([^)]+)\))?/);
47642
- if (!match) continue;
47643
- const prefix = match[1];
47644
- const commit = match[2];
47645
- const path40 = match[3];
47646
- if (ignoreSet.has(path40)) continue;
47647
- submodules.push({
47648
- path: path40,
47649
- commit,
47650
- repoPath: repoRoot + "/" + path40,
47651
- dirty: prefix === "+",
47652
- outOfSync: prefix === "-",
47653
- lastCheckedAt: Date.now()
47654
- });
47655
- }
47656
- return submodules;
47657
- }
48138
+ init_git_status();
47658
48139
  var import_promises2 = require("fs/promises");
47659
48140
  var path23 = __toESM2(require("path"));
47660
48141
  init_git_executor();
@@ -48062,6 +48543,7 @@ ${lastSnapshot}`;
48062
48543
  function createGitSnapshotStore(options = {}) {
48063
48544
  return new InMemoryGitSnapshotStore(options);
48064
48545
  }
48546
+ init_git_status();
48065
48547
  var DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS = 5e3;
48066
48548
  var MIN_GIT_WORKSPACE_POLL_INTERVAL_MS = 1e3;
48067
48549
  function defaultStatusProvider(workspace) {
@@ -48182,6 +48664,7 @@ ${lastSnapshot}`;
48182
48664
  }
48183
48665
  var path32 = __toESM2(require("path"));
48184
48666
  init_git_executor();
48667
+ init_git_status();
48185
48668
  var GIT_COMMAND_NAMES = /* @__PURE__ */ new Set([
48186
48669
  "git_status",
48187
48670
  "git_diff_summary",
@@ -49637,409 +50120,8 @@ ${lastSnapshot}`;
49637
50120
  state.completedAt = (/* @__PURE__ */ new Date()).toISOString();
49638
50121
  return state;
49639
50122
  }
49640
- init_mesh_config();
49641
- async function syncMeshes(transport) {
49642
- const result = { pushed: 0, pulled: 0, deleted: 0, errors: [] };
49643
- let remoteMeshes;
49644
- try {
49645
- const res = await transport.listRemoteMeshes();
49646
- remoteMeshes = res.meshes;
49647
- } catch (e) {
49648
- result.errors.push(`Failed to list remote meshes: ${e.message}`);
49649
- return result;
49650
- }
49651
- const localMeshes = listMeshes();
49652
- const remoteByIdentity = new Map(remoteMeshes.map((m) => [m.repo_identity, m]));
49653
- const localByIdentity = new Map(localMeshes.map((m) => [m.repoIdentity, m]));
49654
- for (const local of localMeshes) {
49655
- if (!remoteByIdentity.has(local.repoIdentity)) {
49656
- try {
49657
- await transport.createRemoteMesh({
49658
- name: local.name,
49659
- repo_identity: local.repoIdentity,
49660
- repo_remote_url: local.repoRemoteUrl,
49661
- default_branch: local.defaultBranch,
49662
- policy: JSON.stringify(local.policy)
49663
- });
49664
- result.pushed++;
49665
- } catch (e) {
49666
- result.errors.push(`Push failed for "${local.name}": ${e.message}`);
49667
- }
49668
- }
49669
- }
49670
- for (const remote of remoteMeshes) {
49671
- if (!localByIdentity.has(remote.repo_identity)) {
49672
- try {
49673
- let policy;
49674
- try {
49675
- policy = JSON.parse(remote.policy);
49676
- } catch {
49677
- policy = void 0;
49678
- }
49679
- createMesh({
49680
- name: remote.name,
49681
- repoIdentity: remote.repo_identity,
49682
- repoRemoteUrl: remote.repo_remote_url || void 0,
49683
- defaultBranch: remote.default_branch || void 0,
49684
- policy
49685
- });
49686
- result.pulled++;
49687
- } catch (e) {
49688
- result.errors.push(`Pull failed for "${remote.name}": ${e.message}`);
49689
- }
49690
- }
49691
- }
49692
- return result;
49693
- }
49694
50123
  init_mesh_ledger();
49695
- init_git_executor();
49696
- var STATUS_OPTIONS = { refreshUpstream: true, includeSubmodules: true, timeoutMs: 15e3 };
49697
- async function fastForwardMeshNode(args) {
49698
- const workspace = typeof args.workspace === "string" ? args.workspace.trim() : "";
49699
- const nodeId = normalizeOptionalString(args.nodeId);
49700
- const meshId = normalizeOptionalString(args.meshId);
49701
- const requestedBranch = normalizeOptionalString(args.branch);
49702
- const updateSubmodules = args.updateSubmodules === true;
49703
- const dryRun = args.dryRun === true || args.execute !== true;
49704
- const plannedSteps = buildPlannedSteps(updateSubmodules);
49705
- const base = {
49706
- ...nodeId ? { nodeId } : {},
49707
- ...meshId ? { meshId } : {},
49708
- workspace,
49709
- dryRun,
49710
- updateSubmodules,
49711
- plannedSteps
49712
- };
49713
- if (!workspace) {
49714
- return block(base, "invalid_workspace", ["workspace_required"]);
49715
- }
49716
- const current = await getGitRepoStatus(workspace, {
49717
- ...STATUS_OPTIONS,
49718
- submoduleIgnorePaths: args.submoduleIgnorePaths,
49719
- timeoutMs: args.timeoutMs ?? STATUS_OPTIONS.timeoutMs
49720
- });
49721
- const earlyBlockers = collectPreflightBlockers(current, requestedBranch);
49722
- if (earlyBlockers.length > 0) {
49723
- return {
49724
- ...block(base, chooseBlockCode(current, earlyBlockers), earlyBlockers),
49725
- current,
49726
- finalBranchConvergenceState: buildConvergenceState(current, codeToConvergenceStatus(chooseBlockCode(current, earlyBlockers)))
49727
- };
49728
- }
49729
- if (current.behind === 0) {
49730
- const result2 = {
49731
- ...base,
49732
- success: true,
49733
- code: "already_up_to_date",
49734
- allowed: true,
49735
- willRun: false,
49736
- executed: false,
49737
- blockingReasons: [],
49738
- current,
49739
- preStatus: current,
49740
- postStatus: current,
49741
- finalBranchConvergenceState: buildConvergenceState(current, "up_to_date")
49742
- };
49743
- await appendFastForwardLedger(result2, "noop");
49744
- return result2;
49745
- }
49746
- const ancestorCheck = await verifyHeadIsAncestorOfUpstream(workspace, current.upstream || "", args.timeoutMs);
49747
- if (!ancestorCheck.ok) {
49748
- const result2 = {
49749
- ...block(base, "non_fast_forward", ["head_is_not_ancestor_of_upstream"]),
49750
- current,
49751
- preStatus: current,
49752
- operationError: ancestorCheck.error,
49753
- finalBranchConvergenceState: buildConvergenceState(current, "not_mergeable")
49754
- };
49755
- await appendFastForwardLedger(result2, "blocked");
49756
- return result2;
49757
- }
49758
- if (dryRun) {
49759
- const result2 = {
49760
- ...base,
49761
- success: true,
49762
- code: "fast_forward_available",
49763
- allowed: true,
49764
- willRun: false,
49765
- executed: false,
49766
- blockingReasons: [],
49767
- current,
49768
- preStatus: current,
49769
- finalBranchConvergenceState: buildConvergenceState(current, "fast_forward_available")
49770
- };
49771
- return result2;
49772
- }
49773
- try {
49774
- await runGit(workspace, ["merge", "--ff-only", current.upstream || ""], { timeoutMs: args.timeoutMs ?? 3e4 });
49775
- } catch (error48) {
49776
- const result2 = {
49777
- ...block(base, "merge_ff_only_failed", ["merge_ff_only_failed"]),
49778
- current,
49779
- preStatus: current,
49780
- operationError: formatGitError2(error48),
49781
- finalBranchConvergenceState: buildConvergenceState(current, "not_mergeable")
49782
- };
49783
- await appendFastForwardLedger(result2, "failed");
49784
- return result2;
49785
- }
49786
- let postStatus = await getGitRepoStatus(workspace, {
49787
- ...STATUS_OPTIONS,
49788
- submoduleIgnorePaths: args.submoduleIgnorePaths,
49789
- timeoutMs: args.timeoutMs ?? STATUS_OPTIONS.timeoutMs
49790
- });
49791
- const submoduleIssues = collectSubmoduleBlockers(postStatus, "post");
49792
- let submoduleFollowUpRequired = false;
49793
- let operationError;
49794
- if (submoduleIssues.length > 0) {
49795
- if (updateSubmodules) {
49796
- try {
49797
- await runGit(workspace, ["submodule", "update", "--init", "--recursive"], { timeoutMs: args.timeoutMs ?? 6e4 });
49798
- postStatus = await getGitRepoStatus(workspace, {
49799
- ...STATUS_OPTIONS,
49800
- submoduleIgnorePaths: args.submoduleIgnorePaths,
49801
- timeoutMs: args.timeoutMs ?? STATUS_OPTIONS.timeoutMs
49802
- });
49803
- } catch (error48) {
49804
- operationError = formatGitError2(error48);
49805
- }
49806
- } else {
49807
- submoduleFollowUpRequired = true;
49808
- }
49809
- }
49810
- const postBlockers = collectPostExecutionBlockers(postStatus);
49811
- if (operationError) postBlockers.push("submodule_update_failed");
49812
- if (submoduleFollowUpRequired) postBlockers.push("submodule_update_required");
49813
- const success2 = postBlockers.length === 0 || submoduleFollowUpRequired;
49814
- const code = postBlockers.length === 0 ? "fast_forward_applied" : submoduleFollowUpRequired ? "fast_forward_applied_submodule_update_required" : "post_verify_failed";
49815
- const result = {
49816
- ...base,
49817
- success: success2,
49818
- code,
49819
- allowed: true,
49820
- willRun: true,
49821
- executed: true,
49822
- blockingReasons: postBlockers,
49823
- current,
49824
- preStatus: current,
49825
- postStatus,
49826
- ...operationError ? { operationError } : {},
49827
- finalBranchConvergenceState: buildConvergenceState(
49828
- postStatus,
49829
- postBlockers.length === 0 ? "fast_forwarded" : submoduleFollowUpRequired ? "follow_up_required" : "post_verify_failed"
49830
- )
49831
- };
49832
- await appendFastForwardLedger(result, success2 ? "executed" : "failed");
49833
- return result;
49834
- }
49835
- function buildPlannedSteps(updateSubmodules) {
49836
- const steps = [
49837
- {
49838
- operation: "refresh_upstream",
49839
- description: "Refresh the tracked upstream remote ref before trusting ahead/behind state.",
49840
- safe: true,
49841
- willMutateWorktree: false
49842
- },
49843
- {
49844
- operation: "verify_clean_worktree",
49845
- description: "Require clean staged/modified/untracked/deleted/renamed/conflict/stash/submodule state.",
49846
- safe: true,
49847
- willMutateWorktree: false
49848
- },
49849
- {
49850
- operation: "verify_fast_forward",
49851
- description: "Require ahead=0, behind>0, and HEAD to be an ancestor of the upstream ref.",
49852
- safe: true,
49853
- willMutateWorktree: false
49854
- },
49855
- {
49856
- operation: "merge_ff_only",
49857
- description: "Apply git merge --ff-only against the tracked upstream; no force, reset, rebase, push, or deploy.",
49858
- safe: true,
49859
- willMutateWorktree: true
49860
- }
49861
- ];
49862
- if (updateSubmodules) {
49863
- steps.push({
49864
- operation: "submodule_update",
49865
- description: "If the fast-forward changes gitlinks, run git submodule update --init --recursive and re-verify submodules.",
49866
- safe: true,
49867
- willMutateWorktree: true
49868
- });
49869
- }
49870
- steps.push({
49871
- operation: "verify_post_status",
49872
- description: "Re-read daemon-owned git status and report final branch convergence state.",
49873
- safe: true,
49874
- willMutateWorktree: false
49875
- });
49876
- return steps;
49877
- }
49878
- function collectPreflightBlockers(status, requestedBranch) {
49879
- const blockers = [];
49880
- if (!status.isGitRepo) blockers.push("not_git_repo");
49881
- if (!status.branch) blockers.push("detached_head_or_unknown_branch");
49882
- if (requestedBranch && status.branch !== requestedBranch) blockers.push("branch_mismatch");
49883
- if (!status.upstream) blockers.push("upstream_missing");
49884
- if (status.upstreamStatus !== "fresh") blockers.push("upstream_not_fresh");
49885
- if (status.hasConflicts) blockers.push("conflicts_present");
49886
- if (status.staged > 0) blockers.push("staged_changes_present");
49887
- if (status.modified > 0) blockers.push("modified_changes_present");
49888
- if (status.untracked > 0) blockers.push("untracked_changes_present");
49889
- if (status.deleted > 0) blockers.push("deleted_changes_present");
49890
- if (status.renamed > 0) blockers.push("renamed_changes_present");
49891
- if (status.stashCount > 0) blockers.push("stash_entries_present");
49892
- blockers.push(...collectSubmoduleBlockers(status, "pre"));
49893
- if (status.ahead > 0 && status.behind > 0) {
49894
- blockers.push("branch_diverged_from_upstream");
49895
- blockers.push("branch_has_local_commits");
49896
- } else if (status.ahead > 0) blockers.push("branch_has_local_commits");
49897
- return blockers;
49898
- }
49899
- function collectPostExecutionBlockers(status) {
49900
- const blockers = [];
49901
- if (!status.isGitRepo) blockers.push("post_not_git_repo");
49902
- if (status.hasConflicts) blockers.push("post_conflicts_present");
49903
- if (status.ahead !== 0) blockers.push("post_branch_ahead");
49904
- if (status.behind !== 0) blockers.push("post_branch_still_behind");
49905
- if (status.staged > 0 || status.modified > 0 || status.untracked > 0 || status.deleted > 0 || status.renamed > 0) {
49906
- blockers.push("post_working_tree_not_clean");
49907
- }
49908
- if (status.stashCount > 0) blockers.push("post_stash_entries_present");
49909
- blockers.push(...collectSubmoduleBlockers(status, "post"));
49910
- return blockers;
49911
- }
49912
- function collectSubmoduleBlockers(status, phase) {
49913
- const submodules = Array.isArray(status.submodules) ? status.submodules : [];
49914
- const blockers = [];
49915
- for (const submodule of submodules) {
49916
- if (submodule.error) blockers.push(`${phase}_submodule_status_error:${submodule.path}`);
49917
- if (submodule.dirty) blockers.push(`${phase}_submodule_dirty:${submodule.path}`);
49918
- if (submodule.outOfSync) blockers.push(`${phase}_submodule_out_of_sync:${submodule.path}`);
49919
- }
49920
- return blockers;
49921
- }
49922
- function chooseBlockCode(status, blockers) {
49923
- if (blockers.includes("not_git_repo")) return "not_git_repo";
49924
- if (blockers.includes("branch_mismatch")) return "branch_mismatch";
49925
- if (blockers.includes("upstream_missing")) return "upstream_missing";
49926
- if (blockers.includes("upstream_not_fresh")) return "upstream_not_fresh";
49927
- if (blockers.some((reason) => reason.includes("submodule"))) return "submodule_not_clean";
49928
- if (blockers.includes("branch_diverged_from_upstream")) return "branch_diverged";
49929
- if (blockers.includes("branch_has_local_commits") || status.ahead > 0) return "branch_ahead";
49930
- if (blockers.some((reason) => reason.includes("changes") || reason.includes("conflicts") || reason.includes("stash"))) return "dirty_worktree";
49931
- return "preflight_blocked";
49932
- }
49933
- function codeToConvergenceStatus(code) {
49934
- if (code === "branch_diverged" || code === "branch_ahead" || code === "non_fast_forward") return "not_mergeable";
49935
- if (code === "dirty_worktree" || code === "submodule_not_clean") return "blocked_review";
49936
- return "blocked";
49937
- }
49938
- async function verifyHeadIsAncestorOfUpstream(workspace, upstream, timeoutMs) {
49939
- if (!upstream) return { ok: false, error: "missing upstream" };
49940
- try {
49941
- await runGit(workspace, ["merge-base", "--is-ancestor", "HEAD", upstream], { timeoutMs: timeoutMs ?? 15e3 });
49942
- return { ok: true };
49943
- } catch (error48) {
49944
- return { ok: false, error: formatGitError2(error48) };
49945
- }
49946
- }
49947
- function block(base, code, blockingReasons) {
49948
- const normalizedReasons = normalizeBlockingReasons(blockingReasons);
49949
- return {
49950
- ...base,
49951
- success: false,
49952
- code,
49953
- allowed: false,
49954
- willRun: false,
49955
- executed: false,
49956
- blockingReasons: normalizedReasons
49957
- };
49958
- }
49959
- function normalizeBlockingReasons(reasons) {
49960
- const normalized = /* @__PURE__ */ new Set();
49961
- for (const reason of reasons) {
49962
- normalized.add(reason);
49963
- }
49964
- if ([
49965
- "conflicts_present",
49966
- "staged_changes_present",
49967
- "modified_changes_present",
49968
- "untracked_changes_present",
49969
- "deleted_changes_present",
49970
- "renamed_changes_present"
49971
- ].some((reason) => normalized.has(reason))) {
49972
- normalized.add("working_tree_not_clean");
49973
- }
49974
- return Array.from(normalized);
49975
- }
49976
- function buildConvergenceState(status, convergenceStatus) {
49977
- return {
49978
- status: convergenceStatus,
49979
- branch: status.branch,
49980
- headCommit: status.headCommit,
49981
- upstream: status.upstream,
49982
- ahead: status.ahead,
49983
- behind: status.behind,
49984
- dirty: status.staged + status.modified + status.untracked + status.deleted + status.renamed > 0 || status.hasConflicts,
49985
- stashCount: status.stashCount,
49986
- submodules: summarizeSubmodules(status.submodules)
49987
- };
49988
- }
49989
- function summarizeSubmodules(submodules) {
49990
- return (submodules || []).map((submodule) => ({
49991
- path: submodule.path,
49992
- commit: submodule.commit,
49993
- dirty: submodule.dirty,
49994
- outOfSync: submodule.outOfSync,
49995
- ...submodule.error ? { error: submodule.error } : {}
49996
- }));
49997
- }
49998
- function normalizeOptionalString(value) {
49999
- return typeof value === "string" && value.trim() ? value.trim() : void 0;
50000
- }
50001
- function formatGitError2(error48) {
50002
- if (error48 instanceof GitCommandError) {
50003
- return error48.stderr || error48.stdout || error48.message;
50004
- }
50005
- if (error48 instanceof Error) return error48.message;
50006
- return String(error48);
50007
- }
50008
- async function appendFastForwardLedger(result, outcome) {
50009
- if (!result.meshId) return;
50010
- try {
50011
- const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
50012
- appendLedgerEntry2(result.meshId, {
50013
- kind: "direct_fast_forward",
50014
- ...result.nodeId ? { nodeId: result.nodeId } : {},
50015
- payload: {
50016
- operation: "mesh_fast_forward_node",
50017
- outcome,
50018
- code: result.code,
50019
- workspace: result.workspace,
50020
- allowed: result.allowed,
50021
- dryRun: result.dryRun,
50022
- willRun: result.willRun,
50023
- executed: result.executed,
50024
- branch: result.postStatus?.branch ?? result.current?.branch,
50025
- upstream: result.postStatus?.upstream ?? result.current?.upstream,
50026
- before: result.current ? {
50027
- headCommit: result.current.headCommit,
50028
- ahead: result.current.ahead,
50029
- behind: result.current.behind
50030
- } : void 0,
50031
- after: result.postStatus ? {
50032
- headCommit: result.postStatus.headCommit,
50033
- ahead: result.postStatus.ahead,
50034
- behind: result.postStatus.behind
50035
- } : void 0,
50036
- blockingReasons: result.blockingReasons
50037
- }
50038
- });
50039
- } catch (error48) {
50040
- result.ledgerError = error48 instanceof Error ? error48.message : String(error48);
50041
- }
50042
- }
50124
+ init_mesh_fast_forward();
50043
50125
  function lastTimestamp(slice) {
50044
50126
  const entries = Array.isArray(slice?.entries) ? slice.entries : [];
50045
50127
  return entries.length ? entries[entries.length - 1].timestamp : null;
@@ -69346,6 +69428,7 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
69346
69428
  }
69347
69429
  init_config();
69348
69430
  init_cli_detector();
69431
+ init_git_status();
69349
69432
  init_logger();
69350
69433
  var fs20 = __toESM2(require("fs"));
69351
69434
  var path33 = __toESM2(require("path"));
@@ -69494,6 +69577,7 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
69494
69577
  init_mesh_coordinator();
69495
69578
  init_mesh_events();
69496
69579
  init_mesh_host_ownership();
69580
+ init_mesh_fast_forward();
69497
69581
  var import_node_child_process4 = require("child_process");
69498
69582
  var import_node_fs4 = require("fs");
69499
69583
  var import_node_path2 = require("path");
@@ -72140,8 +72224,8 @@ ${e?.stderr || ""}`
72140
72224
  }
72141
72225
  }
72142
72226
  try {
72143
- const { getMesh: getMesh3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
72144
- const mesh = getMesh3(meshId);
72227
+ const { getMesh: getMesh2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
72228
+ const mesh = getMesh2(meshId);
72145
72229
  if (mesh) return { mesh, inline: false, source: "local_config" };
72146
72230
  } catch {
72147
72231
  }
@@ -74066,8 +74150,8 @@ ${e?.stderr || ""}`
74066
74150
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
74067
74151
  if (!meshId) return { success: false, error: "meshId required" };
74068
74152
  try {
74069
- const { deleteMesh: deleteMesh3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
74070
- const deleted = deleteMesh3(meshId);
74153
+ const { deleteMesh: deleteMesh2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
74154
+ const deleted = deleteMesh2(meshId);
74071
74155
  return { success: true, deleted };
74072
74156
  } catch (e) {
74073
74157
  return { success: false, error: e.message };
@@ -74185,7 +74269,7 @@ ${e?.stderr || ""}`
74185
74269
  const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "node addition");
74186
74270
  if (ownerFailure) return ownerFailure;
74187
74271
  try {
74188
- const { addNode: addNode3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
74272
+ const { addNode: addNode2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
74189
74273
  const providerPriority = Array.isArray(args?.providerPriority) ? args.providerPriority.map((type) => typeof type === "string" ? type.trim() : "").filter(Boolean) : [];
74190
74274
  const readOnly = args?.readOnly === true;
74191
74275
  const policy = {
@@ -74196,7 +74280,7 @@ ${e?.stderr || ""}`
74196
74280
  const daemonId = typeof args?.daemonId === "string" && args.daemonId.trim() ? args.daemonId.trim() : void 0;
74197
74281
  const machineId = typeof args?.machineId === "string" && args.machineId.trim() ? args.machineId.trim() : void 0;
74198
74282
  const repoRoot = typeof args?.repoRoot === "string" && args.repoRoot.trim() ? args.repoRoot.trim() : void 0;
74199
- const node = addNode3(meshId, {
74283
+ const node = addNode2(meshId, {
74200
74284
  workspace,
74201
74285
  ...repoRoot ? { repoRoot } : {},
74202
74286
  ...daemonId ? { daemonId } : {},
@@ -74387,8 +74471,8 @@ ${e?.stderr || ""}`
74387
74471
  if (meshRecord?.inline) {
74388
74472
  removed = this.removeInlineMeshNode(meshId, mesh, nodeId);
74389
74473
  } else {
74390
- const { removeNode: removeNode3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
74391
- removed = removeNode3(meshId, nodeId);
74474
+ const { removeNode: removeNode2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
74475
+ removed = removeNode2(meshId, nodeId);
74392
74476
  if (removed) this.invalidateAggregateMeshStatus(meshId);
74393
74477
  }
74394
74478
  if (removed) {
@@ -74457,8 +74541,8 @@ ${e?.stderr || ""}`
74457
74541
  };
74458
74542
  this.updateInlineMeshNode(meshId, mesh, node);
74459
74543
  } else {
74460
- const { addNode: addNode3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
74461
- node = addNode3(meshId, {
74544
+ const { addNode: addNode2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
74545
+ node = addNode2(meshId, {
74462
74546
  workspace: result.worktreePath,
74463
74547
  repoRoot: result.worktreePath,
74464
74548
  daemonId: sourceNode.daemonId,
@@ -74613,8 +74697,8 @@ ${e?.stderr || ""}`
74613
74697
  mesh = args.inlineMesh;
74614
74698
  this.inlineMeshCache.set(meshId, mesh);
74615
74699
  } else {
74616
- const { getMesh: getMesh3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
74617
- mesh = getMesh3(meshId);
74700
+ const { getMesh: getMesh2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
74701
+ mesh = getMesh2(meshId);
74618
74702
  }
74619
74703
  if (!mesh) return { success: false, error: "Mesh not found" };
74620
74704
  const meshHost = resolveMeshHostStatus(mesh);