@adhdev/daemon-core 0.9.82-rc.406 → 0.9.82-rc.408

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
@@ -399,10 +399,10 @@ function readInjected(value) {
399
399
  }
400
400
  function getDaemonBuildInfo() {
401
401
  if (cached) return cached;
402
- const commit = readInjected(true ? "1a663e37a08bce5e83cc14be4663d33ad60e81c9" : void 0) ?? "unknown";
403
- const commitShort = readInjected(true ? "1a663e37" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
404
- const version = readInjected(true ? "0.9.82-rc.406" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
405
- const builtAt = readInjected(true ? "2026-06-28T04:52:22.784Z" : void 0);
402
+ const commit = readInjected(true ? "b6de7b335f9b6b6bfb202135c2ae017429182efa" : void 0) ?? "unknown";
403
+ const commitShort = readInjected(true ? "b6de7b33" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
404
+ const version = readInjected(true ? "0.9.82-rc.408" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
405
+ const builtAt = readInjected(true ? "2026-06-28T07:53:15.742Z" : void 0);
406
406
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
407
407
  return cached;
408
408
  }
@@ -655,6 +655,11 @@ var init_change_impact_config = __esm({
655
655
  });
656
656
 
657
657
  // src/git/git-status.ts
658
+ function statusCacheKey(workspace, options) {
659
+ const includeSubmodules = options.includeSubmodules !== false;
660
+ const refreshUpstream = options.refreshUpstream === true;
661
+ return `${workspace}\0sub=${includeSubmodules ? 1 : 0}\0up=${refreshUpstream ? 1 : 0}`;
662
+ }
658
663
  function isTransientGitFailure(error) {
659
664
  return error.reason === "timeout" || error.reason === "git_command_failed";
660
665
  }
@@ -662,15 +667,22 @@ async function getGitRepoStatus(workspace, options = {}) {
662
667
  const lastCheckedAt = Date.now();
663
668
  const includeSubmodules = options.includeSubmodules !== false;
664
669
  const effectiveOptions = options.timeoutMs === void 0 ? { ...options, timeoutMs: GIT_STATUS_TIMEOUT_MS } : options;
670
+ const cacheKey = statusCacheKey(workspace, options);
671
+ if (!options.forceFresh) {
672
+ const cached3 = lastKnownGoodStatus.get(cacheKey);
673
+ if (cached3 && lastCheckedAt - cached3.cachedAt < GIT_STATUS_CACHE_TTL_MS) {
674
+ return cached3.status;
675
+ }
676
+ }
665
677
  try {
666
678
  const repo = await resolveGitRepository(workspace, effectiveOptions);
667
679
  const status = await collectGitRepoStatus(repo, includeSubmodules, lastCheckedAt, effectiveOptions);
668
- lastKnownGoodStatus.set(workspace, status);
680
+ lastKnownGoodStatus.set(cacheKey, { status, cachedAt: lastCheckedAt });
669
681
  return status;
670
682
  } catch (error) {
671
683
  const gitError = error instanceof GitCommandError ? error : new GitCommandError("git_command_failed", "Failed to read Git status", { cause: error });
672
684
  if (isTransientGitFailure(gitError)) {
673
- const cached3 = lastKnownGoodStatus.get(workspace);
685
+ const cached3 = lastKnownGoodStatus.get(cacheKey)?.status;
674
686
  if (cached3) {
675
687
  return {
676
688
  ...cached3,
@@ -689,19 +701,25 @@ async function collectGitRepoStatus(repo, includeSubmodules, lastCheckedAt, opti
689
701
  let upstreamProbe = getInitialUpstreamProbe(parsed);
690
702
  if (options.refreshUpstream) {
691
703
  upstreamProbe = await refreshTrackedUpstream(repo, parsed, options);
692
- if (upstreamProbe.upstreamStatus === "fresh") {
704
+ if (upstreamProbe.upstreamStatus === "fresh" && upstreamProbe.didFetch) {
693
705
  parsed = await readPorcelainStatus(repo, options);
694
706
  }
695
707
  }
696
708
  const head = await readHead(repo, options);
697
709
  const stashCount = await readStashCount(repo, options);
698
710
  let submodules;
711
+ let submoduleHeadOids = /* @__PURE__ */ new Map();
699
712
  if (includeSubmodules) {
700
- submodules = await getSubmoduleStatuses(repo, options);
713
+ const subResult = await getSubmoduleStatuses(repo, options);
714
+ submodules = subResult.submodules;
715
+ submoduleHeadOids = subResult.headOidByPath;
701
716
  }
702
717
  const submoduleDirty = (submodules || []).some((submodule) => submodule.dirty || submodule.outOfSync || !!submodule.error);
703
718
  const dirty = parsed.staged + parsed.modified + parsed.untracked + parsed.deleted + parsed.renamed > 0 || parsed.conflictFiles.length > 0 || stashCount > 0 || submoduleDirty;
704
- const daemonBuildBehind = await detectDaemonBuildBehind(repo, submodules, options);
719
+ const daemonBuildBehind = await detectDaemonBuildBehind(repo, submodules, options, {
720
+ rootHeadOid: parsed.headOid,
721
+ submoduleHeadOids
722
+ });
705
723
  return {
706
724
  workspace: repo.workspace,
707
725
  repoRoot: repo.repoRoot,
@@ -804,25 +822,50 @@ function resolveChangeImpactConfigForRepo(repoRoot, options) {
804
822
  changeImpactConfigCache.set(repoRoot, { sourceKey: loaded.sourceKey, config });
805
823
  return { config, sourceKey: loaded.sourceKey };
806
824
  }
807
- async function detectDaemonBuildBehind(repo, submodules, options) {
825
+ async function detectDaemonBuildBehind(repo, submodules, options, headOids = { rootHeadOid: null, submoduleHeadOids: /* @__PURE__ */ new Map() }) {
808
826
  const build = options.daemonBuildInfo ?? getDaemonBuildInfo();
809
827
  if (!build.commit || build.commit === "unknown") return void 0;
810
828
  const { config, sourceKey: configKey } = resolveChangeImpactConfigForRepo(repo.repoRoot, options);
811
829
  const policy = resolveChangeImpactPolicy(config);
812
830
  const scopes = [
813
- { scope: "root", repoPath: repo.repoRoot || repo.workspace }
831
+ { scope: "root", repoPath: repo.repoRoot || repo.workspace, knownHeadOid: headOids.rootHeadOid }
814
832
  ];
815
833
  for (const sub of submodules || []) {
816
- if (sub.repoPath && !sub.error) scopes.push({ scope: sub.path, repoPath: sub.repoPath });
834
+ if (sub.repoPath && !sub.error) {
835
+ scopes.push({ scope: sub.path, repoPath: sub.repoPath, knownHeadOid: headOids.submoduleHeadOids.get(sub.path) ?? null });
836
+ }
817
837
  }
818
- for (const { scope, repoPath } of scopes) {
838
+ for (const { scope, repoPath, knownHeadOid } of scopes) {
819
839
  try {
820
- await runGit(repoPath, ["cat-file", "-e", `${build.commit}^{commit}`], options);
821
- const headResult = await runGit(repoPath, ["rev-parse", "HEAD"], options);
822
- const head = headResult.stdout.trim();
823
- if (!head || head === build.commit) continue;
824
- await runGit(repoPath, ["merge-base", "--is-ancestor", build.commit, "HEAD"], options);
840
+ let head = knownHeadOid;
841
+ const ancestryKey = head ? `${repoPath}::${build.commit}::${head}` : null;
842
+ if (ancestryKey) {
843
+ const cachedVerdict = buildBehindAncestryCache.get(ancestryKey);
844
+ if (cachedVerdict === false) continue;
845
+ if (cachedVerdict === void 0) {
846
+ if (head === build.commit) {
847
+ buildBehindAncestryCache.set(ancestryKey, false);
848
+ continue;
849
+ }
850
+ await runGit(repoPath, ["cat-file", "-e", `${build.commit}^{commit}`], options);
851
+ try {
852
+ await runGit(repoPath, ["merge-base", "--is-ancestor", build.commit, "HEAD"], options);
853
+ } catch {
854
+ buildBehindAncestryCache.set(ancestryKey, false);
855
+ continue;
856
+ }
857
+ buildBehindAncestryCache.set(ancestryKey, true);
858
+ }
859
+ } else {
860
+ await runGit(repoPath, ["cat-file", "-e", `${build.commit}^{commit}`], options);
861
+ const headResult = await runGit(repoPath, ["rev-parse", "HEAD"], options);
862
+ head = headResult.stdout.trim();
863
+ if (!head || head === build.commit) continue;
864
+ await runGit(repoPath, ["merge-base", "--is-ancestor", build.commit, "HEAD"], options);
865
+ buildBehindAncestryCache.set(`${repoPath}::${build.commit}::${head}`, true);
866
+ }
825
867
  const evalKey = `${repoPath}\0${build.commit}\0${head}\0${configKey}`;
868
+ if (!head) continue;
826
869
  let evaluated = changeImpactEvalCache.get(evalKey);
827
870
  if (!evaluated) {
828
871
  evaluated = await classifyDaemonBuildChange(repoPath, build.commit, options, policy);
@@ -864,6 +907,15 @@ async function refreshTrackedUpstream(repo, parsed, options) {
864
907
  if (!parsed.upstream || !parsed.branch) {
865
908
  return { upstreamStatus: "no_upstream" };
866
909
  }
910
+ const now = Date.now();
911
+ const lastFetch = upstreamFetchedAt.get(repo.workspace);
912
+ if (!options.forceFresh && lastFetch !== void 0 && now - lastFetch < GIT_FETCH_THROTTLE_MS) {
913
+ return {
914
+ upstreamStatus: "fresh",
915
+ upstreamFetchedAt: lastFetch,
916
+ didFetch: false
917
+ };
918
+ }
867
919
  const remoteName = await readBranchRemote(repo, parsed.branch, options) ?? inferRemoteName(parsed.upstream);
868
920
  if (!remoteName) {
869
921
  return {
@@ -873,9 +925,12 @@ async function refreshTrackedUpstream(repo, parsed, options) {
873
925
  }
874
926
  try {
875
927
  await runGit(repo, ["fetch", "--quiet", "--prune", "--no-tags", remoteName], options);
928
+ const fetchedAt = Date.now();
929
+ upstreamFetchedAt.set(repo.workspace, fetchedAt);
876
930
  return {
877
931
  upstreamStatus: "fresh",
878
- upstreamFetchedAt: Date.now()
932
+ upstreamFetchedAt: fetchedAt,
933
+ didFetch: true
879
934
  };
880
935
  } catch (error) {
881
936
  return {
@@ -908,6 +963,7 @@ function formatGitError(error) {
908
963
  function parsePorcelainV2Status(output) {
909
964
  const parsed = {
910
965
  branch: null,
966
+ headOid: null,
911
967
  upstream: null,
912
968
  ahead: 0,
913
969
  behind: 0,
@@ -920,6 +976,11 @@ function parsePorcelainV2Status(output) {
920
976
  };
921
977
  for (const line of output.split("\n")) {
922
978
  if (!line) continue;
979
+ if (line.startsWith("# branch.oid ")) {
980
+ const oid = line.slice("# branch.oid ".length).trim();
981
+ parsed.headOid = oid && oid !== "(initial)" && /^[0-9a-f]{7,64}$/.test(oid) ? oid : null;
982
+ continue;
983
+ }
923
984
  if (line.startsWith("# branch.head ")) {
924
985
  const branch = line.slice("# branch.head ".length).trim();
925
986
  parsed.branch = branch && branch !== "(detached)" ? branch : null;
@@ -1017,25 +1078,27 @@ function emptyStatus(workspace, lastCheckedAt, error) {
1017
1078
  };
1018
1079
  }
1019
1080
  async function getSubmoduleStatuses(repo, options) {
1020
- if (!repo.repoRoot) return [];
1081
+ if (!repo.repoRoot) return { submodules: [], headOidByPath: /* @__PURE__ */ new Map() };
1021
1082
  try {
1022
- const submodules = await deriveSubmoduleGitlinkStatuses(repo, options);
1083
+ const { submodules, headOidByPath } = await deriveSubmoduleGitlinkStatuses(repo, options);
1023
1084
  await Promise.all(submodules.map((submodule) => enrichSubmoduleWorktreeStatus(repo, submodule, options)));
1024
- return submodules;
1085
+ return { submodules, headOidByPath };
1025
1086
  } catch {
1026
- return [];
1087
+ return { submodules: [], headOidByPath: /* @__PURE__ */ new Map() };
1027
1088
  }
1028
1089
  }
1029
1090
  async function deriveSubmoduleGitlinkStatuses(repo, options) {
1030
- if (!repo.repoRoot) return [];
1091
+ if (!repo.repoRoot) return { submodules: [], headOidByPath: /* @__PURE__ */ new Map() };
1031
1092
  const paths = await readSubmodulePaths(repo, options);
1032
1093
  const ignoreSet = new Set(options.submoduleIgnorePaths || []);
1033
1094
  const lastCheckedAt = Date.now();
1095
+ const headOidByPath = /* @__PURE__ */ new Map();
1034
1096
  const entries = await Promise.all(
1035
1097
  paths.filter((path43) => !ignoreSet.has(path43)).map(async (path43) => {
1036
1098
  const repoPath = repo.repoRoot + "/" + path43;
1037
1099
  const expected = await readGitlinkExpectedSha(repo, path43, options);
1038
1100
  const actual = await readSubmoduleHeadSha(repo, repoPath, options);
1101
+ if (actual) headOidByPath.set(path43, actual);
1039
1102
  const outOfSync = actual === null ? true : expected !== null && expected !== actual;
1040
1103
  return {
1041
1104
  path: path43,
@@ -1049,7 +1112,7 @@ async function deriveSubmoduleGitlinkStatuses(repo, options) {
1049
1112
  };
1050
1113
  })
1051
1114
  );
1052
- return entries;
1115
+ return { submodules: entries, headOidByPath };
1053
1116
  }
1054
1117
  async function readSubmodulePaths(repo, options) {
1055
1118
  if (!repo.repoRoot) return [];
@@ -1106,16 +1169,20 @@ async function enrichSubmoduleWorktreeStatus(repo, submodule, options) {
1106
1169
  submodule.error = formatGitError(error);
1107
1170
  }
1108
1171
  }
1109
- var lastKnownGoodStatus, changeImpactEvalCache, changeImpactConfigCache, DEFAULT_DAEMON_RUNTIME_PACKAGES, DEFAULT_WEB_ONLY_PACKAGES, DEFAULT_IMPACT_TARGETS;
1172
+ var GIT_STATUS_CACHE_TTL_MS, lastKnownGoodStatus, changeImpactEvalCache, changeImpactConfigCache, buildBehindAncestryCache, GIT_FETCH_THROTTLE_MS, upstreamFetchedAt, DEFAULT_DAEMON_RUNTIME_PACKAGES, DEFAULT_WEB_ONLY_PACKAGES, DEFAULT_IMPACT_TARGETS;
1110
1173
  var init_git_status = __esm({
1111
1174
  "src/git/git-status.ts"() {
1112
1175
  "use strict";
1113
1176
  init_git_executor();
1114
1177
  init_build_info();
1115
1178
  init_change_impact_config();
1179
+ GIT_STATUS_CACHE_TTL_MS = 1500;
1116
1180
  lastKnownGoodStatus = /* @__PURE__ */ new Map();
1117
1181
  changeImpactEvalCache = /* @__PURE__ */ new Map();
1118
1182
  changeImpactConfigCache = /* @__PURE__ */ new Map();
1183
+ buildBehindAncestryCache = /* @__PURE__ */ new Map();
1184
+ GIT_FETCH_THROTTLE_MS = 3e4;
1185
+ upstreamFetchedAt = /* @__PURE__ */ new Map();
1119
1186
  DEFAULT_DAEMON_RUNTIME_PACKAGES = [
1120
1187
  "daemon-core",
1121
1188
  "daemon-standalone",
@@ -2995,7 +3062,7 @@ function normalizeGitStatus(status, node, options) {
2995
3062
  const repoRoot = readString3(status.repoRoot, status.repo_root, node.repoRoot, node.repo_root, status.workspace, node.workspace) || void 0;
2996
3063
  const submodules = readGitSubmodules(status.submodules, repoRoot);
2997
3064
  const upstreamStatus = readString3(status.upstreamStatus, status.upstream_status);
2998
- const upstreamFetchedAt = readNumber(status.upstreamFetchedAt, status.upstream_fetched_at);
3065
+ const upstreamFetchedAt2 = readNumber(status.upstreamFetchedAt, status.upstream_fetched_at);
2999
3066
  const upstreamFetchError = readString3(status.upstreamFetchError, status.upstream_fetch_error);
3000
3067
  const error = readString3(status.error);
3001
3068
  const staged = readNumber(status.staged) ?? 0;
@@ -3012,7 +3079,7 @@ function normalizeGitStatus(status, node, options) {
3012
3079
  headMessage: readString3(status.headMessage) ?? null,
3013
3080
  upstream: readString3(status.upstream) ?? null,
3014
3081
  upstreamStatus: upstreamStatus ?? "unchecked",
3015
- ...upstreamFetchedAt !== void 0 ? { upstreamFetchedAt } : {},
3082
+ ...upstreamFetchedAt2 !== void 0 ? { upstreamFetchedAt: upstreamFetchedAt2 } : {},
3016
3083
  ...upstreamFetchError ? { upstreamFetchError } : {},
3017
3084
  ahead: readNumber(status.ahead) ?? 0,
3018
3085
  behind: readNumber(status.behind) ?? 0,
@@ -4685,6 +4752,7 @@ __export(mesh_work_queue_exports, {
4685
4752
  getQueue: () => getQueue,
4686
4753
  hasPendingDependents: () => hasPendingDependents,
4687
4754
  insertDirectDispatch: () => insertDirectDispatch,
4755
+ isTaskReadonly: () => isTaskReadonly,
4688
4756
  markStaleDirectDispatches: () => markStaleDirectDispatches,
4689
4757
  nodeSatisfiesRequiredTags: () => nodeSatisfiesRequiredTags,
4690
4758
  normalizeMeshCapabilityTags: () => normalizeMeshCapabilityTags,
@@ -4700,6 +4768,10 @@ __export(mesh_work_queue_exports, {
4700
4768
  updateTaskStatus: () => updateTaskStatus,
4701
4769
  validateMeshTaskModeRequest: () => validateMeshTaskModeRequest
4702
4770
  });
4771
+ function isTaskReadonly(task) {
4772
+ if (!task) return false;
4773
+ return task.readonly === true || task.taskMode === "live_debug_readonly";
4774
+ }
4703
4775
  function hasNegationBefore(text, matchIndex) {
4704
4776
  const before = text.slice(0, matchIndex);
4705
4777
  const clauseStart = Math.max(
@@ -4885,13 +4957,11 @@ function normalizeMeshTaskMode(value) {
4885
4957
  const normalized = value.trim();
4886
4958
  return MESH_TASK_MODES.includes(normalized) ? normalized : void 0;
4887
4959
  }
4888
- function validateMeshTaskModeRequest(mode, message) {
4960
+ function validateMeshTaskModeRequest(mode, message, readonly) {
4889
4961
  const taskMode = normalizeMeshTaskMode(mode);
4890
- if (!taskMode) {
4891
- return { valid: true, violations: [] };
4892
- }
4893
- if (taskMode !== "live_debug_readonly") {
4894
- return { valid: true, taskMode, violations: [] };
4962
+ const isReadonly = isTaskReadonly({ readonly, taskMode });
4963
+ if (!isReadonly) {
4964
+ return taskMode ? { valid: true, taskMode, violations: [] } : { valid: true, violations: [] };
4895
4965
  }
4896
4966
  const text = message || "";
4897
4967
  const violations = LIVE_DEBUG_READONLY_FORBIDDEN.filter((rule) => patternHasRealMutation(rule.pattern, text)).map((rule) => rule.label);
@@ -5018,7 +5088,8 @@ function assertNoDependencyCycle(meshId, newTaskId, dependsOn) {
5018
5088
  }
5019
5089
  function enqueueTask(meshId, message, opts) {
5020
5090
  requireMeshHostQueueOwner(opts);
5021
- const modeValidation = validateMeshTaskModeRequest(opts?.taskMode, message);
5091
+ const readonly = opts?.readonly === true;
5092
+ const modeValidation = validateMeshTaskModeRequest(opts?.taskMode, message, readonly);
5022
5093
  if (!modeValidation.valid) {
5023
5094
  throw new Error(`live_debug_readonly_guardrail_violation: forbidden operations (${modeValidation.violations.join(", ")})`);
5024
5095
  }
@@ -5042,6 +5113,7 @@ function enqueueTask(meshId, message, opts) {
5042
5113
  message,
5043
5114
  status: "pending",
5044
5115
  taskMode: modeValidation.taskMode,
5116
+ ...readonly ? { readonly: true } : {},
5045
5117
  targetNodeId: opts?.targetNodeId,
5046
5118
  targetSessionId: opts?.targetSessionId,
5047
5119
  requiredTags: resolvedRequiredTags,
@@ -5060,7 +5132,8 @@ function recordDirectDispatchTask(meshId, message, opts) {
5060
5132
  if (!missionId) return null;
5061
5133
  const taskId = typeof opts.id === "string" ? opts.id.trim() : "";
5062
5134
  if (!taskId) return null;
5063
- const modeValidation = validateMeshTaskModeRequest(opts.taskMode, message);
5135
+ const readonly = opts.readonly === true;
5136
+ const modeValidation = validateMeshTaskModeRequest(opts.taskMode, message, readonly);
5064
5137
  if (!modeValidation.valid) {
5065
5138
  throw new Error(`live_debug_readonly_guardrail_violation: forbidden operations (${modeValidation.violations.join(", ")})`);
5066
5139
  }
@@ -5075,6 +5148,7 @@ function recordDirectDispatchTask(meshId, message, opts) {
5075
5148
  message,
5076
5149
  status: "assigned",
5077
5150
  ...modeValidation.taskMode ? { taskMode: modeValidation.taskMode } : {},
5151
+ ...readonly ? { readonly: true } : {},
5078
5152
  missionId,
5079
5153
  ...opts.assignedNodeId ? { targetNodeId: opts.assignedNodeId, assignedNodeId: opts.assignedNodeId } : {},
5080
5154
  ...opts.assignedSessionId ? { targetSessionId: opts.assignedSessionId, assignedSessionId: opts.assignedSessionId } : {},
@@ -6055,7 +6129,7 @@ var init_mesh_runtime_store = __esm({
6055
6129
  return deps.every((depId) => depStatus.get(depId) === "completed");
6056
6130
  };
6057
6131
  const nodeConflictAllows = (candidate) => {
6058
- if (candidate.taskMode === "live_debug_readonly") return true;
6132
+ if (isTaskReadonly(candidate)) return true;
6059
6133
  return !nodeBusy;
6060
6134
  };
6061
6135
  const nodeIsWorktree = opts?.nodeIsWorktree === true;
@@ -9267,7 +9341,7 @@ var init_mesh_fast_forward = __esm({
9267
9341
  "use strict";
9268
9342
  init_git_status();
9269
9343
  init_git_executor();
9270
- STATUS_OPTIONS = { refreshUpstream: true, includeSubmodules: true, timeoutMs: 15e3 };
9344
+ STATUS_OPTIONS = { refreshUpstream: true, includeSubmodules: true, timeoutMs: 15e3, forceFresh: true };
9271
9345
  }
9272
9346
  });
9273
9347
 
@@ -9667,9 +9741,6 @@ var mesh_scheduling_runtime_exports = {};
9667
9741
  __export(mesh_scheduling_runtime_exports, {
9668
9742
  buildMeshSchedulingRuntime: () => buildMeshSchedulingRuntime
9669
9743
  });
9670
- function isReadonly(task) {
9671
- return task.taskMode === "live_debug_readonly";
9672
- }
9673
9744
  function isAssigned(task) {
9674
9745
  return task.status === "assigned";
9675
9746
  }
@@ -9678,8 +9749,8 @@ function buildMeshSchedulingRuntime(mesh, queue) {
9678
9749
  const maxParallelTasks = resolveMaxParallelTasks(mesh?.policy?.maxParallelTasks);
9679
9750
  const maxReadonlyParallelTasks = resolveMaxReadonlyParallelTasks(maxParallelTasks);
9680
9751
  const assignedTasks = (Array.isArray(queue) ? queue : []).filter(isAssigned);
9681
- const activeWriteAssigned = assignedTasks.filter((t) => !isReadonly(t)).length;
9682
- const activeReadonlyAssigned = assignedTasks.filter(isReadonly).length;
9752
+ const activeWriteAssigned = assignedTasks.filter((t) => !isTaskReadonly(t)).length;
9753
+ const activeReadonlyAssigned = assignedTasks.filter(isTaskReadonly).length;
9683
9754
  const globalWriteCapReached = activeWriteAssigned >= maxParallelTasks;
9684
9755
  const globalReadonlyCapReached = activeReadonlyAssigned >= maxReadonlyParallelTasks;
9685
9756
  const writeAssignedByNode = /* @__PURE__ */ new Map();
@@ -9689,7 +9760,7 @@ function buildMeshSchedulingRuntime(mesh, queue) {
9689
9760
  const nodeId = typeof task.assignedNodeId === "string" ? task.assignedNodeId.trim() : "";
9690
9761
  if (!nodeId) continue;
9691
9762
  assignedByNode.set(nodeId, (assignedByNode.get(nodeId) ?? 0) + 1);
9692
- if (!isReadonly(task)) {
9763
+ if (!isTaskReadonly(task)) {
9693
9764
  writeAssignedByNode.set(nodeId, (writeAssignedByNode.get(nodeId) ?? 0) + 1);
9694
9765
  }
9695
9766
  const provider = typeof task.assignedProviderType === "string" ? task.assignedProviderType : "";
@@ -9762,6 +9833,7 @@ var init_mesh_scheduling_runtime = __esm({
9762
9833
  "use strict";
9763
9834
  init_repo_mesh_types();
9764
9835
  init_dist();
9836
+ init_mesh_work_queue();
9765
9837
  }
9766
9838
  });
9767
9839
 
@@ -12161,10 +12233,10 @@ function resolveAutoLaunchTarget(components, node) {
12161
12233
  return { mode: "remote", daemonId, coordinatorDaemonId };
12162
12234
  }
12163
12235
  function activeWriteAssignedCount(meshId) {
12164
- return getQueue(meshId, { status: ["assigned"] }).filter((task) => task.taskMode !== "live_debug_readonly").length;
12236
+ return getQueue(meshId, { status: ["assigned"] }).filter((task) => !isTaskReadonly(task)).length;
12165
12237
  }
12166
12238
  function activeReadonlyAssignedCount(meshId) {
12167
- return getQueue(meshId, { status: ["assigned"] }).filter((task) => task.taskMode === "live_debug_readonly").length;
12239
+ return getQueue(meshId, { status: ["assigned"] }).filter(isTaskReadonly).length;
12168
12240
  }
12169
12241
  function nodeHasActiveAssignment(meshId, nodeId) {
12170
12242
  return getQueue(meshId, { status: ["assigned"] }).some((task) => task.assignedNodeId === nodeId);
@@ -12340,8 +12412,8 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
12340
12412
  );
12341
12413
  const maxReadonlyParallelTasks = resolveMaxReadonlyParallelTasks(maxParallelTasks, schedulingOverride?.readonlyMultiplier);
12342
12414
  for (const task of pending) {
12343
- const isReadonly2 = task.taskMode === "live_debug_readonly";
12344
- if (isReadonly2) {
12415
+ const isReadonly = isTaskReadonly(task);
12416
+ if (isReadonly) {
12345
12417
  if (activeReadonlyAssignedCount(meshId) >= maxReadonlyParallelTasks) {
12346
12418
  markAutoLaunch(meshId, task.id, { status: "skipped", reason: "max_readonly_parallel_tasks_reached" });
12347
12419
  continue;
@@ -12423,7 +12495,7 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
12423
12495
  sweepExpiredCooldowns();
12424
12496
  continue;
12425
12497
  }
12426
- if (task.taskMode !== "live_debug_readonly" && nodeHasActiveAssignment(meshId, nodeId)) {
12498
+ if (!isTaskReadonly(task) && nodeHasActiveAssignment(meshId, nodeId)) {
12427
12499
  markAutoLaunch(meshId, task.id, { status: "skipped", reason: "node_has_active_assignment", nodeId });
12428
12500
  continue;
12429
12501
  }
@@ -13171,6 +13243,9 @@ function isModuleNotFoundError(error, ref) {
13171
13243
  const code = "code" in error ? error.code : void 0;
13172
13244
  return code === "MODULE_NOT_FOUND" && message.includes(ref);
13173
13245
  }
13246
+ function runtimeTriplet() {
13247
+ return `${process.platform}-${process.arch}-node${process.versions.modules}`;
13248
+ }
13174
13249
  function normalizeBinding(mod, ref) {
13175
13250
  const binding = mod?.default?.createTerminal ? mod.default : mod?.createTerminal ? mod : null;
13176
13251
  if (!binding) {
@@ -13205,7 +13280,7 @@ function loadGhosttyVtBinding() {
13205
13280
  }
13206
13281
  cachedBinding = null;
13207
13282
  cachedBindingError = new Error(
13208
- `ghostty-vt binding unavailable (${errors.join("; ") || "no candidates tried"})`
13283
+ `ghostty-vt binding unavailable for runtime ${runtimeTriplet()} (${errors.join("; ") || "no candidates tried"})`
13209
13284
  );
13210
13285
  throw cachedBindingError;
13211
13286
  }
@@ -16371,6 +16446,17 @@ function resolveReconcileIntervalMs() {
16371
16446
  }
16372
16447
  return DEFAULT_RECONCILE_INTERVAL_MS;
16373
16448
  }
16449
+ function resolveTunedReconcileMs(envName, def, min, max) {
16450
+ const raw = readNonEmptyString2(process.env[envName]);
16451
+ if (raw) {
16452
+ const parsed = Number.parseInt(raw, 10);
16453
+ if (Number.isFinite(parsed) && parsed >= min && parsed <= max) return parsed;
16454
+ }
16455
+ return def;
16456
+ }
16457
+ function resolveAckedDeathDeadlineMs() {
16458
+ return resolveTunedReconcileMs("MESH_INFLIGHT_ACKED_DEATH_DEADLINE_MS", 8 * 6e4, 0, 60 * 6e4);
16459
+ }
16374
16460
  function inFlightSynthKey(meshId, taskId) {
16375
16461
  return `${meshId}::${taskId}`;
16376
16462
  }
@@ -16947,15 +17033,42 @@ function unwrapReadChatPayload(raw) {
16947
17033
  function readChatPayloadStatus(payload) {
16948
17034
  return readNonEmptyString2(payload?.status).toLowerCase();
16949
17035
  }
17036
+ function realTerminalEmitPendingForTask(meshId, taskId) {
17037
+ let pending;
17038
+ try {
17039
+ pending = getPendingMeshCoordinatorEvents(meshId);
17040
+ } catch {
17041
+ return false;
17042
+ }
17043
+ return pending.some((e) => readNonEmptyString2(e.metadataEvent?.taskId) === taskId && (e.event === "agent:generating_completed" || e.event === "agent:stopped"));
17044
+ }
17045
+ async function reprobeWorkerStatus(components, args) {
17046
+ try {
17047
+ if (args.isLocalNode) {
17048
+ const r = await components.commandHandler.handle("read_chat", args.readArgs);
17049
+ if (r && r.success === false) return null;
17050
+ return readChatPayloadStatus(unwrapReadChatPayload(r));
17051
+ }
17052
+ if (components.dispatchMeshCommand) {
17053
+ const r = await components.dispatchMeshCommand(args.nodeDaemonId, "read_chat", args.readArgs);
17054
+ const p = unwrapReadChatPayload(r);
17055
+ if (p && p.success === false) return null;
17056
+ return readChatPayloadStatus(p);
17057
+ }
17058
+ } catch {
17059
+ return null;
17060
+ }
17061
+ return null;
17062
+ }
16950
17063
  async function reconcileUnterminatedDirectDispatches(components, mesh, selfIds, localDaemonId) {
16951
17064
  const dispatches = getActiveDirectDispatches(mesh.id);
16952
17065
  if (dispatches.length === 0) return;
16953
17066
  const activeTaskKeys = new Set(
16954
17067
  dispatches.map((d) => readNonEmptyString2(d.taskId)).filter(Boolean).map((taskId) => inFlightSynthKey(mesh.id, taskId))
16955
17068
  );
16956
- for (const key of inFlightIdleObservationCounts.keys()) {
17069
+ for (const key of inFlightAckedHoldState.keys()) {
16957
17070
  if (key.startsWith(`${mesh.id}::`) && !activeTaskKeys.has(key)) {
16958
- inFlightIdleObservationCounts.delete(key);
17071
+ inFlightAckedHoldState.delete(key);
16959
17072
  }
16960
17073
  }
16961
17074
  const dispatchMeshCommand = components.dispatchMeshCommand;
@@ -16976,35 +17089,63 @@ async function reconcileUnterminatedDirectDispatches(components, mesh, selfIds,
16976
17089
  ...node?.workspace ? { workspace: node.workspace } : {},
16977
17090
  ...providerType ? { agentType: providerType, providerType } : {}
16978
17091
  };
17092
+ const synthKey = inFlightSynthKey(mesh.id, taskId);
17093
+ const isAcked = dispatch.status === "acked";
16979
17094
  let payload = null;
17095
+ let readFailed = false;
16980
17096
  try {
16981
17097
  if (isLocalNode) {
16982
17098
  const result = await components.commandHandler.handle("read_chat", readArgs);
16983
- if (result && result.success === false) continue;
16984
- payload = unwrapReadChatPayload(result);
17099
+ if (result && result.success === false) {
17100
+ readFailed = true;
17101
+ } else {
17102
+ payload = unwrapReadChatPayload(result);
17103
+ }
16985
17104
  } else if (dispatchMeshCommand) {
16986
17105
  const result = await dispatchMeshCommand(nodeDaemonId, "read_chat", readArgs);
16987
17106
  payload = unwrapReadChatPayload(result);
16988
- if (payload && payload.success === false) continue;
17107
+ if (payload && payload.success === false) {
17108
+ payload = null;
17109
+ readFailed = true;
17110
+ }
16989
17111
  } else {
16990
17112
  continue;
16991
17113
  }
16992
17114
  } catch {
17115
+ readFailed = true;
17116
+ }
17117
+ if (!payload && !readFailed) continue;
17118
+ if (readFailed || !payload) {
17119
+ if (isAcked) {
17120
+ const prior = inFlightAckedHoldState.get(synthKey);
17121
+ const failures = (prior?.consecutiveReadFailures ?? 0) + 1;
17122
+ const liveConfirmedSinceAck = prior?.liveConfirmedSinceAck ?? false;
17123
+ inFlightAckedHoldState.set(synthKey, { liveConfirmedSinceAck, consecutiveReadFailures: failures });
17124
+ if (liveConfirmedSinceAck && failures >= ACKED_DEATH_CONSECUTIVE_READ_FAILURES) {
17125
+ LOG.warn("MeshReconcile", `Acked-hold death signal: task ${taskId} on node ${nodeId} (mesh ${mesh.id}) read_chat failed ${failures}x consecutively after a live-confirmed ack \u2014 worker session presumed gone mid-turn; releasing the indefinite synth hold to the stranded-reclaim / orphan-prune nets`);
17126
+ }
17127
+ }
16993
17128
  continue;
16994
17129
  }
16995
- if (!payload) continue;
16996
- const synthKey = inFlightSynthKey(mesh.id, taskId);
17130
+ inFlightAckedHoldState.set(synthKey, { liveConfirmedSinceAck: true, consecutiveReadFailures: 0 });
17131
+ const nowMs = Date.now();
16997
17132
  if (readChatPayloadStatus(payload) !== "idle") {
16998
- inFlightIdleObservationCounts.delete(synthKey);
16999
17133
  continue;
17000
17134
  }
17001
- if (dispatch.status === "acked") {
17002
- const idleStreak = (inFlightIdleObservationCounts.get(synthKey) ?? 0) + 1;
17003
- inFlightIdleObservationCounts.set(synthKey, idleStreak);
17004
- if (idleStreak < REQUIRED_CONSECUTIVE_IDLE_TICKS_FOR_INFLIGHT_SYNTH) {
17005
- LOG.info("MeshReconcile", `In-flight synth hold: task ${taskId} on node ${nodeId} (mesh ${mesh.id}) read idle ${idleStreak}/${REQUIRED_CONSECUTIVE_IDLE_TICKS_FOR_INFLIGHT_SYNTH} consecutive tick(s) after generating_started \u2014 deferring completion synth until the idle settle is confirmed (guards against a mid-turn idle flicker pre-empting the real completion)`);
17135
+ if (isAcked) {
17136
+ const ackedAtMs = Date.parse(readNonEmptyString2(dispatch.updatedAt));
17137
+ const sinceAckMs = Number.isFinite(ackedAtMs) ? nowMs - ackedAtMs : Number.POSITIVE_INFINITY;
17138
+ const deathDeadlineMs = resolveAckedDeathDeadlineMs();
17139
+ if (sinceAckMs < deathDeadlineMs) {
17140
+ LOG.info("MeshReconcile", `Acked-hold: task ${taskId} on node ${nodeId} (mesh ${mesh.id}) read idle ${Number.isFinite(sinceAckMs) ? Math.round(sinceAckMs / 1e3) + "s" : "\u221E"} since the generating_started ack \u2014 HOLDING synth indefinitely (worker is alive and will emit; a later real emit is idempotent). Death backstop fires at ${Math.round(deathDeadlineMs / 1e3)}s or on consecutive read failures.`);
17006
17141
  continue;
17007
17142
  }
17143
+ LOG.warn("MeshReconcile", `Acked-hold death deadline reached: task ${taskId} on node ${nodeId} (mesh ${mesh.id}) still idle ${Math.round(sinceAckMs / 1e3)}s after the ack (deadline ${Math.round(deathDeadlineMs / 1e3)}s) \u2014 synthesizing the missing completion as a notification-loss net (a real emit, if it ever lands, no-ops idempotently).`);
17144
+ }
17145
+ if (realTerminalEmitPendingForTask(mesh.id, taskId)) {
17146
+ inFlightAckedHoldState.delete(synthKey);
17147
+ LOG.info("MeshReconcile", `Worker-emit priority: task ${taskId} on node ${nodeId} (mesh ${mesh.id}) has a real terminal completion already queued \u2014 yielding synth to the worker's own emit`);
17148
+ continue;
17008
17149
  }
17009
17150
  const messages = Array.isArray(payload.messages) ? payload.messages : [];
17010
17151
  const evidence = extractFinalAssistantSummaryEvidence(messages);
@@ -17022,6 +17163,12 @@ async function reconcileUnterminatedDirectDispatches(components, mesh, selfIds,
17022
17163
  }, `transcriptAt=${evidence.transcriptMessageAt} < dispatchedAt=${dispatch.dispatchedAt}`);
17023
17164
  continue;
17024
17165
  }
17166
+ const reprobeStatus = await reprobeWorkerStatus(components, { isLocalNode, nodeDaemonId, readArgs });
17167
+ if (reprobeStatus && reprobeStatus !== "idle") {
17168
+ inFlightAckedHoldState.delete(synthKey);
17169
+ LOG.info("MeshReconcile", `Live re-probe defer: task ${taskId} on node ${nodeId} (mesh ${mesh.id}) read '${reprobeStatus}' at synth-commit time \u2014 worker resumed generating; deferring synth to a later tick`);
17170
+ continue;
17171
+ }
17025
17172
  const providerSessionId = readNonEmptyString2(payload.providerSessionId);
17026
17173
  const coordinatorDaemonId = selfIds.find((id) => !!id);
17027
17174
  try {
@@ -17160,7 +17307,7 @@ function setupMeshReconcileLoop(components) {
17160
17307
  }
17161
17308
  };
17162
17309
  }
17163
- var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_AUTO_PRUNE_MIN_AGE_MS, REQUIRED_CONSECUTIVE_IDLE_TICKS_FOR_INFLIGHT_SYNTH, inFlightIdleObservationCounts, coordinatorModalParkState, heldEventLedgerRecorded, ASSIGNED_STRANDED_DEADLINE_MS, STRICT_SESSION_MATCH_TTL_MS, unresolvedForwardRejectionCounts, MAX_FORWARD_REJECTIONS;
17310
+ var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_AUTO_PRUNE_MIN_AGE_MS, ACKED_DEATH_CONSECUTIVE_READ_FAILURES, inFlightAckedHoldState, coordinatorModalParkState, heldEventLedgerRecorded, ASSIGNED_STRANDED_DEADLINE_MS, STRICT_SESSION_MATCH_TTL_MS, unresolvedForwardRejectionCounts, MAX_FORWARD_REJECTIONS;
17164
17311
  var init_mesh_reconcile_loop = __esm({
17165
17312
  "src/mesh/mesh-reconcile-loop.ts"() {
17166
17313
  "use strict";
@@ -17182,8 +17329,8 @@ var init_mesh_reconcile_loop = __esm({
17182
17329
  init_chat_message_normalization();
17183
17330
  DEFAULT_RECONCILE_INTERVAL_MS = 4e3;
17184
17331
  DEFAULT_AUTO_PRUNE_MIN_AGE_MS = 24 * 60 * 6e4;
17185
- REQUIRED_CONSECUTIVE_IDLE_TICKS_FOR_INFLIGHT_SYNTH = 2;
17186
- inFlightIdleObservationCounts = /* @__PURE__ */ new Map();
17332
+ ACKED_DEATH_CONSECUTIVE_READ_FAILURES = 3;
17333
+ inFlightAckedHoldState = /* @__PURE__ */ new Map();
17187
17334
  coordinatorModalParkState = /* @__PURE__ */ new Map();
17188
17335
  heldEventLedgerRecorded = /* @__PURE__ */ new Set();
17189
17336
  ASSIGNED_STRANDED_DEADLINE_MS = 5 * 6e4;
@@ -23075,6 +23222,7 @@ __export(index_exports, {
23075
23222
  isSessionHostLiveRuntime: () => isSessionHostLiveRuntime,
23076
23223
  isSessionHostRecoverySnapshot: () => isSessionHostRecoverySnapshot,
23077
23224
  isSetupComplete: () => isSetupComplete,
23225
+ isTaskReadonly: () => isTaskReadonly,
23078
23226
  isUserFacingChatMessage: () => isUserFacingChatMessage,
23079
23227
  killIdeProcess: () => killIdeProcess,
23080
23228
  launchIDE: () => launchIDE,
@@ -24166,7 +24314,7 @@ function validateMutatingMessage(value) {
24166
24314
  async function gitCheckpoint(workspace, message, includeUntracked) {
24167
24315
  const repo = await resolveGitRepository(workspace);
24168
24316
  const repoRoot = repo.repoRoot;
24169
- const statusResult = await getGitRepoStatus(workspace);
24317
+ const statusResult = await getGitRepoStatus(workspace, { forceFresh: true });
24170
24318
  if (statusResult.hasConflicts) {
24171
24319
  throw new GitCommandError("conflict", "Repository has conflicts \u2014 resolve before checkpointing");
24172
24320
  }
@@ -52962,7 +53110,10 @@ async function alignRefinerySubmodulesAfterMerge(repoRoot, previousBaseHead, cur
52962
53110
  const preStatus = await getGitRepoStatus(repoRoot, {
52963
53111
  includeSubmodules: true,
52964
53112
  submoduleIgnorePaths: options.submoduleIgnorePaths,
52965
- timeoutMs: 15e3
53113
+ timeoutMs: 15e3,
53114
+ // Decision path — the out-of-sync submodule set drives a mutating `submodule
53115
+ // update`. Must not act on a TTL-cached status; bypass the C1 cache.
53116
+ forceFresh: true
52966
53117
  });
52967
53118
  const outOfSyncPaths = (preStatus.submodules || []).filter((submodule) => submodule.dirty || submodule.outOfSync || !!submodule.error).map((submodule) => submodule.path);
52968
53119
  const updatePaths = [.../* @__PURE__ */ new Set([...changedGitlinkPaths, ...outOfSyncPaths])].sort();
@@ -52991,7 +53142,10 @@ async function alignRefinerySubmodulesAfterMerge(repoRoot, previousBaseHead, cur
52991
53142
  const postStatus = await getGitRepoStatus(repoRoot, {
52992
53143
  includeSubmodules: true,
52993
53144
  submoduleIgnorePaths: options.submoduleIgnorePaths,
52994
- timeoutMs: 15e3
53145
+ timeoutMs: 15e3,
53146
+ // Re-read AFTER `submodule update` mutated the tree — MUST be fresh, never the
53147
+ // cached preStatus from moments ago (which would falsely report still-dirty).
53148
+ forceFresh: true
52995
53149
  });
52996
53150
  const remaining = (postStatus.submodules || []).filter((submodule) => updatePaths.includes(submodule.path) && (submodule.dirty || submodule.outOfSync || !!submodule.error));
52997
53151
  return {
@@ -64440,6 +64594,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
64440
64594
  isSessionHostLiveRuntime,
64441
64595
  isSessionHostRecoverySnapshot,
64442
64596
  isSetupComplete,
64597
+ isTaskReadonly,
64443
64598
  isUserFacingChatMessage,
64444
64599
  killIdeProcess,
64445
64600
  launchIDE,