@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.js CHANGED
@@ -45,6 +45,7 @@ var init_repo_mesh_types = __esm({
45
45
  maxParallelTasks: 2,
46
46
  spawnedSessionVisibility: "visible",
47
47
  sessionCleanupOnNodeRemove: "preserve",
48
+ autoFastForward: { enabled: true },
48
49
  maxTaskRetries: 1
49
50
  };
50
51
  }
@@ -1359,7 +1360,17 @@ function normalizeRepoIdentity(remoteUrl) {
1359
1360
  return identity;
1360
1361
  }
1361
1362
  function mergeMeshPolicy(base, patch) {
1362
- const policy = { ...DEFAULT_MESH_POLICY, ...base || {}, ...patch || {} };
1363
+ const autoFastForward = normalizeAutoFastForwardPolicy({
1364
+ ...DEFAULT_MESH_POLICY.autoFastForward,
1365
+ ...base?.autoFastForward && typeof base.autoFastForward === "object" ? base.autoFastForward : {},
1366
+ ...patch?.autoFastForward && typeof patch.autoFastForward === "object" ? patch.autoFastForward : {}
1367
+ });
1368
+ const policy = {
1369
+ ...DEFAULT_MESH_POLICY,
1370
+ ...base || {},
1371
+ ...patch || {},
1372
+ autoFastForward
1373
+ };
1363
1374
  if (!["block", "warn", "checkpoint_then_continue"].includes(policy.dirtyWorkspaceBehavior)) {
1364
1375
  policy.dirtyWorkspaceBehavior = "warn";
1365
1376
  }
@@ -1374,6 +1385,15 @@ function mergeMeshPolicy(base, patch) {
1374
1385
  }
1375
1386
  return policy;
1376
1387
  }
1388
+ function normalizeAutoFastForwardPolicy(value) {
1389
+ const record = value && typeof value === "object" && !Array.isArray(value) ? value : {};
1390
+ const maxBehind = Number(record.maxBehind);
1391
+ return {
1392
+ enabled: record.enabled !== false,
1393
+ ...Number.isFinite(maxBehind) && maxBehind >= 0 ? { maxBehind: Math.floor(maxBehind) } : {},
1394
+ requireCleanSubmodules: record.requireCleanSubmodules !== false
1395
+ };
1396
+ }
1377
1397
  function listMeshes() {
1378
1398
  return loadMeshConfig().meshes;
1379
1399
  }
@@ -6725,6 +6745,34 @@ function isIdleSessionState(state) {
6725
6745
  function isDirtyNode(node) {
6726
6746
  return node?.health === "dirty" || node?.git?.dirty === true;
6727
6747
  }
6748
+ function resolveAutoFastForwardPolicy(mesh) {
6749
+ const record = mesh?.policy?.autoFastForward && typeof mesh.policy.autoFastForward === "object" && !Array.isArray(mesh.policy.autoFastForward) ? mesh.policy.autoFastForward : {};
6750
+ const maxBehind = Number(record.maxBehind);
6751
+ return {
6752
+ enabled: record.enabled !== false,
6753
+ ...Number.isFinite(maxBehind) && maxBehind >= 0 ? { maxBehind: Math.floor(maxBehind) } : {},
6754
+ requireCleanSubmodules: record.requireCleanSubmodules !== false
6755
+ };
6756
+ }
6757
+ function sessionStateLooksActive(state) {
6758
+ const status = readNonEmptyString2(state?.status).toLowerCase();
6759
+ const chatStatus = readNonEmptyString2(state?.activeChat?.status).toLowerCase();
6760
+ const active = /* @__PURE__ */ new Set(["generating", "streaming", "long_generating", "working", "starting", "waiting_approval"]);
6761
+ return active.has(status) || active.has(chatStatus);
6762
+ }
6763
+ function nodeHasActiveMeshWork(components, meshId, nodeId, currentSessionId) {
6764
+ if (nodeHasActiveAssignment(meshId, nodeId)) return true;
6765
+ return components.instanceManager.getByCategory("cli").some((inst) => {
6766
+ const state = inst.getState();
6767
+ const settings = state.settings || {};
6768
+ if (readNonEmptyString2(settings.meshNodeFor) !== meshId) return false;
6769
+ const instNodeId = readNonEmptyString2(settings.meshNodeId) || readNonEmptyString2(settings.nodeId);
6770
+ if (instNodeId !== nodeId) return false;
6771
+ const sessionId = readNonEmptyString2(state.instanceId);
6772
+ if (currentSessionId && sessionId === currentSessionId && isIdleSessionState(state)) return false;
6773
+ return sessionStateLooksActive(state);
6774
+ });
6775
+ }
6728
6776
  function isLaunchableNode(node) {
6729
6777
  if (!node || node.status === "disabled" || node.status === "removed") return false;
6730
6778
  const health = readNonEmptyString2(node.health).toLowerCase();
@@ -7053,6 +7101,9 @@ async function maybeAutoFastForwardIdleNode(components, args) {
7053
7101
  const workspace = readNonEmptyString2(node?.workspace);
7054
7102
  if (!workspace) return;
7055
7103
  if (!(0, import_fs10.existsSync)(workspace)) return;
7104
+ const policy = resolveAutoFastForwardPolicy(mesh);
7105
+ if (!policy.enabled) return;
7106
+ if (nodeHasActiveMeshWork(components, args.meshId, args.nodeId, args.sessionId)) return;
7056
7107
  const throttleKey = `${args.meshId}:${args.nodeId}`;
7057
7108
  const now = Date.now();
7058
7109
  const lastAttempt = idleAutoFastForwardLastAttempt.get(throttleKey) || 0;
@@ -7071,6 +7122,12 @@ async function maybeAutoFastForwardIdleNode(components, args) {
7071
7122
  trigger: "idle_auto"
7072
7123
  });
7073
7124
  if (!dryRun || dryRun.code !== "fast_forward_available" || dryRun.allowed !== true) return;
7125
+ const behind = Number(dryRun.current?.behind);
7126
+ if (policy.maxBehind !== void 0 && Number.isFinite(behind) && behind > policy.maxBehind) return;
7127
+ if (policy.requireCleanSubmodules) {
7128
+ const submodules = Array.isArray(dryRun.current?.submodules) ? dryRun.current.submodules : [];
7129
+ if (submodules.some((submodule) => submodule?.dirty || submodule?.outOfSync || submodule?.error)) return;
7130
+ }
7074
7131
  await fastForwardMeshNode({
7075
7132
  meshId: args.meshId,
7076
7133
  nodeId: args.nodeId,
@@ -40221,6 +40278,23 @@ function getGitSubmoduleDriftState(git) {
40221
40278
  }
40222
40279
  return { dirty, outOfSync };
40223
40280
  }
40281
+ function isInlineMeshAutoFastForwardEligible(git) {
40282
+ if (!git) return false;
40283
+ if (readBooleanValue(git.isGitRepo) !== true) return false;
40284
+ if (!readStringValue(git.branch)) return false;
40285
+ if (!readStringValue(git.upstream)) return false;
40286
+ const upstreamStatus = readStringValue(git.upstreamStatus, git.upstream_status);
40287
+ if (upstreamStatus !== "fresh") return false;
40288
+ if ((readNumberValue(git.ahead) ?? 0) !== 0) return false;
40289
+ if ((readNumberValue(git.behind) ?? 0) <= 0) return false;
40290
+ const hasConflicts = readBooleanValue(git.hasConflicts) ?? (Array.isArray(git.conflictFiles) && git.conflictFiles.length > 0);
40291
+ if (hasConflicts) return false;
40292
+ if ((readNumberValue(git.stashCount, git.stash_count) ?? 0) > 0) return false;
40293
+ const submoduleDrift = getGitSubmoduleDriftState(git);
40294
+ if (submoduleDrift.dirty || submoduleDrift.outOfSync) return false;
40295
+ const dirty = readBooleanValue(git.dirty) ?? countGitWorktreeChanges(git) > 0;
40296
+ return dirty !== true && countGitWorktreeChanges(git) === 0;
40297
+ }
40224
40298
  function deriveMeshNodeHealthFromGit(git) {
40225
40299
  if (!git || readBooleanValue(git.isGitRepo) === false) return "degraded";
40226
40300
  const branch = readStringValue(git.branch);
@@ -40350,6 +40424,12 @@ function applyInlineMeshBranchConvergence(mesh, node, status) {
40350
40424
  status.isDirty = uncommittedChanges > 0;
40351
40425
  status.uncommittedChanges = uncommittedChanges;
40352
40426
  status.branchConvergence = buildInlineMeshBranchConvergence({ mesh, node, status });
40427
+ status.autoFastForwardEligible = isInlineMeshAutoFastForwardEligible(git);
40428
+ if (status.autoFastForwardEligible) {
40429
+ status.suggestedAction = "auto_fast_forward";
40430
+ } else {
40431
+ delete status.suggestedAction;
40432
+ }
40353
40433
  }
40354
40434
  function summarizeInlineMeshBranchConvergence(nodes) {
40355
40435
  const followUps = nodes.filter((node) => readObjectRecord(node.branchConvergence).needsConvergence === true).map((node) => {