@adhdev/daemon-core 0.9.82-rc.407 → 0.9.82-rc.409
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/git/git-status.d.ts +34 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +221 -95
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +220 -95
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-queue-assignment.d.ts +1 -1
- package/dist/mesh/mesh-work-queue.d.ts +35 -1
- package/dist/providers/chat-message-normalization.d.ts +18 -0
- package/package.json +2 -2
- package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +11 -1
- package/src/commands/chat-commands-write.ts +11 -0
- package/src/git/git-commands.ts +4 -1
- package/src/git/git-status.ts +219 -31
- package/src/index.ts +1 -1
- package/src/mesh/mesh-fast-forward.ts +5 -1
- package/src/mesh/mesh-queue-assignment.ts +8 -8
- package/src/mesh/mesh-reconcile-loop.ts +151 -94
- package/src/mesh/mesh-refine-gates.ts +6 -0
- package/src/mesh/mesh-runtime-store.ts +6 -5
- package/src/mesh/mesh-scheduling-runtime.ts +4 -7
- package/src/mesh/mesh-work-queue.ts +49 -8
- package/src/providers/chat-message-normalization.ts +53 -5
- package/src/providers/cli-provider-instance.ts +49 -8
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 ? "
|
|
403
|
-
const commitShort = readInjected(true ? "
|
|
404
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
405
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
402
|
+
const commit = readInjected(true ? "69cd9c459ec9efafe19a05f66899bb791d6e0561" : void 0) ?? "unknown";
|
|
403
|
+
const commitShort = readInjected(true ? "69cd9c45" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
404
|
+
const version = readInjected(true ? "0.9.82-rc.409" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
405
|
+
const builtAt = readInjected(true ? "2026-06-28T09:03:15.861Z" : 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(
|
|
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(
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
821
|
-
const
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
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:
|
|
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
|
|
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
|
-
...
|
|
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
|
-
|
|
4891
|
-
|
|
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
|
|
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
|
|
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
|
|
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) => !
|
|
9682
|
-
const activeReadonlyAssigned = assignedTasks.filter(
|
|
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 (!
|
|
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
|
|
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(
|
|
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
|
|
12344
|
-
if (
|
|
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
|
|
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
|
}
|
|
@@ -13755,21 +13830,42 @@ function extractFinalSummaryFromMessages(messages, maxChars = DEFAULT_FINAL_SUMM
|
|
|
13755
13830
|
}
|
|
13756
13831
|
return "";
|
|
13757
13832
|
}
|
|
13758
|
-
function
|
|
13833
|
+
function readChatMessageTimestampMs(message) {
|
|
13759
13834
|
if (!message) return void 0;
|
|
13760
13835
|
const record = message;
|
|
13761
|
-
for (const value of [record.timestamp, record.createdAt, record.created_at, record.updatedAt, record.time]) {
|
|
13836
|
+
for (const value of [record.timestamp, record.createdAt, record.created_at, record.updatedAt, record.time, record.receivedAt]) {
|
|
13762
13837
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
13763
|
-
|
|
13764
|
-
return new Date(ms).toISOString();
|
|
13838
|
+
return value > 1e10 ? value : value * 1e3;
|
|
13765
13839
|
}
|
|
13766
13840
|
if (typeof value === "string" && value.trim()) {
|
|
13767
13841
|
const ms = new Date(value.trim()).getTime();
|
|
13768
|
-
if (Number.isFinite(ms)) return
|
|
13842
|
+
if (Number.isFinite(ms)) return ms;
|
|
13769
13843
|
}
|
|
13770
13844
|
}
|
|
13771
13845
|
return void 0;
|
|
13772
13846
|
}
|
|
13847
|
+
function readChatMessageTimestampIso(message) {
|
|
13848
|
+
const ms = readChatMessageTimestampMs(message);
|
|
13849
|
+
return typeof ms === "number" ? new Date(ms).toISOString() : void 0;
|
|
13850
|
+
}
|
|
13851
|
+
function extractFinalSummaryFromMessagesAfter(messages, minTimestampMs, maxChars = DEFAULT_FINAL_SUMMARY_MAX_CHARS) {
|
|
13852
|
+
if (!Array.isArray(messages) || messages.length === 0) return "";
|
|
13853
|
+
const hasBoundary = typeof minTimestampMs === "number" && Number.isFinite(minTimestampMs);
|
|
13854
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
13855
|
+
const msg = messages[i];
|
|
13856
|
+
if (!msg) continue;
|
|
13857
|
+
if (hasBoundary) {
|
|
13858
|
+
const ts2 = readChatMessageTimestampMs(msg);
|
|
13859
|
+
if (typeof ts2 === "number" && ts2 < minTimestampMs) continue;
|
|
13860
|
+
}
|
|
13861
|
+
const classification = classifyChatMessageVisibility(msg);
|
|
13862
|
+
if (classification.isUserFacing && (msg.role === "assistant" || msg.role === "model")) {
|
|
13863
|
+
const text = flattenContent(msg.content).trim();
|
|
13864
|
+
if (text) return text.slice(0, maxChars);
|
|
13865
|
+
}
|
|
13866
|
+
}
|
|
13867
|
+
return "";
|
|
13868
|
+
}
|
|
13773
13869
|
function extractFinalAssistantSummaryEvidence(messages, maxChars = DEFAULT_FINAL_SUMMARY_MAX_CHARS) {
|
|
13774
13870
|
if (!Array.isArray(messages) || messages.length === 0) return { finalSummary: "" };
|
|
13775
13871
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
@@ -16379,11 +16475,8 @@ function resolveTunedReconcileMs(envName, def, min, max) {
|
|
|
16379
16475
|
}
|
|
16380
16476
|
return def;
|
|
16381
16477
|
}
|
|
16382
|
-
function
|
|
16383
|
-
return resolveTunedReconcileMs("
|
|
16384
|
-
}
|
|
16385
|
-
function resolveAckedTurnSettleMs() {
|
|
16386
|
-
return resolveTunedReconcileMs("MESH_INFLIGHT_ACKED_TURN_SETTLE_MS", 2e4, 0, 18e4);
|
|
16478
|
+
function resolveAckedDeathDeadlineMs() {
|
|
16479
|
+
return resolveTunedReconcileMs("MESH_INFLIGHT_ACKED_DEATH_DEADLINE_MS", 8 * 6e4, 0, 60 * 6e4);
|
|
16387
16480
|
}
|
|
16388
16481
|
function inFlightSynthKey(meshId, taskId) {
|
|
16389
16482
|
return `${meshId}::${taskId}`;
|
|
@@ -16994,9 +17087,9 @@ async function reconcileUnterminatedDirectDispatches(components, mesh, selfIds,
|
|
|
16994
17087
|
const activeTaskKeys = new Set(
|
|
16995
17088
|
dispatches.map((d) => readNonEmptyString2(d.taskId)).filter(Boolean).map((taskId) => inFlightSynthKey(mesh.id, taskId))
|
|
16996
17089
|
);
|
|
16997
|
-
for (const key of
|
|
17090
|
+
for (const key of inFlightAckedHoldState.keys()) {
|
|
16998
17091
|
if (key.startsWith(`${mesh.id}::`) && !activeTaskKeys.has(key)) {
|
|
16999
|
-
|
|
17092
|
+
inFlightAckedHoldState.delete(key);
|
|
17000
17093
|
}
|
|
17001
17094
|
}
|
|
17002
17095
|
const dispatchMeshCommand = components.dispatchMeshCommand;
|
|
@@ -17017,49 +17110,61 @@ async function reconcileUnterminatedDirectDispatches(components, mesh, selfIds,
|
|
|
17017
17110
|
...node?.workspace ? { workspace: node.workspace } : {},
|
|
17018
17111
|
...providerType ? { agentType: providerType, providerType } : {}
|
|
17019
17112
|
};
|
|
17113
|
+
const synthKey = inFlightSynthKey(mesh.id, taskId);
|
|
17114
|
+
const isAcked = dispatch.status === "acked";
|
|
17020
17115
|
let payload = null;
|
|
17116
|
+
let readFailed = false;
|
|
17021
17117
|
try {
|
|
17022
17118
|
if (isLocalNode) {
|
|
17023
17119
|
const result = await components.commandHandler.handle("read_chat", readArgs);
|
|
17024
|
-
if (result && result.success === false)
|
|
17025
|
-
|
|
17120
|
+
if (result && result.success === false) {
|
|
17121
|
+
readFailed = true;
|
|
17122
|
+
} else {
|
|
17123
|
+
payload = unwrapReadChatPayload(result);
|
|
17124
|
+
}
|
|
17026
17125
|
} else if (dispatchMeshCommand) {
|
|
17027
17126
|
const result = await dispatchMeshCommand(nodeDaemonId, "read_chat", readArgs);
|
|
17028
17127
|
payload = unwrapReadChatPayload(result);
|
|
17029
|
-
if (payload && payload.success === false)
|
|
17128
|
+
if (payload && payload.success === false) {
|
|
17129
|
+
payload = null;
|
|
17130
|
+
readFailed = true;
|
|
17131
|
+
}
|
|
17030
17132
|
} else {
|
|
17031
17133
|
continue;
|
|
17032
17134
|
}
|
|
17033
17135
|
} catch {
|
|
17136
|
+
readFailed = true;
|
|
17137
|
+
}
|
|
17138
|
+
if (!payload && !readFailed) continue;
|
|
17139
|
+
if (readFailed || !payload) {
|
|
17140
|
+
if (isAcked) {
|
|
17141
|
+
const prior = inFlightAckedHoldState.get(synthKey);
|
|
17142
|
+
const failures = (prior?.consecutiveReadFailures ?? 0) + 1;
|
|
17143
|
+
const liveConfirmedSinceAck = prior?.liveConfirmedSinceAck ?? false;
|
|
17144
|
+
inFlightAckedHoldState.set(synthKey, { liveConfirmedSinceAck, consecutiveReadFailures: failures });
|
|
17145
|
+
if (liveConfirmedSinceAck && failures >= ACKED_DEATH_CONSECUTIVE_READ_FAILURES) {
|
|
17146
|
+
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`);
|
|
17147
|
+
}
|
|
17148
|
+
}
|
|
17034
17149
|
continue;
|
|
17035
17150
|
}
|
|
17036
|
-
|
|
17037
|
-
const synthKey = inFlightSynthKey(mesh.id, taskId);
|
|
17151
|
+
inFlightAckedHoldState.set(synthKey, { liveConfirmedSinceAck: true, consecutiveReadFailures: 0 });
|
|
17038
17152
|
const nowMs = Date.now();
|
|
17039
17153
|
if (readChatPayloadStatus(payload) !== "idle") {
|
|
17040
|
-
inFlightIdleObservationCounts.delete(synthKey);
|
|
17041
17154
|
continue;
|
|
17042
17155
|
}
|
|
17043
|
-
if (
|
|
17044
|
-
const prior = inFlightIdleObservationCounts.get(synthKey);
|
|
17045
|
-
const firstIdleAtMs = prior?.firstIdleAtMs ?? nowMs;
|
|
17046
|
-
const idleStreak = (prior?.count ?? 0) + 1;
|
|
17047
|
-
inFlightIdleObservationCounts.set(synthKey, { count: idleStreak, firstIdleAtMs });
|
|
17048
|
-
const idleSettleMs = nowMs - firstIdleAtMs;
|
|
17049
|
-
const minIdleSettleMs = resolveMinIdleSettleMs();
|
|
17050
|
-
const ackedTurnSettleMs = resolveAckedTurnSettleMs();
|
|
17156
|
+
if (isAcked) {
|
|
17051
17157
|
const ackedAtMs = Date.parse(readNonEmptyString2(dispatch.updatedAt));
|
|
17052
17158
|
const sinceAckMs = Number.isFinite(ackedAtMs) ? nowMs - ackedAtMs : Number.POSITIVE_INFINITY;
|
|
17053
|
-
const
|
|
17054
|
-
|
|
17055
|
-
|
|
17056
|
-
if (!tickGuardMet || !settleGuardMet || !ackGuardMet) {
|
|
17057
|
-
LOG.info("MeshReconcile", `In-flight synth hold: task ${taskId} on node ${nodeId} (mesh ${mesh.id}) idle ${idleStreak}/${REQUIRED_CONSECUTIVE_IDLE_TICKS_FOR_INFLIGHT_SYNTH} tick(s), settle ${Math.round(idleSettleMs / 1e3)}s/${Math.round(minIdleSettleMs / 1e3)}s, since-ack ${Number.isFinite(sinceAckMs) ? Math.round(sinceAckMs / 1e3) + "s" : "\u221E"}/${Math.round(ackedTurnSettleMs / 1e3)}s \u2014 deferring completion synth until the worker's turn genuinely settles (guards against a mid-turn idle window pre-empting the real completion)`);
|
|
17159
|
+
const deathDeadlineMs = resolveAckedDeathDeadlineMs();
|
|
17160
|
+
if (sinceAckMs < deathDeadlineMs) {
|
|
17161
|
+
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.`);
|
|
17058
17162
|
continue;
|
|
17059
17163
|
}
|
|
17164
|
+
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).`);
|
|
17060
17165
|
}
|
|
17061
17166
|
if (realTerminalEmitPendingForTask(mesh.id, taskId)) {
|
|
17062
|
-
|
|
17167
|
+
inFlightAckedHoldState.delete(synthKey);
|
|
17063
17168
|
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`);
|
|
17064
17169
|
continue;
|
|
17065
17170
|
}
|
|
@@ -17081,7 +17186,7 @@ async function reconcileUnterminatedDirectDispatches(components, mesh, selfIds,
|
|
|
17081
17186
|
}
|
|
17082
17187
|
const reprobeStatus = await reprobeWorkerStatus(components, { isLocalNode, nodeDaemonId, readArgs });
|
|
17083
17188
|
if (reprobeStatus && reprobeStatus !== "idle") {
|
|
17084
|
-
|
|
17189
|
+
inFlightAckedHoldState.delete(synthKey);
|
|
17085
17190
|
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`);
|
|
17086
17191
|
continue;
|
|
17087
17192
|
}
|
|
@@ -17223,7 +17328,7 @@ function setupMeshReconcileLoop(components) {
|
|
|
17223
17328
|
}
|
|
17224
17329
|
};
|
|
17225
17330
|
}
|
|
17226
|
-
var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_AUTO_PRUNE_MIN_AGE_MS,
|
|
17331
|
+
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;
|
|
17227
17332
|
var init_mesh_reconcile_loop = __esm({
|
|
17228
17333
|
"src/mesh/mesh-reconcile-loop.ts"() {
|
|
17229
17334
|
"use strict";
|
|
@@ -17245,8 +17350,8 @@ var init_mesh_reconcile_loop = __esm({
|
|
|
17245
17350
|
init_chat_message_normalization();
|
|
17246
17351
|
DEFAULT_RECONCILE_INTERVAL_MS = 4e3;
|
|
17247
17352
|
DEFAULT_AUTO_PRUNE_MIN_AGE_MS = 24 * 60 * 6e4;
|
|
17248
|
-
|
|
17249
|
-
|
|
17353
|
+
ACKED_DEATH_CONSECUTIVE_READ_FAILURES = 3;
|
|
17354
|
+
inFlightAckedHoldState = /* @__PURE__ */ new Map();
|
|
17250
17355
|
coordinatorModalParkState = /* @__PURE__ */ new Map();
|
|
17251
17356
|
heldEventLedgerRecorded = /* @__PURE__ */ new Set();
|
|
17252
17357
|
ASSIGNED_STRANDED_DEADLINE_MS = 5 * 6e4;
|
|
@@ -23138,6 +23243,7 @@ __export(index_exports, {
|
|
|
23138
23243
|
isSessionHostLiveRuntime: () => isSessionHostLiveRuntime,
|
|
23139
23244
|
isSessionHostRecoverySnapshot: () => isSessionHostRecoverySnapshot,
|
|
23140
23245
|
isSetupComplete: () => isSetupComplete,
|
|
23246
|
+
isTaskReadonly: () => isTaskReadonly,
|
|
23141
23247
|
isUserFacingChatMessage: () => isUserFacingChatMessage,
|
|
23142
23248
|
killIdeProcess: () => killIdeProcess,
|
|
23143
23249
|
launchIDE: () => launchIDE,
|
|
@@ -24229,7 +24335,7 @@ function validateMutatingMessage(value) {
|
|
|
24229
24335
|
async function gitCheckpoint(workspace, message, includeUntracked) {
|
|
24230
24336
|
const repo = await resolveGitRepository(workspace);
|
|
24231
24337
|
const repoRoot = repo.repoRoot;
|
|
24232
|
-
const statusResult = await getGitRepoStatus(workspace);
|
|
24338
|
+
const statusResult = await getGitRepoStatus(workspace, { forceFresh: true });
|
|
24233
24339
|
if (statusResult.hasConflicts) {
|
|
24234
24340
|
throw new GitCommandError("conflict", "Repository has conflicts \u2014 resolve before checkpointing");
|
|
24235
24341
|
}
|
|
@@ -32711,6 +32817,10 @@ async function handleResolveAction(h, args) {
|
|
|
32711
32817
|
const effectiveStatus = status?.status === "waiting_approval" || targetState?.activeChat?.status === "waiting_approval" || parsedStatus?.status === "waiting_approval" ? "waiting_approval" : status?.status;
|
|
32712
32818
|
LOG.info("Command", `[resolveAction] CLI PTY gate target=${String(args?.targetSessionId || "")} rawStatus=${String(status?.status || "")} effectiveStatus=${String(effectiveStatus || "")} statusModal=${statusModal ? "yes" : "no"} surfacedModal=${surfacedModal ? "yes" : "no"} parsedModal=${parsedModal ? "yes" : "no"} instance=${targetInstance ? "yes" : "no"}`);
|
|
32713
32819
|
if (!effectiveModal) {
|
|
32820
|
+
if (typeof adapter.isApprovalRecentlyResolved === "function" && adapter.isApprovalRecentlyResolved()) {
|
|
32821
|
+
LOG.info("Command", `[resolveAction] CLI PTY \u2192 already_resolved (modal gone, resolved within cooldown)`);
|
|
32822
|
+
return { success: true, alreadyResolved: true, status: "already_resolved" };
|
|
32823
|
+
}
|
|
32714
32824
|
return { success: false, error: "Not in approval state" };
|
|
32715
32825
|
}
|
|
32716
32826
|
const buttons = Array.isArray(effectiveModal.buttons) ? effectiveModal.buttons : [];
|
|
@@ -40732,14 +40842,14 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
40732
40842
|
source: "unavailable"
|
|
40733
40843
|
};
|
|
40734
40844
|
}
|
|
40735
|
-
completionFinalSummary(parsedMessages) {
|
|
40845
|
+
completionFinalSummary(parsedMessages, turnStartedAt) {
|
|
40736
40846
|
const adapterOwnsMessagesElsewhere = this.adapter?.chatMessagesOwnedExternally === true;
|
|
40737
40847
|
const parsedSummary = extractFinalSummaryFromMessages(
|
|
40738
40848
|
this.completionHasFinalAssistantMessage(parsedMessages) ? Array.isArray(parsedMessages) ? parsedMessages : [] : []
|
|
40739
40849
|
);
|
|
40740
40850
|
if (adapterOwnsMessagesElsewhere) {
|
|
40741
40851
|
const externalMessages = this.readExternalCompletionMessages();
|
|
40742
|
-
const externalSummary = externalMessages ?
|
|
40852
|
+
const externalSummary = externalMessages ? extractFinalSummaryFromMessagesAfter(externalMessages, turnStartedAt) : "";
|
|
40743
40853
|
if (externalSummary) return externalSummary;
|
|
40744
40854
|
return parsedSummary || void 0;
|
|
40745
40855
|
}
|
|
@@ -41026,7 +41136,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
41026
41136
|
// delegated session's inbox preview blank — or, for a LOCAL worktree session,
|
|
41027
41137
|
// stuck on the dispatched user task. If the parser DID surface assistant text,
|
|
41028
41138
|
// prefer it; only fall back to '' when no assistant summary can be derived.
|
|
41029
|
-
finalSummary: blockReason.startsWith("parsed_status:") ? this.completionFinalSummary(this.adapter?.getScriptParsedStatus()?.messages) ?? "" : this.completionFinalSummary(this.adapter?.getScriptParsedStatus()?.messages),
|
|
41139
|
+
finalSummary: blockReason.startsWith("parsed_status:") ? this.completionFinalSummary(this.adapter?.getScriptParsedStatus()?.messages, pending.turnStartedAt) ?? "" : this.completionFinalSummary(this.adapter?.getScriptParsedStatus()?.messages, pending.turnStartedAt),
|
|
41030
41140
|
completionDiagnostic
|
|
41031
41141
|
});
|
|
41032
41142
|
this.completedDebouncePending = null;
|
|
@@ -41046,7 +41156,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
41046
41156
|
timestamp: pending.timestamp,
|
|
41047
41157
|
// ARCH-REFACTOR R1: attribute to the turn captured at idle-transition.
|
|
41048
41158
|
...pending.taskId ? { taskId: pending.taskId } : {},
|
|
41049
|
-
finalSummary: this.completionFinalSummary(this.adapter?.getScriptParsedStatus()?.messages)
|
|
41159
|
+
finalSummary: this.completionFinalSummary(this.adapter?.getScriptParsedStatus()?.messages, pending.turnStartedAt)
|
|
41050
41160
|
});
|
|
41051
41161
|
this.completedDebouncePending = null;
|
|
41052
41162
|
this.completedDebounceTimer = null;
|
|
@@ -41162,7 +41272,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
41162
41272
|
*/
|
|
41163
41273
|
recheckAutoApproveSettled() {
|
|
41164
41274
|
try {
|
|
41165
|
-
const adapterStatus = this.adapter.getStatus({ allowParse:
|
|
41275
|
+
const adapterStatus = this.adapter.getStatus({ allowParse: true });
|
|
41166
41276
|
this.maybeAutoApproveStatus(adapterStatus, Date.now());
|
|
41167
41277
|
} catch {
|
|
41168
41278
|
}
|
|
@@ -41393,7 +41503,16 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
41393
41503
|
// ARCH-REFACTOR R1: snapshot the completing turn's taskId NOW (sync),
|
|
41394
41504
|
// before any follow-up task's flush can start a new turn and move
|
|
41395
41505
|
// engine.currentTurnTaskId.
|
|
41396
|
-
...this.completingTurnTaskId() ? { taskId: this.completingTurnTaskId() } : {}
|
|
41506
|
+
...this.completingTurnTaskId() ? { taskId: this.completingTurnTaskId() } : {},
|
|
41507
|
+
// NOTIF Defect-B: snapshot the producing turn's START instant NOW, for the
|
|
41508
|
+
// same reason as taskId — a follow-up turn moves engine.currentTurnStartedAt.
|
|
41509
|
+
// Prefer the engine's per-turn start (set at onTurnStarted, earliest reliable
|
|
41510
|
+
// anchor) and fall back to generatingStartedAt (when generating was observed).
|
|
41511
|
+
...(() => {
|
|
41512
|
+
const engineTurnStart = typeof this.adapter?.currentTurnStartedAt === "number" && Number.isFinite(this.adapter.currentTurnStartedAt) ? this.adapter.currentTurnStartedAt : 0;
|
|
41513
|
+
const turnStartedAt = engineTurnStart || this.generatingStartedAt || 0;
|
|
41514
|
+
return turnStartedAt ? { turnStartedAt } : {};
|
|
41515
|
+
})()
|
|
41397
41516
|
};
|
|
41398
41517
|
const ownsExternalHistory = !!this.adapter?.chatMessagesOwnedExternally;
|
|
41399
41518
|
const meshWorkerSession = this.isMeshWorkerSession();
|
|
@@ -53025,7 +53144,10 @@ async function alignRefinerySubmodulesAfterMerge(repoRoot, previousBaseHead, cur
|
|
|
53025
53144
|
const preStatus = await getGitRepoStatus(repoRoot, {
|
|
53026
53145
|
includeSubmodules: true,
|
|
53027
53146
|
submoduleIgnorePaths: options.submoduleIgnorePaths,
|
|
53028
|
-
timeoutMs: 15e3
|
|
53147
|
+
timeoutMs: 15e3,
|
|
53148
|
+
// Decision path — the out-of-sync submodule set drives a mutating `submodule
|
|
53149
|
+
// update`. Must not act on a TTL-cached status; bypass the C1 cache.
|
|
53150
|
+
forceFresh: true
|
|
53029
53151
|
});
|
|
53030
53152
|
const outOfSyncPaths = (preStatus.submodules || []).filter((submodule) => submodule.dirty || submodule.outOfSync || !!submodule.error).map((submodule) => submodule.path);
|
|
53031
53153
|
const updatePaths = [.../* @__PURE__ */ new Set([...changedGitlinkPaths, ...outOfSyncPaths])].sort();
|
|
@@ -53054,7 +53176,10 @@ async function alignRefinerySubmodulesAfterMerge(repoRoot, previousBaseHead, cur
|
|
|
53054
53176
|
const postStatus = await getGitRepoStatus(repoRoot, {
|
|
53055
53177
|
includeSubmodules: true,
|
|
53056
53178
|
submoduleIgnorePaths: options.submoduleIgnorePaths,
|
|
53057
|
-
timeoutMs: 15e3
|
|
53179
|
+
timeoutMs: 15e3,
|
|
53180
|
+
// Re-read AFTER `submodule update` mutated the tree — MUST be fresh, never the
|
|
53181
|
+
// cached preStatus from moments ago (which would falsely report still-dirty).
|
|
53182
|
+
forceFresh: true
|
|
53058
53183
|
});
|
|
53059
53184
|
const remaining = (postStatus.submodules || []).filter((submodule) => updatePaths.includes(submodule.path) && (submodule.dirty || submodule.outOfSync || !!submodule.error));
|
|
53060
53185
|
return {
|
|
@@ -64503,6 +64628,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
|
|
|
64503
64628
|
isSessionHostLiveRuntime,
|
|
64504
64629
|
isSessionHostRecoverySnapshot,
|
|
64505
64630
|
isSetupComplete,
|
|
64631
|
+
isTaskReadonly,
|
|
64506
64632
|
isUserFacingChatMessage,
|
|
64507
64633
|
killIdeProcess,
|
|
64508
64634
|
launchIDE,
|