@adhdev/daemon-core 0.9.82-rc.212 → 0.9.82-rc.213

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.mjs CHANGED
@@ -40,6 +40,7 @@ var init_repo_mesh_types = __esm({
40
40
  maxParallelTasks: 2,
41
41
  spawnedSessionVisibility: "visible",
42
42
  sessionCleanupOnNodeRemove: "preserve",
43
+ autoFastForward: { enabled: true },
43
44
  maxTaskRetries: 1
44
45
  };
45
46
  }
@@ -1357,7 +1358,17 @@ function normalizeRepoIdentity(remoteUrl) {
1357
1358
  return identity;
1358
1359
  }
1359
1360
  function mergeMeshPolicy(base, patch) {
1360
- const policy = { ...DEFAULT_MESH_POLICY, ...base || {}, ...patch || {} };
1361
+ const autoFastForward = normalizeAutoFastForwardPolicy({
1362
+ ...DEFAULT_MESH_POLICY.autoFastForward,
1363
+ ...base?.autoFastForward && typeof base.autoFastForward === "object" ? base.autoFastForward : {},
1364
+ ...patch?.autoFastForward && typeof patch.autoFastForward === "object" ? patch.autoFastForward : {}
1365
+ });
1366
+ const policy = {
1367
+ ...DEFAULT_MESH_POLICY,
1368
+ ...base || {},
1369
+ ...patch || {},
1370
+ autoFastForward
1371
+ };
1361
1372
  if (!["block", "warn", "checkpoint_then_continue"].includes(policy.dirtyWorkspaceBehavior)) {
1362
1373
  policy.dirtyWorkspaceBehavior = "warn";
1363
1374
  }
@@ -1372,6 +1383,15 @@ function mergeMeshPolicy(base, patch) {
1372
1383
  }
1373
1384
  return policy;
1374
1385
  }
1386
+ function normalizeAutoFastForwardPolicy(value) {
1387
+ const record = value && typeof value === "object" && !Array.isArray(value) ? value : {};
1388
+ const maxBehind = Number(record.maxBehind);
1389
+ return {
1390
+ enabled: record.enabled !== false,
1391
+ ...Number.isFinite(maxBehind) && maxBehind >= 0 ? { maxBehind: Math.floor(maxBehind) } : {},
1392
+ requireCleanSubmodules: record.requireCleanSubmodules !== false
1393
+ };
1394
+ }
1375
1395
  function listMeshes() {
1376
1396
  return loadMeshConfig().meshes;
1377
1397
  }
@@ -6719,6 +6739,34 @@ function isIdleSessionState(state) {
6719
6739
  function isDirtyNode(node) {
6720
6740
  return node?.health === "dirty" || node?.git?.dirty === true;
6721
6741
  }
6742
+ function resolveAutoFastForwardPolicy(mesh) {
6743
+ const record = mesh?.policy?.autoFastForward && typeof mesh.policy.autoFastForward === "object" && !Array.isArray(mesh.policy.autoFastForward) ? mesh.policy.autoFastForward : {};
6744
+ const maxBehind = Number(record.maxBehind);
6745
+ return {
6746
+ enabled: record.enabled !== false,
6747
+ ...Number.isFinite(maxBehind) && maxBehind >= 0 ? { maxBehind: Math.floor(maxBehind) } : {},
6748
+ requireCleanSubmodules: record.requireCleanSubmodules !== false
6749
+ };
6750
+ }
6751
+ function sessionStateLooksActive(state) {
6752
+ const status = readNonEmptyString2(state?.status).toLowerCase();
6753
+ const chatStatus = readNonEmptyString2(state?.activeChat?.status).toLowerCase();
6754
+ const active = /* @__PURE__ */ new Set(["generating", "streaming", "long_generating", "working", "starting", "waiting_approval"]);
6755
+ return active.has(status) || active.has(chatStatus);
6756
+ }
6757
+ function nodeHasActiveMeshWork(components, meshId, nodeId, currentSessionId) {
6758
+ if (nodeHasActiveAssignment(meshId, nodeId)) return true;
6759
+ return components.instanceManager.getByCategory("cli").some((inst) => {
6760
+ const state = inst.getState();
6761
+ const settings = state.settings || {};
6762
+ if (readNonEmptyString2(settings.meshNodeFor) !== meshId) return false;
6763
+ const instNodeId = readNonEmptyString2(settings.meshNodeId) || readNonEmptyString2(settings.nodeId);
6764
+ if (instNodeId !== nodeId) return false;
6765
+ const sessionId = readNonEmptyString2(state.instanceId);
6766
+ if (currentSessionId && sessionId === currentSessionId && isIdleSessionState(state)) return false;
6767
+ return sessionStateLooksActive(state);
6768
+ });
6769
+ }
6722
6770
  function isLaunchableNode(node) {
6723
6771
  if (!node || node.status === "disabled" || node.status === "removed") return false;
6724
6772
  const health = readNonEmptyString2(node.health).toLowerCase();
@@ -7047,6 +7095,9 @@ async function maybeAutoFastForwardIdleNode(components, args) {
7047
7095
  const workspace = readNonEmptyString2(node?.workspace);
7048
7096
  if (!workspace) return;
7049
7097
  if (!existsSync14(workspace)) return;
7098
+ const policy = resolveAutoFastForwardPolicy(mesh);
7099
+ if (!policy.enabled) return;
7100
+ if (nodeHasActiveMeshWork(components, args.meshId, args.nodeId, args.sessionId)) return;
7050
7101
  const throttleKey = `${args.meshId}:${args.nodeId}`;
7051
7102
  const now = Date.now();
7052
7103
  const lastAttempt = idleAutoFastForwardLastAttempt.get(throttleKey) || 0;
@@ -7065,6 +7116,12 @@ async function maybeAutoFastForwardIdleNode(components, args) {
7065
7116
  trigger: "idle_auto"
7066
7117
  });
7067
7118
  if (!dryRun || dryRun.code !== "fast_forward_available" || dryRun.allowed !== true) return;
7119
+ const behind = Number(dryRun.current?.behind);
7120
+ if (policy.maxBehind !== void 0 && Number.isFinite(behind) && behind > policy.maxBehind) return;
7121
+ if (policy.requireCleanSubmodules) {
7122
+ const submodules = Array.isArray(dryRun.current?.submodules) ? dryRun.current.submodules : [];
7123
+ if (submodules.some((submodule) => submodule?.dirty || submodule?.outOfSync || submodule?.error)) return;
7124
+ }
7068
7125
  await fastForwardMeshNode({
7069
7126
  meshId: args.meshId,
7070
7127
  nodeId: args.nodeId,
@@ -39893,6 +39950,23 @@ function getGitSubmoduleDriftState(git) {
39893
39950
  }
39894
39951
  return { dirty, outOfSync };
39895
39952
  }
39953
+ function isInlineMeshAutoFastForwardEligible(git) {
39954
+ if (!git) return false;
39955
+ if (readBooleanValue(git.isGitRepo) !== true) return false;
39956
+ if (!readStringValue(git.branch)) return false;
39957
+ if (!readStringValue(git.upstream)) return false;
39958
+ const upstreamStatus = readStringValue(git.upstreamStatus, git.upstream_status);
39959
+ if (upstreamStatus !== "fresh") return false;
39960
+ if ((readNumberValue(git.ahead) ?? 0) !== 0) return false;
39961
+ if ((readNumberValue(git.behind) ?? 0) <= 0) return false;
39962
+ const hasConflicts = readBooleanValue(git.hasConflicts) ?? (Array.isArray(git.conflictFiles) && git.conflictFiles.length > 0);
39963
+ if (hasConflicts) return false;
39964
+ if ((readNumberValue(git.stashCount, git.stash_count) ?? 0) > 0) return false;
39965
+ const submoduleDrift = getGitSubmoduleDriftState(git);
39966
+ if (submoduleDrift.dirty || submoduleDrift.outOfSync) return false;
39967
+ const dirty = readBooleanValue(git.dirty) ?? countGitWorktreeChanges(git) > 0;
39968
+ return dirty !== true && countGitWorktreeChanges(git) === 0;
39969
+ }
39896
39970
  function deriveMeshNodeHealthFromGit(git) {
39897
39971
  if (!git || readBooleanValue(git.isGitRepo) === false) return "degraded";
39898
39972
  const branch = readStringValue(git.branch);
@@ -40022,6 +40096,12 @@ function applyInlineMeshBranchConvergence(mesh, node, status) {
40022
40096
  status.isDirty = uncommittedChanges > 0;
40023
40097
  status.uncommittedChanges = uncommittedChanges;
40024
40098
  status.branchConvergence = buildInlineMeshBranchConvergence({ mesh, node, status });
40099
+ status.autoFastForwardEligible = isInlineMeshAutoFastForwardEligible(git);
40100
+ if (status.autoFastForwardEligible) {
40101
+ status.suggestedAction = "auto_fast_forward";
40102
+ } else {
40103
+ delete status.suggestedAction;
40104
+ }
40025
40105
  }
40026
40106
  function summarizeInlineMeshBranchConvergence(nodes) {
40027
40107
  const followUps = nodes.filter((node) => readObjectRecord(node.branchConvergence).needsConvergence === true).map((node) => {