@adhdev/daemon-standalone 1.0.56-rc.3 → 1.0.56-rc.5
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 +2252 -165
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/vendor/mcp-server/index.js +638 -22
- package/vendor/mcp-server/index.js.map +1 -1
package/dist/index.js
CHANGED
|
@@ -35904,6 +35904,15 @@ var require_dist3 = __commonJS({
|
|
|
35904
35904
|
} else {
|
|
35905
35905
|
delete policy.quotaRouting;
|
|
35906
35906
|
}
|
|
35907
|
+
if (policy.onDependencyFailure === void 0 || policy.onDependencyFailure === "block") {
|
|
35908
|
+
delete policy.onDependencyFailure;
|
|
35909
|
+
} else if (policy.onDependencyFailure === "cancel") {
|
|
35910
|
+
policy.onDependencyFailure = "cancel";
|
|
35911
|
+
} else {
|
|
35912
|
+
throw new Error(
|
|
35913
|
+
`invalid_on_dependency_failure: must be 'block' or 'cancel' (got ${JSON.stringify(policy.onDependencyFailure)}). Invalid values are rejected; they do not silently become 'block'.`
|
|
35914
|
+
);
|
|
35915
|
+
}
|
|
35907
35916
|
return policy;
|
|
35908
35917
|
}
|
|
35909
35918
|
function resolveDelegatedWorkerAutoApprove(meshPolicy, nodePolicy, provider, repoConfig, providerType, overrideModeId, overrideLegacyAutoApprove) {
|
|
@@ -36040,7 +36049,8 @@ var require_dist3 = __commonJS({
|
|
|
36040
36049
|
// notifies the owner. Opt into 'auto_silent_on_dispatch' to one-shot-silence the
|
|
36041
36050
|
// routine idle push for a coordinator-driven task (approval/failure notifications
|
|
36042
36051
|
// are never affected — see coordinatorIdlePushPolicy).
|
|
36043
|
-
coordinatorIdlePushPolicy: "always"
|
|
36052
|
+
coordinatorIdlePushPolicy: "always",
|
|
36053
|
+
onDependencyFailure: "block"
|
|
36044
36054
|
};
|
|
36045
36055
|
DEFAULT_QUOTA_ROUTING_POLICY = {
|
|
36046
36056
|
staleAfterMs: 30 * 60 * 1e3,
|
|
@@ -36610,10 +36620,10 @@ var require_dist3 = __commonJS({
|
|
|
36610
36620
|
}
|
|
36611
36621
|
function getDaemonBuildInfo() {
|
|
36612
36622
|
if (cached2) return cached2;
|
|
36613
|
-
const commit = readInjected(true ? "
|
|
36614
|
-
const commitShort = readInjected(true ? "
|
|
36615
|
-
const version2 = readInjected(true ? "1.0.56-rc.
|
|
36616
|
-
const builtAt = readInjected(true ? "2026-08-
|
|
36623
|
+
const commit = readInjected(true ? "d41089a0d560d75e670a90d3c4e9955bf955bbbc" : void 0) ?? "unknown";
|
|
36624
|
+
const commitShort = readInjected(true ? "d41089a0" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
36625
|
+
const version2 = readInjected(true ? "1.0.56-rc.5" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
36626
|
+
const builtAt = readInjected(true ? "2026-08-19T05:42:19.473Z" : void 0);
|
|
36617
36627
|
cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
|
|
36618
36628
|
return cached2;
|
|
36619
36629
|
}
|
|
@@ -42842,9 +42852,14 @@ ${error48.message || ""}`;
|
|
|
42842
42852
|
CANONICAL_MESH_TOOL_NAMES = [
|
|
42843
42853
|
"mesh_status",
|
|
42844
42854
|
"mesh_list_nodes",
|
|
42845
|
-
|
|
42855
|
+
// GRAPH-ORCHESTRATION Phase F — batch before task, mirroring ALL_MESH_TOOLS.
|
|
42846
42856
|
"mesh_enqueue_batch",
|
|
42857
|
+
"mesh_enqueue_task",
|
|
42847
42858
|
"mesh_view_queue",
|
|
42859
|
+
// GRAPH-ORCHESTRATION Phase E — the coordinator gate + graph view surface.
|
|
42860
|
+
"mesh_graph_view",
|
|
42861
|
+
"mesh_graph_gate_claim",
|
|
42862
|
+
"mesh_graph_gate_release",
|
|
42848
42863
|
"mesh_queue_cancel",
|
|
42849
42864
|
"mesh_queue_requeue",
|
|
42850
42865
|
"mesh_send_task",
|
|
@@ -43952,6 +43967,10 @@ ${error48.message || ""}`;
|
|
|
43952
43967
|
return `[System] ${args.nodeLabel} already has completion evidence${metadata}. The no-progress monitor reconciled the terminal handoff and marked the session complete; wait for the queued completion event/status refresh before doing any manual transcript check.`;
|
|
43953
43968
|
}
|
|
43954
43969
|
const reviewRecommended = args.metadataEvent.reviewRecommended === true;
|
|
43970
|
+
const timedOutNoResponse = args.metadataEvent.completionDiagnostic && typeof args.metadataEvent.completionDiagnostic === "object" && args.metadataEvent.completionDiagnostic.timedOutWithoutFinalAssistant === true;
|
|
43971
|
+
if (timedOutNoResponse) {
|
|
43972
|
+
return `[System] ${args.nodeLabel} was forcibly terminated WITHOUT a response${metadata}: the finalization wait expired with no final assistant answer, so the task was recorded as a forced termination (failed), NOT as a completion. Verify the actual state with mesh_read_chat / git status and re-dispatch the task if the work is still needed.`;
|
|
43973
|
+
}
|
|
43955
43974
|
const weakCompletion = isWeakCompletionMetadata(args.metadataEvent);
|
|
43956
43975
|
const completionLead = weakCompletion ? `[System] ${args.nodeLabel} reported a possible completion (weak evidence) \u2014 awaiting confirmation${metadata}.` : `[System] ${args.nodeLabel} has completed its task and is now idle${metadata}.`;
|
|
43957
43976
|
const verifyTextNote = " Completion evidence is weak \u2014 verify via mesh_read_chat or git status before declaring the task done; the worker may still be mid-turn or parked on an approval/modal.";
|
|
@@ -45352,14 +45371,40 @@ Next step: ${nextStep}`;
|
|
|
45352
45371
|
sessionOrderChains = /* @__PURE__ */ new Map();
|
|
45353
45372
|
}
|
|
45354
45373
|
});
|
|
45374
|
+
function newMeshGraphId() {
|
|
45375
|
+
return (0, import_crypto6.randomUUID)();
|
|
45376
|
+
}
|
|
45377
|
+
function newMeshGraphNodeId() {
|
|
45378
|
+
return (0, import_crypto6.randomUUID)();
|
|
45379
|
+
}
|
|
45380
|
+
function newMeshGraphGateId() {
|
|
45381
|
+
return (0, import_crypto6.randomUUID)();
|
|
45382
|
+
}
|
|
45355
45383
|
function newMeshGraphOutboxId() {
|
|
45356
45384
|
return (0, import_crypto6.randomUUID)();
|
|
45357
45385
|
}
|
|
45358
45386
|
var import_crypto6;
|
|
45387
|
+
var MESH_GRAPH_SCHEMA_VERSION;
|
|
45388
|
+
var MESH_GRAPH_ON_UPSTREAM_SKIP_POLICIES;
|
|
45389
|
+
var MESH_GRAPH_GATE_ACTIONS;
|
|
45390
|
+
var MESH_GRAPH_GATE_TIMEOUT_POLICIES;
|
|
45391
|
+
var MESH_GRAPH_ON_DEPENDENCY_FAILURE_POLICIES;
|
|
45359
45392
|
var init_mesh_graph_types = __esm2({
|
|
45360
45393
|
"src/mesh/mesh-graph-types.ts"() {
|
|
45361
45394
|
"use strict";
|
|
45362
45395
|
import_crypto6 = require("crypto");
|
|
45396
|
+
MESH_GRAPH_SCHEMA_VERSION = 2;
|
|
45397
|
+
MESH_GRAPH_ON_UPSTREAM_SKIP_POLICIES = ["skip", "omit_dependency"];
|
|
45398
|
+
MESH_GRAPH_GATE_ACTIONS = [
|
|
45399
|
+
"refinery",
|
|
45400
|
+
"approval",
|
|
45401
|
+
"ci_wait",
|
|
45402
|
+
"publish",
|
|
45403
|
+
"deploy",
|
|
45404
|
+
"custom"
|
|
45405
|
+
];
|
|
45406
|
+
MESH_GRAPH_GATE_TIMEOUT_POLICIES = ["hold", "cancel_downstream", "fail_graph"];
|
|
45407
|
+
MESH_GRAPH_ON_DEPENDENCY_FAILURE_POLICIES = ["block", "cancel"];
|
|
45363
45408
|
}
|
|
45364
45409
|
});
|
|
45365
45410
|
function maskKeepTail(token) {
|
|
@@ -45895,6 +45940,188 @@ ${blocks.join("\n\n")}`;
|
|
|
45895
45940
|
init_mesh_graph_workspace_identity();
|
|
45896
45941
|
}
|
|
45897
45942
|
});
|
|
45943
|
+
function parseOnDependencyFailurePolicy(value) {
|
|
45944
|
+
if (value === void 0 || value === null || value === "") return "block";
|
|
45945
|
+
if (typeof value === "string" && MESH_GRAPH_ON_DEPENDENCY_FAILURE_POLICIES.includes(value)) {
|
|
45946
|
+
return value;
|
|
45947
|
+
}
|
|
45948
|
+
throw new MeshGraphPolicyError(
|
|
45949
|
+
`invalid_on_dependency_failure: must be 'block' or 'cancel' (got ${JSON.stringify(value)}). Invalid values are rejected; they do not silently become 'block'.`
|
|
45950
|
+
);
|
|
45951
|
+
}
|
|
45952
|
+
function resolveOnDependencyFailurePolicy(value) {
|
|
45953
|
+
return value === "cancel" ? "cancel" : "block";
|
|
45954
|
+
}
|
|
45955
|
+
function parseGraphPolicy(policyJson) {
|
|
45956
|
+
const parsed = safeParseObject(policyJson);
|
|
45957
|
+
const raw = parsed?.on_dependency_failure ?? parsed?.onDependencyFailure;
|
|
45958
|
+
return { on_dependency_failure: parseOnDependencyFailurePolicy(raw) };
|
|
45959
|
+
}
|
|
45960
|
+
function projectGraphPublicPolicy(policyJson) {
|
|
45961
|
+
try {
|
|
45962
|
+
return parseGraphPolicy(policyJson);
|
|
45963
|
+
} catch (e) {
|
|
45964
|
+
return {
|
|
45965
|
+
on_dependency_failure: "block",
|
|
45966
|
+
policyInvalid: e instanceof Error ? e.message : String(e)
|
|
45967
|
+
};
|
|
45968
|
+
}
|
|
45969
|
+
}
|
|
45970
|
+
function serializeGraphPolicy(policy) {
|
|
45971
|
+
return JSON.stringify({ on_dependency_failure: policy.on_dependency_failure });
|
|
45972
|
+
}
|
|
45973
|
+
function isGraphSkipPlaceholder(entry) {
|
|
45974
|
+
if (!entry) return false;
|
|
45975
|
+
const reason = entry.blockedReason ?? entry.cancelReason ?? "";
|
|
45976
|
+
return entry.status === "cancelled" && reason.startsWith(MESH_GRAPH_SKIP_BLOCK_PREFIX);
|
|
45977
|
+
}
|
|
45978
|
+
function parseLegacyDependencyFailedPredecessor(reason) {
|
|
45979
|
+
if (typeof reason !== "string") return null;
|
|
45980
|
+
if (!reason.startsWith(LEGACY_DEPENDENCY_FAILED_PREFIX)) return null;
|
|
45981
|
+
const rest = reason.slice(LEGACY_DEPENDENCY_FAILED_PREFIX.length);
|
|
45982
|
+
if (!rest || rest.startsWith(":")) return null;
|
|
45983
|
+
return rest;
|
|
45984
|
+
}
|
|
45985
|
+
function deriveDependencyFailures(dependsOn, statusById, depMetaById) {
|
|
45986
|
+
const deps = Array.isArray(dependsOn) ? dependsOn : [];
|
|
45987
|
+
const failures = [];
|
|
45988
|
+
for (const taskId of deps) {
|
|
45989
|
+
const meta3 = depMetaById?.get(taskId);
|
|
45990
|
+
const status = statusById.get(taskId) ?? meta3?.status;
|
|
45991
|
+
if (isGraphSkipPlaceholder({ status, blockedReason: meta3?.blockedReason, cancelReason: meta3?.cancelReason })) {
|
|
45992
|
+
continue;
|
|
45993
|
+
}
|
|
45994
|
+
if (status !== "failed" && status !== "cancelled") continue;
|
|
45995
|
+
const reason = meta3?.cancelReason ?? meta3?.blockedReason;
|
|
45996
|
+
failures.push({
|
|
45997
|
+
taskId,
|
|
45998
|
+
status,
|
|
45999
|
+
...reason ? { reason } : {}
|
|
46000
|
+
});
|
|
46001
|
+
}
|
|
46002
|
+
return failures;
|
|
46003
|
+
}
|
|
46004
|
+
function classifyGraphRollup(nodes) {
|
|
46005
|
+
const gates = nodes.filter((n) => n.kind === "coordinator_gate");
|
|
46006
|
+
const workers = nodes.filter((n) => n.kind !== "coordinator_gate");
|
|
46007
|
+
if (gates.some((g) => !GATE_SETTLED.has(g.state))) return null;
|
|
46008
|
+
if (workers.some((w) => !WORKER_SETTLED.has(w.state))) return null;
|
|
46009
|
+
if (workers.some((w) => w.state === "failed")) return "failed";
|
|
46010
|
+
if (workers.some((w) => w.state === "cancelled") || gates.some((g) => g.state === "cancelled" || g.state === "expired")) {
|
|
46011
|
+
return "cancelled";
|
|
46012
|
+
}
|
|
46013
|
+
if (workers.length === 0 && gates.length === 0) return "completed";
|
|
46014
|
+
if (workers.every((w) => SUCCESS_TERMINAL.has(w.state)) && gates.every((g) => g.state === "released" || SUCCESS_TERMINAL.has(g.state))) {
|
|
46015
|
+
return "completed";
|
|
46016
|
+
}
|
|
46017
|
+
return "completed";
|
|
46018
|
+
}
|
|
46019
|
+
function applyGraphCancelCascade(store, nodes, edges, origin, nowIso, updateNodeState) {
|
|
46020
|
+
const byId = new Map(nodes.map((n) => [n.nodeId, n]));
|
|
46021
|
+
const frontier = [origin.nodeId];
|
|
46022
|
+
const seen = new Set(frontier);
|
|
46023
|
+
const cancelledNodeIds = [];
|
|
46024
|
+
const cancelledTaskIds = [];
|
|
46025
|
+
const sourceTaskId = origin.queueTaskId ?? origin.nodeId;
|
|
46026
|
+
const sourceRef = origin.ref ?? origin.nodeId;
|
|
46027
|
+
while (frontier.length > 0) {
|
|
46028
|
+
const currentId = frontier.pop();
|
|
46029
|
+
const outgoing = edges.filter((e) => e.fromNodeId === currentId && e.kind !== "gate");
|
|
46030
|
+
for (const edge of outgoing) {
|
|
46031
|
+
const target = byId.get(edge.toNodeId);
|
|
46032
|
+
if (!target || seen.has(target.nodeId)) continue;
|
|
46033
|
+
seen.add(target.nodeId);
|
|
46034
|
+
if (target.kind !== "worker_task") continue;
|
|
46035
|
+
if (target.state === "skipped") continue;
|
|
46036
|
+
if (!CANCELABLE_NODE_STATES.has(target.state)) continue;
|
|
46037
|
+
const reason = `${LEGACY_DEPENDENCY_FAILED_PREFIX}${sourceRef}`;
|
|
46038
|
+
updateNodeState(target, reason);
|
|
46039
|
+
target.state = "cancelled";
|
|
46040
|
+
target.failureReason = reason;
|
|
46041
|
+
cancelledNodeIds.push(target.nodeId);
|
|
46042
|
+
if (target.queueTaskId) {
|
|
46043
|
+
const entry = store.findQueueEntryById(target.meshId, target.queueTaskId);
|
|
46044
|
+
if (entry && entry.status === "pending") {
|
|
46045
|
+
cancelledTaskIds.push(entry.id);
|
|
46046
|
+
}
|
|
46047
|
+
}
|
|
46048
|
+
frontier.push(target.nodeId);
|
|
46049
|
+
}
|
|
46050
|
+
}
|
|
46051
|
+
return { cancelledNodeIds, cancelledTaskIds };
|
|
46052
|
+
}
|
|
46053
|
+
function migrateLegacyDependencyFailedQueueBlocks(db) {
|
|
46054
|
+
let rows;
|
|
46055
|
+
try {
|
|
46056
|
+
rows = db.prepare(`SELECT id, mesh_id, payload FROM mesh_queue`).all();
|
|
46057
|
+
} catch {
|
|
46058
|
+
return [];
|
|
46059
|
+
}
|
|
46060
|
+
const cleared = [];
|
|
46061
|
+
const update = db.prepare(`UPDATE mesh_queue SET payload = ?, updated_at = ? WHERE id = ? AND mesh_id = ?`);
|
|
46062
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
46063
|
+
for (const row of rows) {
|
|
46064
|
+
let entry;
|
|
46065
|
+
try {
|
|
46066
|
+
entry = JSON.parse(row.payload);
|
|
46067
|
+
} catch {
|
|
46068
|
+
continue;
|
|
46069
|
+
}
|
|
46070
|
+
const predecessorId = parseLegacyDependencyFailedPredecessor(entry.blockedReason);
|
|
46071
|
+
if (!predecessorId) continue;
|
|
46072
|
+
delete entry.blockedReason;
|
|
46073
|
+
update.run(JSON.stringify(entry), nowIso, row.id, row.mesh_id);
|
|
46074
|
+
cleared.push({ taskId: row.id, meshId: row.mesh_id, predecessorId });
|
|
46075
|
+
}
|
|
46076
|
+
return cleared;
|
|
46077
|
+
}
|
|
46078
|
+
function safeParseObject(text) {
|
|
46079
|
+
if (!text) return null;
|
|
46080
|
+
try {
|
|
46081
|
+
const value = JSON.parse(text);
|
|
46082
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
46083
|
+
return value;
|
|
46084
|
+
} catch {
|
|
46085
|
+
return null;
|
|
46086
|
+
}
|
|
46087
|
+
}
|
|
46088
|
+
var MESH_ON_DEPENDENCY_FAILURE_PUBLIC_TEXT;
|
|
46089
|
+
var MESH_GRAPH_SKIP_BLOCK_PREFIX;
|
|
46090
|
+
var LEGACY_DEPENDENCY_FAILED_PREFIX;
|
|
46091
|
+
var MeshGraphPolicyError;
|
|
46092
|
+
var SUCCESS_TERMINAL;
|
|
46093
|
+
var WORKER_SETTLED;
|
|
46094
|
+
var GATE_SETTLED;
|
|
46095
|
+
var CANCELABLE_NODE_STATES;
|
|
46096
|
+
var init_mesh_graph_derived_failure = __esm2({
|
|
46097
|
+
"src/mesh/mesh-graph-derived-failure.ts"() {
|
|
46098
|
+
"use strict";
|
|
46099
|
+
init_mesh_graph_types();
|
|
46100
|
+
MESH_ON_DEPENDENCY_FAILURE_PUBLIC_TEXT = "on_dependency_failure controls downstream tasks when a required worker task fails or is cancelled. `block` (default) keeps downstream pending and automatically recovers if the predecessor is retried and later completes. `cancel` terminally cancels the dependent branch; it is not revived by predecessor retry.";
|
|
46101
|
+
MESH_GRAPH_SKIP_BLOCK_PREFIX = "graph_skipped:";
|
|
46102
|
+
LEGACY_DEPENDENCY_FAILED_PREFIX = "dependency_failed:";
|
|
46103
|
+
MeshGraphPolicyError = class extends Error {
|
|
46104
|
+
constructor(message, code = "invalid_on_dependency_failure") {
|
|
46105
|
+
super(message);
|
|
46106
|
+
this.code = code;
|
|
46107
|
+
this.name = "MeshGraphPolicyError";
|
|
46108
|
+
}
|
|
46109
|
+
};
|
|
46110
|
+
SUCCESS_TERMINAL = /* @__PURE__ */ new Set(["completed", "released", "skipped"]);
|
|
46111
|
+
WORKER_SETTLED = /* @__PURE__ */ new Set([
|
|
46112
|
+
"completed",
|
|
46113
|
+
"skipped",
|
|
46114
|
+
"failed",
|
|
46115
|
+
"cancelled"
|
|
46116
|
+
]);
|
|
46117
|
+
GATE_SETTLED = /* @__PURE__ */ new Set(["released", "cancelled", "expired"]);
|
|
46118
|
+
CANCELABLE_NODE_STATES = /* @__PURE__ */ new Set([
|
|
46119
|
+
"declared",
|
|
46120
|
+
"blocked",
|
|
46121
|
+
"materialized"
|
|
46122
|
+
]);
|
|
46123
|
+
}
|
|
46124
|
+
});
|
|
45898
46125
|
function graphMaterializationBlockReason(nodeId, materializationVersion) {
|
|
45899
46126
|
return `${GRAPH_BLOCK_PREFIX}${nodeId}:${materializationVersion}`;
|
|
45900
46127
|
}
|
|
@@ -45907,6 +46134,14 @@ ${blocks.join("\n\n")}`;
|
|
|
45907
46134
|
if (!Number.isInteger(version2) || version2 < 0) return null;
|
|
45908
46135
|
return { nodeId: rest.slice(0, sep15), version: version2 };
|
|
45909
46136
|
}
|
|
46137
|
+
function coordinatorGateBlockReason(gateId) {
|
|
46138
|
+
return `${GATE_BLOCK_PREFIX}${gateId}`;
|
|
46139
|
+
}
|
|
46140
|
+
function parseCoordinatorGateBlock(reason) {
|
|
46141
|
+
if (!reason || !reason.startsWith(GATE_BLOCK_PREFIX)) return null;
|
|
46142
|
+
const gateId = reason.slice(GATE_BLOCK_PREFIX.length);
|
|
46143
|
+
return gateId.length > 0 ? { gateId } : null;
|
|
46144
|
+
}
|
|
45910
46145
|
function registerMeshGraphQueueWakeHandler(handler) {
|
|
45911
46146
|
queueWakeHandler = handler;
|
|
45912
46147
|
}
|
|
@@ -46030,11 +46265,59 @@ ${blocks.join("\n\n")}`;
|
|
|
46030
46265
|
});
|
|
46031
46266
|
const nodes = graphStore.listNodes(node.graphId);
|
|
46032
46267
|
const byId = new Map(nodes.map((n) => [n.nodeId, n]));
|
|
46033
|
-
|
|
46034
|
-
|
|
46035
|
-
|
|
46036
|
-
|
|
46037
|
-
|
|
46268
|
+
const graph = graphStore.getGraph(node.graphId);
|
|
46269
|
+
const failurePolicy = projectGraphPublicPolicy(graph?.policyJson).on_dependency_failure;
|
|
46270
|
+
if ((terminal.status === "failed" || terminal.status === "cancelled") && failurePolicy === "cancel") {
|
|
46271
|
+
const edges = graphStore.listEdges(node.graphId);
|
|
46272
|
+
const cascade = applyGraphCancelCascade(
|
|
46273
|
+
store,
|
|
46274
|
+
nodes,
|
|
46275
|
+
edges,
|
|
46276
|
+
node,
|
|
46277
|
+
nowIso,
|
|
46278
|
+
(target, reason) => {
|
|
46279
|
+
graphStore.updateNodeState(target.graphId, target.nodeId, "cancelled", nowIso, {
|
|
46280
|
+
failureReason: reason
|
|
46281
|
+
});
|
|
46282
|
+
}
|
|
46283
|
+
);
|
|
46284
|
+
for (const taskId of cascade.cancelledTaskIds) {
|
|
46285
|
+
const entry = store.findQueueEntryById(node.meshId, taskId);
|
|
46286
|
+
if (entry && entry.status === "pending") {
|
|
46287
|
+
entry.status = "cancelled";
|
|
46288
|
+
entry.cancelledAt = nowIso;
|
|
46289
|
+
entry.cancelReason = `dependency_failed:${terminal.taskId}`;
|
|
46290
|
+
store.updateQueueEntry(entry);
|
|
46291
|
+
}
|
|
46292
|
+
}
|
|
46293
|
+
for (const nodeId of cascade.cancelledNodeIds) {
|
|
46294
|
+
const target = byId.get(nodeId);
|
|
46295
|
+
outbox.push({
|
|
46296
|
+
kind: "graph_node_cancelled",
|
|
46297
|
+
payload: {
|
|
46298
|
+
graphId: node.graphId,
|
|
46299
|
+
nodeId,
|
|
46300
|
+
ref: target?.ref,
|
|
46301
|
+
taskId: target?.queueTaskId,
|
|
46302
|
+
reason: `dependency_failed:${node.ref ?? node.nodeId}`
|
|
46303
|
+
}
|
|
46304
|
+
});
|
|
46305
|
+
}
|
|
46306
|
+
} else if (terminal.status === "failed" || terminal.status === "cancelled") {
|
|
46307
|
+
const dependents = graphStore.listEdges(node.graphId).filter((e) => e.fromNodeId === node.nodeId && e.kind !== "gate").map((e) => byId.get(e.toNodeId)).filter((n) => !!n && n.kind === "worker_task");
|
|
46308
|
+
outbox.push({
|
|
46309
|
+
kind: "graph_derived_failure",
|
|
46310
|
+
payload: {
|
|
46311
|
+
graphId: node.graphId,
|
|
46312
|
+
nodeId: node.nodeId,
|
|
46313
|
+
ref: node.ref,
|
|
46314
|
+
taskId: terminal.taskId,
|
|
46315
|
+
outcome: terminal.status,
|
|
46316
|
+
policy: "block",
|
|
46317
|
+
dependentNodeIds: dependents.map((d) => d.nodeId),
|
|
46318
|
+
dependentTaskIds: dependents.map((d) => d.queueTaskId).filter(Boolean)
|
|
46319
|
+
}
|
|
46320
|
+
});
|
|
46038
46321
|
}
|
|
46039
46322
|
const materialized = [];
|
|
46040
46323
|
if (terminal.status === "completed") {
|
|
@@ -46047,7 +46330,12 @@ ${blocks.join("\n\n")}`;
|
|
|
46047
46330
|
);
|
|
46048
46331
|
for (const targetId of downstreamIds) {
|
|
46049
46332
|
const target = byId.get(targetId);
|
|
46050
|
-
if (!target
|
|
46333
|
+
if (!target) continue;
|
|
46334
|
+
if (target.kind === "coordinator_gate") {
|
|
46335
|
+
maybeOpenCoordinatorGate(store, target, edges, byId, nowIso);
|
|
46336
|
+
continue;
|
|
46337
|
+
}
|
|
46338
|
+
if (target.kind !== "worker_task" || !target.queueTaskId) continue;
|
|
46051
46339
|
if (target.state !== "declared" && target.state !== "blocked") continue;
|
|
46052
46340
|
const outcome = settleDownstreamNode(store, target, edges, byId, nowIso);
|
|
46053
46341
|
if (outcome.kind === "materialized") {
|
|
@@ -46091,10 +46379,12 @@ ${blocks.join("\n\n")}`;
|
|
|
46091
46379
|
}
|
|
46092
46380
|
frontier = nextFrontier;
|
|
46093
46381
|
}
|
|
46094
|
-
|
|
46095
|
-
|
|
46096
|
-
|
|
46097
|
-
|
|
46382
|
+
}
|
|
46383
|
+
const rolled = classifyGraphRollup(graphStore.listNodes(node.graphId));
|
|
46384
|
+
if (rolled && graph?.status !== rolled) {
|
|
46385
|
+
graphStore.updateGraphStatus(node.graphId, rolled, nowIso, true);
|
|
46386
|
+
const kind = rolled === "completed" ? "graph_completed" : rolled === "failed" ? "graph_failed" : "graph_cancelled";
|
|
46387
|
+
outbox.push({ kind, payload: { graphId: node.graphId, status: rolled } });
|
|
46098
46388
|
}
|
|
46099
46389
|
if (materialized.length > 0) {
|
|
46100
46390
|
outbox.push({ kind: "queue_wake", payload: { meshId: node.meshId, reason: "graph_materialization", graphId: node.graphId } });
|
|
@@ -46117,9 +46407,95 @@ ${blocks.join("\n\n")}`;
|
|
|
46117
46407
|
function isTerminalEquivalent(n) {
|
|
46118
46408
|
return n.state === "completed" || n.state === "released" || n.state === "skipped";
|
|
46119
46409
|
}
|
|
46410
|
+
function maybeOpenCoordinatorGate(store, gateNode, edges, byId, nowIso) {
|
|
46411
|
+
const graphStore = store.graphStore();
|
|
46412
|
+
if (gateNode.state !== "declared" && gateNode.state !== "blocked") return false;
|
|
46413
|
+
const gate = graphStore.findGateByNodeId(gateNode.graphId, gateNode.nodeId);
|
|
46414
|
+
if (!gate || gate.state !== "declared") return false;
|
|
46415
|
+
const incoming = edges.filter((e) => e.toNodeId === gateNode.nodeId);
|
|
46416
|
+
const satisfied = incoming.every((e) => {
|
|
46417
|
+
const sourceState = byId.get(e.fromNodeId)?.state;
|
|
46418
|
+
return sourceState === "completed" || sourceState === "released" || sourceState === "skipped" && e.omitOnSkip;
|
|
46419
|
+
});
|
|
46420
|
+
if (!satisfied) return false;
|
|
46421
|
+
const spec = safeParseJson(gateNode.baseSpecJson);
|
|
46422
|
+
const deadlineSeconds = typeof spec?.deadline_seconds === "number" ? spec.deadline_seconds : void 0;
|
|
46423
|
+
const deadlineAt = deadlineSeconds && deadlineSeconds > 0 ? new Date(Date.parse(nowIso) + deadlineSeconds * 1e3).toISOString() : void 0;
|
|
46424
|
+
graphStore.patchGate(gate.gateId, {
|
|
46425
|
+
state: "awaiting_coordinator",
|
|
46426
|
+
...deadlineAt ? { deadlineAt } : {}
|
|
46427
|
+
}, nowIso);
|
|
46428
|
+
graphStore.updateNodeState(gateNode.graphId, gateNode.nodeId, "awaiting_coordinator", nowIso);
|
|
46429
|
+
gateNode.state = "awaiting_coordinator";
|
|
46430
|
+
byId.set(gateNode.nodeId, gateNode);
|
|
46431
|
+
const block2 = coordinatorGateBlockReason(gate.gateId);
|
|
46432
|
+
for (const edge of edges.filter((e) => e.fromNodeId === gateNode.nodeId)) {
|
|
46433
|
+
const target = byId.get(edge.toNodeId);
|
|
46434
|
+
if (!target || target.kind !== "worker_task" || !target.queueTaskId) continue;
|
|
46435
|
+
if (target.state !== "declared" && target.state !== "blocked") continue;
|
|
46436
|
+
if (target.state !== "blocked") {
|
|
46437
|
+
graphStore.updateNodeState(target.graphId, target.nodeId, "blocked", nowIso);
|
|
46438
|
+
target.state = "blocked";
|
|
46439
|
+
byId.set(target.nodeId, target);
|
|
46440
|
+
}
|
|
46441
|
+
const entry = store.findQueueEntryById(target.meshId, target.queueTaskId);
|
|
46442
|
+
if (!entry || entry.status !== "pending") continue;
|
|
46443
|
+
const owned = !entry.blockedReason || parseGraphMaterializationBlock(entry.blockedReason) !== null || parseCoordinatorGateBlock(entry.blockedReason) !== null || entry.blockedReason.startsWith("materialization_error:");
|
|
46444
|
+
if (owned && entry.blockedReason !== block2) {
|
|
46445
|
+
entry.blockedReason = block2;
|
|
46446
|
+
store.updateQueueEntry(entry);
|
|
46447
|
+
}
|
|
46448
|
+
}
|
|
46449
|
+
graphStore.insertOutboxEvent({
|
|
46450
|
+
id: newMeshGraphOutboxId(),
|
|
46451
|
+
meshId: gateNode.meshId,
|
|
46452
|
+
graphId: gateNode.graphId,
|
|
46453
|
+
kind: "graph_gate_awaiting",
|
|
46454
|
+
payload: JSON.stringify({
|
|
46455
|
+
graphId: gateNode.graphId,
|
|
46456
|
+
gateId: gate.gateId,
|
|
46457
|
+
nodeId: gateNode.nodeId,
|
|
46458
|
+
ref: gate.ref ?? gateNode.ref,
|
|
46459
|
+
action: gate.action,
|
|
46460
|
+
instructions: gate.instructions,
|
|
46461
|
+
deadlineAt
|
|
46462
|
+
}),
|
|
46463
|
+
status: "pending",
|
|
46464
|
+
attemptCount: 0,
|
|
46465
|
+
createdAt: nowIso,
|
|
46466
|
+
updatedAt: nowIso
|
|
46467
|
+
});
|
|
46468
|
+
const graph = graphStore.getGraph(gateNode.graphId);
|
|
46469
|
+
if (graph?.status === "active") {
|
|
46470
|
+
graphStore.updateGraphStatus(gateNode.graphId, "waiting_gate", nowIso);
|
|
46471
|
+
}
|
|
46472
|
+
return true;
|
|
46473
|
+
}
|
|
46474
|
+
function gateOutcomeOutputForNode(graphStore, gateNode) {
|
|
46475
|
+
const gate = graphStore.findGateByNodeId(gateNode.graphId, gateNode.nodeId);
|
|
46476
|
+
if (!gate || gate.state !== "released") return null;
|
|
46477
|
+
const stored = typeof gate.releaseEvidenceJson === "string" ? safeParseJson(gate.releaseEvidenceJson) : void 0;
|
|
46478
|
+
return {
|
|
46479
|
+
ref: gateNode.ref ?? gate.ref ?? gateNode.nodeId,
|
|
46480
|
+
envelope: {
|
|
46481
|
+
gate_outcome: gate.releaseOutcome,
|
|
46482
|
+
action: gate.action,
|
|
46483
|
+
gate_id: gate.gateId,
|
|
46484
|
+
ref: gate.ref ?? gateNode.ref,
|
|
46485
|
+
result: stored?.result,
|
|
46486
|
+
evidence: stored?.evidence,
|
|
46487
|
+
evidence_digest: gate.releaseEvidenceDigest,
|
|
46488
|
+
released_at: gate.updatedAt
|
|
46489
|
+
}
|
|
46490
|
+
};
|
|
46491
|
+
}
|
|
46120
46492
|
function settleDownstreamNode(store, target, edges, byId, nowIso) {
|
|
46121
46493
|
const graphStore = store.graphStore();
|
|
46122
46494
|
const incoming = edges.filter((e) => e.toNodeId === target.nodeId && e.kind !== "gate");
|
|
46495
|
+
const gateIncoming = edges.filter((e) => e.toNodeId === target.nodeId && e.kind === "gate");
|
|
46496
|
+
for (const edge of gateIncoming) {
|
|
46497
|
+
if (byId.get(edge.fromNodeId)?.state !== "released") return { kind: "deferred" };
|
|
46498
|
+
}
|
|
46123
46499
|
const skippedSources = incoming.filter((e) => byId.get(e.fromNodeId)?.state === "skipped");
|
|
46124
46500
|
const propagatingSkip = skippedSources.find((e) => !e.omitOnSkip);
|
|
46125
46501
|
if (propagatingSkip) {
|
|
@@ -46132,6 +46508,12 @@ ${blocks.join("\n\n")}`;
|
|
|
46132
46508
|
const queueEntry = store.findQueueEntryById(target.meshId, target.queueTaskId);
|
|
46133
46509
|
if (!queueEntry) return { kind: "deferred" };
|
|
46134
46510
|
const outputsByRef = collectUpstreamOutputs(store, activeIncoming, byId);
|
|
46511
|
+
for (const edge of gateIncoming) {
|
|
46512
|
+
const gateNode = byId.get(edge.fromNodeId);
|
|
46513
|
+
if (!gateNode) continue;
|
|
46514
|
+
const output = gateOutcomeOutputForNode(graphStore, gateNode);
|
|
46515
|
+
if (output && !outputsByRef.has(output.ref)) outputsByRef.set(output.ref, output);
|
|
46516
|
+
}
|
|
46135
46517
|
const baseSpec = safeParseJson(target.baseSpecJson);
|
|
46136
46518
|
let conditionsHold = true;
|
|
46137
46519
|
let falseCondition;
|
|
@@ -46287,6 +46669,10 @@ ${blocks.join("\n\n")}`;
|
|
|
46287
46669
|
}
|
|
46288
46670
|
if (queueEntry.blockedReason?.startsWith("materialization_error:")) {
|
|
46289
46671
|
delete queueEntry.blockedReason;
|
|
46672
|
+
return;
|
|
46673
|
+
}
|
|
46674
|
+
if (parseCoordinatorGateBlock(queueEntry.blockedReason)) {
|
|
46675
|
+
delete queueEntry.blockedReason;
|
|
46290
46676
|
}
|
|
46291
46677
|
}
|
|
46292
46678
|
function syncNodeRowState(byId, target, state, patch) {
|
|
@@ -46375,6 +46761,7 @@ ${blocks.join("\n\n")}`;
|
|
|
46375
46761
|
return result.materialized;
|
|
46376
46762
|
}
|
|
46377
46763
|
var GRAPH_BLOCK_PREFIX;
|
|
46764
|
+
var GATE_BLOCK_PREFIX;
|
|
46378
46765
|
var queueWakeHandler;
|
|
46379
46766
|
var init_mesh_graph_transition_runner = __esm2({
|
|
46380
46767
|
"src/mesh/mesh-graph-transition-runner.ts"() {
|
|
@@ -46386,7 +46773,9 @@ ${blocks.join("\n\n")}`;
|
|
|
46386
46773
|
init_mesh_graph_types();
|
|
46387
46774
|
init_mesh_graph_input_binding();
|
|
46388
46775
|
init_mesh_graph_workspace_bind();
|
|
46776
|
+
init_mesh_graph_derived_failure();
|
|
46389
46777
|
GRAPH_BLOCK_PREFIX = "graph_materialization_pending:";
|
|
46778
|
+
GATE_BLOCK_PREFIX = "coordinator_gate:";
|
|
46390
46779
|
}
|
|
46391
46780
|
});
|
|
46392
46781
|
function formatMeshTaskModeViolations(result) {
|
|
@@ -48212,7 +48601,10 @@ ${blocks.join("\n\n")}`;
|
|
|
48212
48601
|
return record2 ? { ...record2, status: normalizeMissionStatus(record2.status), source: normalizeMissionSource(record2.source) } : null;
|
|
48213
48602
|
}
|
|
48214
48603
|
function summarizeMissionTasks(meshId, missionId) {
|
|
48215
|
-
const
|
|
48604
|
+
const queue = getQueue(meshId);
|
|
48605
|
+
const tasks = queue.filter((task) => task.missionId === missionId);
|
|
48606
|
+
const statusById = new Map(queue.map((task) => [task.id, task.status]));
|
|
48607
|
+
const depMetaById = new Map(queue.map((task) => [task.id, { blockedReason: task.blockedReason, cancelReason: task.cancelReason, status: task.status }]));
|
|
48216
48608
|
const aggregate = {
|
|
48217
48609
|
total: tasks.length,
|
|
48218
48610
|
pending: 0,
|
|
@@ -48229,7 +48621,9 @@ ${blocks.join("\n\n")}`;
|
|
|
48229
48621
|
else if (task.status === "completed") aggregate.completed += 1;
|
|
48230
48622
|
else if (task.status === "failed") aggregate.failed += 1;
|
|
48231
48623
|
else if (task.status === "cancelled") aggregate.cancelled += 1;
|
|
48232
|
-
if (task.status === "pending" && task.blockedReason
|
|
48624
|
+
if (task.status === "pending" && (task.blockedReason || deriveDependencyFailures(task.dependsOn, statusById, depMetaById).length > 0)) {
|
|
48625
|
+
aggregate.blocked += 1;
|
|
48626
|
+
}
|
|
48233
48627
|
if (task.updatedAt && (!aggregate.lastActivityAt || task.updatedAt > aggregate.lastActivityAt)) {
|
|
48234
48628
|
aggregate.lastActivityAt = task.updatedAt;
|
|
48235
48629
|
}
|
|
@@ -48425,6 +48819,7 @@ ${blocks.join("\n\n")}`;
|
|
|
48425
48819
|
init_logger();
|
|
48426
48820
|
init_mesh_runtime_store();
|
|
48427
48821
|
init_mesh_work_queue();
|
|
48822
|
+
init_mesh_graph_derived_failure();
|
|
48428
48823
|
init_mesh_task_stats();
|
|
48429
48824
|
init_mesh_ledger();
|
|
48430
48825
|
init_mesh_events_pending();
|
|
@@ -48857,13 +49252,14 @@ ${blocks.join("\n\n")}`;
|
|
|
48857
49252
|
function resolveDependencyFailurePolicy(meshId) {
|
|
48858
49253
|
try {
|
|
48859
49254
|
const policy = getMesh(meshId)?.policy ?? {};
|
|
48860
|
-
return policy.onDependencyFailure
|
|
49255
|
+
return resolveOnDependencyFailurePolicy(policy.onDependencyFailure ?? policy.on_dependency_failure);
|
|
48861
49256
|
} catch {
|
|
48862
49257
|
return "block";
|
|
48863
49258
|
}
|
|
48864
49259
|
}
|
|
48865
49260
|
function propagateDependencyFailure(meshId, failedTaskId) {
|
|
48866
49261
|
const policy = resolveDependencyFailurePolicy(meshId);
|
|
49262
|
+
if (policy !== "cancel") return [];
|
|
48867
49263
|
const store = MeshRuntimeStore.getInstance();
|
|
48868
49264
|
const cancelled = [];
|
|
48869
49265
|
const frontier = [failedTaskId];
|
|
@@ -48874,17 +49270,12 @@ ${blocks.join("\n\n")}`;
|
|
|
48874
49270
|
for (const dependent of dependents) {
|
|
48875
49271
|
if (seen.has(dependent.id)) continue;
|
|
48876
49272
|
seen.add(dependent.id);
|
|
48877
|
-
|
|
48878
|
-
|
|
48879
|
-
|
|
48880
|
-
|
|
48881
|
-
|
|
48882
|
-
|
|
48883
|
-
frontier.push(dependent.id);
|
|
48884
|
-
} else {
|
|
48885
|
-
dependent.blockedReason = `dependency_failed:${currentId}`;
|
|
48886
|
-
store.updateQueueEntry(dependent);
|
|
48887
|
-
}
|
|
49273
|
+
dependent.status = "cancelled";
|
|
49274
|
+
dependent.cancelledAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
49275
|
+
dependent.cancelReason = `dependency_failed:${currentId}`;
|
|
49276
|
+
store.updateQueueEntry(dependent);
|
|
49277
|
+
cancelled.push(dependent);
|
|
49278
|
+
frontier.push(dependent.id);
|
|
48888
49279
|
}
|
|
48889
49280
|
}
|
|
48890
49281
|
return cancelled;
|
|
@@ -49180,12 +49571,13 @@ ${blocks.join("\n\n")}`;
|
|
|
49180
49571
|
const deps = Array.isArray(entry.dependsOn) ? entry.dependsOn : [];
|
|
49181
49572
|
return deps.every((depId) => statusById.get(depId) === "completed");
|
|
49182
49573
|
}
|
|
49183
|
-
function describeTaskDependencyState(entry, statusById) {
|
|
49574
|
+
function describeTaskDependencyState(entry, statusById, depMetaById) {
|
|
49184
49575
|
const deps = Array.isArray(entry.dependsOn) ? entry.dependsOn : [];
|
|
49185
49576
|
const waitingOn = deps.filter((depId) => statusById.get(depId) !== "completed");
|
|
49186
49577
|
return {
|
|
49187
49578
|
waitingOn,
|
|
49188
|
-
dependenciesSatisfied: taskDependenciesSatisfied(entry, statusById)
|
|
49579
|
+
dependenciesSatisfied: taskDependenciesSatisfied(entry, statusById),
|
|
49580
|
+
dependencyFailures: deriveDependencyFailures(entry.dependsOn, statusById, depMetaById)
|
|
49189
49581
|
};
|
|
49190
49582
|
}
|
|
49191
49583
|
function getMeshQueueStats(meshId) {
|
|
@@ -49305,6 +49697,7 @@ ${blocks.join("\n\n")}`;
|
|
|
49305
49697
|
init_mesh_task_inflight();
|
|
49306
49698
|
init_mesh_turn_ledger();
|
|
49307
49699
|
init_mesh_graph_transition_runner();
|
|
49700
|
+
init_mesh_graph_derived_failure();
|
|
49308
49701
|
init_dist();
|
|
49309
49702
|
init_mesh_task_mode_guardrail();
|
|
49310
49703
|
init_mesh_task_mode_guardrail();
|
|
@@ -49520,10 +49913,12 @@ ${blocks.join("\n\n")}`;
|
|
|
49520
49913
|
CREATE INDEX IF NOT EXISTS idx_mesh_graph_outbox_graph
|
|
49521
49914
|
ON mesh_graph_outbox(graph_id, status);
|
|
49522
49915
|
`);
|
|
49916
|
+
migrateLegacyDependencyFailedQueueBlocks(db);
|
|
49523
49917
|
}
|
|
49524
49918
|
var init_mesh_graph_schema = __esm2({
|
|
49525
49919
|
"src/mesh/mesh-graph-schema.ts"() {
|
|
49526
49920
|
"use strict";
|
|
49921
|
+
init_mesh_graph_derived_failure();
|
|
49527
49922
|
}
|
|
49528
49923
|
});
|
|
49529
49924
|
function mapGraphRow(r) {
|
|
@@ -49704,6 +50099,39 @@ ${blocks.join("\n\n")}`;
|
|
|
49704
50099
|
).get(meshId, batchId);
|
|
49705
50100
|
return r ? mapGraphRow(r) : null;
|
|
49706
50101
|
}
|
|
50102
|
+
/**
|
|
50103
|
+
* Phase-E graph listing for the graph view / metrics (design :759-775). Newest
|
|
50104
|
+
* first so a view without an explicit graph id shows current work. `statuses`
|
|
50105
|
+
* narrows to e.g. the in-flight set.
|
|
50106
|
+
*/
|
|
50107
|
+
listGraphsByMesh(meshId, opts) {
|
|
50108
|
+
const limit = Math.max(1, Math.min(opts?.limit ?? 50, 500));
|
|
50109
|
+
const statuses = opts?.statuses;
|
|
50110
|
+
if (statuses && statuses.length > 0) {
|
|
50111
|
+
const placeholders = statuses.map(() => "?").join(",");
|
|
50112
|
+
const rows2 = this.db.prepare(
|
|
50113
|
+
`SELECT * FROM mesh_task_graphs WHERE mesh_id = ? AND status IN (${placeholders})
|
|
50114
|
+
ORDER BY created_at DESC LIMIT ?`
|
|
50115
|
+
).all(meshId, ...statuses, limit);
|
|
50116
|
+
return rows2.map(mapGraphRow);
|
|
50117
|
+
}
|
|
50118
|
+
const rows = this.db.prepare(
|
|
50119
|
+
`SELECT * FROM mesh_task_graphs WHERE mesh_id = ? ORDER BY created_at DESC LIMIT ?`
|
|
50120
|
+
).all(meshId, limit);
|
|
50121
|
+
return rows.map(mapGraphRow);
|
|
50122
|
+
}
|
|
50123
|
+
/**
|
|
50124
|
+
* Phase-E: replace the graph's normalized policy document. Used at plan-commit
|
|
50125
|
+
* time to attach the enqueue-decision provenance record next to the persisted
|
|
50126
|
+
* `on_dependency_failure` (design :697-710, :733-738). Deliberately a whole-
|
|
50127
|
+
* document write — the policy is normalized once at commit and never patched
|
|
50128
|
+
* field-by-field afterwards.
|
|
50129
|
+
*/
|
|
50130
|
+
updateGraphPolicyJson(graphId, policyJson, nowIso) {
|
|
50131
|
+
this.db.prepare(
|
|
50132
|
+
`UPDATE mesh_task_graphs SET policy_json = ?, updated_at = ? WHERE graph_id = ?`
|
|
50133
|
+
).run(policyJson, nowIso, graphId);
|
|
50134
|
+
}
|
|
49707
50135
|
/** Phase-B rollup: graph status transition once every node reached a terminal-equivalent state. */
|
|
49708
50136
|
updateGraphStatus(graphId, status, nowIso, terminal = false) {
|
|
49709
50137
|
this.db.prepare(`
|
|
@@ -49893,6 +50321,79 @@ ${blocks.join("\n\n")}`;
|
|
|
49893
50321
|
const r = this.db.prepare(`SELECT * FROM mesh_graph_gates WHERE gate_id = ?`).get(gateId);
|
|
49894
50322
|
return r ? mapGateRow(r) : null;
|
|
49895
50323
|
}
|
|
50324
|
+
/** Reverse lookup from a graph node (kind `coordinator_gate`) to its gate row (phase C2). */
|
|
50325
|
+
findGateByNodeId(graphId, nodeId) {
|
|
50326
|
+
const r = this.db.prepare(
|
|
50327
|
+
`SELECT * FROM mesh_graph_gates WHERE graph_id = ? AND node_id = ?`
|
|
50328
|
+
).get(graphId, nodeId);
|
|
50329
|
+
return r ? mapGateRow(r) : null;
|
|
50330
|
+
}
|
|
50331
|
+
listGatesByGraph(graphId) {
|
|
50332
|
+
const rows = this.db.prepare(
|
|
50333
|
+
`SELECT * FROM mesh_graph_gates WHERE graph_id = ? ORDER BY created_at ASC`
|
|
50334
|
+
).all(graphId);
|
|
50335
|
+
return rows.map(mapGateRow);
|
|
50336
|
+
}
|
|
50337
|
+
/** Phase-C2 timeout sweep: gates of one mesh, optionally narrowed to states (e.g. awaiting/claimed). */
|
|
50338
|
+
listGatesByMesh(meshId, states) {
|
|
50339
|
+
if (states && states.length > 0) {
|
|
50340
|
+
const placeholders = states.map(() => "?").join(",");
|
|
50341
|
+
const rows2 = this.db.prepare(
|
|
50342
|
+
`SELECT * FROM mesh_graph_gates WHERE mesh_id = ? AND state IN (${placeholders}) ORDER BY created_at ASC`
|
|
50343
|
+
).all(meshId, ...states);
|
|
50344
|
+
return rows2.map(mapGateRow);
|
|
50345
|
+
}
|
|
50346
|
+
const rows = this.db.prepare(
|
|
50347
|
+
`SELECT * FROM mesh_graph_gates WHERE mesh_id = ? ORDER BY created_at ASC`
|
|
50348
|
+
).all(meshId);
|
|
50349
|
+
return rows.map(mapGateRow);
|
|
50350
|
+
}
|
|
50351
|
+
/**
|
|
50352
|
+
* Row write for phase C2 (claim / release / deadline sweep). `undefined`
|
|
50353
|
+
* fields are left unchanged; explicit `null` clears a nullable field. The
|
|
50354
|
+
* optional generation CAS rejects a stale fenced writer: the update lands
|
|
50355
|
+
* only while the row still carries `cas.leaseGeneration`.
|
|
50356
|
+
*/
|
|
50357
|
+
patchGate(gateId, patch, nowIso, cas) {
|
|
50358
|
+
const current = this.getGate(gateId);
|
|
50359
|
+
if (!current) return false;
|
|
50360
|
+
if (cas && current.leaseGeneration !== cas.leaseGeneration) return false;
|
|
50361
|
+
const next = {
|
|
50362
|
+
state: patch.state ?? current.state,
|
|
50363
|
+
leaseOwnerSessionId: patch.leaseOwnerSessionId === void 0 ? current.leaseOwnerSessionId ?? null : patch.leaseOwnerSessionId,
|
|
50364
|
+
leaseGeneration: patch.leaseGeneration ?? current.leaseGeneration,
|
|
50365
|
+
fencingToken: patch.fencingToken === void 0 ? current.fencingToken ?? null : patch.fencingToken,
|
|
50366
|
+
leaseExpiresAt: patch.leaseExpiresAt === void 0 ? current.leaseExpiresAt ?? null : patch.leaseExpiresAt,
|
|
50367
|
+
deadlineAt: patch.deadlineAt === void 0 ? current.deadlineAt ?? null : patch.deadlineAt,
|
|
50368
|
+
releaseOutcome: patch.releaseOutcome === void 0 ? current.releaseOutcome ?? null : patch.releaseOutcome,
|
|
50369
|
+
releaseEvidenceJson: patch.releaseEvidenceJson === void 0 ? current.releaseEvidenceJson ?? null : patch.releaseEvidenceJson,
|
|
50370
|
+
releaseEvidenceDigest: patch.releaseEvidenceDigest === void 0 ? current.releaseEvidenceDigest ?? null : patch.releaseEvidenceDigest,
|
|
50371
|
+
releaseIdempotencyKey: patch.releaseIdempotencyKey === void 0 ? current.releaseIdempotencyKey ?? null : patch.releaseIdempotencyKey
|
|
50372
|
+
};
|
|
50373
|
+
const r = this.db.prepare(`
|
|
50374
|
+
UPDATE mesh_graph_gates
|
|
50375
|
+
SET state = ?, lease_owner_session_id = ?, lease_generation = ?, fencing_token = ?,
|
|
50376
|
+
lease_expires_at = ?, deadline_at = ?, release_outcome = ?,
|
|
50377
|
+
release_evidence_json = ?, release_evidence_digest = ?,
|
|
50378
|
+
release_idempotency_key = ?, updated_at = ?
|
|
50379
|
+
WHERE gate_id = ? AND lease_generation = ?
|
|
50380
|
+
`).run(
|
|
50381
|
+
next.state,
|
|
50382
|
+
next.leaseOwnerSessionId,
|
|
50383
|
+
next.leaseGeneration,
|
|
50384
|
+
next.fencingToken,
|
|
50385
|
+
next.leaseExpiresAt,
|
|
50386
|
+
next.deadlineAt,
|
|
50387
|
+
next.releaseOutcome,
|
|
50388
|
+
next.releaseEvidenceJson,
|
|
50389
|
+
next.releaseEvidenceDigest,
|
|
50390
|
+
next.releaseIdempotencyKey,
|
|
50391
|
+
nowIso,
|
|
50392
|
+
gateId,
|
|
50393
|
+
current.leaseGeneration
|
|
50394
|
+
);
|
|
50395
|
+
return r.changes > 0;
|
|
50396
|
+
}
|
|
49896
50397
|
// ── mesh_graph_workspace_intents ─────────────────────────────────────────
|
|
49897
50398
|
insertWorkspaceIntent(row) {
|
|
49898
50399
|
this.db.prepare(`
|
|
@@ -51927,62 +52428,6 @@ ${blocks.join("\n\n")}`;
|
|
|
51927
52428
|
MAX_CLONE_CHAIN = 16;
|
|
51928
52429
|
}
|
|
51929
52430
|
});
|
|
51930
|
-
function loadDatabaseCtor() {
|
|
51931
|
-
if (DatabaseCtor) return DatabaseCtor;
|
|
51932
|
-
DatabaseCtor = loadBetterSqlite3();
|
|
51933
|
-
return DatabaseCtor;
|
|
51934
|
-
}
|
|
51935
|
-
function safeMeshId(meshId) {
|
|
51936
|
-
return meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
51937
|
-
}
|
|
51938
|
-
function legacyQueuePath(meshId) {
|
|
51939
|
-
return (0, import_path6.join)(getLedgerDir2(), `${safeMeshId(meshId)}.queue.json`);
|
|
51940
|
-
}
|
|
51941
|
-
function cleanupStrayRootRuntimeDb(canonicalPath2) {
|
|
51942
|
-
try {
|
|
51943
|
-
const strayPath = (0, import_path6.join)(getConfigDir2(), "mesh-runtime.db");
|
|
51944
|
-
if (strayPath === canonicalPath2) return;
|
|
51945
|
-
if (!(0, import_fs6.existsSync)(strayPath)) return;
|
|
51946
|
-
if ((0, import_fs6.statSync)(strayPath).size !== 0) return;
|
|
51947
|
-
(0, import_fs6.unlinkSync)(strayPath);
|
|
51948
|
-
if (!loggedStrayCleanup) {
|
|
51949
|
-
loggedStrayCleanup = true;
|
|
51950
|
-
LOG2.info("MeshRuntimeStore", `Removed stray 0-byte root mesh-runtime.db at ${strayPath}`);
|
|
51951
|
-
}
|
|
51952
|
-
} catch (err) {
|
|
51953
|
-
if (!loggedStrayCleanup) {
|
|
51954
|
-
loggedStrayCleanup = true;
|
|
51955
|
-
LOG2.warn("MeshRuntimeStore", `Stray root mesh-runtime.db cleanup failed (ignored): ${err?.message || err}`);
|
|
51956
|
-
}
|
|
51957
|
-
}
|
|
51958
|
-
}
|
|
51959
|
-
function meshRuntimeStorePath() {
|
|
51960
|
-
const dir = getLedgerDir2();
|
|
51961
|
-
const nextPath = (0, import_path6.join)(dir, "mesh-runtime.db");
|
|
51962
|
-
cleanupStrayRootRuntimeDb(nextPath);
|
|
51963
|
-
if ((0, import_fs6.existsSync)(nextPath)) return nextPath;
|
|
51964
|
-
const legacyPath = (0, import_path6.join)(dir, "beads.db");
|
|
51965
|
-
if (!(0, import_fs6.existsSync)(legacyPath)) return nextPath;
|
|
51966
|
-
try {
|
|
51967
|
-
(0, import_fs6.renameSync)(legacyPath, nextPath);
|
|
51968
|
-
for (const suffix of ["-wal", "-shm"]) {
|
|
51969
|
-
const legacyCompanion = `${legacyPath}${suffix}`;
|
|
51970
|
-
if ((0, import_fs6.existsSync)(legacyCompanion)) {
|
|
51971
|
-
(0, import_fs6.renameSync)(legacyCompanion, `${nextPath}${suffix}`);
|
|
51972
|
-
}
|
|
51973
|
-
}
|
|
51974
|
-
} catch (err) {
|
|
51975
|
-
if (!loggedMigrationFailure) {
|
|
51976
|
-
loggedMigrationFailure = true;
|
|
51977
|
-
LOG2.warn(
|
|
51978
|
-
"MeshRuntimeStore",
|
|
51979
|
-
`Legacy beads.db\u2192mesh-runtime.db migration failed; using existing DB in-place to avoid data loss: ${err?.message || err}`
|
|
51980
|
-
);
|
|
51981
|
-
}
|
|
51982
|
-
return (0, import_fs6.existsSync)(nextPath) ? nextPath : legacyPath;
|
|
51983
|
-
}
|
|
51984
|
-
return nextPath;
|
|
51985
|
-
}
|
|
51986
52431
|
function meshTurnAttemptFromRow(r) {
|
|
51987
52432
|
return {
|
|
51988
52433
|
attemptId: r.attempt_id,
|
|
@@ -52040,15 +52485,82 @@ ${blocks.join("\n\n")}`;
|
|
|
52040
52485
|
return { ledger: 0, toolCalls: 0, terminalQueue: 0, sessionDelivery: 0 };
|
|
52041
52486
|
}
|
|
52042
52487
|
}
|
|
52488
|
+
var MESH_EVENT_LEDGER_RETENTION_MS;
|
|
52489
|
+
var MESH_TOOL_CALL_LOG_RETENTION_MS;
|
|
52490
|
+
var MESH_TERMINAL_QUEUE_RETENTION_MS;
|
|
52491
|
+
var init_mesh_runtime_store_turn_rows = __esm2({
|
|
52492
|
+
"src/mesh/mesh-runtime-store-turn-rows.ts"() {
|
|
52493
|
+
"use strict";
|
|
52494
|
+
init_logger();
|
|
52495
|
+
init_mesh_retention_config();
|
|
52496
|
+
init_mesh_runtime_store();
|
|
52497
|
+
MESH_EVENT_LEDGER_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
52498
|
+
MESH_TOOL_CALL_LOG_RETENTION_MS = 14 * 24 * 60 * 60 * 1e3;
|
|
52499
|
+
MESH_TERMINAL_QUEUE_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
52500
|
+
}
|
|
52501
|
+
});
|
|
52502
|
+
function loadDatabaseCtor() {
|
|
52503
|
+
if (DatabaseCtor) return DatabaseCtor;
|
|
52504
|
+
DatabaseCtor = loadBetterSqlite3();
|
|
52505
|
+
return DatabaseCtor;
|
|
52506
|
+
}
|
|
52507
|
+
function safeMeshId(meshId) {
|
|
52508
|
+
return meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
52509
|
+
}
|
|
52510
|
+
function legacyQueuePath(meshId) {
|
|
52511
|
+
return (0, import_path6.join)(getLedgerDir2(), `${safeMeshId(meshId)}.queue.json`);
|
|
52512
|
+
}
|
|
52513
|
+
function cleanupStrayRootRuntimeDb(canonicalPath2) {
|
|
52514
|
+
try {
|
|
52515
|
+
const strayPath = (0, import_path6.join)(getConfigDir2(), "mesh-runtime.db");
|
|
52516
|
+
if (strayPath === canonicalPath2) return;
|
|
52517
|
+
if (!(0, import_fs6.existsSync)(strayPath)) return;
|
|
52518
|
+
if ((0, import_fs6.statSync)(strayPath).size !== 0) return;
|
|
52519
|
+
(0, import_fs6.unlinkSync)(strayPath);
|
|
52520
|
+
if (!loggedStrayCleanup) {
|
|
52521
|
+
loggedStrayCleanup = true;
|
|
52522
|
+
LOG2.info("MeshRuntimeStore", `Removed stray 0-byte root mesh-runtime.db at ${strayPath}`);
|
|
52523
|
+
}
|
|
52524
|
+
} catch (err) {
|
|
52525
|
+
if (!loggedStrayCleanup) {
|
|
52526
|
+
loggedStrayCleanup = true;
|
|
52527
|
+
LOG2.warn("MeshRuntimeStore", `Stray root mesh-runtime.db cleanup failed (ignored): ${err?.message || err}`);
|
|
52528
|
+
}
|
|
52529
|
+
}
|
|
52530
|
+
}
|
|
52531
|
+
function meshRuntimeStorePath() {
|
|
52532
|
+
const dir = getLedgerDir2();
|
|
52533
|
+
const nextPath = (0, import_path6.join)(dir, "mesh-runtime.db");
|
|
52534
|
+
cleanupStrayRootRuntimeDb(nextPath);
|
|
52535
|
+
if ((0, import_fs6.existsSync)(nextPath)) return nextPath;
|
|
52536
|
+
const legacyPath = (0, import_path6.join)(dir, "beads.db");
|
|
52537
|
+
if (!(0, import_fs6.existsSync)(legacyPath)) return nextPath;
|
|
52538
|
+
try {
|
|
52539
|
+
(0, import_fs6.renameSync)(legacyPath, nextPath);
|
|
52540
|
+
for (const suffix of ["-wal", "-shm"]) {
|
|
52541
|
+
const legacyCompanion = `${legacyPath}${suffix}`;
|
|
52542
|
+
if ((0, import_fs6.existsSync)(legacyCompanion)) {
|
|
52543
|
+
(0, import_fs6.renameSync)(legacyCompanion, `${nextPath}${suffix}`);
|
|
52544
|
+
}
|
|
52545
|
+
}
|
|
52546
|
+
} catch (err) {
|
|
52547
|
+
if (!loggedMigrationFailure) {
|
|
52548
|
+
loggedMigrationFailure = true;
|
|
52549
|
+
LOG2.warn(
|
|
52550
|
+
"MeshRuntimeStore",
|
|
52551
|
+
`Legacy beads.db\u2192mesh-runtime.db migration failed; using existing DB in-place to avoid data loss: ${err?.message || err}`
|
|
52552
|
+
);
|
|
52553
|
+
}
|
|
52554
|
+
return (0, import_fs6.existsSync)(nextPath) ? nextPath : legacyPath;
|
|
52555
|
+
}
|
|
52556
|
+
return nextPath;
|
|
52557
|
+
}
|
|
52043
52558
|
var import_fs6;
|
|
52044
52559
|
var import_path6;
|
|
52045
52560
|
var DatabaseCtor;
|
|
52046
52561
|
var loggedMigrationFailure;
|
|
52047
52562
|
var loggedStrayCleanup;
|
|
52048
52563
|
var MeshRuntimeStore;
|
|
52049
|
-
var MESH_EVENT_LEDGER_RETENTION_MS;
|
|
52050
|
-
var MESH_TOOL_CALL_LOG_RETENTION_MS;
|
|
52051
|
-
var MESH_TERMINAL_QUEUE_RETENTION_MS;
|
|
52052
52564
|
var init_mesh_runtime_store = __esm2({
|
|
52053
52565
|
"src/mesh/mesh-runtime-store.ts"() {
|
|
52054
52566
|
"use strict";
|
|
@@ -52058,13 +52570,14 @@ ${blocks.join("\n\n")}`;
|
|
|
52058
52570
|
init_load_better_sqlite3();
|
|
52059
52571
|
init_config();
|
|
52060
52572
|
init_mesh_ledger();
|
|
52061
|
-
init_mesh_retention_config();
|
|
52062
52573
|
init_mesh_work_queue();
|
|
52063
52574
|
init_mesh_graph_schema();
|
|
52064
52575
|
init_mesh_graph_store();
|
|
52065
52576
|
init_slot_model_enforcement();
|
|
52066
52577
|
init_mesh_daemon_slot_axis();
|
|
52067
52578
|
init_dist();
|
|
52579
|
+
init_mesh_runtime_store_turn_rows();
|
|
52580
|
+
init_mesh_runtime_store_turn_rows();
|
|
52068
52581
|
loggedMigrationFailure = false;
|
|
52069
52582
|
loggedStrayCleanup = false;
|
|
52070
52583
|
MeshRuntimeStore = class _MeshRuntimeStore {
|
|
@@ -52260,6 +52773,7 @@ ${blocks.join("\n\n")}`;
|
|
|
52260
52773
|
mesh_id TEXT NOT NULL,
|
|
52261
52774
|
tool TEXT NOT NULL,
|
|
52262
52775
|
session_id TEXT,
|
|
52776
|
+
caller_role TEXT,
|
|
52263
52777
|
called_at INTEGER NOT NULL
|
|
52264
52778
|
);
|
|
52265
52779
|
|
|
@@ -52589,6 +53103,10 @@ ${blocks.join("\n\n")}`;
|
|
|
52589
53103
|
ON mesh_event_ledger(mesh_id, task_id, timestamp)
|
|
52590
53104
|
WHERE task_id IS NOT NULL
|
|
52591
53105
|
`);
|
|
53106
|
+
const toolCallCols = this.tableColumns("mesh_tool_call_log");
|
|
53107
|
+
if (!toolCallCols.has("caller_role")) {
|
|
53108
|
+
this.db.exec(`ALTER TABLE mesh_tool_call_log ADD COLUMN caller_role TEXT`);
|
|
53109
|
+
}
|
|
52592
53110
|
} catch (err) {
|
|
52593
53111
|
if (!loggedMigrationFailure) {
|
|
52594
53112
|
loggedMigrationFailure = true;
|
|
@@ -53522,16 +54040,23 @@ ${blocks.join("\n\n")}`;
|
|
|
53522
54040
|
* Returns a rate-limit advisory string when the call rate is too high, null otherwise.
|
|
53523
54041
|
* windowMs: sliding window size in ms (default 10s)
|
|
53524
54042
|
* maxCalls: max allowed calls within the window (default 5)
|
|
54043
|
+
*
|
|
54044
|
+
* `callerRole` is a diagnostic, not an auth boundary: it reflects whether
|
|
54045
|
+
* ADHDEV_COORDINATOR_SESSION_ID was present in this process's env at call
|
|
54046
|
+
* time, and a process can set that env var on itself. It exists to answer
|
|
54047
|
+
* "was this call made by a coordinator-launched process or not" for the
|
|
54048
|
+
* mesh-tool-call-caller-instrumentation investigation, not to gate access —
|
|
54049
|
+
* do not wire it into any block/allow decision.
|
|
53525
54050
|
*/
|
|
53526
54051
|
recordMeshToolCall(opts) {
|
|
53527
|
-
const { meshId, tool, sessionId = null } = opts;
|
|
54052
|
+
const { meshId, tool, sessionId = null, callerRole = null } = opts;
|
|
53528
54053
|
const windowMs = opts.windowMs ?? 1e4;
|
|
53529
54054
|
const maxCalls = opts.maxCalls ?? 5;
|
|
53530
54055
|
const now = Date.now();
|
|
53531
54056
|
const windowStart = now - windowMs;
|
|
53532
54057
|
this.db.prepare(
|
|
53533
|
-
"INSERT INTO mesh_tool_call_log (mesh_id, tool, session_id, called_at) VALUES (?, ?, ?, ?)"
|
|
53534
|
-
).run(meshId, tool, sessionId, now);
|
|
54058
|
+
"INSERT INTO mesh_tool_call_log (mesh_id, tool, session_id, caller_role, called_at) VALUES (?, ?, ?, ?, ?)"
|
|
54059
|
+
).run(meshId, tool, sessionId, callerRole, now);
|
|
53535
54060
|
const row = this.db.prepare(
|
|
53536
54061
|
"SELECT COUNT(*) as cnt FROM mesh_tool_call_log WHERE mesh_id = ? AND tool = ? AND called_at >= ?"
|
|
53537
54062
|
).get(meshId, tool, windowStart);
|
|
@@ -53557,6 +54082,18 @@ ${blocks.join("\n\n")}`;
|
|
|
53557
54082
|
pruneToolCallLog(olderThanMs) {
|
|
53558
54083
|
return this.db.prepare("DELETE FROM mesh_tool_call_log WHERE called_at < ?").run(Date.now() - olderThanMs).changes;
|
|
53559
54084
|
}
|
|
54085
|
+
/**
|
|
54086
|
+
* Read back recent mesh_tool_call_log rows for one mesh, most recent first.
|
|
54087
|
+
* Diagnostic reader for the MESH-TOOL-CALL-CALLER-INSTRUMENTATION 1단계
|
|
54088
|
+
* investigation (was session_id/caller_role actually getting recorded, and
|
|
54089
|
+
* does the coordinator/unknown split hold up) — not on any hot path.
|
|
54090
|
+
*/
|
|
54091
|
+
getRecentToolCalls(meshId, limit = 100) {
|
|
54092
|
+
const rows = this.db.prepare(
|
|
54093
|
+
"SELECT tool, session_id, caller_role, called_at FROM mesh_tool_call_log WHERE mesh_id = ? ORDER BY called_at DESC LIMIT ?"
|
|
54094
|
+
).all(meshId, limit);
|
|
54095
|
+
return rows.map((r) => ({ tool: r.tool, sessionId: r.session_id, callerRole: r.caller_role, calledAt: r.called_at }));
|
|
54096
|
+
}
|
|
53560
54097
|
/**
|
|
53561
54098
|
* Retention prune for mesh_event_ledger (SoT 1-11 (b)). The ledger is append-only
|
|
53562
54099
|
* with NO lifecycle GC of its own, so lifecycle events accumulate without bound
|
|
@@ -54699,9 +55236,6 @@ ${blocks.join("\n\n")}`;
|
|
|
54699
55236
|
return res.changes > 0;
|
|
54700
55237
|
}
|
|
54701
55238
|
};
|
|
54702
|
-
MESH_EVENT_LEDGER_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
54703
|
-
MESH_TOOL_CALL_LOG_RETENTION_MS = 14 * 24 * 60 * 60 * 1e3;
|
|
54704
|
-
MESH_TERMINAL_QUEUE_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
54705
55239
|
}
|
|
54706
55240
|
});
|
|
54707
55241
|
var mesh_ledger_exports = {};
|
|
@@ -56255,7 +56789,7 @@ Default branch: \`${mesh.defaultBranch}\`` : ""}`);
|
|
|
56255
56789
|
const lines = [
|
|
56256
56790
|
"## Task difficulty",
|
|
56257
56791
|
"",
|
|
56258
|
-
"Pass `difficulty` on `mesh_enqueue_task` (`easy` / `medium` / `difficult` / `freeform`)
|
|
56792
|
+
"Pass `difficulty` on every worker entry in `mesh_enqueue_batch`, or on `mesh_enqueue_task` for the single-task fallback. The values (`easy` / `medium` / `difficult` / `freeform`) describe how hard the work is. It is a ROUTING HINT: it is matched against each node's capability slots, so a task goes to a slot configured for that difficulty.",
|
|
56259
56793
|
"",
|
|
56260
56794
|
'**The slot decides the model and thinking level \u2014 not the difficulty.** `difficulty: "difficult"` does not mean "use opus"; it means "route to a slot that handles difficult work", and that slot\'s own model/thinking is what launches. So classify honestly by how hard the task is, and change what a difficulty RUNS ON by editing the node\'s slots (`mesh_node_slots_set`), never by picking a different difficulty. Passing an explicit `model`/`thinkingLevel` still overrides everything for one task.'
|
|
56261
56795
|
];
|
|
@@ -56325,6 +56859,10 @@ Default branch: \`${mesh.defaultBranch}\`` : ""}`);
|
|
|
56325
56859
|
if (policy.coordinatorIdlePushPolicy === "auto_silent_on_dispatch") {
|
|
56326
56860
|
rules.push("- Delegated-worker completions are **auto-silenced**: the routine idle/completion push for a task you dispatch is suppressed once (approval-needed, failure, and long-running alerts still notify the owner normally)");
|
|
56327
56861
|
}
|
|
56862
|
+
const failurePolicy = policy.onDependencyFailure === "cancel" ? "cancel" : "block";
|
|
56863
|
+
rules.push(
|
|
56864
|
+
`- on_dependency_failure: **${failurePolicy}** \u2014 controls downstream tasks when a required worker task fails or is cancelled. \`block\` (default) keeps downstream pending and automatically recovers if the predecessor is retried and later completes. \`cancel\` terminally cancels the dependent branch; it is not revived by predecessor retry.`
|
|
56865
|
+
);
|
|
56328
56866
|
return `## Policy
|
|
56329
56867
|
${rules.join("\n")}`;
|
|
56330
56868
|
}
|
|
@@ -56335,8 +56873,8 @@ ${rules.join("\n")}`;
|
|
|
56335
56873
|
return `## Rules
|
|
56336
56874
|
|
|
56337
56875
|
- **Route, don't implement.** Delegate all code reading, analysis, and execution to node agents. Never read source files or run commands in the coordinator \u2014 keep context lean. See also: **Never use local sub-agents** below.${destructiveGitRule}
|
|
56338
|
-
- **Never use local sub-agents.** Do NOT spawn your runtime's own sub-agents (e.g. Claude Code's Task/Explore/Agent tools, or any equivalent in-process agent-spawning tool) to read code, investigate, run RCA, or implement. Such sub-agents execute on the coordinator's machine, outside the mesh \u2014 they escape mesh parallelism, the ledger/audit trail, node capability profiles, and worktree isolation, and leave no \`mesh_task_history\` record. ALL code reading, analysis, RCA, and implementation must be delegated
|
|
56339
|
-
- **Front-load task
|
|
56876
|
+
- **Never use local sub-agents.** Do NOT spawn your runtime's own sub-agents (e.g. Claude Code's Task/Explore/Agent tools, or any equivalent in-process agent-spawning tool) to read code, investigate, run RCA, or implement. Such sub-agents execute on the coordinator's machine, outside the mesh \u2014 they escape mesh parallelism, the ledger/audit trail, node capability profiles, and worktree isolation, and leave no \`mesh_task_history\` record. ALL code reading, analysis, RCA, and implementation must be delegated through \`mesh_enqueue_batch\` for a currently known multi-step graph, \`mesh_enqueue_task\` for one ready task, or \`mesh_send_task\` for a same-session continuation (use \`task_mode: "live_debug_readonly"\` for read-only investigation), or cross-verified via \`mesh_magi_review\` for read-only fan-out. The coordinator's own actions are limited to \`mesh_*\` tool orchestration and synthesizing results.
|
|
56877
|
+
- **Front-load immutable task instructions.** Include everything the agent needs (files, problem, expected fix) in \`mesh_enqueue_batch\` / \`mesh_enqueue_task\` / \`mesh_send_task\`. Put predecessor-produced data in explicit \`inputs_from\` bindings and coordinator decisions in gates; never copy untrusted worker output into a new instruction by hand when a binding can preserve provenance. Append a structured result request at the end: ask the worker to conclude with a JSON block containing \`status\`, \`changedFiles\`, \`gitStatus\`, \`validationResults\`, \`errors\`, \`nextAction\`. The daemon parses this automatically; you can read it from \`mesh_task_history\`.
|
|
56340
56878
|
- **Reuse idle sessions.** For follow-up, retry, commit/push, or cleanup on the same issue, send only the delta to the existing idle session. Start a fresh session only when: (a) branch/worktree isolation is required, (b) the existing session had a dispatch failure or provider mismatch, (c) the transcript/runtime is contaminated or interrupted, (d) the user explicitly asks for a different provider/session, or (e) **the delta is a genuinely NEW subject rather than a continuation** \u2014 a new topic appended to an existing session can be dropped or re-run as the previous task, so give it its own task even when a session sits idle. Continuation of the same issue in an already-idle session is allowed and preferred \u2014 this rule blocks concurrent unrelated work interleaved into a live (still-generating) session, not sequential same-issue follow-ups. The test is subject continuity, not timing: carrying an investigation forward into its own fix is the SAME subject and belongs in that session (Workflow 3f), while an unrelated bug is a new subject even if the same session just went idle.
|
|
56341
56879
|
- **Nodes are separate machines with separate checkouts \u2014 not interchangeable execution slots.** Each node is a different physical computer with its own clone of the repo. Work done on another node must be committed, pushed, and pulled back before this machine sees it, and since RELEASE/DEPLOY runs on the coordinator's own machine, sending a code change elsewhere buys a round trip out and another one back. So **default to this coordinator's own machine for code changes** \u2014 its local node (base or a worktree cloned from it). Routing to a DIFFERENT machine is the exception and needs a reason, of which there are exactly two: (a) **platform-specific verification** that cannot be done here \u2014 win32 PATH/registry, a clean install/uninstall on that OS, that machine's package-manager state; or (b) **parallelizing read-only investigation** across machines. "That node is idle" is not a reason. If you catch yourself dispatching a fix to another machine without (a) or (b), route it here instead.
|
|
56342
56880
|
- **Don't split investigation from the fix.** When a task will plainly end in a code change, dispatch it as \`code_change\` from the start \u2014 the in-session handoff and split criteria live in Workflow 3f. Split only when the fix genuinely belongs on another machine for reason (a) above; redoing an investigator's context in a fresh session (worse, on another machine) is pure loss.
|
|
@@ -56400,9 +56938,12 @@ When you compose the task message you dispatch to a node, include this requireme
|
|
|
56400
56938
|
|------|---------|
|
|
56401
56939
|
| \`mesh_status\` | Nodes' health, git state, sessions, branch convergence |
|
|
56402
56940
|
| \`mesh_list_nodes\` | List nodes with workspace paths |
|
|
56403
|
-
| \`
|
|
56404
|
-
| \`
|
|
56941
|
+
| \`mesh_enqueue_batch\` | **DEFAULT enqueue surface** for a plan with two or more known graph steps. Atomically enqueues a dependency-wired task set; \`depends_on\` may name batch-local \`ref\`s (forward refs OK). Carries the full graph surface: \`inputs_from\`, \`run_if\`, \`gates\` + \`gated_by\`, \`workspaces\` + \`workspace_ref\` |
|
|
56942
|
+
| \`mesh_enqueue_task\` | SINGLE-TASK FALLBACK \u2014 one ready task with no declarable downstream step; idle nodes auto-claim |
|
|
56405
56943
|
| \`mesh_view_queue\` | Queue status \u2014 pending/assigned/completed/failed/cancelled |
|
|
56944
|
+
| \`mesh_graph_view\` | Inspect orchestration graphs \u2014 node states, gates awaiting you, workspace sagas, why something is blocked |
|
|
56945
|
+
| \`mesh_graph_gate_claim\` | Take the lease on a gate awaiting a coordinator; returns the fencing token + generation a release needs |
|
|
56946
|
+
| \`mesh_graph_gate_release\` | Pass a gate you hold \u2014 the ONLY way through one (no timeout ever passes a gate) |
|
|
56406
56947
|
| \`mesh_queue_cancel\` | Cancel a queue task (audit history kept) |
|
|
56407
56948
|
| \`mesh_queue_requeue\` | Return a task to pending for retry |
|
|
56408
56949
|
| \`mesh_send_task\` | Push a task straight to a specific node/session |
|
|
@@ -56455,6 +56996,8 @@ When you compose the task message you dispatch to a node, include this requireme
|
|
|
56455
56996
|
| \`mesh_coordinator_prompt_append_set\` | Write/clear that APPEND (append-only; base prompt is not replaceable) |`;
|
|
56456
56997
|
TOOL_EXPOSURE_PREFLIGHT_SECTION = `## Tool Exposure Preflight
|
|
56457
56998
|
|
|
56999
|
+
Before searching for an enqueue tool, classify the whole currently known work frontier. For every new delegation search, include \`mesh_enqueue_batch\` by exact name; never search for or load only \`mesh_enqueue_task\`. Load \`mesh_enqueue_task\` only as the single-task fallback after the batch eligibility check below fails.
|
|
57000
|
+
|
|
56458
57001
|
Before doing any coordinator work, confirm that the actual callable tool list includes \`mesh_status\` and the other \`mesh_*\` tools from the table above. If this Repo Mesh coordinator prompt is present but the callable \`mesh_*\` tools are missing, the MCP server/tool manifest is stale or not injected yet. Do not substitute terminal/file/git tools, do not inspect or edit the repository directly, and do not continue as a non-mesh local coding agent. Stop immediately and tell the user to run \`/reload-mcp\` or start a fresh coordinator session so ADHDev can reconnect \`adhdev-mesh\`.`;
|
|
56459
57002
|
QUOTA_SECTION = `## Provider Quota
|
|
56460
57003
|
|
|
@@ -56465,9 +57008,15 @@ Before doing any coordinator work, confirm that the actual callable tool list in
|
|
|
56465
57008
|
WORKFLOW_SECTION = `## Orchestration Workflow
|
|
56466
57009
|
|
|
56467
57010
|
1. **Assess** \u2014 Call \`mesh_status\` to see which nodes are healthy and available. Check \`mesh_task_history\` to understand what has already been done in this mesh \u2014 previous delegations, completions, and failures.
|
|
56468
|
-
2. **Plan** \u2014 Decompose the user's request into independent tasks for parallel execution, or sequential tasks when dependencies exist. If \`mesh_task_history\` shows a recent failure for a task, decide whether to retry or reassign. **For multi-task work, create a mission first**: call \`mesh_mission_upsert\` with a title and goal, then attach every enqueued task with \`mission_id\`. Express "B after A" ordering with \`depends_on\`
|
|
57011
|
+
2. **Plan** \u2014 Decompose the user's request into independent tasks for parallel execution, or sequential tasks when dependencies exist. If \`mesh_task_history\` shows a recent failure for a task, decide whether to retry or reassign. **For multi-task work, create a mission first**: call \`mesh_mission_upsert\` with a title and goal, then attach every enqueued task with \`mission_id\`. Express "B after A" ordering with \`depends_on\` instead of waiting and polling \u2014 the system claims dependents automatically when their dependencies complete. When the mission's outcome is decided, update its status (\`completed\`/\`abandoned\`) via \`mesh_mission_upsert\`. If the prompt already shows an **Active Mission**, continue it from its current task state \u2014 do not re-enqueue tasks that already exist.
|
|
56469
57012
|
3. **Queue / Delegate** \u2014 The Mesh uses an autonomous pull-based Work Queue:
|
|
56470
|
-
a. **
|
|
57013
|
+
a. **Batch-first rule:** submit one \`mesh_enqueue_batch\` whenever the currently known plan contains two or more graph steps. A graph may include worker tasks, selected predecessor outputs via \`inputs_from\`, declarative \`run_if\` branches, delayed \`workspace_ref\` preparation, and coordinator gates for approval, Refinery, CI waiting, publish, or deployment. Submit the whole materializable plan once; do not wait and enqueue each known step separately.
|
|
57014
|
+
|
|
57015
|
+
Use \`mesh_enqueue_task\` only when exactly one new worker task is currently known and there is no known downstream step to declare, or when continuing an already-running session via \`mesh_send_task\` is the correct same-subject handoff. If later work is genuinely unknown, enqueue the one ready task and start a new batch when the next frontier becomes known.
|
|
57016
|
+
|
|
57017
|
+
Do not invent speculative downstream instructions merely to form a batch. Use an \`inputs_from\` binding when the instruction is stable but needs predecessor evidence; use a coordinator gate when a human/coordinator decision or external side effect must happen first; use \`run_if\` for a structured result predicate. If none can state the future step faithfully, single-task enqueue is correct.
|
|
57018
|
+
|
|
57019
|
+
Either way, idle node agents automatically pull tasks from the queue and begin working. A batch inserts atomically (a mid-batch error such as a cycle or bad difficulty rolls the whole set back, so no half-registered chain), and each task's \`depends_on\` may name sibling tasks by their batch-local \`ref\` label, forward references included.
|
|
56471
57020
|
b. **Node Preparation**: Reuse an existing idle session on the correct node/provider before launching a new chat/session. Call \`mesh_launch_session\` only when no suitable session exists, when the user explicitly asks for a fresh provider/session, or when branch/worktree isolation requires it. **A node is not limited to one live session for read-only work** \u2014 \`readonly\`/\`live_debug_readonly\` tasks are exempt from the one-active-per-node invariant, so the SAME node can auto-launch multiple concurrent read-only sessions with no worktree needed. Cloning a worktree costs roughly 10 seconds, so it is cheap enough to create one whenever write work needs a free node; use it for branch isolation, for parallel write tasks (one active write per node), or when a node's read-only queue is deep enough that a second node would clearly finish faster \u2014 call \`mesh_clone_node\` to create the worktree node first.
|
|
56472
57021
|
b0. **Base nodes are for environment-specific testing, not for general code changes.** Before dispatching any write task, answer ONE question: *does this task verify the physical environment of a specific machine or OS, or does it only change code?*
|
|
56473
57022
|
- **Physical-environment task \u2192 base node, targeted.** Pin it with \`required_tags\` (e.g. \`["os=win32"]\`) or \`target_node_id\`. Examples that genuinely require the real machine: verifying a win32 \`PATH\`/registry/installer layout, a clean-install or uninstall on a specific OS, Homebrew or package-manager state on one particular machine, an OS-dependent runtime behavior (path separators, process spawn, native bindings), or reproducing a bug reported only on that node. A worktree CANNOT substitute for these \u2014 the point is the machine itself.
|
|
@@ -59764,6 +60313,593 @@ ${rendered}`, "utf-8");
|
|
|
59764
60313
|
ACTIVE_SAGA_STATES = ["declared", "preparing", "compensating"];
|
|
59765
60314
|
}
|
|
59766
60315
|
});
|
|
60316
|
+
function readGateSpec(node) {
|
|
60317
|
+
if (!node) return {};
|
|
60318
|
+
try {
|
|
60319
|
+
const parsed = JSON.parse(node.baseSpecJson);
|
|
60320
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
60321
|
+
} catch {
|
|
60322
|
+
return {};
|
|
60323
|
+
}
|
|
60324
|
+
}
|
|
60325
|
+
function isoAfter(nowMs, seconds) {
|
|
60326
|
+
return new Date(nowMs + seconds * 1e3).toISOString();
|
|
60327
|
+
}
|
|
60328
|
+
function claimMeshGraphGate(input) {
|
|
60329
|
+
const store = MeshRuntimeStore.getInstance();
|
|
60330
|
+
const nowMs = input.nowMs ?? Date.now();
|
|
60331
|
+
const nowIso = new Date(nowMs).toISOString();
|
|
60332
|
+
const outboxEvents = [];
|
|
60333
|
+
const result = store.transaction(() => {
|
|
60334
|
+
const graphStore = store.graphStore();
|
|
60335
|
+
const gate = graphStore.getGate(input.gateId);
|
|
60336
|
+
if (!gate || gate.meshId !== input.meshId) return { claimed: false, reason: "gate_not_found" };
|
|
60337
|
+
if (gate.eligibleCoordinatorSessionId && gate.eligibleCoordinatorSessionId !== input.coordinatorSessionId) {
|
|
60338
|
+
return { claimed: false, reason: "gate_not_eligible" };
|
|
60339
|
+
}
|
|
60340
|
+
if (gate.state === "released" || gate.state === "cancelled") {
|
|
60341
|
+
return { claimed: false, reason: `gate_terminal:${gate.state}` };
|
|
60342
|
+
}
|
|
60343
|
+
if (gate.state === "declared") {
|
|
60344
|
+
return { claimed: false, reason: "gate_not_awaiting" };
|
|
60345
|
+
}
|
|
60346
|
+
const leaseLive = !!gate.leaseExpiresAt && gate.leaseExpiresAt > nowIso;
|
|
60347
|
+
const sameOwner = gate.leaseOwnerSessionId === input.coordinatorSessionId;
|
|
60348
|
+
const priorState = gate.state;
|
|
60349
|
+
let takeoverFromClaimed = false;
|
|
60350
|
+
if (priorState === "claimed" && leaseLive && !sameOwner) {
|
|
60351
|
+
return { claimed: false, reason: "gate_lease_held", gate };
|
|
60352
|
+
}
|
|
60353
|
+
if (priorState === "expired" && gate.onTimeout !== "hold") {
|
|
60354
|
+
return { claimed: false, reason: "gate_terminal:expired", gate };
|
|
60355
|
+
}
|
|
60356
|
+
const node = graphStore.getNode(gate.graphId, gate.nodeId);
|
|
60357
|
+
const spec = readGateSpec(node);
|
|
60358
|
+
const leaseSeconds = input.leaseSeconds ?? spec.lease_seconds ?? MESH_GATE_DEFAULT_LEASE_SECONDS;
|
|
60359
|
+
const leaseExpiresAt = isoAfter(nowMs, leaseSeconds);
|
|
60360
|
+
let generation;
|
|
60361
|
+
let fencingToken;
|
|
60362
|
+
if (priorState === "claimed" && sameOwner && leaseLive) {
|
|
60363
|
+
generation = gate.leaseGeneration;
|
|
60364
|
+
fencingToken = gate.fencingToken;
|
|
60365
|
+
} else {
|
|
60366
|
+
takeoverFromClaimed = priorState === "claimed";
|
|
60367
|
+
generation = gate.leaseGeneration + 1;
|
|
60368
|
+
fencingToken = (0, import_crypto13.randomUUID)();
|
|
60369
|
+
}
|
|
60370
|
+
const deadlineAt = input.extendDeadlineSeconds && input.extendDeadlineSeconds > 0 ? isoAfter(nowMs, input.extendDeadlineSeconds) : gate.deadlineAt;
|
|
60371
|
+
const won = graphStore.patchGate(gate.gateId, {
|
|
60372
|
+
state: "claimed",
|
|
60373
|
+
leaseOwnerSessionId: input.coordinatorSessionId,
|
|
60374
|
+
leaseGeneration: generation,
|
|
60375
|
+
fencingToken,
|
|
60376
|
+
leaseExpiresAt,
|
|
60377
|
+
...deadlineAt !== void 0 ? { deadlineAt } : {}
|
|
60378
|
+
}, nowIso, { leaseGeneration: gate.leaseGeneration });
|
|
60379
|
+
if (!won) return { claimed: false, reason: "gate_claim_race" };
|
|
60380
|
+
if (node && node.state === "expired") {
|
|
60381
|
+
graphStore.updateNodeState(gate.graphId, gate.nodeId, "awaiting_coordinator", nowIso);
|
|
60382
|
+
}
|
|
60383
|
+
if (takeoverFromClaimed) {
|
|
60384
|
+
outboxEvents.push({
|
|
60385
|
+
kind: "graph_gate_lease_expired",
|
|
60386
|
+
payload: {
|
|
60387
|
+
graphId: gate.graphId,
|
|
60388
|
+
gateId: gate.gateId,
|
|
60389
|
+
ref: gate.ref,
|
|
60390
|
+
previousOwnerSessionId: gate.leaseOwnerSessionId,
|
|
60391
|
+
previousGeneration: gate.leaseGeneration,
|
|
60392
|
+
newGeneration: generation
|
|
60393
|
+
}
|
|
60394
|
+
});
|
|
60395
|
+
}
|
|
60396
|
+
outboxEvents.push({
|
|
60397
|
+
kind: "graph_gate_claimed",
|
|
60398
|
+
payload: {
|
|
60399
|
+
graphId: gate.graphId,
|
|
60400
|
+
gateId: gate.gateId,
|
|
60401
|
+
ref: gate.ref,
|
|
60402
|
+
action: gate.action,
|
|
60403
|
+
ownerSessionId: input.coordinatorSessionId,
|
|
60404
|
+
generation,
|
|
60405
|
+
leaseExpiresAt
|
|
60406
|
+
}
|
|
60407
|
+
});
|
|
60408
|
+
for (const event of outboxEvents) {
|
|
60409
|
+
graphStore.insertOutboxEvent({
|
|
60410
|
+
id: newMeshGraphOutboxId(),
|
|
60411
|
+
meshId: gate.meshId,
|
|
60412
|
+
graphId: gate.graphId,
|
|
60413
|
+
kind: event.kind,
|
|
60414
|
+
payload: JSON.stringify(event.payload),
|
|
60415
|
+
status: "pending",
|
|
60416
|
+
attemptCount: 0,
|
|
60417
|
+
createdAt: nowIso,
|
|
60418
|
+
updatedAt: nowIso
|
|
60419
|
+
});
|
|
60420
|
+
}
|
|
60421
|
+
return {
|
|
60422
|
+
claimed: true,
|
|
60423
|
+
gate: graphStore.getGate(gate.gateId),
|
|
60424
|
+
leaseGeneration: generation,
|
|
60425
|
+
fencingToken,
|
|
60426
|
+
leaseExpiresAt,
|
|
60427
|
+
deadlineAt,
|
|
60428
|
+
ambiguousExternalOutcome: takeoverFromClaimed || priorState === "expired" && gate.leaseGeneration > 0,
|
|
60429
|
+
previousLeaseOwnerSessionId: gate.leaseOwnerSessionId
|
|
60430
|
+
};
|
|
60431
|
+
});
|
|
60432
|
+
if (result.claimed) {
|
|
60433
|
+
try {
|
|
60434
|
+
drainMeshGraphOutbox(input.meshId);
|
|
60435
|
+
} catch {
|
|
60436
|
+
}
|
|
60437
|
+
}
|
|
60438
|
+
return result;
|
|
60439
|
+
}
|
|
60440
|
+
function releaseMeshGraphGate(input) {
|
|
60441
|
+
const store = MeshRuntimeStore.getInstance();
|
|
60442
|
+
const nowMs = input.nowMs ?? Date.now();
|
|
60443
|
+
const nowIso = new Date(nowMs).toISOString();
|
|
60444
|
+
const result = store.transaction(() => {
|
|
60445
|
+
const graphStore = store.graphStore();
|
|
60446
|
+
const gate = graphStore.getGate(input.gateId);
|
|
60447
|
+
if (!gate || gate.meshId !== input.meshId) throw new Error(`gate_not_found: no gate '${input.gateId}'`);
|
|
60448
|
+
const releaseDigest = sha256Hex2(canonicalJson({
|
|
60449
|
+
outcome: input.outcome,
|
|
60450
|
+
result: input.result ?? null,
|
|
60451
|
+
evidence: input.evidence ?? null,
|
|
60452
|
+
patches: input.patches ?? []
|
|
60453
|
+
}));
|
|
60454
|
+
if (gate.releaseIdempotencyKey === input.idempotencyKey) {
|
|
60455
|
+
if (gate.releaseEvidenceDigest === releaseDigest) {
|
|
60456
|
+
return { released: true, duplicate: true, gate, materializedNodeIds: [] };
|
|
60457
|
+
}
|
|
60458
|
+
throw new Error(
|
|
60459
|
+
`gate_release_conflict: idempotency key '${input.idempotencyKey}' already committed with a different release digest (gate '${input.gateId}')`
|
|
60460
|
+
);
|
|
60461
|
+
}
|
|
60462
|
+
if (gate.state === "released") {
|
|
60463
|
+
throw new Error(`gate_already_released: gate '${input.gateId}' was released under a different idempotency key`);
|
|
60464
|
+
}
|
|
60465
|
+
if (gate.state !== "claimed") {
|
|
60466
|
+
throw new Error(`gate_not_claimed: gate '${input.gateId}' is '${gate.state}' \u2014 only a claimed gate can be released`);
|
|
60467
|
+
}
|
|
60468
|
+
if (input.fencingToken !== gate.fencingToken || input.leaseGeneration !== gate.leaseGeneration) {
|
|
60469
|
+
throw new Error(
|
|
60470
|
+
`stale_fence: gate '${input.gateId}' is at lease generation ${gate.leaseGeneration}; the presented token/generation (${input.leaseGeneration}) is stale`
|
|
60471
|
+
);
|
|
60472
|
+
}
|
|
60473
|
+
if (gate.leaseExpiresAt && gate.leaseExpiresAt <= nowIso) {
|
|
60474
|
+
throw new Error(
|
|
60475
|
+
`gate_lease_expired: gate '${input.gateId}' lease expired at ${gate.leaseExpiresAt} \u2014 reclaim the gate (higher generation) and reconcile external evidence before releasing`
|
|
60476
|
+
);
|
|
60477
|
+
}
|
|
60478
|
+
const node = graphStore.getNode(gate.graphId, gate.nodeId);
|
|
60479
|
+
if (!node) throw new Error(`graph_node_not_found: gate '${input.gateId}' backs a missing node '${gate.nodeId}'`);
|
|
60480
|
+
const nodes = graphStore.listNodes(gate.graphId);
|
|
60481
|
+
const edges = graphStore.listEdges(gate.graphId);
|
|
60482
|
+
const byId = new Map(nodes.map((n) => [n.nodeId, n]));
|
|
60483
|
+
const incoming = edges.filter((e) => e.toNodeId === node.nodeId);
|
|
60484
|
+
const upstreamSettled = incoming.every((e) => {
|
|
60485
|
+
const sourceState = byId.get(e.fromNodeId)?.state;
|
|
60486
|
+
return sourceState === "completed" || sourceState === "released" || sourceState === "skipped" && e.omitOnSkip;
|
|
60487
|
+
});
|
|
60488
|
+
if (!upstreamSettled) {
|
|
60489
|
+
throw new Error(`gate_upstream_unsettled: gate '${input.gateId}' has unsatisfied predecessors`);
|
|
60490
|
+
}
|
|
60491
|
+
const directDownstream = new Set(edges.filter((e) => e.fromNodeId === node.nodeId).map((e) => e.toNodeId));
|
|
60492
|
+
for (const patch of input.patches ?? []) {
|
|
60493
|
+
const target = nodes.find((n) => n.nodeId === patch.node || n.ref === patch.node);
|
|
60494
|
+
if (!target || !directDownstream.has(target.nodeId)) {
|
|
60495
|
+
throw new Error(`gate_patch_not_downstream: '${patch.node}' is not a direct downstream node of gate '${input.gateId}'`);
|
|
60496
|
+
}
|
|
60497
|
+
for (const key2 of Object.keys(patch.baseSpecPatch)) {
|
|
60498
|
+
if (!MESH_GATE_RELEASE_PATCH_KEYS.includes(key2)) {
|
|
60499
|
+
throw new Error(`gate_patch_forbidden: key '${key2}' is outside the permitted release patch surface (design :294-299)`);
|
|
60500
|
+
}
|
|
60501
|
+
}
|
|
60502
|
+
if (target.queueTaskId) {
|
|
60503
|
+
const entry = store.findQueueEntryById(target.meshId, target.queueTaskId);
|
|
60504
|
+
if (entry && entry.status !== "pending") {
|
|
60505
|
+
throw new Error(
|
|
60506
|
+
`task_already_claimed: gate release cannot patch node '${target.nodeId}' \u2014 queue task '${target.queueTaskId}' is '${entry.status}' and immutable (design :334)`
|
|
60507
|
+
);
|
|
60508
|
+
}
|
|
60509
|
+
}
|
|
60510
|
+
const rawSpec = (() => {
|
|
60511
|
+
try {
|
|
60512
|
+
return JSON.parse(target.baseSpecJson);
|
|
60513
|
+
} catch {
|
|
60514
|
+
return {};
|
|
60515
|
+
}
|
|
60516
|
+
})();
|
|
60517
|
+
graphStore.updateNodeBaseSpec(
|
|
60518
|
+
target.graphId,
|
|
60519
|
+
target.nodeId,
|
|
60520
|
+
JSON.stringify({ ...rawSpec && typeof rawSpec === "object" ? rawSpec : {}, ...patch.baseSpecPatch }),
|
|
60521
|
+
nowIso
|
|
60522
|
+
);
|
|
60523
|
+
const inMap = byId.get(target.nodeId);
|
|
60524
|
+
if (inMap) inMap.materializationVersion += 1;
|
|
60525
|
+
}
|
|
60526
|
+
const won = graphStore.patchGate(gate.gateId, {
|
|
60527
|
+
state: "released",
|
|
60528
|
+
releaseOutcome: input.outcome,
|
|
60529
|
+
releaseEvidenceJson: canonicalJson({ result: input.result ?? null, evidence: input.evidence ?? null }),
|
|
60530
|
+
releaseEvidenceDigest: releaseDigest,
|
|
60531
|
+
releaseIdempotencyKey: input.idempotencyKey
|
|
60532
|
+
}, nowIso, { leaseGeneration: gate.leaseGeneration });
|
|
60533
|
+
if (!won) throw new Error(`stale_fence: gate '${input.gateId}' moved under the release (generation race)`);
|
|
60534
|
+
graphStore.updateNodeState(gate.graphId, node.nodeId, "released", nowIso);
|
|
60535
|
+
const mapped = byId.get(node.nodeId);
|
|
60536
|
+
if (mapped) mapped.state = "released";
|
|
60537
|
+
const materializedNodeIds = advanceReleasedGateDownstream(store, gate, node, nowIso);
|
|
60538
|
+
const freshNodes = graphStore.listNodes(gate.graphId);
|
|
60539
|
+
const graph = graphStore.getGraph(gate.graphId);
|
|
60540
|
+
if (graph && freshNodes.every(isTerminalEquivalent)) {
|
|
60541
|
+
graphStore.updateGraphStatus(gate.graphId, "completed", nowIso, true);
|
|
60542
|
+
insertGateOutbox(graphStore, gate.meshId, gate.graphId, "graph_completed", { graphId: gate.graphId }, nowIso);
|
|
60543
|
+
} else if (graph?.status === "waiting_gate") {
|
|
60544
|
+
const stillWaiting = graphStore.listGatesByGraph(gate.graphId).some((g) => g.gateId !== gate.gateId && (g.state === "awaiting_coordinator" || g.state === "claimed"));
|
|
60545
|
+
if (!stillWaiting) graphStore.updateGraphStatus(gate.graphId, "active", nowIso);
|
|
60546
|
+
}
|
|
60547
|
+
insertGateOutbox(graphStore, gate.meshId, gate.graphId, "graph_gate_released", {
|
|
60548
|
+
graphId: gate.graphId,
|
|
60549
|
+
gateId: gate.gateId,
|
|
60550
|
+
nodeId: node.nodeId,
|
|
60551
|
+
ref: gate.ref,
|
|
60552
|
+
action: gate.action,
|
|
60553
|
+
outcome: input.outcome,
|
|
60554
|
+
releaseDigest,
|
|
60555
|
+
generation: gate.leaseGeneration,
|
|
60556
|
+
materializedNodeIds
|
|
60557
|
+
}, nowIso);
|
|
60558
|
+
if (materializedNodeIds.length > 0) {
|
|
60559
|
+
insertGateOutbox(graphStore, gate.meshId, gate.graphId, "queue_wake", {
|
|
60560
|
+
meshId: gate.meshId,
|
|
60561
|
+
reason: "graph_gate_released",
|
|
60562
|
+
graphId: gate.graphId,
|
|
60563
|
+
gateId: gate.gateId
|
|
60564
|
+
}, nowIso);
|
|
60565
|
+
}
|
|
60566
|
+
return { released: true, duplicate: false, gate: graphStore.getGate(gate.gateId), materializedNodeIds };
|
|
60567
|
+
});
|
|
60568
|
+
try {
|
|
60569
|
+
drainMeshGraphOutbox(input.meshId);
|
|
60570
|
+
} catch {
|
|
60571
|
+
}
|
|
60572
|
+
return result;
|
|
60573
|
+
}
|
|
60574
|
+
function advanceReleasedGateDownstream(store, gate, gateNode, nowIso) {
|
|
60575
|
+
const graphStore = store.graphStore();
|
|
60576
|
+
const nodes = graphStore.listNodes(gate.graphId);
|
|
60577
|
+
const edges = graphStore.listEdges(gate.graphId);
|
|
60578
|
+
const byId = new Map(nodes.map((n) => [n.nodeId, n]));
|
|
60579
|
+
const mappedGate = byId.get(gateNode.nodeId);
|
|
60580
|
+
if (mappedGate) mappedGate.state = "released";
|
|
60581
|
+
const materialized = [];
|
|
60582
|
+
let frontier = /* @__PURE__ */ new Set([gateNode.nodeId]);
|
|
60583
|
+
for (let pass = 0; pass < nodes.length + 1 && frontier.size > 0; pass += 1) {
|
|
60584
|
+
const nextFrontier = /* @__PURE__ */ new Set();
|
|
60585
|
+
const downstreamIds = new Set(edges.filter((e) => frontier.has(e.fromNodeId)).map((e) => e.toNodeId));
|
|
60586
|
+
for (const targetId of downstreamIds) {
|
|
60587
|
+
const target = byId.get(targetId);
|
|
60588
|
+
if (!target) continue;
|
|
60589
|
+
if (target.kind === "coordinator_gate") {
|
|
60590
|
+
maybeOpenCoordinatorGate(store, target, edges, byId, nowIso);
|
|
60591
|
+
continue;
|
|
60592
|
+
}
|
|
60593
|
+
if (target.kind !== "worker_task" || !target.queueTaskId) continue;
|
|
60594
|
+
if (target.state !== "declared" && target.state !== "blocked") continue;
|
|
60595
|
+
clearReleasedGateHold(store, target, gate.gateId);
|
|
60596
|
+
const outcome = settleDownstreamNode(store, target, edges, byId, nowIso);
|
|
60597
|
+
if (outcome.kind === "materialized") {
|
|
60598
|
+
materialized.push(target.nodeId);
|
|
60599
|
+
insertGateOutbox(graphStore, target.meshId, target.graphId, "graph_node_materialized", {
|
|
60600
|
+
graphId: target.graphId,
|
|
60601
|
+
nodeId: target.nodeId,
|
|
60602
|
+
ref: target.ref,
|
|
60603
|
+
taskId: target.queueTaskId,
|
|
60604
|
+
gateId: gate.gateId,
|
|
60605
|
+
materializationVersion: target.materializationVersion,
|
|
60606
|
+
digest: outcome.digest,
|
|
60607
|
+
receipts: outcome.receipts
|
|
60608
|
+
}, nowIso);
|
|
60609
|
+
} else if (outcome.kind === "skipped") {
|
|
60610
|
+
nextFrontier.add(target.nodeId);
|
|
60611
|
+
insertGateOutbox(graphStore, target.meshId, target.graphId, "graph_node_skipped", {
|
|
60612
|
+
graphId: target.graphId,
|
|
60613
|
+
nodeId: target.nodeId,
|
|
60614
|
+
ref: target.ref,
|
|
60615
|
+
taskId: target.queueTaskId,
|
|
60616
|
+
gateId: gate.gateId,
|
|
60617
|
+
reason: outcome.reason
|
|
60618
|
+
}, nowIso);
|
|
60619
|
+
} else if (outcome.kind === "error") {
|
|
60620
|
+
insertGateOutbox(graphStore, target.meshId, target.graphId, "graph_node_materialization_failed", {
|
|
60621
|
+
graphId: target.graphId,
|
|
60622
|
+
nodeId: target.nodeId,
|
|
60623
|
+
ref: target.ref,
|
|
60624
|
+
taskId: target.queueTaskId,
|
|
60625
|
+
gateId: gate.gateId,
|
|
60626
|
+
blockedReason: outcome.blockedReason
|
|
60627
|
+
}, nowIso);
|
|
60628
|
+
} else {
|
|
60629
|
+
const entry = store.findQueueEntryById(target.meshId, target.queueTaskId);
|
|
60630
|
+
if (entry && entry.status === "pending" && !entry.blockedReason) {
|
|
60631
|
+
entry.blockedReason = graphMaterializationBlockReason(target.nodeId, target.materializationVersion);
|
|
60632
|
+
store.updateQueueEntry(entry);
|
|
60633
|
+
}
|
|
60634
|
+
}
|
|
60635
|
+
}
|
|
60636
|
+
frontier = nextFrontier;
|
|
60637
|
+
}
|
|
60638
|
+
return materialized;
|
|
60639
|
+
}
|
|
60640
|
+
function clearReleasedGateHold(store, target, gateId) {
|
|
60641
|
+
if (!target.queueTaskId) return;
|
|
60642
|
+
const entry = store.findQueueEntryById(target.meshId, target.queueTaskId);
|
|
60643
|
+
if (!entry || entry.status !== "pending") return;
|
|
60644
|
+
const hold = parseCoordinatorGateBlock(entry.blockedReason);
|
|
60645
|
+
if (hold && hold.gateId === gateId) {
|
|
60646
|
+
delete entry.blockedReason;
|
|
60647
|
+
store.updateQueueEntry(entry);
|
|
60648
|
+
}
|
|
60649
|
+
}
|
|
60650
|
+
function sweepMeshGraphGateTimeouts(meshId, nowMs) {
|
|
60651
|
+
const store = MeshRuntimeStore.getInstance();
|
|
60652
|
+
const now = nowMs ?? Date.now();
|
|
60653
|
+
const nowIso = new Date(now).toISOString();
|
|
60654
|
+
const result = store.transaction(() => {
|
|
60655
|
+
const graphStore = store.graphStore();
|
|
60656
|
+
const candidates = graphStore.listGatesByMesh(meshId, ["awaiting_coordinator", "claimed"]);
|
|
60657
|
+
const expiredLeaseGateIds = candidates.filter((g) => g.state === "claimed" && g.leaseExpiresAt && g.leaseExpiresAt <= nowIso).map((g) => g.gateId);
|
|
60658
|
+
const expiredGateIds = [];
|
|
60659
|
+
for (const gate of candidates) {
|
|
60660
|
+
if (!gate.deadlineAt || gate.deadlineAt > nowIso) continue;
|
|
60661
|
+
const node = graphStore.getNode(gate.graphId, gate.nodeId);
|
|
60662
|
+
const won = graphStore.patchGate(gate.gateId, { state: "expired" }, nowIso, { leaseGeneration: gate.leaseGeneration });
|
|
60663
|
+
if (!won) continue;
|
|
60664
|
+
if (node && node.state !== "expired") {
|
|
60665
|
+
graphStore.updateNodeState(gate.graphId, gate.nodeId, "expired", nowIso);
|
|
60666
|
+
}
|
|
60667
|
+
let cancelledNodeIds = [];
|
|
60668
|
+
if (gate.onTimeout === "cancel_downstream" && node) {
|
|
60669
|
+
cancelledNodeIds = cancelGateDownstreamSubtree(store, node, nowIso);
|
|
60670
|
+
} else if (gate.onTimeout === "fail_graph") {
|
|
60671
|
+
graphStore.updateGraphStatus(gate.graphId, "failed", nowIso, true);
|
|
60672
|
+
}
|
|
60673
|
+
insertGateOutbox(graphStore, gate.meshId, gate.graphId, "graph_gate_expired", {
|
|
60674
|
+
graphId: gate.graphId,
|
|
60675
|
+
gateId: gate.gateId,
|
|
60676
|
+
nodeId: gate.nodeId,
|
|
60677
|
+
ref: gate.ref,
|
|
60678
|
+
action: gate.action,
|
|
60679
|
+
policy: gate.onTimeout,
|
|
60680
|
+
deadlineAt: gate.deadlineAt,
|
|
60681
|
+
urgent: true,
|
|
60682
|
+
...cancelledNodeIds.length > 0 ? { cancelledNodeIds } : {}
|
|
60683
|
+
}, nowIso);
|
|
60684
|
+
expiredGateIds.push(gate.gateId);
|
|
60685
|
+
}
|
|
60686
|
+
return { expiredGateIds, expiredLeaseGateIds };
|
|
60687
|
+
});
|
|
60688
|
+
if (result.expiredGateIds.length > 0) {
|
|
60689
|
+
try {
|
|
60690
|
+
drainMeshGraphOutbox(meshId);
|
|
60691
|
+
} catch {
|
|
60692
|
+
}
|
|
60693
|
+
}
|
|
60694
|
+
if (result.expiredGateIds.length > 0 || result.expiredLeaseGateIds.length > 0) {
|
|
60695
|
+
LOG2.info("MeshGraph", `Gate sweep for mesh ${meshId}: expired=[${result.expiredGateIds.join(",")}] lease-lapsed=[${result.expiredLeaseGateIds.join(",")}]`);
|
|
60696
|
+
}
|
|
60697
|
+
return result;
|
|
60698
|
+
}
|
|
60699
|
+
function isCancelExempt(state) {
|
|
60700
|
+
return state === "completed" || state === "released" || state === "skipped" || state === "cancelled" || state === "failed" || state === "expired";
|
|
60701
|
+
}
|
|
60702
|
+
function cancelGateDownstreamSubtree(store, gateNode, nowIso) {
|
|
60703
|
+
const graphStore = store.graphStore();
|
|
60704
|
+
const nodes = graphStore.listNodes(gateNode.graphId);
|
|
60705
|
+
const edges = graphStore.listEdges(gateNode.graphId);
|
|
60706
|
+
const byId = new Map(nodes.map((n) => [n.nodeId, n]));
|
|
60707
|
+
const cancelled = [];
|
|
60708
|
+
const visited = /* @__PURE__ */ new Set([gateNode.nodeId]);
|
|
60709
|
+
let frontier = [gateNode.nodeId];
|
|
60710
|
+
while (frontier.length > 0) {
|
|
60711
|
+
const next = [];
|
|
60712
|
+
for (const id of frontier) {
|
|
60713
|
+
for (const edge of edges.filter((e) => e.fromNodeId === id)) {
|
|
60714
|
+
if (visited.has(edge.toNodeId)) continue;
|
|
60715
|
+
visited.add(edge.toNodeId);
|
|
60716
|
+
const target = byId.get(edge.toNodeId);
|
|
60717
|
+
if (!target || isCancelExempt(target.state)) continue;
|
|
60718
|
+
graphStore.updateNodeState(target.graphId, target.nodeId, "cancelled", nowIso, {
|
|
60719
|
+
failureReason: `coordinator_gate_timeout:${gateNode.nodeId}`
|
|
60720
|
+
});
|
|
60721
|
+
if (target.queueTaskId) {
|
|
60722
|
+
const entry = store.findQueueEntryById(target.meshId, target.queueTaskId);
|
|
60723
|
+
if (entry && entry.status === "pending") {
|
|
60724
|
+
entry.status = "cancelled";
|
|
60725
|
+
entry.blockedReason = `coordinator_gate_timeout:${gateNode.nodeId}`;
|
|
60726
|
+
store.updateQueueEntry(entry);
|
|
60727
|
+
}
|
|
60728
|
+
}
|
|
60729
|
+
cancelled.push(target.nodeId);
|
|
60730
|
+
next.push(target.nodeId);
|
|
60731
|
+
}
|
|
60732
|
+
}
|
|
60733
|
+
frontier = next;
|
|
60734
|
+
}
|
|
60735
|
+
return cancelled;
|
|
60736
|
+
}
|
|
60737
|
+
function insertGateOutbox(graphStore, meshId, graphId, kind, payload, nowIso) {
|
|
60738
|
+
graphStore.insertOutboxEvent({
|
|
60739
|
+
id: newMeshGraphOutboxId(),
|
|
60740
|
+
meshId,
|
|
60741
|
+
graphId,
|
|
60742
|
+
kind,
|
|
60743
|
+
payload: JSON.stringify(payload),
|
|
60744
|
+
status: "pending",
|
|
60745
|
+
attemptCount: 0,
|
|
60746
|
+
createdAt: nowIso,
|
|
60747
|
+
updatedAt: nowIso
|
|
60748
|
+
});
|
|
60749
|
+
}
|
|
60750
|
+
var import_crypto13;
|
|
60751
|
+
var MESH_GATE_DEFAULT_LEASE_SECONDS;
|
|
60752
|
+
var MESH_GATE_NAMED_OUTCOMES;
|
|
60753
|
+
var MESH_GATE_RELEASE_PATCH_KEYS;
|
|
60754
|
+
var init_mesh_graph_gates = __esm2({
|
|
60755
|
+
"src/mesh/mesh-graph-gates.ts"() {
|
|
60756
|
+
"use strict";
|
|
60757
|
+
import_crypto13 = require("crypto");
|
|
60758
|
+
init_mesh_runtime_store();
|
|
60759
|
+
init_logger();
|
|
60760
|
+
init_mesh_graph_input_binding();
|
|
60761
|
+
init_mesh_graph_types();
|
|
60762
|
+
init_mesh_graph_transition_runner();
|
|
60763
|
+
init_mesh_graph_transition_runner();
|
|
60764
|
+
MESH_GATE_DEFAULT_LEASE_SECONDS = 900;
|
|
60765
|
+
MESH_GATE_NAMED_OUTCOMES = ["passed", "failed", "rejected"];
|
|
60766
|
+
MESH_GATE_RELEASE_PATCH_KEYS = ["run_if", "on_false", "inputs_from", "workspace_ref"];
|
|
60767
|
+
}
|
|
60768
|
+
});
|
|
60769
|
+
function normalizeOrchestrationDecision(raw, surface) {
|
|
60770
|
+
const source = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
60771
|
+
const readCount = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : void 0;
|
|
60772
|
+
const readyWorkerTasks = readCount(source.ready_worker_tasks ?? source.readyWorkerTasks);
|
|
60773
|
+
const knownGraphSteps = readCount(source.known_graph_steps ?? source.knownGraphSteps);
|
|
60774
|
+
const rawReason = source.single_reason ?? source.singleReason;
|
|
60775
|
+
const singleReason = typeof rawReason === "string" && rawReason.trim() ? rawReason.trim() : null;
|
|
60776
|
+
const rawBlockers = source.capability_blockers ?? source.capabilityBlockers;
|
|
60777
|
+
const blockers = Array.isArray(rawBlockers) ? rawBlockers.filter((b) => typeof b === "string" && b.trim().length > 0).map((b) => b.trim()) : [];
|
|
60778
|
+
const decision = {
|
|
60779
|
+
decision: surface,
|
|
60780
|
+
...readyWorkerTasks !== void 0 ? { ready_worker_tasks: readyWorkerTasks } : {},
|
|
60781
|
+
...knownGraphSteps !== void 0 ? { known_graph_steps: knownGraphSteps } : {},
|
|
60782
|
+
single_reason: surface === "single" ? singleReason : null,
|
|
60783
|
+
...blockers.length > 0 ? { capability_blockers: blockers } : {}
|
|
60784
|
+
};
|
|
60785
|
+
const result = { decision };
|
|
60786
|
+
if (surface !== "single") return result;
|
|
60787
|
+
const superseded = [singleReason, ...blockers].find(
|
|
60788
|
+
(value) => !!value && MESH_SUPERSEDED_SINGLE_REASONS.includes(value)
|
|
60789
|
+
);
|
|
60790
|
+
if (superseded) {
|
|
60791
|
+
result.batchCapabilityAvailable = {
|
|
60792
|
+
code: "batch_capability_available",
|
|
60793
|
+
reportedReason: superseded,
|
|
60794
|
+
message: `'${superseded}' is no longer a blocker: mesh_enqueue_batch supports selected predecessor outputs (inputs_from), delayed worktree preparation (workspace_ref) and coordinator gates. Declare the whole known plan in one batch instead of enqueueing the steps separately.`
|
|
60795
|
+
};
|
|
60796
|
+
}
|
|
60797
|
+
if (knownGraphSteps !== void 0 && knownGraphSteps >= 2) {
|
|
60798
|
+
result.declaredEligibleSingle = true;
|
|
60799
|
+
}
|
|
60800
|
+
return result;
|
|
60801
|
+
}
|
|
60802
|
+
function safeAppend(meshId, kind, payload, taskId) {
|
|
60803
|
+
try {
|
|
60804
|
+
appendLedgerEntry(meshId, { kind, ...taskId ? { taskId } : {}, payload });
|
|
60805
|
+
} catch (e) {
|
|
60806
|
+
LOG2.warn("MeshGraph", `Ledger provenance append (${kind}) failed: ${e?.message || e}`);
|
|
60807
|
+
}
|
|
60808
|
+
}
|
|
60809
|
+
function recordGraphEnqueueCommitted(meshId, p) {
|
|
60810
|
+
safeAppend(meshId, "graph_enqueue_committed", {
|
|
60811
|
+
graphId: p.graphId,
|
|
60812
|
+
batchId: p.batchId,
|
|
60813
|
+
enqueueSurface: p.enqueueSurface,
|
|
60814
|
+
schemaVersion: p.schemaVersion,
|
|
60815
|
+
planDigest: p.planDigest,
|
|
60816
|
+
...p.missionId ? { missionId: p.missionId } : {},
|
|
60817
|
+
...p.coordinatorSessionId ? { coordinatorSessionId: p.coordinatorSessionId } : {},
|
|
60818
|
+
taskCount: p.taskCount,
|
|
60819
|
+
gateCount: p.gateCount,
|
|
60820
|
+
workspaceCount: p.workspaceCount,
|
|
60821
|
+
dependencyEdgeCount: p.dependencyEdgeCount,
|
|
60822
|
+
onDependencyFailure: p.onDependencyFailure,
|
|
60823
|
+
...p.orchestrationDecision ? { orchestrationDecision: p.orchestrationDecision } : {},
|
|
60824
|
+
...p.replayed ? { replayed: true } : {}
|
|
60825
|
+
});
|
|
60826
|
+
}
|
|
60827
|
+
function recordGraphEnqueueValidationFailed(meshId, p) {
|
|
60828
|
+
safeAppend(meshId, "graph_enqueue_validation_failed", {
|
|
60829
|
+
code: p.code,
|
|
60830
|
+
...p.batchId ? { batchId: p.batchId } : {},
|
|
60831
|
+
...p.taskCount !== void 0 ? { taskCount: p.taskCount } : {},
|
|
60832
|
+
...p.gateCount !== void 0 ? { gateCount: p.gateCount } : {},
|
|
60833
|
+
...p.workspaceCount !== void 0 ? { workspaceCount: p.workspaceCount } : {},
|
|
60834
|
+
...p.orchestrationDecision ? { orchestrationDecision: p.orchestrationDecision } : {}
|
|
60835
|
+
});
|
|
60836
|
+
}
|
|
60837
|
+
function recordGraphEnqueueRolledBack(meshId, p) {
|
|
60838
|
+
safeAppend(meshId, "graph_enqueue_rolled_back", {
|
|
60839
|
+
code: p.code,
|
|
60840
|
+
...p.batchId ? { batchId: p.batchId } : {},
|
|
60841
|
+
...p.taskCount !== void 0 ? { taskCount: p.taskCount } : {},
|
|
60842
|
+
// The error STRING is a validation message authored by this codebase
|
|
60843
|
+
// (never worker output or a task message), so it is safe provenance.
|
|
60844
|
+
...p.error ? { error: p.error.slice(0, 500) } : {}
|
|
60845
|
+
});
|
|
60846
|
+
}
|
|
60847
|
+
function recordGraphGateClaimed(meshId, p) {
|
|
60848
|
+
safeAppend(meshId, "graph_gate_claimed", {
|
|
60849
|
+
graphId: p.graphId,
|
|
60850
|
+
gateId: p.gateId,
|
|
60851
|
+
...p.ref ? { ref: p.ref } : {},
|
|
60852
|
+
action: p.action,
|
|
60853
|
+
generation: p.generation,
|
|
60854
|
+
ownerSessionId: p.ownerSessionId,
|
|
60855
|
+
...p.leaseExpiresAt ? { leaseExpiresAt: p.leaseExpiresAt } : {},
|
|
60856
|
+
...p.ambiguousExternalOutcome ? { ambiguousExternalOutcome: true } : {},
|
|
60857
|
+
...p.previousLeaseOwnerSessionId ? { previousLeaseOwnerSessionId: p.previousLeaseOwnerSessionId } : {}
|
|
60858
|
+
});
|
|
60859
|
+
}
|
|
60860
|
+
function recordGraphGateReleased(meshId, p) {
|
|
60861
|
+
safeAppend(meshId, "graph_gate_released", {
|
|
60862
|
+
graphId: p.graphId,
|
|
60863
|
+
gateId: p.gateId,
|
|
60864
|
+
...p.ref ? { ref: p.ref } : {},
|
|
60865
|
+
action: p.action,
|
|
60866
|
+
outcome: p.outcome,
|
|
60867
|
+
generation: p.generation,
|
|
60868
|
+
...p.releaseDigest ? { releaseDigest: p.releaseDigest } : {},
|
|
60869
|
+
...p.materializedNodeIds?.length ? { materializedNodeIds: p.materializedNodeIds } : {},
|
|
60870
|
+
...p.duplicate ? { duplicate: true } : {}
|
|
60871
|
+
});
|
|
60872
|
+
}
|
|
60873
|
+
function recordGraphGateExpired(meshId, p) {
|
|
60874
|
+
safeAppend(meshId, "graph_gate_expired", {
|
|
60875
|
+
gateId: p.gateId,
|
|
60876
|
+
...p.graphId ? { graphId: p.graphId } : {},
|
|
60877
|
+
policy: p.policy,
|
|
60878
|
+
...p.deadlineAt ? { deadlineAt: p.deadlineAt } : {},
|
|
60879
|
+
...p.cancelledNodeIds?.length ? { cancelledNodeIds: p.cancelledNodeIds } : {}
|
|
60880
|
+
});
|
|
60881
|
+
}
|
|
60882
|
+
var MESH_VALID_SINGLE_REASONS;
|
|
60883
|
+
var MESH_SUPERSEDED_SINGLE_REASONS;
|
|
60884
|
+
var init_mesh_graph_provenance = __esm2({
|
|
60885
|
+
"src/mesh/mesh-graph-provenance.ts"() {
|
|
60886
|
+
"use strict";
|
|
60887
|
+
init_mesh_ledger();
|
|
60888
|
+
init_logger();
|
|
60889
|
+
MESH_VALID_SINGLE_REASONS = [
|
|
60890
|
+
"only_one_step_known",
|
|
60891
|
+
"future_step_not_specifiable",
|
|
60892
|
+
"same_session_continuation",
|
|
60893
|
+
"legacy_client",
|
|
60894
|
+
"operator_override"
|
|
60895
|
+
];
|
|
60896
|
+
MESH_SUPERSEDED_SINGLE_REASONS = [
|
|
60897
|
+
"output_needed",
|
|
60898
|
+
"workspace_unresolved",
|
|
60899
|
+
"coordinator_action_between"
|
|
60900
|
+
];
|
|
60901
|
+
}
|
|
60902
|
+
});
|
|
59767
60903
|
function turnProjectionActiveWorkStatus(meshId, taskId) {
|
|
59768
60904
|
const row = resolveTurnAttemptRow({ meshId, taskId });
|
|
59769
60905
|
if (!row) return null;
|
|
@@ -61805,15 +62941,15 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
61805
62941
|
}
|
|
61806
62942
|
return "";
|
|
61807
62943
|
}
|
|
61808
|
-
function extractFinalAssistantSummaryEvidence(messages, maxChars = DEFAULT_FINAL_SUMMARY_MAX_CHARS) {
|
|
61809
|
-
const turnEnd = selectFinalAssistantTurnEndMessage(messages);
|
|
62944
|
+
function extractFinalAssistantSummaryEvidence(messages, maxChars = DEFAULT_FINAL_SUMMARY_MAX_CHARS, opts) {
|
|
62945
|
+
const turnEnd = selectFinalAssistantTurnEndMessage(messages, opts);
|
|
61810
62946
|
if (!turnEnd) return { finalSummary: "" };
|
|
61811
62947
|
return {
|
|
61812
62948
|
finalSummary: flattenContent(turnEnd.content).trim().slice(0, maxChars),
|
|
61813
62949
|
transcriptMessageAt: readChatMessageTimestampIso(turnEnd)
|
|
61814
62950
|
};
|
|
61815
62951
|
}
|
|
61816
|
-
function selectFinalAssistantTurnEndMessage(messages) {
|
|
62952
|
+
function selectFinalAssistantTurnEndMessage(messages, opts) {
|
|
61817
62953
|
if (!Array.isArray(messages) || messages.length === 0) return null;
|
|
61818
62954
|
let chromeFallbacks = 0;
|
|
61819
62955
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
@@ -61828,6 +62964,13 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
61828
62964
|
chromeFallbacks++;
|
|
61829
62965
|
continue;
|
|
61830
62966
|
}
|
|
62967
|
+
const boundaryMs = opts?.turnStartedAtMs;
|
|
62968
|
+
if (typeof boundaryMs === "number" && Number.isFinite(boundaryMs)) {
|
|
62969
|
+
const msgAt = readChatMessageTimestampMs(msg);
|
|
62970
|
+
if (typeof msgAt === "number" && Number.isFinite(msgAt) && msgAt >= boundaryMs && msgAt - boundaryMs < FINAL_ANSWER_MIN_TURN_AGE_MS && Date.now() - msgAt < FINAL_ANSWER_MIN_TURN_AGE_MS) {
|
|
62971
|
+
return null;
|
|
62972
|
+
}
|
|
62973
|
+
}
|
|
61831
62974
|
return msg;
|
|
61832
62975
|
}
|
|
61833
62976
|
return null;
|
|
@@ -62164,6 +63307,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
62164
63307
|
return (Array.isArray(messages) ? messages : []).filter((message) => classifyChatMessageVisibility(message).surface === surface);
|
|
62165
63308
|
}
|
|
62166
63309
|
var DEFAULT_FINAL_SUMMARY_MAX_CHARS;
|
|
63310
|
+
var FINAL_ANSWER_MIN_TURN_AGE_MS;
|
|
62167
63311
|
var FINAL_SUMMARY_CHROME_FALLBACK_MAX_DEPTH;
|
|
62168
63312
|
var TRANSCRIPT_CHROME_HARD_LINE_PATTERNS;
|
|
62169
63313
|
var TRANSCRIPT_CHROME_PANEL_LINE_PATTERNS;
|
|
@@ -62187,6 +63331,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
62187
63331
|
"use strict";
|
|
62188
63332
|
init_contracts2();
|
|
62189
63333
|
DEFAULT_FINAL_SUMMARY_MAX_CHARS = 16e3;
|
|
63334
|
+
FINAL_ANSWER_MIN_TURN_AGE_MS = 3e4;
|
|
62190
63335
|
FINAL_SUMMARY_CHROME_FALLBACK_MAX_DEPTH = 2;
|
|
62191
63336
|
TRANSCRIPT_CHROME_HARD_LINE_PATTERNS = [
|
|
62192
63337
|
// Bottom status bar: "auto K3 thinking: high ~/Work/adhdev main [±]"
|
|
@@ -66765,7 +67910,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
66765
67910
|
const fingerprint = `${target}::${buildPendingEventFingerprint(fingerprintSource)}`;
|
|
66766
67911
|
try {
|
|
66767
67912
|
const inserted = store.insertPendingEvent({
|
|
66768
|
-
id: (0,
|
|
67913
|
+
id: (0, import_crypto15.randomUUID)(),
|
|
66769
67914
|
meshId: UNRESOLVED_FORWARD_OUTBOX_MESH_ID,
|
|
66770
67915
|
coordinatorDaemonId: target,
|
|
66771
67916
|
event,
|
|
@@ -66828,14 +67973,14 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
66828
67973
|
return 0;
|
|
66829
67974
|
}
|
|
66830
67975
|
}
|
|
66831
|
-
var
|
|
67976
|
+
var import_crypto15;
|
|
66832
67977
|
var UNRESOLVED_FORWARD_OUTBOX_MESH_ID;
|
|
66833
67978
|
var UNRESOLVED_FORWARD_MAX_AGE_MS;
|
|
66834
67979
|
var retryNudgeHandler;
|
|
66835
67980
|
var init_mesh_unresolved_forward_outbox = __esm2({
|
|
66836
67981
|
"src/mesh/mesh-unresolved-forward-outbox.ts"() {
|
|
66837
67982
|
"use strict";
|
|
66838
|
-
|
|
67983
|
+
import_crypto15 = require("crypto");
|
|
66839
67984
|
init_logger();
|
|
66840
67985
|
init_mesh_runtime_store();
|
|
66841
67986
|
init_mesh_events_pending();
|
|
@@ -69514,6 +70659,15 @@ ${cleanBody}`;
|
|
|
69514
70659
|
}
|
|
69515
70660
|
};
|
|
69516
70661
|
}
|
|
70662
|
+
if (admission.kind === "admit") {
|
|
70663
|
+
traceMeshEventStage(
|
|
70664
|
+
"terminal_admission_admitted",
|
|
70665
|
+
traceCtx,
|
|
70666
|
+
`${admission.evidenceLevel}:${admission.reason}`
|
|
70667
|
+
);
|
|
70668
|
+
} else {
|
|
70669
|
+
traceMeshEventStage("terminal_admission_not_enforced", traceCtx, admission.reason);
|
|
70670
|
+
}
|
|
69517
70671
|
}
|
|
69518
70672
|
if (!readNonEmptyString(args.metadataEvent.taskId) && !sessionHasActiveAssignment(args.meshId, eventSessionId) && !findRecentTerminalLedgerEvidence({ meshId: args.meshId, sessionId: eventSessionId, nodeId: eventNodeId || void 0 }) && isWeakCompletionEvidence(args.metadataEvent)) {
|
|
69519
70673
|
LOG2.info("MeshEvents", `Suppressed agent:generating_completed for session ${eventSessionId} (mesh ${args.meshId}): no taskId, no active assignment, and no prior terminal ledger evidence \u2014 a pre-dispatch startup/greeting artifact, not a real task completion`);
|
|
@@ -69876,13 +71030,13 @@ ${cleanBody}`;
|
|
|
69876
71030
|
const emittedAfterFinalizationTimeout = completionDiagnostic?.emittedAfterFinalizationTimeout === true;
|
|
69877
71031
|
const weakEvidenceAtEntry = isWeakCompletionEvidence(args.metadataEvent);
|
|
69878
71032
|
const weakCompleted = outcome === "completed" && weakEvidenceAtEntry && !emittedAfterFinalizationTimeout;
|
|
69879
|
-
if (
|
|
71033
|
+
if (emittedAfterFinalizationTimeout && weakEvidenceAtEntry) {
|
|
69880
71034
|
args.metadataEvent.reviewRecommended = true;
|
|
69881
71035
|
args.metadataEvent.completionDiagnostic = {
|
|
69882
71036
|
...completionDiagnostic || {},
|
|
69883
71037
|
timedOutWithoutFinalAssistant: true
|
|
69884
71038
|
};
|
|
69885
|
-
LOG2.info("MeshQueue", `Completion for session ${sessionId} exhausted the finalization wait with no confirmed final assistant \u2014
|
|
71039
|
+
LOG2.info("MeshQueue", `Completion for session ${sessionId} exhausted the finalization wait with no confirmed final assistant \u2014 recording FORCED TERMINATION (outcome=${outcome}; terminal, never wedge) flagged reviewRecommended; NOT a completion`);
|
|
69886
71040
|
}
|
|
69887
71041
|
let reducerAllowsFlip = true;
|
|
69888
71042
|
if (eventTaskId && !weakCompleted) {
|
|
@@ -70006,6 +71160,7 @@ ${cleanBody}`;
|
|
|
70006
71160
|
return task ? { id: task.id, taskMode: task.taskMode } : null;
|
|
70007
71161
|
}
|
|
70008
71162
|
let completedTaskForLedger = null;
|
|
71163
|
+
let forcedTimeoutNoResponse = false;
|
|
70009
71164
|
let directDispatchTaskIdForLedger;
|
|
70010
71165
|
let worktreeHasQueuedTask = false;
|
|
70011
71166
|
if (args.event === "agent:generating_completed") {
|
|
@@ -70015,7 +71170,8 @@ ${cleanBody}`;
|
|
|
70015
71170
|
if (sessionId) {
|
|
70016
71171
|
directDispatchTaskIdForLedger = readNonEmptyString(args.metadataEvent.taskId) || resolveActiveDirectDispatchTaskId(args.meshId, sessionId);
|
|
70017
71172
|
const isFalseIdle = isFalseIdleCompletion(args.metadataEvent);
|
|
70018
|
-
|
|
71173
|
+
forcedTimeoutNoResponse = isWeakCompletionEvidence(args.metadataEvent) && (args.metadataEvent.completionDiagnostic && typeof args.metadataEvent.completionDiagnostic === "object" && args.metadataEvent.completionDiagnostic.emittedAfterFinalizationTimeout === true) === true;
|
|
71174
|
+
completedTaskForLedger = markSessionTerminal(sessionId, forcedTimeoutNoResponse ? "failed" : "completed", eventTimestamp, { tentativeIfDirect: isFalseIdle });
|
|
70019
71175
|
if (nodeId && providerType) {
|
|
70020
71176
|
if (!isFalseIdle) {
|
|
70021
71177
|
sweepExpiredRemoteIdleSessions();
|
|
@@ -70038,7 +71194,7 @@ ${cleanBody}`;
|
|
|
70038
71194
|
}
|
|
70039
71195
|
}
|
|
70040
71196
|
const completedTaskId = completedTaskForLedger?.id;
|
|
70041
|
-
if (completedTaskId && hasPendingDependents(args.meshId, completedTaskId)) {
|
|
71197
|
+
if (completedTaskId && !forcedTimeoutNoResponse && hasPendingDependents(args.meshId, completedTaskId)) {
|
|
70042
71198
|
setImmediate(() => {
|
|
70043
71199
|
triggerMeshQueue(components, args.meshId).catch((e) => {
|
|
70044
71200
|
LOG2.warn("MeshQueue", `Dependent wake after task ${completedTaskId} failed: ${e?.message || e}`);
|
|
@@ -70264,7 +71420,7 @@ ${cleanBody}`;
|
|
|
70264
71420
|
});
|
|
70265
71421
|
});
|
|
70266
71422
|
}
|
|
70267
|
-
const ledgerKind = EVENT_TO_LEDGER_KIND[args.event];
|
|
71423
|
+
const ledgerKind = forcedTimeoutNoResponse ? "task_failed" : EVENT_TO_LEDGER_KIND[args.event];
|
|
70268
71424
|
if (ledgerKind) {
|
|
70269
71425
|
try {
|
|
70270
71426
|
const ledgerNodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0;
|
|
@@ -72466,6 +73622,20 @@ ${cleanBody}`;
|
|
|
72466
73622
|
}
|
|
72467
73623
|
return newest;
|
|
72468
73624
|
}
|
|
73625
|
+
function escapeTailFingerprint(messages) {
|
|
73626
|
+
let fp = "";
|
|
73627
|
+
for (const msg of messages) {
|
|
73628
|
+
if (!msg) continue;
|
|
73629
|
+
const ts2 = readChatMessageTimestampMs(msg) ?? 0;
|
|
73630
|
+
let len = -1;
|
|
73631
|
+
try {
|
|
73632
|
+
len = JSON.stringify(msg.content)?.length ?? -1;
|
|
73633
|
+
} catch {
|
|
73634
|
+
}
|
|
73635
|
+
fp += `${msg.role}:${ts2}:${len};`;
|
|
73636
|
+
}
|
|
73637
|
+
return fp;
|
|
73638
|
+
}
|
|
72469
73639
|
async function reconcileUnterminatedDirectDispatches(components, mesh, selfIds, localDaemonId) {
|
|
72470
73640
|
const dispatches = getActiveDirectDispatches(mesh.id);
|
|
72471
73641
|
rehydrateAckedHoldsForMesh(mesh.id);
|
|
@@ -72476,6 +73646,9 @@ ${cleanBody}`;
|
|
|
72476
73646
|
for (const key2 of heldKeys) {
|
|
72477
73647
|
if (!activeTaskKeys.has(key2)) deleteHoldState(key2, mesh.id);
|
|
72478
73648
|
}
|
|
73649
|
+
for (const key2 of [...nonIdleEscapeTracks.keys()]) {
|
|
73650
|
+
if (key2.startsWith(`${mesh.id}::`) && !activeTaskKeys.has(key2)) nonIdleEscapeTracks.delete(key2);
|
|
73651
|
+
}
|
|
72479
73652
|
if (dispatches.length === 0) return;
|
|
72480
73653
|
const dispatchMeshCommand = components.dispatchMeshCommand;
|
|
72481
73654
|
const nodeById = new Map(mesh.nodes.map((n) => [n.id, n]));
|
|
@@ -72540,23 +73713,43 @@ ${cleanBody}`;
|
|
|
72540
73713
|
consecutiveReadFailures: 0,
|
|
72541
73714
|
...priorHoldState?.transcriptIdleSinceMs !== void 0 ? { transcriptIdleSinceMs: priorHoldState.transcriptIdleSinceMs } : {}
|
|
72542
73715
|
});
|
|
72543
|
-
let nonIdleEscapeSinceAckMs = null;
|
|
72544
73716
|
const nowMs = Date.now();
|
|
72545
73717
|
if (readChatPayloadStatus(payload) !== "idle") {
|
|
72546
73718
|
if (isAcked && readChatPayloadStatus(payload) === "generating") {
|
|
72547
|
-
const
|
|
72548
|
-
const
|
|
72549
|
-
|
|
72550
|
-
|
|
73719
|
+
const escapeMessages = Array.isArray(payload.messages) ? payload.messages : [];
|
|
73720
|
+
const fingerprint = escapeTailFingerprint(escapeMessages);
|
|
73721
|
+
const prior = nonIdleEscapeTracks.get(synthKey);
|
|
73722
|
+
if (!prior || prior.tailFingerprint !== fingerprint) {
|
|
73723
|
+
nonIdleEscapeTracks.set(synthKey, { anchorMs: nowMs, tailFingerprint: fingerprint, candidateRecorded: false });
|
|
73724
|
+
if (prior) {
|
|
73725
|
+
LOG2.info("MeshReconcile", `Non-idle escape re-armed: task ${taskId} on node ${nodeId} (mesh ${mesh.id}) reads 'generating' and its transcript tail moved \u2014 worker is alive mid-turn; the escape deadline restarts from now`);
|
|
73726
|
+
}
|
|
73727
|
+
} else {
|
|
73728
|
+
const staticMs = nowMs - prior.anchorMs;
|
|
73729
|
+
if (staticMs >= resolveAckedDeathDeadlineMs() && !prior.candidateRecorded) {
|
|
73730
|
+
prior.candidateRecorded = true;
|
|
73731
|
+
const candidateEvidence = extractFinalAssistantSummaryEvidence(escapeMessages);
|
|
73732
|
+
LOG2.warn("MeshReconcile", `Non-idle escape WEAK CANDIDATE (no completion): task ${taskId} on node ${nodeId} (mesh ${mesh.id}) has read 'generating' with a static transcript tail for ${Math.round(staticMs / 1e3)}s (deadline ${Math.round(resolveAckedDeathDeadlineMs() / 1e3)}s)${candidateEvidence.finalSummary ? " and a final-looking assistant bubble" : ""} \u2014 recorded as a weak candidate only; NOT completed (a timeout is never completion evidence); the hold/reclaim nets own the terminal`);
|
|
73733
|
+
traceMeshEventDrop("non_idle_escape_weak_candidate_held", {
|
|
73734
|
+
taskId,
|
|
73735
|
+
sessionId,
|
|
73736
|
+
nodeId,
|
|
73737
|
+
meshId: mesh.id,
|
|
73738
|
+
event: "agent:generating_completed"
|
|
73739
|
+
}, `staticTailMs=${staticMs} finalAssistant=${candidateEvidence.finalSummary ? "present" : "absent"} transcriptAt=${candidateEvidence.transcriptMessageAt ?? "n/a"}`);
|
|
73740
|
+
}
|
|
72551
73741
|
}
|
|
72552
73742
|
}
|
|
72553
|
-
|
|
72554
|
-
|
|
72555
|
-
continue;
|
|
72556
|
-
}
|
|
73743
|
+
setHoldState(synthKey, mesh.id, { liveConfirmedSinceAck: true, consecutiveReadFailures: 0 });
|
|
73744
|
+
continue;
|
|
72557
73745
|
}
|
|
72558
73746
|
const messages = Array.isArray(payload.messages) ? payload.messages : [];
|
|
72559
|
-
const
|
|
73747
|
+
const dispatchedAtMs = Date.parse(readNonEmptyString(dispatch.dispatchedAt));
|
|
73748
|
+
const evidence = extractFinalAssistantSummaryEvidence(
|
|
73749
|
+
messages,
|
|
73750
|
+
void 0,
|
|
73751
|
+
Number.isFinite(dispatchedAtMs) ? { turnStartedAtMs: dispatchedAtMs } : void 0
|
|
73752
|
+
);
|
|
72560
73753
|
if (hasTrailingToolActivityAfterFinalAssistant(messages)) {
|
|
72561
73754
|
setHoldState(synthKey, mesh.id, { liveConfirmedSinceAck: true, consecutiveReadFailures: 0 });
|
|
72562
73755
|
LOG2.info("MeshReconcile", `Mid-turn causal admission: task ${taskId} on node ${nodeId} (mesh ${mesh.id}) \u2014 the latest final-looking assistant bubble is followed by trailing tool/terminal activity (interim narration; the turn is still executing); holding the transcript synth`);
|
|
@@ -72588,7 +73781,7 @@ ${cleanBody}`;
|
|
|
72588
73781
|
const deathDeadlineMs = resolveAckedDeathDeadlineMs();
|
|
72589
73782
|
const holdState = getHoldState(synthKey, mesh.id);
|
|
72590
73783
|
let fastTrackReady = false;
|
|
72591
|
-
if (
|
|
73784
|
+
if (evidence.finalSummary) {
|
|
72592
73785
|
const idleSinceMs = holdState?.transcriptIdleSinceMs ?? nowMs;
|
|
72593
73786
|
if (holdState && holdState.transcriptIdleSinceMs === void 0) {
|
|
72594
73787
|
setHoldState(synthKey, mesh.id, { ...holdState, transcriptIdleSinceMs: idleSinceMs });
|
|
@@ -72609,9 +73802,7 @@ ${cleanBody}`;
|
|
|
72609
73802
|
}
|
|
72610
73803
|
if (!fastTrackReady) {
|
|
72611
73804
|
backstopKind = "ackedHoldDeathDeadlineFired";
|
|
72612
|
-
|
|
72613
|
-
LOG2.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).`);
|
|
72614
|
-
}
|
|
73805
|
+
LOG2.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).`);
|
|
72615
73806
|
}
|
|
72616
73807
|
}
|
|
72617
73808
|
if (realTerminalEmitPendingForTask(mesh.id, taskId)) {
|
|
@@ -72620,21 +73811,8 @@ ${cleanBody}`;
|
|
|
72620
73811
|
continue;
|
|
72621
73812
|
}
|
|
72622
73813
|
if (!evidence.finalSummary) continue;
|
|
72623
|
-
const dispatchedAtMs = Date.parse(readNonEmptyString(dispatch.dispatchedAt));
|
|
72624
73814
|
const transcriptAtMs = Date.parse(evidence.transcriptMessageAt ?? "");
|
|
72625
|
-
if (
|
|
72626
|
-
if (!Number.isFinite(dispatchedAtMs) || !Number.isFinite(transcriptAtMs) || transcriptAtMs < dispatchedAtMs) {
|
|
72627
|
-
LOG2.info("MeshReconcile", `Non-idle-escape stale-summary guard: refusing transcript synth for task ${taskId} on node ${nodeId} (mesh ${mesh.id}) \u2014 final assistant message (${evidence.transcriptMessageAt ?? "unparseable"}) is not provably at/after this task's dispatch (${dispatch.dispatchedAt})`);
|
|
72628
|
-
traceMeshEventDrop("reconcile_non_idle_escape_stale_summary", {
|
|
72629
|
-
taskId,
|
|
72630
|
-
sessionId,
|
|
72631
|
-
nodeId,
|
|
72632
|
-
meshId: mesh.id,
|
|
72633
|
-
event: "agent:generating_completed"
|
|
72634
|
-
}, `transcriptAt=${evidence.transcriptMessageAt} dispatchedAt=${dispatch.dispatchedAt}`);
|
|
72635
|
-
continue;
|
|
72636
|
-
}
|
|
72637
|
-
} else if (Number.isFinite(dispatchedAtMs) && Number.isFinite(transcriptAtMs) && transcriptAtMs < dispatchedAtMs) {
|
|
73815
|
+
if (Number.isFinite(dispatchedAtMs) && Number.isFinite(transcriptAtMs) && transcriptAtMs < dispatchedAtMs) {
|
|
72638
73816
|
LOG2.info("MeshReconcile", `Stale-summary guard: skipping transcript reconcile for task ${taskId} on node ${nodeId} (mesh ${mesh.id}) \u2014 final assistant message (${evidence.transcriptMessageAt}) predates this task's dispatch (${dispatch.dispatchedAt}); it is a prior task's summary`);
|
|
72639
73817
|
traceMeshEventDrop("reconcile_stale_summary_before_dispatch", {
|
|
72640
73818
|
taskId,
|
|
@@ -72645,15 +73823,12 @@ ${cleanBody}`;
|
|
|
72645
73823
|
}, `transcriptAt=${evidence.transcriptMessageAt} < dispatchedAt=${dispatch.dispatchedAt}`);
|
|
72646
73824
|
continue;
|
|
72647
73825
|
}
|
|
72648
|
-
const reprobeStatus =
|
|
73826
|
+
const reprobeStatus = await reprobeWorkerStatus(components, { isLocalNode, nodeDaemonId, readArgs });
|
|
72649
73827
|
if (reprobeStatus && reprobeStatus !== "idle") {
|
|
72650
73828
|
deleteHoldState(synthKey, mesh.id);
|
|
72651
73829
|
LOG2.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`);
|
|
72652
73830
|
continue;
|
|
72653
73831
|
}
|
|
72654
|
-
if (nonIdleEscapeSinceAckMs !== null) {
|
|
72655
|
-
LOG2.warn("MeshReconcile", `Acked-hold death deadline NON-IDLE escape: task ${taskId} on node ${nodeId} (mesh ${mesh.id}) still reads 'generating' ${Math.round(nonIdleEscapeSinceAckMs / 1e3)}s after the ack with a causally-proven final assistant (transcriptAt=${evidence.transcriptMessageAt ?? "n/a"}) \u2014 the floor-class PTY wedge rescue net; synthesizing the missing completion (a real emit, if it ever lands, no-ops idempotently).`);
|
|
72656
|
-
}
|
|
72657
73832
|
const providerSessionId = readNonEmptyString(payload.providerSessionId);
|
|
72658
73833
|
const workerSession = components.instanceManager.getInstance(sessionId);
|
|
72659
73834
|
const workerCoordinatorDaemonId = readNonEmptyString(
|
|
@@ -72814,8 +73989,12 @@ ${cleanBody}`;
|
|
|
72814
73989
|
if (!payload) return declined("chat_tail_unreadable", "worker transcript read returned no payload (offline/unreachable?)");
|
|
72815
73990
|
const payloadStatus = readChatPayloadProviderObservedStatus(payload);
|
|
72816
73991
|
const messages = Array.isArray(payload.messages) ? payload.messages : [];
|
|
72817
|
-
const evidence = extractFinalAssistantSummaryEvidence(messages);
|
|
72818
73992
|
const dispatchedAtMs = Date.parse(readNonEmptyString(row.dispatchTimestamp));
|
|
73993
|
+
const evidence = extractFinalAssistantSummaryEvidence(
|
|
73994
|
+
messages,
|
|
73995
|
+
void 0,
|
|
73996
|
+
Number.isFinite(dispatchedAtMs) ? { turnStartedAtMs: dispatchedAtMs } : void 0
|
|
73997
|
+
);
|
|
72819
73998
|
const transcriptAtMs = Date.parse(evidence.transcriptMessageAt ?? "");
|
|
72820
73999
|
if (Number.isFinite(dispatchedAtMs) && Number.isFinite(transcriptAtMs) && transcriptAtMs < dispatchedAtMs) {
|
|
72821
74000
|
return declined(
|
|
@@ -72861,7 +74040,10 @@ ${cleanBody}`;
|
|
|
72861
74040
|
`dispatchTimestamp=${row.dispatchTimestamp ?? "none"} transcriptMessageAt=${evidence.transcriptMessageAt ?? "none"} \u2014 shape-only evidence (${verdict.reason}) cannot complete an undated tail; reclaim owns it`
|
|
72862
74041
|
);
|
|
72863
74042
|
}
|
|
72864
|
-
const selectedFinalAssistant = finalAssistantPresent ? selectFinalAssistantTurnEndMessage(
|
|
74043
|
+
const selectedFinalAssistant = finalAssistantPresent ? selectFinalAssistantTurnEndMessage(
|
|
74044
|
+
messages,
|
|
74045
|
+
Number.isFinite(dispatchedAtMs) ? { turnStartedAtMs: dispatchedAtMs } : void 0
|
|
74046
|
+
) : null;
|
|
72865
74047
|
const selectedFinalAssistantIndex = selectedFinalAssistant ? messages.lastIndexOf(selectedFinalAssistant) : -1;
|
|
72866
74048
|
const admissionSnapshot = {
|
|
72867
74049
|
producer,
|
|
@@ -72897,6 +74079,7 @@ ${cleanBody}`;
|
|
|
72897
74079
|
admissionSnapshot
|
|
72898
74080
|
};
|
|
72899
74081
|
}
|
|
74082
|
+
var nonIdleEscapeTracks;
|
|
72900
74083
|
var init_mesh_completion_synthesis = __esm2({
|
|
72901
74084
|
"src/mesh/mesh-completion-synthesis.ts"() {
|
|
72902
74085
|
"use strict";
|
|
@@ -72917,6 +74100,7 @@ ${cleanBody}`;
|
|
|
72917
74100
|
init_mesh_reconcile_v2_backstop();
|
|
72918
74101
|
init_mesh_reconcile_acked_hold();
|
|
72919
74102
|
init_mesh_remote_event_pull();
|
|
74103
|
+
nonIdleEscapeTracks = /* @__PURE__ */ new Map();
|
|
72920
74104
|
}
|
|
72921
74105
|
});
|
|
72922
74106
|
function findLiveCoordinators(components) {
|
|
@@ -73527,6 +74711,53 @@ ${cleanBody}`;
|
|
|
73527
74711
|
producer: "early_idle_transcript_evidence"
|
|
73528
74712
|
});
|
|
73529
74713
|
if (terminalEvidence) {
|
|
74714
|
+
const weakCandidateKey = `${meshId}::${row.id}`;
|
|
74715
|
+
if (terminalEvidence.evidenceLevel === "weak") {
|
|
74716
|
+
const candidate = weakCompletionCandidateStreak.get(weakCandidateKey) ?? { count: 0 };
|
|
74717
|
+
if (candidate.finalAssistantAt !== void 0 && candidate.finalAssistantAt !== terminalEvidence.transcriptMessageAt) {
|
|
74718
|
+
candidate.count = 0;
|
|
74719
|
+
}
|
|
74720
|
+
candidate.finalAssistantAt = terminalEvidence.transcriptMessageAt;
|
|
74721
|
+
candidate.count += 1;
|
|
74722
|
+
weakCompletionCandidateStreak.set(weakCandidateKey, candidate);
|
|
74723
|
+
if (candidate.count === 1) {
|
|
74724
|
+
const nodeLabel = row.assignedNodeId ? `Node '${row.assignedNodeId}'` : "Remote agent";
|
|
74725
|
+
const metadataEvent = {
|
|
74726
|
+
evidenceLevel: "weak",
|
|
74727
|
+
finalSummary: terminalEvidence.finalSummary,
|
|
74728
|
+
taskId: row.id,
|
|
74729
|
+
providerType: readNonEmptyString(row.assignedProviderType) || terminalEvidence.providerType,
|
|
74730
|
+
providerSessionId: terminalEvidence.providerSessionId,
|
|
74731
|
+
source: "early_idle_transcript_evidence",
|
|
74732
|
+
targetSessionId: terminalEvidence.sessionId
|
|
74733
|
+
};
|
|
74734
|
+
try {
|
|
74735
|
+
queuePendingMeshCoordinatorEvent({
|
|
74736
|
+
event: "agent:generating_completed",
|
|
74737
|
+
meshId,
|
|
74738
|
+
nodeLabel,
|
|
74739
|
+
nodeId: row.assignedNodeId,
|
|
74740
|
+
metadataEvent,
|
|
74741
|
+
coordinatorMessage: buildMeshSystemMessage({ event: "agent:generating_completed", nodeLabel, metadataEvent }),
|
|
74742
|
+
queuedAt: nowMs
|
|
74743
|
+
});
|
|
74744
|
+
} catch {
|
|
74745
|
+
}
|
|
74746
|
+
}
|
|
74747
|
+
if (candidate.count < WEAK_COMPLETION_CANDIDATE_CONFIRM_TICKS) {
|
|
74748
|
+
traceMeshEventDrop("weak_completion_candidate_held", {
|
|
74749
|
+
taskId: row.id,
|
|
74750
|
+
sessionId: row.assignedSessionId,
|
|
74751
|
+
nodeId: row.assignedNodeId,
|
|
74752
|
+
meshId,
|
|
74753
|
+
event: "agent:generating_completed"
|
|
74754
|
+
}, `weak message-shape completion candidate ${candidate.count}/${WEAK_COMPLETION_CANDIDATE_CONFIRM_TICKS} (early-idle) \u2014 awaiting re-confirmation`);
|
|
74755
|
+
continue;
|
|
74756
|
+
}
|
|
74757
|
+
weakCompletionCandidateStreak.delete(weakCandidateKey);
|
|
74758
|
+
} else {
|
|
74759
|
+
weakCompletionCandidateStreak.delete(weakCandidateKey);
|
|
74760
|
+
}
|
|
73530
74761
|
const propagation = propagateWatchdogTranscriptCompletion(
|
|
73531
74762
|
components,
|
|
73532
74763
|
meshId,
|
|
@@ -74466,6 +75697,30 @@ ${cleanBody}`;
|
|
|
74466
75697
|
LOG2.warn("MeshReconcile", `Completion reconcile failed for mesh ${mesh.id}: ${e?.message || e}`);
|
|
74467
75698
|
}
|
|
74468
75699
|
}
|
|
75700
|
+
if (store) {
|
|
75701
|
+
for (const mesh of listMeshes()) {
|
|
75702
|
+
const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
|
|
75703
|
+
if (!daemonHostsMesh(mesh, selfIds)) continue;
|
|
75704
|
+
try {
|
|
75705
|
+
const gateStore = store.graphStore();
|
|
75706
|
+
const preSweep = new Map(
|
|
75707
|
+
gateStore.listGatesByMesh(mesh.id, ["awaiting_coordinator", "claimed"]).map((g) => [g.gateId, { graphId: g.graphId, onTimeout: g.onTimeout, deadlineAt: g.deadlineAt }])
|
|
75708
|
+
);
|
|
75709
|
+
const swept = sweepMeshGraphGateTimeouts(mesh.id);
|
|
75710
|
+
for (const gateId of swept.expiredGateIds) {
|
|
75711
|
+
const meta3 = preSweep.get(gateId);
|
|
75712
|
+
recordGraphGateExpired(mesh.id, {
|
|
75713
|
+
gateId,
|
|
75714
|
+
graphId: meta3?.graphId,
|
|
75715
|
+
policy: meta3?.onTimeout ?? "hold",
|
|
75716
|
+
deadlineAt: meta3?.deadlineAt
|
|
75717
|
+
});
|
|
75718
|
+
}
|
|
75719
|
+
} catch (e) {
|
|
75720
|
+
LOG2.warn("MeshReconcile", `Graph gate sweep failed for mesh ${mesh.id}: ${e?.message || e}`);
|
|
75721
|
+
}
|
|
75722
|
+
}
|
|
75723
|
+
}
|
|
74469
75724
|
if (store) {
|
|
74470
75725
|
for (const mesh of listMeshes()) {
|
|
74471
75726
|
const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
|
|
@@ -74768,6 +76023,8 @@ ${cleanBody}`;
|
|
|
74768
76023
|
init_mesh_reconcile_stranded_dispatch();
|
|
74769
76024
|
init_mesh_reconcile_unresolved_forward();
|
|
74770
76025
|
init_mesh_graph_workspace_saga();
|
|
76026
|
+
init_mesh_graph_gates();
|
|
76027
|
+
init_mesh_graph_provenance();
|
|
74771
76028
|
init_mesh_reconcile_v2_backstop();
|
|
74772
76029
|
init_mesh_reconcile_acked_hold();
|
|
74773
76030
|
init_mesh_reconcile_coordinator_drain();
|
|
@@ -77195,6 +78452,29 @@ ${cleanBody}`;
|
|
|
77195
78452
|
"use strict";
|
|
77196
78453
|
}
|
|
77197
78454
|
});
|
|
78455
|
+
function resolveRefineVitestMaxWorkers(env2 = process.env) {
|
|
78456
|
+
const raw = env2[REFINE_VITEST_MAX_WORKERS_ENV];
|
|
78457
|
+
if (raw === void 0 || raw.trim() === "") return DEFAULT_REFINE_VITEST_MAX_WORKERS;
|
|
78458
|
+
const parsed = Number.parseInt(raw, 10);
|
|
78459
|
+
if (!Number.isFinite(parsed)) return DEFAULT_REFINE_VITEST_MAX_WORKERS;
|
|
78460
|
+
if (parsed === 0) return null;
|
|
78461
|
+
return Math.max(1, parsed);
|
|
78462
|
+
}
|
|
78463
|
+
function refineGateChildEnv(env2 = process.env) {
|
|
78464
|
+
if (env2.VITEST_MAX_WORKERS) return {};
|
|
78465
|
+
const cap = resolveRefineVitestMaxWorkers(env2);
|
|
78466
|
+
if (cap === null) return {};
|
|
78467
|
+
return { VITEST_MAX_WORKERS: String(cap) };
|
|
78468
|
+
}
|
|
78469
|
+
var REFINE_VITEST_MAX_WORKERS_ENV;
|
|
78470
|
+
var DEFAULT_REFINE_VITEST_MAX_WORKERS;
|
|
78471
|
+
var init_mesh_refine_worker_cap = __esm2({
|
|
78472
|
+
"src/mesh/mesh-refine-worker-cap.ts"() {
|
|
78473
|
+
"use strict";
|
|
78474
|
+
REFINE_VITEST_MAX_WORKERS_ENV = "ADHDEV_REFINE_VITEST_MAX_WORKERS";
|
|
78475
|
+
DEFAULT_REFINE_VITEST_MAX_WORKERS = 2;
|
|
78476
|
+
}
|
|
78477
|
+
});
|
|
77198
78478
|
function truncateValidationOutput(value) {
|
|
77199
78479
|
const text = typeof value === "string" ? value : value == null ? "" : String(value);
|
|
77200
78480
|
if (text.length <= REFINE_VALIDATION_SUMMARY_CHARS) return text;
|
|
@@ -78673,7 +79953,7 @@ ${mergeTreeErr?.stderr || ""}`;
|
|
|
78673
79953
|
encoding: "utf8",
|
|
78674
79954
|
timeout,
|
|
78675
79955
|
maxBuffer: candidate.outputLimitBytes || REFINE_VALIDATION_OUTPUT_LIMIT_BYTES,
|
|
78676
|
-
env: { ...process.env, CI: process.env.CI || "1", ...candidate.env || {} },
|
|
79956
|
+
env: { ...process.env, CI: process.env.CI || "1", ...refineGateChildEnv(), ...candidate.env || {} },
|
|
78677
79957
|
...spawn7.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}
|
|
78678
79958
|
});
|
|
78679
79959
|
summary.bootstrapCommandsRun.push(commandRecord(candidate, cwd, startedAt, result, true, { exitCode: 0 }));
|
|
@@ -78718,7 +79998,7 @@ ${mergeTreeErr?.stderr || ""}`;
|
|
|
78718
79998
|
encoding: "utf8",
|
|
78719
79999
|
timeout,
|
|
78720
80000
|
maxBuffer: candidate.outputLimitBytes || REFINE_VALIDATION_OUTPUT_LIMIT_BYTES,
|
|
78721
|
-
env: { ...process.env, CI: process.env.CI || "1", ...candidate.env || {} },
|
|
80001
|
+
env: { ...process.env, CI: process.env.CI || "1", ...refineGateChildEnv(), ...candidate.env || {} },
|
|
78722
80002
|
...spawn7.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}
|
|
78723
80003
|
});
|
|
78724
80004
|
summary.commandsRun.push(commandRecord(candidate, cwd, startedAt, result, true, { exitCode: 0 }));
|
|
@@ -78784,6 +80064,7 @@ ${mergeTreeErr?.stderr || ""}`;
|
|
|
78784
80064
|
fs48 = __toESM2(require("fs"));
|
|
78785
80065
|
import_node_child_process11 = require("child_process");
|
|
78786
80066
|
init_resolve_executable();
|
|
80067
|
+
init_mesh_refine_worker_cap();
|
|
78787
80068
|
GIT4 = process.platform === "win32" ? resolveWin32Executable("git") : "git";
|
|
78788
80069
|
REFINE_VALIDATION_TIMEOUT_MS = 12e4;
|
|
78789
80070
|
REFINE_VALIDATION_OUTPUT_LIMIT_BYTES = 128 * 1024;
|
|
@@ -78869,6 +80150,45 @@ ${mergeTreeErr?.stderr || ""}`;
|
|
|
78869
80150
|
REFINE_INFLIGHT_FRESHNESS_MS = 30 * 6e4;
|
|
78870
80151
|
}
|
|
78871
80152
|
});
|
|
80153
|
+
function resolveRefineMaxConcurrentJobs(env2 = process.env) {
|
|
80154
|
+
const raw = env2[REFINE_MAX_CONCURRENT_JOBS_ENV];
|
|
80155
|
+
if (raw === void 0 || raw.trim() === "") return DEFAULT_REFINE_MAX_CONCURRENT_JOBS;
|
|
80156
|
+
const parsed = Number.parseInt(raw, 10);
|
|
80157
|
+
if (!Number.isFinite(parsed) || parsed < 1) return DEFAULT_REFINE_MAX_CONCURRENT_JOBS;
|
|
80158
|
+
return parsed;
|
|
80159
|
+
}
|
|
80160
|
+
async function runWithRefineExecutionSlot(label, task) {
|
|
80161
|
+
const limit = resolveRefineMaxConcurrentJobs();
|
|
80162
|
+
if (activeExecutions >= limit) {
|
|
80163
|
+
LOG2.info("Mesh", `[Refinery] ${label} QUEUED \u2014 ${activeExecutions} refine job(s) already executing (limit ${limit}), ${waitQueue.length + 1} waiting. It starts automatically; do not re-invoke.`);
|
|
80164
|
+
await new Promise((resolve30) => {
|
|
80165
|
+
waitQueue.push(resolve30);
|
|
80166
|
+
});
|
|
80167
|
+
LOG2.info("Mesh", `[Refinery] ${label} dequeued \u2014 starting now (${activeExecutions} other job(s) still executing).`);
|
|
80168
|
+
}
|
|
80169
|
+
activeExecutions += 1;
|
|
80170
|
+
try {
|
|
80171
|
+
return await task();
|
|
80172
|
+
} finally {
|
|
80173
|
+
activeExecutions -= 1;
|
|
80174
|
+
const next = waitQueue.shift();
|
|
80175
|
+
if (next) next();
|
|
80176
|
+
}
|
|
80177
|
+
}
|
|
80178
|
+
var REFINE_MAX_CONCURRENT_JOBS_ENV;
|
|
80179
|
+
var DEFAULT_REFINE_MAX_CONCURRENT_JOBS;
|
|
80180
|
+
var activeExecutions;
|
|
80181
|
+
var waitQueue;
|
|
80182
|
+
var init_mesh_refine_concurrency = __esm2({
|
|
80183
|
+
"src/mesh/mesh-refine-concurrency.ts"() {
|
|
80184
|
+
"use strict";
|
|
80185
|
+
init_logger();
|
|
80186
|
+
REFINE_MAX_CONCURRENT_JOBS_ENV = "ADHDEV_REFINE_MAX_CONCURRENT_JOBS";
|
|
80187
|
+
DEFAULT_REFINE_MAX_CONCURRENT_JOBS = 1;
|
|
80188
|
+
activeExecutions = 0;
|
|
80189
|
+
waitQueue = [];
|
|
80190
|
+
}
|
|
80191
|
+
});
|
|
78872
80192
|
function resolveRefineResumeDispatchGraceMs() {
|
|
78873
80193
|
return resolveTunedReconcileMs("MESH_REFINE_RESUME_DISPATCH_GRACE_MS", 6e4, 0, 10 * 6e4);
|
|
78874
80194
|
}
|
|
@@ -80807,7 +82127,10 @@ ${e?.stderr || ""}`;
|
|
|
80807
82127
|
await appendRefineBatchJobLedger(self, "task_dispatched", handle);
|
|
80808
82128
|
queueRefineBatchJobEvent(self, "refine:accepted", handle);
|
|
80809
82129
|
setImmediate(() => {
|
|
80810
|
-
void
|
|
82130
|
+
void runWithRefineExecutionSlot(
|
|
82131
|
+
`batch ${handle.jobId} (mesh ${meshId})`,
|
|
82132
|
+
() => finishMeshRefineBatchJob(self, handle, orderedNodes, ordering, args)
|
|
82133
|
+
);
|
|
80811
82134
|
});
|
|
80812
82135
|
return {
|
|
80813
82136
|
...handle,
|
|
@@ -81006,7 +82329,10 @@ ${e?.stderr || ""}`;
|
|
|
81006
82329
|
queueRefineJobEvent(self, "refine:accepted", handle);
|
|
81007
82330
|
setImmediate(() => {
|
|
81008
82331
|
void recordRefineAcceptBaseDivergence(self, handle, node);
|
|
81009
|
-
void
|
|
82332
|
+
void runWithRefineExecutionSlot(
|
|
82333
|
+
`job ${handle.jobId} (node ${handle.targetNodeId})`,
|
|
82334
|
+
() => finishMeshRefineJob(self, handle, args)
|
|
82335
|
+
);
|
|
81010
82336
|
});
|
|
81011
82337
|
return handle;
|
|
81012
82338
|
}
|
|
@@ -81035,6 +82361,7 @@ ${e?.stderr || ""}`;
|
|
|
81035
82361
|
init_git_locale();
|
|
81036
82362
|
init_mesh_node_identity();
|
|
81037
82363
|
init_mesh_refine_gates();
|
|
82364
|
+
init_mesh_refine_concurrency();
|
|
81038
82365
|
init_router_refine_resume();
|
|
81039
82366
|
RETRYABLE_BASE_MOVEMENT_CODES = /* @__PURE__ */ new Set(["base_moved", "base_locked"]);
|
|
81040
82367
|
}
|
|
@@ -81110,6 +82437,9 @@ ${e?.stderr || ""}`;
|
|
|
81110
82437
|
MAX_SIZE_ROTATION_GENERATIONS: () => MAX_SIZE_ROTATION_GENERATIONS,
|
|
81111
82438
|
MESH_CONVERGE_FAST_FORWARD_TAG: () => MESH_CONVERGE_FAST_FORWARD_TAG,
|
|
81112
82439
|
MESH_CONVERGE_REFINE_TAG: () => MESH_CONVERGE_REFINE_TAG,
|
|
82440
|
+
MESH_GATE_DEFAULT_LEASE_SECONDS: () => MESH_GATE_DEFAULT_LEASE_SECONDS,
|
|
82441
|
+
MESH_GATE_NAMED_OUTCOMES: () => MESH_GATE_NAMED_OUTCOMES,
|
|
82442
|
+
MESH_GATE_RELEASE_PATCH_KEYS: () => MESH_GATE_RELEASE_PATCH_KEYS,
|
|
81113
82443
|
MESH_JSON_CONFIG_LOCATIONS: () => MESH_JSON_CONFIG_LOCATIONS,
|
|
81114
82444
|
MESH_JSON_CONFIG_SCHEMA: () => MESH_JSON_CONFIG_SCHEMA,
|
|
81115
82445
|
MESH_JSON_PROVIDER_DEFAULTS_EXAMPLE: () => MESH_JSON_PROVIDER_DEFAULTS_EXAMPLE,
|
|
@@ -81119,16 +82449,21 @@ ${e?.stderr || ""}`;
|
|
|
81119
82449
|
MESH_MISSION_LIST_STATUS_LIMIT: () => MESH_MISSION_LIST_STATUS_LIMIT,
|
|
81120
82450
|
MESH_MISSION_STATUSES: () => MESH_MISSION_STATUSES,
|
|
81121
82451
|
MESH_NODE_LIVE_TRUTH_MARKER: () => MESH_NODE_LIVE_TRUTH_MARKER,
|
|
82452
|
+
MESH_ON_DEPENDENCY_FAILURE_PUBLIC_TEXT: () => MESH_ON_DEPENDENCY_FAILURE_PUBLIC_TEXT,
|
|
81122
82453
|
MESH_REFINE_CONFIG_LOCATIONS: () => MESH_REFINE_CONFIG_LOCATIONS,
|
|
81123
82454
|
MESH_REFINE_CONFIG_SCHEMA: () => MESH_REFINE_CONFIG_SCHEMA,
|
|
81124
82455
|
MESH_SCHEDULING_STRATEGIES: () => MESH_SCHEDULING_STRATEGIES,
|
|
82456
|
+
MESH_SUPERSEDED_SINGLE_REASONS: () => MESH_SUPERSEDED_SINGLE_REASONS,
|
|
81125
82457
|
MESH_TASK_GRAPH_MAX_TASKS: () => MESH_TASK_GRAPH_MAX_TASKS,
|
|
81126
82458
|
MESH_TASK_PRIORITIES: () => MESH_TASK_PRIORITIES,
|
|
82459
|
+
MESH_VALID_SINGLE_REASONS: () => MESH_VALID_SINGLE_REASONS,
|
|
81127
82460
|
MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS: () => MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS,
|
|
81128
82461
|
MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA: () => MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA,
|
|
81129
82462
|
MIN_GIT_WORKSPACE_POLL_INTERVAL_MS: () => MIN_GIT_WORKSPACE_POLL_INTERVAL_MS,
|
|
81130
82463
|
MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS: () => MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS2,
|
|
81131
82464
|
MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS: () => MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS2,
|
|
82465
|
+
MeshGraphPlanError: () => MeshGraphPlanError,
|
|
82466
|
+
MeshGraphPolicyError: () => MeshGraphPolicyError,
|
|
81132
82467
|
NOT_BEFORE_RELATIVE_THRESHOLD_MS: () => NOT_BEFORE_RELATIVE_THRESHOLD_MS,
|
|
81133
82468
|
NodePtyTransportFactory: () => NodePtyTransportFactory,
|
|
81134
82469
|
OPERATING_NOTE_DEDUPE_WINDOW: () => OPERATING_NOTE_DEDUPE_WINDOW,
|
|
@@ -81185,6 +82520,7 @@ ${e?.stderr || ""}`;
|
|
|
81185
82520
|
buildMeshActiveWork: () => buildMeshActiveWork,
|
|
81186
82521
|
buildMeshActiveWorkSummary: () => buildMeshActiveWorkSummary,
|
|
81187
82522
|
buildMeshAsyncRefineJobs: () => buildMeshAsyncRefineJobs,
|
|
82523
|
+
buildMeshGraphViews: () => buildMeshGraphViews,
|
|
81188
82524
|
buildMeshHostRequiredFailure: () => buildMeshHostRequiredFailure,
|
|
81189
82525
|
buildMeshJsonConfigScaffold: () => buildMeshJsonConfigScaffold,
|
|
81190
82526
|
buildMeshLedgerReconciliationEvidence: () => buildMeshLedgerReconciliationEvidence,
|
|
@@ -81211,6 +82547,7 @@ ${e?.stderr || ""}`;
|
|
|
81211
82547
|
buildUserChatMessage: () => buildUserChatMessage,
|
|
81212
82548
|
cancelTask: () => cancelTask,
|
|
81213
82549
|
canonicalDaemonId: () => canonicalDaemonId,
|
|
82550
|
+
claimMeshGraphGate: () => claimMeshGraphGate,
|
|
81214
82551
|
claimNextTask: () => claimNextTask,
|
|
81215
82552
|
classifyChatMessageVisibility: () => classifyChatMessageVisibility,
|
|
81216
82553
|
classifyDuplicateMeshDispatch: () => classifyDuplicateMeshDispatch,
|
|
@@ -81225,14 +82562,17 @@ ${e?.stderr || ""}`;
|
|
|
81225
82562
|
clearPendingMeshCoordinatorEvents: () => clearPendingMeshCoordinatorEvents,
|
|
81226
82563
|
collectPendingApprovals: () => collectPendingApprovals,
|
|
81227
82564
|
collectSyncTargetTypes: () => collectSyncTargetTypes,
|
|
82565
|
+
commitMeshGraphPlan: () => commitMeshGraphPlan,
|
|
81228
82566
|
compareGitSnapshots: () => compareGitSnapshots,
|
|
81229
82567
|
compensateWorkspaceIntent: () => compensateWorkspaceIntent,
|
|
82568
|
+
computeMeshGraphPlanDigest: () => computeMeshGraphPlanDigest,
|
|
81230
82569
|
computeMeshMissionStats: () => computeMeshMissionStats,
|
|
81231
82570
|
computeMeshTaskStats: () => computeMeshTaskStats,
|
|
81232
82571
|
computeProviderTreeDigest: () => computeProviderTreeDigest,
|
|
81233
82572
|
configureDebugTraceStore: () => configureDebugTraceStore,
|
|
81234
82573
|
connectCdpManager: () => connectCdpManager,
|
|
81235
82574
|
consoleSymbols: () => consoleSymbols,
|
|
82575
|
+
coordinatorGateBlockReason: () => coordinatorGateBlockReason,
|
|
81236
82576
|
coordinatorIdentityFromEmitFields: () => coordinatorIdentityFromEmitFields,
|
|
81237
82577
|
createDebugTraceStore: () => createDebugTraceStore,
|
|
81238
82578
|
createDefaultGitCommandServices: () => createDefaultGitCommandServices,
|
|
@@ -81252,6 +82592,7 @@ ${e?.stderr || ""}`;
|
|
|
81252
82592
|
delegatedWorkerAutoApproveSettings: () => delegatedWorkerAutoApproveSettings,
|
|
81253
82593
|
deleteDirectDispatchesByTaskId: () => deleteDirectDispatchesByTaskId,
|
|
81254
82594
|
deleteMesh: () => deleteMesh,
|
|
82595
|
+
deriveDependencyFailures: () => deriveDependencyFailures,
|
|
81255
82596
|
deriveMeshNodeHealthFromGit: () => deriveMeshNodeHealthFromGit,
|
|
81256
82597
|
deriveMeshReviewInboxItems: () => deriveMeshReviewInboxItems,
|
|
81257
82598
|
deriveWorkspaceBranchIdentity: () => deriveWorkspaceBranchIdentity,
|
|
@@ -81352,6 +82693,7 @@ ${e?.stderr || ""}`;
|
|
|
81352
82693
|
installGlobalInterceptor: () => installGlobalInterceptor,
|
|
81353
82694
|
interactivePromptFromClaudeAskUserQuestion: () => interactivePromptFromClaudeAskUserQuestion,
|
|
81354
82695
|
isActivityChatMessage: () => isActivityChatMessage,
|
|
82696
|
+
isAdvancedGraphTask: () => isAdvancedGraphTask,
|
|
81355
82697
|
isAlwaysOnTraceCategory: () => isAlwaysOnTraceCategory,
|
|
81356
82698
|
isBuiltinChatMessageKind: () => isBuiltinChatMessageKind,
|
|
81357
82699
|
isCdpConnected: () => isCdpConnected,
|
|
@@ -81437,11 +82779,13 @@ ${e?.stderr || ""}`;
|
|
|
81437
82779
|
normalizeMeshTaskPriority: () => normalizeMeshTaskPriority,
|
|
81438
82780
|
normalizeMeshWorkerResult: () => normalizeMeshWorkerResult,
|
|
81439
82781
|
normalizeMessageParts: () => normalizeMessageParts,
|
|
82782
|
+
normalizeOrchestrationDecision: () => normalizeOrchestrationDecision,
|
|
81440
82783
|
normalizeRepoIdentity: () => normalizeRepoIdentity,
|
|
81441
82784
|
normalizeRepoMeshDeclarativeConfig: () => normalizeRepoMeshDeclarativeConfig,
|
|
81442
82785
|
normalizeSessionModalFields: () => normalizeSessionModalFields,
|
|
81443
82786
|
notifyCoordinatorOfOrphanedPins: () => notifyCoordinatorOfOrphanedPins,
|
|
81444
82787
|
otherTrackConfigDir: () => otherTrackConfigDir,
|
|
82788
|
+
parseOnDependencyFailurePolicy: () => parseOnDependencyFailurePolicy,
|
|
81445
82789
|
parsePorcelainV2Status: () => parsePorcelainV2Status,
|
|
81446
82790
|
parseProviderSourceConfigUpdate: () => parseProviderSourceConfigUpdate,
|
|
81447
82791
|
parseWorktreeListOutput: () => parseWorktreeListOutput,
|
|
@@ -81458,6 +82802,7 @@ ${e?.stderr || ""}`;
|
|
|
81458
82802
|
printQuota: () => printQuota2,
|
|
81459
82803
|
printQuotaInstallError: () => printQuotaInstallError2,
|
|
81460
82804
|
probeCdpPort: () => probeCdpPort,
|
|
82805
|
+
projectGraphPublicPolicy: () => projectGraphPublicPolicy,
|
|
81461
82806
|
pruneOperatingNotes: () => pruneOperatingNotes,
|
|
81462
82807
|
pruneStaleDirectDispatches: () => pruneStaleDirectDispatches,
|
|
81463
82808
|
queuePendingMeshCoordinatorEvent: () => queuePendingMeshCoordinatorEvent,
|
|
@@ -81481,15 +82826,23 @@ ${e?.stderr || ""}`;
|
|
|
81481
82826
|
reconcileDirectDispatchCompletionFromTranscript: () => reconcileDirectDispatchCompletionFromTranscript,
|
|
81482
82827
|
recordDebugTrace: () => recordDebugTrace,
|
|
81483
82828
|
recordDirectDispatchTask: () => recordDirectDispatchTask,
|
|
82829
|
+
recordGraphEnqueueCommitted: () => recordGraphEnqueueCommitted,
|
|
82830
|
+
recordGraphEnqueueRolledBack: () => recordGraphEnqueueRolledBack,
|
|
82831
|
+
recordGraphEnqueueValidationFailed: () => recordGraphEnqueueValidationFailed,
|
|
82832
|
+
recordGraphGateClaimed: () => recordGraphGateClaimed,
|
|
82833
|
+
recordGraphGateExpired: () => recordGraphGateExpired,
|
|
82834
|
+
recordGraphGateReleased: () => recordGraphGateReleased,
|
|
81484
82835
|
recordMeshToolCall: () => recordMeshToolCall,
|
|
81485
82836
|
recordMissingSessionAttempt: () => recordMissingSessionAttempt2,
|
|
81486
82837
|
recordSessionUsage: () => recordSessionUsage,
|
|
81487
82838
|
recoverExpiredWorkspaceSagas: () => recoverExpiredWorkspaceSagas,
|
|
81488
82839
|
registerExtensionProviders: () => registerExtensionProviders,
|
|
81489
82840
|
registerMeshCoordinator: () => registerMeshCoordinator,
|
|
82841
|
+
releaseMeshGraphGate: () => releaseMeshGraphGate,
|
|
81490
82842
|
removeMagiKindPanel: () => removeMagiKindPanel,
|
|
81491
82843
|
removeNode: () => removeNode,
|
|
81492
82844
|
removeWorktree: () => removeWorktree,
|
|
82845
|
+
requestUsesGraphV2: () => requestUsesGraphV2,
|
|
81493
82846
|
requeueHeldMeshCoordinatorEvents: () => requeueHeldMeshCoordinatorEvents,
|
|
81494
82847
|
requeueTask: () => requeueTask,
|
|
81495
82848
|
requireMeshHostQueueOwner: () => requireMeshHostQueueOwner,
|
|
@@ -81524,6 +82877,7 @@ ${e?.stderr || ""}`;
|
|
|
81524
82877
|
resolveNodeSchedulingPriority: () => resolveNodeSchedulingPriority,
|
|
81525
82878
|
resolveNotBefore: () => resolveNotBefore,
|
|
81526
82879
|
resolveNpmPublishedVersion: () => resolveNpmPublishedVersion,
|
|
82880
|
+
resolveOnDependencyFailurePolicy: () => resolveOnDependencyFailurePolicy,
|
|
81527
82881
|
resolveProviderChannel: () => resolveProviderChannel,
|
|
81528
82882
|
resolveProviderMaxParallel: () => resolveProviderMaxParallel,
|
|
81529
82883
|
resolveScopedMeshId: () => resolveScopedMeshId,
|
|
@@ -81568,6 +82922,7 @@ ${e?.stderr || ""}`;
|
|
|
81568
82922
|
summarizeMeshUsage: () => summarizeMeshUsage,
|
|
81569
82923
|
summarizeMissionTasks: () => summarizeMissionTasks,
|
|
81570
82924
|
supportsUnicodeSymbols: () => supportsUnicodeSymbols,
|
|
82925
|
+
sweepMeshGraphGateTimeouts: () => sweepMeshGraphGateTimeouts,
|
|
81571
82926
|
taskDependenciesSatisfied: () => taskDependenciesSatisfied,
|
|
81572
82927
|
tombstoneOperatingNote: () => tombstoneOperatingNote,
|
|
81573
82928
|
totalTokens: () => totalTokens,
|
|
@@ -84066,10 +85421,667 @@ ${e?.stderr || ""}`;
|
|
|
84066
85421
|
};
|
|
84067
85422
|
}
|
|
84068
85423
|
init_mesh_work_queue();
|
|
85424
|
+
init_mesh_graph_derived_failure();
|
|
84069
85425
|
init_mesh_graph_workspace_saga();
|
|
84070
85426
|
init_mesh_graph_workspace_safety();
|
|
84071
85427
|
init_mesh_graph_workspace_identity();
|
|
84072
85428
|
init_mesh_graph_workspace_ports();
|
|
85429
|
+
init_mesh_graph_gates();
|
|
85430
|
+
var import_crypto14 = require("crypto");
|
|
85431
|
+
init_logger();
|
|
85432
|
+
init_mesh_runtime_store();
|
|
85433
|
+
init_mesh_graph_input_binding();
|
|
85434
|
+
init_mesh_graph_types();
|
|
85435
|
+
init_mesh_graph_derived_failure();
|
|
85436
|
+
init_mesh_graph_transition_runner();
|
|
85437
|
+
init_mesh_graph_workspace_saga();
|
|
85438
|
+
init_mesh_work_queue();
|
|
85439
|
+
var MeshGraphPlanError = class extends Error {
|
|
85440
|
+
constructor(code, message, extra) {
|
|
85441
|
+
super(`${code}: ${message}`);
|
|
85442
|
+
this.code = code;
|
|
85443
|
+
this.extra = extra;
|
|
85444
|
+
this.name = "MeshGraphPlanError";
|
|
85445
|
+
}
|
|
85446
|
+
};
|
|
85447
|
+
function isAdvancedGraphTask(spec) {
|
|
85448
|
+
return spec.inputs_from !== void 0 || spec.run_if !== void 0 || typeof spec.workspace_ref === "string" && spec.workspace_ref.trim().length > 0 || Array.isArray(spec.gated_by) && spec.gated_by.length > 0;
|
|
85449
|
+
}
|
|
85450
|
+
function requestUsesGraphV2(req) {
|
|
85451
|
+
if (Array.isArray(req.gates) && req.gates.length > 0) return true;
|
|
85452
|
+
if (Array.isArray(req.workspaces) && req.workspaces.length > 0) return true;
|
|
85453
|
+
return (req.tasks ?? []).some(isAdvancedGraphTask);
|
|
85454
|
+
}
|
|
85455
|
+
function normalizeRef(value, label) {
|
|
85456
|
+
const ref = typeof value === "string" ? value.trim() : "";
|
|
85457
|
+
if (!ref) throw new MeshGraphPlanError("invalid_graph_ref", `${label} requires a non-empty ref`);
|
|
85458
|
+
return ref;
|
|
85459
|
+
}
|
|
85460
|
+
function pickEnum(value, allowed, fallback, code, label) {
|
|
85461
|
+
if (value === void 0 || value === null || value === "") return fallback;
|
|
85462
|
+
if (typeof value === "string" && allowed.includes(value)) return value;
|
|
85463
|
+
throw new MeshGraphPlanError(code, `${label} must be one of ${allowed.join(" | ")} (got ${JSON.stringify(value)})`);
|
|
85464
|
+
}
|
|
85465
|
+
function computeMeshGraphPlanDigest(req) {
|
|
85466
|
+
const normalized = {
|
|
85467
|
+
tasks: (req.tasks ?? []).map((t) => ({
|
|
85468
|
+
ref: typeof t.ref === "string" ? t.ref.trim() : null,
|
|
85469
|
+
message: t.message,
|
|
85470
|
+
depends_on: [...t.dependsOn ?? []].sort(),
|
|
85471
|
+
gated_by: [...t.gated_by ?? []].sort(),
|
|
85472
|
+
inputs_from: t.inputs_from ?? null,
|
|
85473
|
+
run_if: t.run_if ?? null,
|
|
85474
|
+
on_false: t.on_false ?? null,
|
|
85475
|
+
on_upstream_skip: t.on_upstream_skip ?? null,
|
|
85476
|
+
workspace_ref: t.workspace_ref ?? null,
|
|
85477
|
+
target_node_id: t.targetNodeId ?? null,
|
|
85478
|
+
task_mode: t.taskMode ?? null,
|
|
85479
|
+
difficulty: t.difficulty ?? null
|
|
85480
|
+
})),
|
|
85481
|
+
gates: (req.gates ?? []).map((g) => ({
|
|
85482
|
+
ref: g.ref,
|
|
85483
|
+
action: g.action ?? "custom",
|
|
85484
|
+
depends_on: [...g.depends_on ?? []].sort(),
|
|
85485
|
+
on_timeout: g.on_timeout ?? "hold",
|
|
85486
|
+
lease_seconds: g.lease_seconds ?? null,
|
|
85487
|
+
deadline_seconds: g.deadline_seconds ?? null
|
|
85488
|
+
})),
|
|
85489
|
+
workspaces: (req.workspaces ?? []).map((w) => ({
|
|
85490
|
+
ref: w.ref,
|
|
85491
|
+
source_node_id: w.source_node_id ?? null,
|
|
85492
|
+
base_revision: w.base_revision ?? null,
|
|
85493
|
+
desired_path: w.desired_path ?? null,
|
|
85494
|
+
cleanup_on_graph_failure: w.cleanup_on_graph_failure === true
|
|
85495
|
+
})),
|
|
85496
|
+
on_dependency_failure: parseOnDependencyFailurePolicy(req.onDependencyFailure)
|
|
85497
|
+
};
|
|
85498
|
+
return (0, import_crypto14.createHash)("sha256").update(canonicalJson(normalized)).digest("hex");
|
|
85499
|
+
}
|
|
85500
|
+
function commitMeshGraphPlan(req) {
|
|
85501
|
+
const store = MeshRuntimeStore.getInstance();
|
|
85502
|
+
const tasks = Array.isArray(req.tasks) ? req.tasks : [];
|
|
85503
|
+
if (tasks.length === 0) {
|
|
85504
|
+
throw new MeshGraphPlanError("empty_task_graph", "a graph plan requires at least one task");
|
|
85505
|
+
}
|
|
85506
|
+
const gates = Array.isArray(req.gates) ? req.gates : [];
|
|
85507
|
+
const workspaces = Array.isArray(req.workspaces) ? req.workspaces : [];
|
|
85508
|
+
const policy = { on_dependency_failure: parseOnDependencyFailurePolicy(req.onDependencyFailure) };
|
|
85509
|
+
const planDigest = computeMeshGraphPlanDigest(req);
|
|
85510
|
+
const batchId = typeof req.batchId === "string" && req.batchId.trim() ? req.batchId.trim() : newMeshGraphId();
|
|
85511
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
85512
|
+
const taskRefs = /* @__PURE__ */ new Map();
|
|
85513
|
+
tasks.forEach((t, i) => {
|
|
85514
|
+
const ref = typeof t.ref === "string" ? t.ref.trim() : "";
|
|
85515
|
+
if (!ref) return;
|
|
85516
|
+
if (taskRefs.has(ref)) {
|
|
85517
|
+
throw new MeshGraphPlanError("duplicate_task_ref", `ref '${ref}' is used by more than one task in this batch`);
|
|
85518
|
+
}
|
|
85519
|
+
taskRefs.set(ref, i);
|
|
85520
|
+
});
|
|
85521
|
+
const gateRefs = /* @__PURE__ */ new Map();
|
|
85522
|
+
gates.forEach((g, i) => {
|
|
85523
|
+
const ref = normalizeRef(g.ref, `gates[${i}]`);
|
|
85524
|
+
if (taskRefs.has(ref) || gateRefs.has(ref)) {
|
|
85525
|
+
throw new MeshGraphPlanError(
|
|
85526
|
+
"duplicate_graph_ref",
|
|
85527
|
+
`ref '${ref}' is declared more than once \u2014 tasks, gates and workspaces share ONE ref namespace`
|
|
85528
|
+
);
|
|
85529
|
+
}
|
|
85530
|
+
gateRefs.set(ref, i);
|
|
85531
|
+
});
|
|
85532
|
+
const workspaceRefs = /* @__PURE__ */ new Set();
|
|
85533
|
+
workspaces.forEach((w, i) => {
|
|
85534
|
+
const ref = normalizeRef(w.ref, `workspaces[${i}]`);
|
|
85535
|
+
if (taskRefs.has(ref) || gateRefs.has(ref) || workspaceRefs.has(ref)) {
|
|
85536
|
+
throw new MeshGraphPlanError(
|
|
85537
|
+
"duplicate_graph_ref",
|
|
85538
|
+
`ref '${ref}' is declared more than once \u2014 tasks, gates and workspaces share ONE ref namespace`
|
|
85539
|
+
);
|
|
85540
|
+
}
|
|
85541
|
+
workspaceRefs.add(ref);
|
|
85542
|
+
});
|
|
85543
|
+
tasks.forEach((t, i) => {
|
|
85544
|
+
for (const dep of t.dependsOn ?? []) {
|
|
85545
|
+
if (gateRefs.has(dep)) {
|
|
85546
|
+
throw new MeshGraphPlanError(
|
|
85547
|
+
"gate_ref_in_depends_on",
|
|
85548
|
+
`task ${t.ref ? `'${t.ref}'` : `#${i}`} lists gate ref '${dep}' in depends_on \u2014 use gated_by to declare a gate edge (a gate is an intentional stop, never an ordinary queue dependency)`
|
|
85549
|
+
);
|
|
85550
|
+
}
|
|
85551
|
+
}
|
|
85552
|
+
for (const gateRef of t.gated_by ?? []) {
|
|
85553
|
+
if (!gateRefs.has(gateRef)) {
|
|
85554
|
+
throw new MeshGraphPlanError(
|
|
85555
|
+
"unknown_gate_ref",
|
|
85556
|
+
`task ${t.ref ? `'${t.ref}'` : `#${i}`} is gated_by '${gateRef}', which is not a gate in this batch` + (gateRefs.size ? ` (gates: ${[...gateRefs.keys()].join(", ")})` : "")
|
|
85557
|
+
);
|
|
85558
|
+
}
|
|
85559
|
+
}
|
|
85560
|
+
const ws = typeof t.workspace_ref === "string" ? t.workspace_ref.trim() : "";
|
|
85561
|
+
if (ws && !workspaceRefs.has(ws)) {
|
|
85562
|
+
throw new MeshGraphPlanError(
|
|
85563
|
+
"unknown_workspace_ref",
|
|
85564
|
+
`task ${t.ref ? `'${t.ref}'` : `#${i}`} names workspace_ref '${ws}', which is not declared in this batch`
|
|
85565
|
+
);
|
|
85566
|
+
}
|
|
85567
|
+
});
|
|
85568
|
+
gates.forEach((g, i) => {
|
|
85569
|
+
for (const dep of g.depends_on ?? []) {
|
|
85570
|
+
if (!taskRefs.has(dep) && !gateRefs.has(dep)) {
|
|
85571
|
+
throw new MeshGraphPlanError(
|
|
85572
|
+
"unknown_gate_dependency",
|
|
85573
|
+
`gate '${g.ref}' depends on '${dep}', which is not a task or gate ref in this batch`
|
|
85574
|
+
);
|
|
85575
|
+
}
|
|
85576
|
+
}
|
|
85577
|
+
pickEnum(g.action, MESH_GRAPH_GATE_ACTIONS, "custom", "invalid_gate_action", `gates[${i}].action`);
|
|
85578
|
+
pickEnum(g.on_timeout, MESH_GRAPH_GATE_TIMEOUT_POLICIES, "hold", "invalid_gate_on_timeout", `gates[${i}].on_timeout`);
|
|
85579
|
+
});
|
|
85580
|
+
tasks.forEach((t, i) => {
|
|
85581
|
+
pickEnum(t.on_upstream_skip, MESH_GRAPH_ON_UPSTREAM_SKIP_POLICIES, "skip", "invalid_on_upstream_skip", `tasks[${i}].on_upstream_skip`);
|
|
85582
|
+
});
|
|
85583
|
+
const result = store.transaction(() => {
|
|
85584
|
+
const graphStore = store.graphStore();
|
|
85585
|
+
const existing = graphStore.getGraphByBatchId(req.meshId, batchId);
|
|
85586
|
+
if (existing) {
|
|
85587
|
+
if (existing.planDigest !== planDigest) {
|
|
85588
|
+
throw new MeshGraphPlanError(
|
|
85589
|
+
"batch_id_conflict",
|
|
85590
|
+
`batch_id '${batchId}' already exists on this mesh with a DIFFERENT plan digest \u2014 reuse of a batch id is only idempotent for an identical plan`,
|
|
85591
|
+
{ graphId: existing.graphId }
|
|
85592
|
+
);
|
|
85593
|
+
}
|
|
85594
|
+
const existingNodes = graphStore.listNodes(existing.graphId);
|
|
85595
|
+
const workerNodes = existingNodes.filter((n) => n.kind === "worker_task");
|
|
85596
|
+
const replayTasks = workerNodes.map((n) => n.queueTaskId ? store.findQueueEntryById(req.meshId, n.queueTaskId) : null).filter((e) => !!e);
|
|
85597
|
+
return {
|
|
85598
|
+
graphId: existing.graphId,
|
|
85599
|
+
batchId,
|
|
85600
|
+
tasks: replayTasks,
|
|
85601
|
+
nodeIdByIndex: workerNodes.map((n) => n.nodeId),
|
|
85602
|
+
gates: graphStore.listGatesByGraph(existing.graphId).map((g) => ({
|
|
85603
|
+
ref: g.ref ?? g.gateId,
|
|
85604
|
+
gateId: g.gateId,
|
|
85605
|
+
nodeId: g.nodeId,
|
|
85606
|
+
action: g.action
|
|
85607
|
+
})),
|
|
85608
|
+
workspaces: graphStore.listWorkspaceIntents(existing.graphId).map((w) => ({
|
|
85609
|
+
ref: w.workspaceRef,
|
|
85610
|
+
sagaState: w.sagaState,
|
|
85611
|
+
branchIdentity: w.branchIdentity
|
|
85612
|
+
})),
|
|
85613
|
+
dependencyEdgeCount: existing.dependencyEdgeCount,
|
|
85614
|
+
planDigest,
|
|
85615
|
+
replayed: true,
|
|
85616
|
+
workspacePreparation: existing.workspaceCount > 0 ? "pending" : "not_requested",
|
|
85617
|
+
heldNodeIds: []
|
|
85618
|
+
};
|
|
85619
|
+
}
|
|
85620
|
+
const queueSpecs = tasks.map((t) => {
|
|
85621
|
+
const {
|
|
85622
|
+
inputs_from: _inputsFrom,
|
|
85623
|
+
run_if: _runIf,
|
|
85624
|
+
on_false: _onFalse,
|
|
85625
|
+
on_upstream_skip: _onUpstreamSkip,
|
|
85626
|
+
workspace_ref: _workspaceRef,
|
|
85627
|
+
gated_by: _gatedBy,
|
|
85628
|
+
...queueSpec
|
|
85629
|
+
} = t;
|
|
85630
|
+
return {
|
|
85631
|
+
...queueSpec,
|
|
85632
|
+
missionId: queueSpec.missionId ?? req.missionId,
|
|
85633
|
+
...req.sourceCoordinatorSessionId ? { sourceCoordinatorSessionId: req.sourceCoordinatorSessionId } : {}
|
|
85634
|
+
};
|
|
85635
|
+
});
|
|
85636
|
+
const inserted = enqueueTaskGraph(req.meshId, queueSpecs);
|
|
85637
|
+
const graphId = newMeshGraphId();
|
|
85638
|
+
const nodeIdByIndex = tasks.map(() => newMeshGraphNodeId());
|
|
85639
|
+
const gateNodeIdByRef = /* @__PURE__ */ new Map();
|
|
85640
|
+
const gateIdByRef = /* @__PURE__ */ new Map();
|
|
85641
|
+
gates.forEach((g) => {
|
|
85642
|
+
gateNodeIdByRef.set(g.ref.trim(), newMeshGraphNodeId());
|
|
85643
|
+
gateIdByRef.set(g.ref.trim(), newMeshGraphGateId());
|
|
85644
|
+
});
|
|
85645
|
+
const nodeIdByRef = /* @__PURE__ */ new Map();
|
|
85646
|
+
tasks.forEach((t, i) => {
|
|
85647
|
+
const ref = typeof t.ref === "string" ? t.ref.trim() : "";
|
|
85648
|
+
if (ref) nodeIdByRef.set(ref, nodeIdByIndex[i]);
|
|
85649
|
+
});
|
|
85650
|
+
for (const [ref, nodeId] of gateNodeIdByRef) nodeIdByRef.set(ref, nodeId);
|
|
85651
|
+
const edges = [];
|
|
85652
|
+
const addEdge = (fromNodeId, toNodeId, kind, omitOnSkip) => {
|
|
85653
|
+
edges.push({ graphId, meshId: req.meshId, fromNodeId, toNodeId, kind, omitOnSkip, createdAt: nowIso });
|
|
85654
|
+
};
|
|
85655
|
+
tasks.forEach((t, i) => {
|
|
85656
|
+
const nodeId = nodeIdByIndex[i];
|
|
85657
|
+
const ref = typeof t.ref === "string" ? t.ref.trim() : void 0;
|
|
85658
|
+
const baseSpec = { message: t.message };
|
|
85659
|
+
if (t.inputs_from !== void 0) baseSpec.inputs_from = t.inputs_from;
|
|
85660
|
+
if (t.run_if !== void 0) baseSpec.run_if = t.run_if;
|
|
85661
|
+
if (t.on_false !== void 0) baseSpec.on_false = t.on_false;
|
|
85662
|
+
if (typeof t.workspace_ref === "string" && t.workspace_ref.trim()) {
|
|
85663
|
+
baseSpec.workspace_ref = t.workspace_ref.trim();
|
|
85664
|
+
}
|
|
85665
|
+
const node = {
|
|
85666
|
+
graphId,
|
|
85667
|
+
nodeId,
|
|
85668
|
+
meshId: req.meshId,
|
|
85669
|
+
...ref ? { ref } : {},
|
|
85670
|
+
kind: "worker_task",
|
|
85671
|
+
queueTaskId: inserted[i].id,
|
|
85672
|
+
state: "declared",
|
|
85673
|
+
baseSpecJson: JSON.stringify(baseSpec),
|
|
85674
|
+
materializationVersion: 0,
|
|
85675
|
+
createdAt: nowIso,
|
|
85676
|
+
updatedAt: nowIso
|
|
85677
|
+
};
|
|
85678
|
+
graphStore.insertNode(node);
|
|
85679
|
+
});
|
|
85680
|
+
gates.forEach((g, i) => {
|
|
85681
|
+
const ref = g.ref.trim();
|
|
85682
|
+
const nodeId = gateNodeIdByRef.get(ref);
|
|
85683
|
+
const gateSpec = {};
|
|
85684
|
+
if (typeof g.lease_seconds === "number" && g.lease_seconds > 0) gateSpec.lease_seconds = g.lease_seconds;
|
|
85685
|
+
if (typeof g.deadline_seconds === "number" && g.deadline_seconds > 0) gateSpec.deadline_seconds = g.deadline_seconds;
|
|
85686
|
+
graphStore.insertNode({
|
|
85687
|
+
graphId,
|
|
85688
|
+
nodeId,
|
|
85689
|
+
meshId: req.meshId,
|
|
85690
|
+
ref,
|
|
85691
|
+
kind: "coordinator_gate",
|
|
85692
|
+
state: "declared",
|
|
85693
|
+
baseSpecJson: JSON.stringify(gateSpec),
|
|
85694
|
+
materializationVersion: 0,
|
|
85695
|
+
createdAt: nowIso,
|
|
85696
|
+
updatedAt: nowIso
|
|
85697
|
+
});
|
|
85698
|
+
graphStore.insertGate({
|
|
85699
|
+
gateId: gateIdByRef.get(ref),
|
|
85700
|
+
graphId,
|
|
85701
|
+
nodeId,
|
|
85702
|
+
meshId: req.meshId,
|
|
85703
|
+
ref,
|
|
85704
|
+
state: "declared",
|
|
85705
|
+
action: pickEnum(g.action, MESH_GRAPH_GATE_ACTIONS, "custom", "invalid_gate_action", `gates[${i}].action`),
|
|
85706
|
+
instructions: typeof g.instructions === "string" ? g.instructions : void 0,
|
|
85707
|
+
eligibleCoordinatorSessionId: typeof g.eligible_coordinator_session_id === "string" ? g.eligible_coordinator_session_id : void 0,
|
|
85708
|
+
leaseGeneration: 0,
|
|
85709
|
+
onTimeout: pickEnum(g.on_timeout, MESH_GRAPH_GATE_TIMEOUT_POLICIES, "hold", "invalid_gate_on_timeout", `gates[${i}].on_timeout`),
|
|
85710
|
+
createdAt: nowIso,
|
|
85711
|
+
updatedAt: nowIso
|
|
85712
|
+
});
|
|
85713
|
+
});
|
|
85714
|
+
tasks.forEach((t, i) => {
|
|
85715
|
+
const omitOnSkip = pickEnum(
|
|
85716
|
+
t.on_upstream_skip,
|
|
85717
|
+
MESH_GRAPH_ON_UPSTREAM_SKIP_POLICIES,
|
|
85718
|
+
"skip",
|
|
85719
|
+
"invalid_on_upstream_skip",
|
|
85720
|
+
`tasks[${i}].on_upstream_skip`
|
|
85721
|
+
) === "omit_dependency";
|
|
85722
|
+
for (const dep of t.dependsOn ?? []) {
|
|
85723
|
+
const fromNodeId = nodeIdByRef.get(dep);
|
|
85724
|
+
if (!fromNodeId) continue;
|
|
85725
|
+
addEdge(fromNodeId, nodeIdByIndex[i], "requires", omitOnSkip);
|
|
85726
|
+
}
|
|
85727
|
+
const gateRefsForNode = new Set((t.gated_by ?? []).map((r) => r.trim()).filter(Boolean));
|
|
85728
|
+
for (const sourceRef of collectRunIfSourceRefs(t.run_if)) {
|
|
85729
|
+
const fromNodeId = nodeIdByRef.get(sourceRef);
|
|
85730
|
+
if (!fromNodeId || fromNodeId === nodeIdByIndex[i]) continue;
|
|
85731
|
+
if (gateNodeIdByRef.has(sourceRef)) {
|
|
85732
|
+
gateRefsForNode.add(sourceRef);
|
|
85733
|
+
continue;
|
|
85734
|
+
}
|
|
85735
|
+
if (edges.some((e) => e.fromNodeId === fromNodeId && e.toNodeId === nodeIdByIndex[i])) continue;
|
|
85736
|
+
addEdge(fromNodeId, nodeIdByIndex[i], "conditional", omitOnSkip);
|
|
85737
|
+
}
|
|
85738
|
+
for (const sourceRef of collectInputSourceRefs(t.inputs_from)) {
|
|
85739
|
+
const fromNodeId = nodeIdByRef.get(sourceRef);
|
|
85740
|
+
if (!fromNodeId || fromNodeId === nodeIdByIndex[i]) continue;
|
|
85741
|
+
if (gateNodeIdByRef.has(sourceRef)) {
|
|
85742
|
+
gateRefsForNode.add(sourceRef);
|
|
85743
|
+
continue;
|
|
85744
|
+
}
|
|
85745
|
+
if (edges.some((e) => e.fromNodeId === fromNodeId && e.toNodeId === nodeIdByIndex[i] && e.kind === "requires")) continue;
|
|
85746
|
+
addEdge(fromNodeId, nodeIdByIndex[i], "requires", omitOnSkip);
|
|
85747
|
+
}
|
|
85748
|
+
for (const gateRef of gateRefsForNode) {
|
|
85749
|
+
const fromNodeId = gateNodeIdByRef.get(gateRef);
|
|
85750
|
+
if (!fromNodeId) continue;
|
|
85751
|
+
addEdge(fromNodeId, nodeIdByIndex[i], "gate", false);
|
|
85752
|
+
}
|
|
85753
|
+
});
|
|
85754
|
+
gates.forEach((g) => {
|
|
85755
|
+
const toNodeId = gateNodeIdByRef.get(g.ref.trim());
|
|
85756
|
+
for (const dep of g.depends_on ?? []) {
|
|
85757
|
+
const fromNodeId = nodeIdByRef.get(dep);
|
|
85758
|
+
if (!fromNodeId) continue;
|
|
85759
|
+
addEdge(fromNodeId, toNodeId, "requires", false);
|
|
85760
|
+
}
|
|
85761
|
+
});
|
|
85762
|
+
for (const edge of edges) graphStore.insertEdge(edge);
|
|
85763
|
+
graphStore.insertGraph({
|
|
85764
|
+
graphId,
|
|
85765
|
+
meshId: req.meshId,
|
|
85766
|
+
batchId,
|
|
85767
|
+
missionId: req.missionId,
|
|
85768
|
+
sourceCoordinatorSessionId: req.sourceCoordinatorSessionId,
|
|
85769
|
+
enqueueSurface: req.enqueueSurface ?? "batch",
|
|
85770
|
+
schemaVersion: MESH_GRAPH_SCHEMA_VERSION,
|
|
85771
|
+
status: "active",
|
|
85772
|
+
taskCount: tasks.length,
|
|
85773
|
+
gateCount: gates.length,
|
|
85774
|
+
workspaceCount: workspaces.length,
|
|
85775
|
+
dependencyEdgeCount: edges.length,
|
|
85776
|
+
planDigest,
|
|
85777
|
+
// ★ E-2: `on_dependency_failure` is now PERSISTED on the graph row
|
|
85778
|
+
// (C3 only validated and echoed it). The C3 derived-failure path reads
|
|
85779
|
+
// it back through parseGraphPolicy/projectGraphPublicPolicy.
|
|
85780
|
+
policyJson: serializeGraphPolicy(policy),
|
|
85781
|
+
createdAt: nowIso,
|
|
85782
|
+
activatedAt: nowIso,
|
|
85783
|
+
updatedAt: nowIso
|
|
85784
|
+
});
|
|
85785
|
+
if (req.orchestrationDecision) {
|
|
85786
|
+
const merged = JSON.stringify({
|
|
85787
|
+
...JSON.parse(serializeGraphPolicy(policy)),
|
|
85788
|
+
orchestration_decision: req.orchestrationDecision
|
|
85789
|
+
});
|
|
85790
|
+
graphStore.updateGraphPolicyJson(graphId, merged, nowIso);
|
|
85791
|
+
}
|
|
85792
|
+
const heldNodeIds = [];
|
|
85793
|
+
tasks.forEach((t, i) => {
|
|
85794
|
+
if (!isAdvancedGraphTask(t)) return;
|
|
85795
|
+
const nodeId = nodeIdByIndex[i];
|
|
85796
|
+
const entry = store.findQueueEntryById(req.meshId, inserted[i].id);
|
|
85797
|
+
if (!entry || entry.status !== "pending" || entry.blockedReason) return;
|
|
85798
|
+
entry.blockedReason = graphMaterializationBlockReason(nodeId, 0);
|
|
85799
|
+
store.updateQueueEntry(entry);
|
|
85800
|
+
graphStore.updateNodeState(graphId, nodeId, "blocked", nowIso);
|
|
85801
|
+
heldNodeIds.push(nodeId);
|
|
85802
|
+
});
|
|
85803
|
+
return {
|
|
85804
|
+
graphId,
|
|
85805
|
+
batchId,
|
|
85806
|
+
tasks: inserted,
|
|
85807
|
+
nodeIdByIndex,
|
|
85808
|
+
gates: gates.map((g) => ({
|
|
85809
|
+
ref: g.ref.trim(),
|
|
85810
|
+
gateId: gateIdByRef.get(g.ref.trim()),
|
|
85811
|
+
nodeId: gateNodeIdByRef.get(g.ref.trim()),
|
|
85812
|
+
action: pickEnum(g.action, MESH_GRAPH_GATE_ACTIONS, "custom", "invalid_gate_action", "gate action")
|
|
85813
|
+
})),
|
|
85814
|
+
workspaces: [],
|
|
85815
|
+
dependencyEdgeCount: edges.length,
|
|
85816
|
+
planDigest,
|
|
85817
|
+
replayed: false,
|
|
85818
|
+
workspacePreparation: workspaces.length > 0 ? "pending" : "not_requested",
|
|
85819
|
+
heldNodeIds
|
|
85820
|
+
};
|
|
85821
|
+
});
|
|
85822
|
+
if (!result.replayed && workspaces.length > 0) {
|
|
85823
|
+
try {
|
|
85824
|
+
const declared = declareWorkspaceIntents({
|
|
85825
|
+
graphId: result.graphId,
|
|
85826
|
+
meshId: req.meshId,
|
|
85827
|
+
workspaces
|
|
85828
|
+
});
|
|
85829
|
+
result.workspaces = declared.map((w) => ({
|
|
85830
|
+
ref: w.workspaceRef,
|
|
85831
|
+
sagaState: w.sagaState,
|
|
85832
|
+
branchIdentity: w.branchIdentity
|
|
85833
|
+
}));
|
|
85834
|
+
} catch (e) {
|
|
85835
|
+
LOG2.warn("MeshGraph", `Workspace intent declaration failed for graph ${result.graphId}: ${e?.message || e}`);
|
|
85836
|
+
}
|
|
85837
|
+
}
|
|
85838
|
+
return result;
|
|
85839
|
+
}
|
|
85840
|
+
function collectRunIfSourceRefs(runIf, depth = 0) {
|
|
85841
|
+
if (!runIf || typeof runIf !== "object" || depth > 8) return [];
|
|
85842
|
+
const out = [];
|
|
85843
|
+
const node = runIf;
|
|
85844
|
+
if (typeof node.from === "string" && node.from.trim()) out.push(node.from.trim());
|
|
85845
|
+
for (const key2 of ["all", "any", "not", "conditions"]) {
|
|
85846
|
+
const child = node[key2];
|
|
85847
|
+
if (Array.isArray(child)) {
|
|
85848
|
+
for (const item of child) out.push(...collectRunIfSourceRefs(item, depth + 1));
|
|
85849
|
+
} else if (child && typeof child === "object") {
|
|
85850
|
+
out.push(...collectRunIfSourceRefs(child, depth + 1));
|
|
85851
|
+
}
|
|
85852
|
+
}
|
|
85853
|
+
return [...new Set(out)];
|
|
85854
|
+
}
|
|
85855
|
+
function collectInputSourceRefs(inputsFrom) {
|
|
85856
|
+
if (!Array.isArray(inputsFrom)) return [];
|
|
85857
|
+
const out = [];
|
|
85858
|
+
for (const item of inputsFrom) {
|
|
85859
|
+
if (item && typeof item === "object" && typeof item.from === "string") {
|
|
85860
|
+
const from = item.from.trim();
|
|
85861
|
+
if (from) out.push(from);
|
|
85862
|
+
}
|
|
85863
|
+
}
|
|
85864
|
+
return [...new Set(out)];
|
|
85865
|
+
}
|
|
85866
|
+
init_mesh_runtime_store();
|
|
85867
|
+
init_mesh_graph_derived_failure();
|
|
85868
|
+
init_mesh_graph_transition_runner();
|
|
85869
|
+
var IN_FLIGHT_GRAPH_STATUSES = [
|
|
85870
|
+
"preparing",
|
|
85871
|
+
"active",
|
|
85872
|
+
"waiting_gate",
|
|
85873
|
+
"compensation_required"
|
|
85874
|
+
];
|
|
85875
|
+
function buildMeshGraphViews(meshId, opts = {}) {
|
|
85876
|
+
const store = MeshRuntimeStore.getInstance();
|
|
85877
|
+
const graphStore = store.graphStore();
|
|
85878
|
+
const nowIso = new Date(opts.nowMs ?? Date.now()).toISOString();
|
|
85879
|
+
let graphs;
|
|
85880
|
+
if (opts.graphId) {
|
|
85881
|
+
const one = graphStore.getGraph(opts.graphId);
|
|
85882
|
+
graphs = one && one.meshId === meshId ? [one] : [];
|
|
85883
|
+
} else if (opts.batchId) {
|
|
85884
|
+
const one = graphStore.getGraphByBatchId(meshId, opts.batchId);
|
|
85885
|
+
graphs = one ? [one] : [];
|
|
85886
|
+
} else {
|
|
85887
|
+
graphs = graphStore.listGraphsByMesh(meshId, {
|
|
85888
|
+
...opts.activeOnly === false ? {} : { statuses: IN_FLIGHT_GRAPH_STATUSES },
|
|
85889
|
+
limit: opts.limit ?? 20
|
|
85890
|
+
});
|
|
85891
|
+
}
|
|
85892
|
+
return graphs.map((graph) => {
|
|
85893
|
+
const nodes = graphStore.listNodes(graph.graphId);
|
|
85894
|
+
const edges = graphStore.listEdges(graph.graphId);
|
|
85895
|
+
const gates = graphStore.listGatesByGraph(graph.graphId);
|
|
85896
|
+
const workspaces = graphStore.listWorkspaceIntents(graph.graphId);
|
|
85897
|
+
const byId = new Map(nodes.map((n) => [n.nodeId, n]));
|
|
85898
|
+
const policy = projectGraphPublicPolicy(graph.policyJson);
|
|
85899
|
+
const decision = readOrchestrationDecision(graph.policyJson);
|
|
85900
|
+
const queueByTaskId = /* @__PURE__ */ new Map();
|
|
85901
|
+
for (const node of nodes) {
|
|
85902
|
+
if (!node.queueTaskId) continue;
|
|
85903
|
+
const entry = store.findQueueEntryById(meshId, node.queueTaskId);
|
|
85904
|
+
if (entry) {
|
|
85905
|
+
queueByTaskId.set(node.queueTaskId, {
|
|
85906
|
+
status: entry.status,
|
|
85907
|
+
blockedReason: entry.blockedReason,
|
|
85908
|
+
cancelReason: entry.cancelReason,
|
|
85909
|
+
dependsOn: entry.dependsOn
|
|
85910
|
+
});
|
|
85911
|
+
}
|
|
85912
|
+
}
|
|
85913
|
+
const statusById = /* @__PURE__ */ new Map();
|
|
85914
|
+
const depMetaById = /* @__PURE__ */ new Map();
|
|
85915
|
+
for (const [taskId, row] of queueByTaskId) {
|
|
85916
|
+
statusById.set(taskId, row.status);
|
|
85917
|
+
depMetaById.set(taskId, { status: row.status, blockedReason: row.blockedReason, cancelReason: row.cancelReason });
|
|
85918
|
+
}
|
|
85919
|
+
for (const row of queueByTaskId.values()) {
|
|
85920
|
+
for (const depId of row.dependsOn ?? []) {
|
|
85921
|
+
if (statusById.has(depId)) continue;
|
|
85922
|
+
const dep = store.findQueueEntryById(meshId, depId);
|
|
85923
|
+
if (!dep) continue;
|
|
85924
|
+
statusById.set(depId, dep.status);
|
|
85925
|
+
depMetaById.set(depId, {
|
|
85926
|
+
status: dep.status,
|
|
85927
|
+
blockedReason: dep.blockedReason,
|
|
85928
|
+
cancelReason: dep.cancelReason
|
|
85929
|
+
});
|
|
85930
|
+
}
|
|
85931
|
+
}
|
|
85932
|
+
const gateByNodeId = new Map(gates.map((g) => [g.nodeId, g]));
|
|
85933
|
+
const nodeViews = nodes.map((node) => {
|
|
85934
|
+
const queue = node.queueTaskId ? queueByTaskId.get(node.queueTaskId) : void 0;
|
|
85935
|
+
const gateBlock = parseCoordinatorGateBlock(queue?.blockedReason);
|
|
85936
|
+
const failures = queue?.dependsOn?.length ? deriveDependencyFailures(queue.dependsOn, statusById, depMetaById) : [];
|
|
85937
|
+
return {
|
|
85938
|
+
nodeId: node.nodeId,
|
|
85939
|
+
...node.ref ? { ref: node.ref } : {},
|
|
85940
|
+
kind: node.kind,
|
|
85941
|
+
state: node.state,
|
|
85942
|
+
...node.queueTaskId ? { taskId: node.queueTaskId } : {},
|
|
85943
|
+
...queue ? { taskStatus: queue.status } : {},
|
|
85944
|
+
...queue?.blockedReason ? { blockedReason: queue.blockedReason } : {},
|
|
85945
|
+
...gateBlock ? { blockedByGateId: gateBlock.gateId } : {},
|
|
85946
|
+
materializationVersion: node.materializationVersion,
|
|
85947
|
+
...node.materializedDigest ? { materializedDigest: node.materializedDigest } : {},
|
|
85948
|
+
...node.skipReason ? { skipReason: node.skipReason } : {},
|
|
85949
|
+
...node.failureReason ? { failureReason: node.failureReason } : {},
|
|
85950
|
+
...failures.length > 0 ? { dependencyFailures: failures } : {},
|
|
85951
|
+
...describeNodeFeatures(node.baseSpecJson).length > 0 ? { features: describeNodeFeatures(node.baseSpecJson) } : {}
|
|
85952
|
+
};
|
|
85953
|
+
});
|
|
85954
|
+
const edgeViews = edges.map((edge) => {
|
|
85955
|
+
const source = byId.get(edge.fromNodeId);
|
|
85956
|
+
const active = !(source?.state === "skipped" && edge.omitOnSkip);
|
|
85957
|
+
return {
|
|
85958
|
+
from: source?.ref ?? edge.fromNodeId,
|
|
85959
|
+
to: byId.get(edge.toNodeId)?.ref ?? edge.toNodeId,
|
|
85960
|
+
kind: edge.kind,
|
|
85961
|
+
omitOnSkip: edge.omitOnSkip,
|
|
85962
|
+
active
|
|
85963
|
+
};
|
|
85964
|
+
});
|
|
85965
|
+
const gateViews = gates.map((gate) => {
|
|
85966
|
+
const blocking = nodeViews.filter((n) => n.blockedByGateId === gate.gateId).map((n) => n.ref ?? n.nodeId);
|
|
85967
|
+
const leaseExpired = gate.state === "claimed" && !!gate.leaseExpiresAt && gate.leaseExpiresAt <= nowIso;
|
|
85968
|
+
return {
|
|
85969
|
+
gateId: gate.gateId,
|
|
85970
|
+
...gate.ref ? { ref: gate.ref } : {},
|
|
85971
|
+
nodeId: gate.nodeId,
|
|
85972
|
+
state: gate.state,
|
|
85973
|
+
action: gate.action,
|
|
85974
|
+
...gate.instructions ? { instructions: gate.instructions } : {},
|
|
85975
|
+
onTimeout: gate.onTimeout,
|
|
85976
|
+
leaseGeneration: gate.leaseGeneration,
|
|
85977
|
+
...gate.leaseOwnerSessionId ? { leaseOwnerSessionId: gate.leaseOwnerSessionId } : {},
|
|
85978
|
+
...gate.leaseExpiresAt ? { leaseExpiresAt: gate.leaseExpiresAt } : {},
|
|
85979
|
+
...leaseExpired ? { leaseExpired: true } : {},
|
|
85980
|
+
...gate.deadlineAt ? { deadlineAt: gate.deadlineAt } : {},
|
|
85981
|
+
...gate.releaseOutcome ? { releaseOutcome: gate.releaseOutcome } : {},
|
|
85982
|
+
...gate.eligibleCoordinatorSessionId ? { eligibleCoordinatorSessionId: gate.eligibleCoordinatorSessionId } : {},
|
|
85983
|
+
...blocking.length > 0 ? { blocking } : {}
|
|
85984
|
+
};
|
|
85985
|
+
});
|
|
85986
|
+
const nodeStates = {};
|
|
85987
|
+
for (const node of nodes) nodeStates[node.state] = (nodeStates[node.state] ?? 0) + 1;
|
|
85988
|
+
const actions = [];
|
|
85989
|
+
for (const gate of gateViews) {
|
|
85990
|
+
if (gate.state === "awaiting_coordinator") {
|
|
85991
|
+
actions.push({
|
|
85992
|
+
kind: "gate_awaiting",
|
|
85993
|
+
gateId: gate.gateId,
|
|
85994
|
+
detail: `Gate '${gate.ref ?? gate.gateId}' (${gate.action}) is awaiting a coordinator. Claim it with mesh_graph_gate_claim, perform the action, then release it with mesh_graph_gate_release.`
|
|
85995
|
+
});
|
|
85996
|
+
} else if (gate.state === "claimed" && gate.leaseExpired) {
|
|
85997
|
+
actions.push({
|
|
85998
|
+
kind: "gate_reclaim",
|
|
85999
|
+
gateId: gate.gateId,
|
|
86000
|
+
detail: `Gate '${gate.ref ?? gate.gateId}' has a LAPSED lease (generation ${gate.leaseGeneration}). Reclaim it to get a higher generation; the previous owner may already have performed the external side effect, so reconcile external evidence before acting again.`
|
|
86001
|
+
});
|
|
86002
|
+
} else if (gate.state === "claimed") {
|
|
86003
|
+
actions.push({
|
|
86004
|
+
kind: "gate_claimed",
|
|
86005
|
+
gateId: gate.gateId,
|
|
86006
|
+
detail: `Gate '${gate.ref ?? gate.gateId}' is claimed by ${gate.leaseOwnerSessionId ?? "a coordinator"} until ${gate.leaseExpiresAt ?? "lease expiry"}; release it to advance the graph.`
|
|
86007
|
+
});
|
|
86008
|
+
} else if (gate.state === "expired" && gate.onTimeout === "hold") {
|
|
86009
|
+
actions.push({
|
|
86010
|
+
kind: "gate_reclaim",
|
|
86011
|
+
gateId: gate.gateId,
|
|
86012
|
+
detail: `Gate '${gate.ref ?? gate.gateId}' passed its deadline under on_timeout=hold \u2014 downstream is still held. Reclaim it (optionally with extend_deadline_seconds) to resume, or cancel the branch explicitly. A timeout is never passage: the gate cannot release itself.`
|
|
86013
|
+
});
|
|
86014
|
+
}
|
|
86015
|
+
}
|
|
86016
|
+
for (const ws of workspaces) {
|
|
86017
|
+
if (ws.sagaState === "compensation_required") {
|
|
86018
|
+
actions.push({
|
|
86019
|
+
kind: "workspace_compensation",
|
|
86020
|
+
workspaceRef: ws.workspaceRef,
|
|
86021
|
+
detail: `Workspace '${ws.workspaceRef}' needs manual compensation: ${ws.lastError ?? "compensation refused"}.`
|
|
86022
|
+
});
|
|
86023
|
+
}
|
|
86024
|
+
}
|
|
86025
|
+
return {
|
|
86026
|
+
graphId: graph.graphId,
|
|
86027
|
+
batchId: graph.batchId,
|
|
86028
|
+
status: graph.status,
|
|
86029
|
+
...graph.missionId ? { missionId: graph.missionId } : {},
|
|
86030
|
+
enqueueSurface: graph.enqueueSurface,
|
|
86031
|
+
schemaVersion: graph.schemaVersion,
|
|
86032
|
+
createdAt: graph.createdAt,
|
|
86033
|
+
...graph.terminalAt ? { terminalAt: graph.terminalAt } : {},
|
|
86034
|
+
onDependencyFailure: policy.on_dependency_failure,
|
|
86035
|
+
...policy.policyInvalid ? { policyInvalid: policy.policyInvalid } : {},
|
|
86036
|
+
...decision ? { orchestrationDecision: decision } : {},
|
|
86037
|
+
counts: {
|
|
86038
|
+
tasks: graph.taskCount,
|
|
86039
|
+
gates: graph.gateCount,
|
|
86040
|
+
workspaces: graph.workspaceCount,
|
|
86041
|
+
edges: graph.dependencyEdgeCount
|
|
86042
|
+
},
|
|
86043
|
+
nodeStates,
|
|
86044
|
+
nodes: nodeViews,
|
|
86045
|
+
edges: edgeViews,
|
|
86046
|
+
gates: gateViews,
|
|
86047
|
+
workspaces: workspaces.map((w) => ({
|
|
86048
|
+
ref: w.workspaceRef,
|
|
86049
|
+
sagaState: w.sagaState,
|
|
86050
|
+
...w.branchIdentity ? { branchIdentity: w.branchIdentity } : {},
|
|
86051
|
+
...w.createdNodeId ? { createdNodeId: w.createdNodeId } : {},
|
|
86052
|
+
...w.createdWorktreePath ? { createdWorktreePath: w.createdWorktreePath } : {},
|
|
86053
|
+
...w.lastError ? { lastError: w.lastError } : {}
|
|
86054
|
+
})),
|
|
86055
|
+
...actions.length > 0 ? { nextCoordinatorAction: actions } : {}
|
|
86056
|
+
};
|
|
86057
|
+
});
|
|
86058
|
+
}
|
|
86059
|
+
function describeNodeFeatures(baseSpecJson) {
|
|
86060
|
+
let spec;
|
|
86061
|
+
try {
|
|
86062
|
+
const parsed = JSON.parse(baseSpecJson);
|
|
86063
|
+
spec = parsed && typeof parsed === "object" ? parsed : void 0;
|
|
86064
|
+
} catch {
|
|
86065
|
+
return ["invalid_base_spec"];
|
|
86066
|
+
}
|
|
86067
|
+
if (!spec) return [];
|
|
86068
|
+
const features = [];
|
|
86069
|
+
if (Array.isArray(spec.inputs_from) && spec.inputs_from.length > 0) features.push("inputs_from");
|
|
86070
|
+
if (spec.run_if !== void 0) features.push("run_if");
|
|
86071
|
+
if (typeof spec.workspace_ref === "string" && spec.workspace_ref) features.push("workspace_ref");
|
|
86072
|
+
return features;
|
|
86073
|
+
}
|
|
86074
|
+
function readOrchestrationDecision(policyJson) {
|
|
86075
|
+
if (!policyJson) return void 0;
|
|
86076
|
+
try {
|
|
86077
|
+
const parsed = JSON.parse(policyJson);
|
|
86078
|
+
const decision = parsed?.orchestration_decision;
|
|
86079
|
+
return decision && typeof decision === "object" && !Array.isArray(decision) ? decision : void 0;
|
|
86080
|
+
} catch {
|
|
86081
|
+
return void 0;
|
|
86082
|
+
}
|
|
86083
|
+
}
|
|
86084
|
+
init_mesh_graph_provenance();
|
|
84073
86085
|
init_mesh_node_identity();
|
|
84074
86086
|
init_mesh_active_work();
|
|
84075
86087
|
init_mesh_idle_reminder();
|
|
@@ -111685,7 +113697,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
111685
113697
|
var path47 = __toESM2(require("path"));
|
|
111686
113698
|
var fs41 = __toESM2(require("fs"));
|
|
111687
113699
|
var path46 = __toESM2(require("path"));
|
|
111688
|
-
var
|
|
113700
|
+
var import_crypto16 = require("crypto");
|
|
111689
113701
|
var TREE_DIGEST_ALGORITHM = "adhdev-provider-tree-sha256-v1";
|
|
111690
113702
|
function computeProviderTreeDigest(rootDir, providerType) {
|
|
111691
113703
|
const relPaths = [];
|
|
@@ -111698,7 +113710,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
111698
113710
|
);
|
|
111699
113711
|
}
|
|
111700
113712
|
relPaths.sort();
|
|
111701
|
-
const hash2 = (0,
|
|
113713
|
+
const hash2 = (0, import_crypto16.createHash)("sha256");
|
|
111702
113714
|
for (const relPath of relPaths) {
|
|
111703
113715
|
const absPath = path46.join(rootDir, ...relPath.split("/"));
|
|
111704
113716
|
const bytes = fs41.readFileSync(absPath);
|
|
@@ -115775,11 +117787,11 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
|
|
|
115775
117787
|
let node;
|
|
115776
117788
|
const { migrateProviderRolesToSlots: migrateProviderRolesToSlots2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
115777
117789
|
if (meshRecord.inline) {
|
|
115778
|
-
const { randomUUID:
|
|
117790
|
+
const { randomUUID: randomUUID20 } = await import("crypto");
|
|
115779
117791
|
const clonedPolicy = { ...sourceNode.policy || {} };
|
|
115780
117792
|
migrateProviderRolesToSlots2(clonedPolicy);
|
|
115781
117793
|
node = {
|
|
115782
|
-
id: `node_${
|
|
117794
|
+
id: `node_${randomUUID20().replace(/-/g, "")}`,
|
|
115783
117795
|
workspace: result.worktreePath,
|
|
115784
117796
|
repoRoot: result.worktreePath,
|
|
115785
117797
|
daemonId: sourceNode.daemonId,
|
|
@@ -116311,8 +118323,10 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
|
|
|
116311
118323
|
const { getMeshQueueStats: getMeshQueueStats2, getQueue: getQueue2, describeTaskDependencyState: describeTaskDependencyState2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
|
|
116312
118324
|
const status = Array.isArray(args?.status) ? args.status.map((s2) => typeof s2 === "string" ? s2.trim() : "").filter(Boolean) : void 0;
|
|
116313
118325
|
const rawQueue = getQueue2(meshId, { status });
|
|
116314
|
-
const
|
|
116315
|
-
const
|
|
118326
|
+
const allQueue = getQueue2(meshId);
|
|
118327
|
+
const statusById = new Map(allQueue.map((task) => [task.id, task.status]));
|
|
118328
|
+
const depMetaById = new Map(allQueue.map((task) => [task.id, task]));
|
|
118329
|
+
const queue = rawQueue.map((task) => Array.isArray(task.dependsOn) && task.dependsOn.length > 0 ? { ...task, ...describeTaskDependencyState2(task, statusById, depMetaById) } : task);
|
|
116316
118330
|
const summary = getMeshQueueStats2(meshId);
|
|
116317
118331
|
return {
|
|
116318
118332
|
success: true,
|
|
@@ -127391,7 +129405,7 @@ data: ${JSON.stringify(msg.data)}
|
|
|
127391
129405
|
});
|
|
127392
129406
|
}
|
|
127393
129407
|
};
|
|
127394
|
-
var
|
|
129408
|
+
var import_crypto17 = require("crypto");
|
|
127395
129409
|
var import_session_host_core12 = require_dist();
|
|
127396
129410
|
var BASE_KEY_SEQUENCES = {
|
|
127397
129411
|
enter: "\r",
|
|
@@ -127489,7 +129503,7 @@ data: ${JSON.stringify(msg.data)}
|
|
|
127489
129503
|
const sessionId = String(options.sessionId || "").trim();
|
|
127490
129504
|
if (!sessionId) throw new Error("sessionId is required");
|
|
127491
129505
|
const mode = options.mode || "read";
|
|
127492
|
-
const clientId = options.clientId || `raw-terminal-${process.pid}-${(0,
|
|
129506
|
+
const clientId = options.clientId || `raw-terminal-${process.pid}-${(0, import_crypto17.randomUUID)().slice(0, 8)}`;
|
|
127493
129507
|
const client = options.client || new import_session_host_core12.SessionHostClient({ endpoint: options.endpoint });
|
|
127494
129508
|
await client.connect();
|
|
127495
129509
|
const attachResponse = await client.request({
|
|
@@ -128253,7 +130267,7 @@ data: ${JSON.stringify(msg.data)}
|
|
|
128253
130267
|
init_mesh_reconcile_loop();
|
|
128254
130268
|
var import_fs19 = require("fs");
|
|
128255
130269
|
var import_path20 = require("path");
|
|
128256
|
-
var
|
|
130270
|
+
var import_crypto18 = require("crypto");
|
|
128257
130271
|
init_logger();
|
|
128258
130272
|
init_mesh_ledger();
|
|
128259
130273
|
init_mesh_runtime_store();
|
|
@@ -128287,7 +130301,7 @@ data: ${JSON.stringify(msg.data)}
|
|
|
128287
130301
|
try {
|
|
128288
130302
|
const fingerprint = buildPendingEventFingerprint(event);
|
|
128289
130303
|
MeshRuntimeStore.getInstance().insertPendingEvent({
|
|
128290
|
-
id: (0,
|
|
130304
|
+
id: (0, import_crypto18.randomUUID)(),
|
|
128291
130305
|
meshId: event.meshId,
|
|
128292
130306
|
coordinatorDaemonId: event.targetCoordinatorDaemonId ?? null,
|
|
128293
130307
|
event: event.event,
|
|
@@ -128408,6 +130422,73 @@ data: ${JSON.stringify(msg.data)}
|
|
|
128408
130422
|
}
|
|
128409
130423
|
return newlyFrozen;
|
|
128410
130424
|
}
|
|
130425
|
+
var import_node_perf_hooks = require("perf_hooks");
|
|
130426
|
+
init_logger();
|
|
130427
|
+
var EVENT_LOOP_MONITOR_INTERVAL_ENV = "ADHDEV_EVENT_LOOP_MONITOR_INTERVAL_MS";
|
|
130428
|
+
var EVENT_LOOP_LAG_WARN_ENV = "ADHDEV_EVENT_LOOP_LAG_WARN_MS";
|
|
130429
|
+
var DEFAULT_EVENT_LOOP_MONITOR_INTERVAL_MS = 1e4;
|
|
130430
|
+
var DEFAULT_EVENT_LOOP_LAG_WARN_MS = 5e3;
|
|
130431
|
+
function parseMs(raw, fallback) {
|
|
130432
|
+
if (raw === void 0 || raw.trim() === "") return fallback;
|
|
130433
|
+
const parsed = Number.parseInt(raw, 10);
|
|
130434
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
|
|
130435
|
+
}
|
|
130436
|
+
function resolveEventLoopMonitorIntervalMs(env2 = process.env) {
|
|
130437
|
+
return parseMs(env2[EVENT_LOOP_MONITOR_INTERVAL_ENV], DEFAULT_EVENT_LOOP_MONITOR_INTERVAL_MS);
|
|
130438
|
+
}
|
|
130439
|
+
function resolveEventLoopLagWarnMs(env2 = process.env) {
|
|
130440
|
+
return parseMs(env2[EVENT_LOOP_LAG_WARN_ENV], DEFAULT_EVENT_LOOP_LAG_WARN_MS);
|
|
130441
|
+
}
|
|
130442
|
+
function defaultLogFn(level, message) {
|
|
130443
|
+
if (level === "warn") LOG2.warn("EventLoop", message);
|
|
130444
|
+
else LOG2.debug("EventLoop", message);
|
|
130445
|
+
}
|
|
130446
|
+
function startEventLoopMonitor(opts) {
|
|
130447
|
+
const intervalMs = opts?.intervalMs ?? resolveEventLoopMonitorIntervalMs();
|
|
130448
|
+
const warnMs = opts?.warnMs ?? resolveEventLoopLagWarnMs();
|
|
130449
|
+
const logFn = opts?.logFn ?? defaultLogFn;
|
|
130450
|
+
if (intervalMs <= 0) return { stop: () => {
|
|
130451
|
+
} };
|
|
130452
|
+
let histogram;
|
|
130453
|
+
try {
|
|
130454
|
+
histogram = (0, import_node_perf_hooks.monitorEventLoopDelay)({ resolution: 20 });
|
|
130455
|
+
histogram.enable();
|
|
130456
|
+
} catch {
|
|
130457
|
+
return { stop: () => {
|
|
130458
|
+
} };
|
|
130459
|
+
}
|
|
130460
|
+
let expectedTickAt = null;
|
|
130461
|
+
const timer = setInterval(() => {
|
|
130462
|
+
try {
|
|
130463
|
+
const now = Date.now();
|
|
130464
|
+
const driftMs = expectedTickAt === null ? 0 : Math.max(0, now - expectedTickAt);
|
|
130465
|
+
expectedTickAt = now + intervalMs;
|
|
130466
|
+
const maxMs = histogram.max / 1e6;
|
|
130467
|
+
const p99Ms = histogram.percentile(99) / 1e6;
|
|
130468
|
+
const meanMs = histogram.mean / 1e6;
|
|
130469
|
+
histogram.reset();
|
|
130470
|
+
const worstMs = Math.max(driftMs, maxMs);
|
|
130471
|
+
const message = `event-loop lag: drift=${driftMs.toFixed(0)}ms max=${maxMs.toFixed(0)}ms p99=${p99Ms.toFixed(0)}ms mean=${meanMs.toFixed(1)}ms over ${intervalMs}ms window`;
|
|
130472
|
+
if (worstMs >= warnMs) {
|
|
130473
|
+
logFn("warn", `${message} \u2014 EXCEEDS ${warnMs}ms: the whole daemon process was frozen/unscheduled (machine saturation), not one slow handler. Expect IPC probe timeouts and reconcile-tick gaps in this window.`);
|
|
130474
|
+
} else {
|
|
130475
|
+
logFn("debug", message);
|
|
130476
|
+
}
|
|
130477
|
+
} catch {
|
|
130478
|
+
}
|
|
130479
|
+
}, intervalMs);
|
|
130480
|
+
expectedTickAt = Date.now() + intervalMs;
|
|
130481
|
+
timer.unref?.();
|
|
130482
|
+
return {
|
|
130483
|
+
stop: () => {
|
|
130484
|
+
clearInterval(timer);
|
|
130485
|
+
try {
|
|
130486
|
+
histogram.disable();
|
|
130487
|
+
} catch {
|
|
130488
|
+
}
|
|
130489
|
+
}
|
|
130490
|
+
};
|
|
130491
|
+
}
|
|
128411
130492
|
init_require_whitelist();
|
|
128412
130493
|
async function initDaemonComponents2(config2) {
|
|
128413
130494
|
applyProcessHardening();
|
|
@@ -128707,6 +130788,7 @@ ${upgradeFailureNotice.notice}${supersededHint}`);
|
|
|
128707
130788
|
refreshQuotaCacheOnBoot(isQuotaProviderEnabled);
|
|
128708
130789
|
});
|
|
128709
130790
|
setImmediate(() => void router.resumePendingRefineJobsOnStartup());
|
|
130791
|
+
components.eventLoopMonitor = startEventLoopMonitor();
|
|
128710
130792
|
setImmediate(() => router.resumeDeferredRestartsOnStartup());
|
|
128711
130793
|
return components;
|
|
128712
130794
|
}
|
|
@@ -128736,10 +130818,15 @@ ${upgradeFailureNotice.notice}${supersededHint}`);
|
|
|
128736
130818
|
meshReconcileLoop,
|
|
128737
130819
|
quotaRefreshLoop,
|
|
128738
130820
|
quotaEventRefresh,
|
|
128739
|
-
providerStalenessProbe
|
|
130821
|
+
providerStalenessProbe,
|
|
130822
|
+
eventLoopMonitor
|
|
128740
130823
|
} = components;
|
|
128741
130824
|
poller.stop();
|
|
128742
130825
|
cdpInitializer.stop();
|
|
130826
|
+
try {
|
|
130827
|
+
eventLoopMonitor?.stop();
|
|
130828
|
+
} catch {
|
|
130829
|
+
}
|
|
128743
130830
|
try {
|
|
128744
130831
|
meshReconcileLoop?.stop();
|
|
128745
130832
|
} catch {
|