@adhdev/daemon-standalone 1.0.56-rc.3 → 1.0.56-rc.4
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 +2128 -92
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/vendor/mcp-server/index.js +597 -16
- 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 ? "0ac5de5a5c119ce79644b72b5cf2e44ae1afc677" : void 0) ?? "unknown";
|
|
36624
|
+
const commitShort = readInjected(true ? "0ac5de5a" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
36625
|
+
const version2 = readInjected(true ? "1.0.56-rc.4" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
36626
|
+
const builtAt = readInjected(true ? "2026-08-19T02:15:04.773Z" : void 0);
|
|
36617
36627
|
cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
|
|
36618
36628
|
return cached2;
|
|
36619
36629
|
}
|
|
@@ -42845,6 +42855,10 @@ ${error48.message || ""}`;
|
|
|
42845
42855
|
"mesh_enqueue_task",
|
|
42846
42856
|
"mesh_enqueue_batch",
|
|
42847
42857
|
"mesh_view_queue",
|
|
42858
|
+
// GRAPH-ORCHESTRATION Phase E — the coordinator gate + graph view surface.
|
|
42859
|
+
"mesh_graph_view",
|
|
42860
|
+
"mesh_graph_gate_claim",
|
|
42861
|
+
"mesh_graph_gate_release",
|
|
42848
42862
|
"mesh_queue_cancel",
|
|
42849
42863
|
"mesh_queue_requeue",
|
|
42850
42864
|
"mesh_send_task",
|
|
@@ -43952,6 +43966,10 @@ ${error48.message || ""}`;
|
|
|
43952
43966
|
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
43967
|
}
|
|
43954
43968
|
const reviewRecommended = args.metadataEvent.reviewRecommended === true;
|
|
43969
|
+
const timedOutNoResponse = args.metadataEvent.completionDiagnostic && typeof args.metadataEvent.completionDiagnostic === "object" && args.metadataEvent.completionDiagnostic.timedOutWithoutFinalAssistant === true;
|
|
43970
|
+
if (timedOutNoResponse) {
|
|
43971
|
+
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.`;
|
|
43972
|
+
}
|
|
43955
43973
|
const weakCompletion = isWeakCompletionMetadata(args.metadataEvent);
|
|
43956
43974
|
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
43975
|
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 +45370,40 @@ Next step: ${nextStep}`;
|
|
|
45352
45370
|
sessionOrderChains = /* @__PURE__ */ new Map();
|
|
45353
45371
|
}
|
|
45354
45372
|
});
|
|
45373
|
+
function newMeshGraphId() {
|
|
45374
|
+
return (0, import_crypto6.randomUUID)();
|
|
45375
|
+
}
|
|
45376
|
+
function newMeshGraphNodeId() {
|
|
45377
|
+
return (0, import_crypto6.randomUUID)();
|
|
45378
|
+
}
|
|
45379
|
+
function newMeshGraphGateId() {
|
|
45380
|
+
return (0, import_crypto6.randomUUID)();
|
|
45381
|
+
}
|
|
45355
45382
|
function newMeshGraphOutboxId() {
|
|
45356
45383
|
return (0, import_crypto6.randomUUID)();
|
|
45357
45384
|
}
|
|
45358
45385
|
var import_crypto6;
|
|
45386
|
+
var MESH_GRAPH_SCHEMA_VERSION;
|
|
45387
|
+
var MESH_GRAPH_ON_UPSTREAM_SKIP_POLICIES;
|
|
45388
|
+
var MESH_GRAPH_GATE_ACTIONS;
|
|
45389
|
+
var MESH_GRAPH_GATE_TIMEOUT_POLICIES;
|
|
45390
|
+
var MESH_GRAPH_ON_DEPENDENCY_FAILURE_POLICIES;
|
|
45359
45391
|
var init_mesh_graph_types = __esm2({
|
|
45360
45392
|
"src/mesh/mesh-graph-types.ts"() {
|
|
45361
45393
|
"use strict";
|
|
45362
45394
|
import_crypto6 = require("crypto");
|
|
45395
|
+
MESH_GRAPH_SCHEMA_VERSION = 2;
|
|
45396
|
+
MESH_GRAPH_ON_UPSTREAM_SKIP_POLICIES = ["skip", "omit_dependency"];
|
|
45397
|
+
MESH_GRAPH_GATE_ACTIONS = [
|
|
45398
|
+
"refinery",
|
|
45399
|
+
"approval",
|
|
45400
|
+
"ci_wait",
|
|
45401
|
+
"publish",
|
|
45402
|
+
"deploy",
|
|
45403
|
+
"custom"
|
|
45404
|
+
];
|
|
45405
|
+
MESH_GRAPH_GATE_TIMEOUT_POLICIES = ["hold", "cancel_downstream", "fail_graph"];
|
|
45406
|
+
MESH_GRAPH_ON_DEPENDENCY_FAILURE_POLICIES = ["block", "cancel"];
|
|
45363
45407
|
}
|
|
45364
45408
|
});
|
|
45365
45409
|
function maskKeepTail(token) {
|
|
@@ -45895,6 +45939,188 @@ ${blocks.join("\n\n")}`;
|
|
|
45895
45939
|
init_mesh_graph_workspace_identity();
|
|
45896
45940
|
}
|
|
45897
45941
|
});
|
|
45942
|
+
function parseOnDependencyFailurePolicy(value) {
|
|
45943
|
+
if (value === void 0 || value === null || value === "") return "block";
|
|
45944
|
+
if (typeof value === "string" && MESH_GRAPH_ON_DEPENDENCY_FAILURE_POLICIES.includes(value)) {
|
|
45945
|
+
return value;
|
|
45946
|
+
}
|
|
45947
|
+
throw new MeshGraphPolicyError(
|
|
45948
|
+
`invalid_on_dependency_failure: must be 'block' or 'cancel' (got ${JSON.stringify(value)}). Invalid values are rejected; they do not silently become 'block'.`
|
|
45949
|
+
);
|
|
45950
|
+
}
|
|
45951
|
+
function resolveOnDependencyFailurePolicy(value) {
|
|
45952
|
+
return value === "cancel" ? "cancel" : "block";
|
|
45953
|
+
}
|
|
45954
|
+
function parseGraphPolicy(policyJson) {
|
|
45955
|
+
const parsed = safeParseObject(policyJson);
|
|
45956
|
+
const raw = parsed?.on_dependency_failure ?? parsed?.onDependencyFailure;
|
|
45957
|
+
return { on_dependency_failure: parseOnDependencyFailurePolicy(raw) };
|
|
45958
|
+
}
|
|
45959
|
+
function projectGraphPublicPolicy(policyJson) {
|
|
45960
|
+
try {
|
|
45961
|
+
return parseGraphPolicy(policyJson);
|
|
45962
|
+
} catch (e) {
|
|
45963
|
+
return {
|
|
45964
|
+
on_dependency_failure: "block",
|
|
45965
|
+
policyInvalid: e instanceof Error ? e.message : String(e)
|
|
45966
|
+
};
|
|
45967
|
+
}
|
|
45968
|
+
}
|
|
45969
|
+
function serializeGraphPolicy(policy) {
|
|
45970
|
+
return JSON.stringify({ on_dependency_failure: policy.on_dependency_failure });
|
|
45971
|
+
}
|
|
45972
|
+
function isGraphSkipPlaceholder(entry) {
|
|
45973
|
+
if (!entry) return false;
|
|
45974
|
+
const reason = entry.blockedReason ?? entry.cancelReason ?? "";
|
|
45975
|
+
return entry.status === "cancelled" && reason.startsWith(MESH_GRAPH_SKIP_BLOCK_PREFIX);
|
|
45976
|
+
}
|
|
45977
|
+
function parseLegacyDependencyFailedPredecessor(reason) {
|
|
45978
|
+
if (typeof reason !== "string") return null;
|
|
45979
|
+
if (!reason.startsWith(LEGACY_DEPENDENCY_FAILED_PREFIX)) return null;
|
|
45980
|
+
const rest = reason.slice(LEGACY_DEPENDENCY_FAILED_PREFIX.length);
|
|
45981
|
+
if (!rest || rest.startsWith(":")) return null;
|
|
45982
|
+
return rest;
|
|
45983
|
+
}
|
|
45984
|
+
function deriveDependencyFailures(dependsOn, statusById, depMetaById) {
|
|
45985
|
+
const deps = Array.isArray(dependsOn) ? dependsOn : [];
|
|
45986
|
+
const failures = [];
|
|
45987
|
+
for (const taskId of deps) {
|
|
45988
|
+
const meta3 = depMetaById?.get(taskId);
|
|
45989
|
+
const status = statusById.get(taskId) ?? meta3?.status;
|
|
45990
|
+
if (isGraphSkipPlaceholder({ status, blockedReason: meta3?.blockedReason, cancelReason: meta3?.cancelReason })) {
|
|
45991
|
+
continue;
|
|
45992
|
+
}
|
|
45993
|
+
if (status !== "failed" && status !== "cancelled") continue;
|
|
45994
|
+
const reason = meta3?.cancelReason ?? meta3?.blockedReason;
|
|
45995
|
+
failures.push({
|
|
45996
|
+
taskId,
|
|
45997
|
+
status,
|
|
45998
|
+
...reason ? { reason } : {}
|
|
45999
|
+
});
|
|
46000
|
+
}
|
|
46001
|
+
return failures;
|
|
46002
|
+
}
|
|
46003
|
+
function classifyGraphRollup(nodes) {
|
|
46004
|
+
const gates = nodes.filter((n) => n.kind === "coordinator_gate");
|
|
46005
|
+
const workers = nodes.filter((n) => n.kind !== "coordinator_gate");
|
|
46006
|
+
if (gates.some((g) => !GATE_SETTLED.has(g.state))) return null;
|
|
46007
|
+
if (workers.some((w) => !WORKER_SETTLED.has(w.state))) return null;
|
|
46008
|
+
if (workers.some((w) => w.state === "failed")) return "failed";
|
|
46009
|
+
if (workers.some((w) => w.state === "cancelled") || gates.some((g) => g.state === "cancelled" || g.state === "expired")) {
|
|
46010
|
+
return "cancelled";
|
|
46011
|
+
}
|
|
46012
|
+
if (workers.length === 0 && gates.length === 0) return "completed";
|
|
46013
|
+
if (workers.every((w) => SUCCESS_TERMINAL.has(w.state)) && gates.every((g) => g.state === "released" || SUCCESS_TERMINAL.has(g.state))) {
|
|
46014
|
+
return "completed";
|
|
46015
|
+
}
|
|
46016
|
+
return "completed";
|
|
46017
|
+
}
|
|
46018
|
+
function applyGraphCancelCascade(store, nodes, edges, origin, nowIso, updateNodeState) {
|
|
46019
|
+
const byId = new Map(nodes.map((n) => [n.nodeId, n]));
|
|
46020
|
+
const frontier = [origin.nodeId];
|
|
46021
|
+
const seen = new Set(frontier);
|
|
46022
|
+
const cancelledNodeIds = [];
|
|
46023
|
+
const cancelledTaskIds = [];
|
|
46024
|
+
const sourceTaskId = origin.queueTaskId ?? origin.nodeId;
|
|
46025
|
+
const sourceRef = origin.ref ?? origin.nodeId;
|
|
46026
|
+
while (frontier.length > 0) {
|
|
46027
|
+
const currentId = frontier.pop();
|
|
46028
|
+
const outgoing = edges.filter((e) => e.fromNodeId === currentId && e.kind !== "gate");
|
|
46029
|
+
for (const edge of outgoing) {
|
|
46030
|
+
const target = byId.get(edge.toNodeId);
|
|
46031
|
+
if (!target || seen.has(target.nodeId)) continue;
|
|
46032
|
+
seen.add(target.nodeId);
|
|
46033
|
+
if (target.kind !== "worker_task") continue;
|
|
46034
|
+
if (target.state === "skipped") continue;
|
|
46035
|
+
if (!CANCELABLE_NODE_STATES.has(target.state)) continue;
|
|
46036
|
+
const reason = `${LEGACY_DEPENDENCY_FAILED_PREFIX}${sourceRef}`;
|
|
46037
|
+
updateNodeState(target, reason);
|
|
46038
|
+
target.state = "cancelled";
|
|
46039
|
+
target.failureReason = reason;
|
|
46040
|
+
cancelledNodeIds.push(target.nodeId);
|
|
46041
|
+
if (target.queueTaskId) {
|
|
46042
|
+
const entry = store.findQueueEntryById(target.meshId, target.queueTaskId);
|
|
46043
|
+
if (entry && entry.status === "pending") {
|
|
46044
|
+
cancelledTaskIds.push(entry.id);
|
|
46045
|
+
}
|
|
46046
|
+
}
|
|
46047
|
+
frontier.push(target.nodeId);
|
|
46048
|
+
}
|
|
46049
|
+
}
|
|
46050
|
+
return { cancelledNodeIds, cancelledTaskIds };
|
|
46051
|
+
}
|
|
46052
|
+
function migrateLegacyDependencyFailedQueueBlocks(db) {
|
|
46053
|
+
let rows;
|
|
46054
|
+
try {
|
|
46055
|
+
rows = db.prepare(`SELECT id, mesh_id, payload FROM mesh_queue`).all();
|
|
46056
|
+
} catch {
|
|
46057
|
+
return [];
|
|
46058
|
+
}
|
|
46059
|
+
const cleared = [];
|
|
46060
|
+
const update = db.prepare(`UPDATE mesh_queue SET payload = ?, updated_at = ? WHERE id = ? AND mesh_id = ?`);
|
|
46061
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
46062
|
+
for (const row of rows) {
|
|
46063
|
+
let entry;
|
|
46064
|
+
try {
|
|
46065
|
+
entry = JSON.parse(row.payload);
|
|
46066
|
+
} catch {
|
|
46067
|
+
continue;
|
|
46068
|
+
}
|
|
46069
|
+
const predecessorId = parseLegacyDependencyFailedPredecessor(entry.blockedReason);
|
|
46070
|
+
if (!predecessorId) continue;
|
|
46071
|
+
delete entry.blockedReason;
|
|
46072
|
+
update.run(JSON.stringify(entry), nowIso, row.id, row.mesh_id);
|
|
46073
|
+
cleared.push({ taskId: row.id, meshId: row.mesh_id, predecessorId });
|
|
46074
|
+
}
|
|
46075
|
+
return cleared;
|
|
46076
|
+
}
|
|
46077
|
+
function safeParseObject(text) {
|
|
46078
|
+
if (!text) return null;
|
|
46079
|
+
try {
|
|
46080
|
+
const value = JSON.parse(text);
|
|
46081
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
46082
|
+
return value;
|
|
46083
|
+
} catch {
|
|
46084
|
+
return null;
|
|
46085
|
+
}
|
|
46086
|
+
}
|
|
46087
|
+
var MESH_ON_DEPENDENCY_FAILURE_PUBLIC_TEXT;
|
|
46088
|
+
var MESH_GRAPH_SKIP_BLOCK_PREFIX;
|
|
46089
|
+
var LEGACY_DEPENDENCY_FAILED_PREFIX;
|
|
46090
|
+
var MeshGraphPolicyError;
|
|
46091
|
+
var SUCCESS_TERMINAL;
|
|
46092
|
+
var WORKER_SETTLED;
|
|
46093
|
+
var GATE_SETTLED;
|
|
46094
|
+
var CANCELABLE_NODE_STATES;
|
|
46095
|
+
var init_mesh_graph_derived_failure = __esm2({
|
|
46096
|
+
"src/mesh/mesh-graph-derived-failure.ts"() {
|
|
46097
|
+
"use strict";
|
|
46098
|
+
init_mesh_graph_types();
|
|
46099
|
+
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.";
|
|
46100
|
+
MESH_GRAPH_SKIP_BLOCK_PREFIX = "graph_skipped:";
|
|
46101
|
+
LEGACY_DEPENDENCY_FAILED_PREFIX = "dependency_failed:";
|
|
46102
|
+
MeshGraphPolicyError = class extends Error {
|
|
46103
|
+
constructor(message, code = "invalid_on_dependency_failure") {
|
|
46104
|
+
super(message);
|
|
46105
|
+
this.code = code;
|
|
46106
|
+
this.name = "MeshGraphPolicyError";
|
|
46107
|
+
}
|
|
46108
|
+
};
|
|
46109
|
+
SUCCESS_TERMINAL = /* @__PURE__ */ new Set(["completed", "released", "skipped"]);
|
|
46110
|
+
WORKER_SETTLED = /* @__PURE__ */ new Set([
|
|
46111
|
+
"completed",
|
|
46112
|
+
"skipped",
|
|
46113
|
+
"failed",
|
|
46114
|
+
"cancelled"
|
|
46115
|
+
]);
|
|
46116
|
+
GATE_SETTLED = /* @__PURE__ */ new Set(["released", "cancelled", "expired"]);
|
|
46117
|
+
CANCELABLE_NODE_STATES = /* @__PURE__ */ new Set([
|
|
46118
|
+
"declared",
|
|
46119
|
+
"blocked",
|
|
46120
|
+
"materialized"
|
|
46121
|
+
]);
|
|
46122
|
+
}
|
|
46123
|
+
});
|
|
45898
46124
|
function graphMaterializationBlockReason(nodeId, materializationVersion) {
|
|
45899
46125
|
return `${GRAPH_BLOCK_PREFIX}${nodeId}:${materializationVersion}`;
|
|
45900
46126
|
}
|
|
@@ -45907,6 +46133,14 @@ ${blocks.join("\n\n")}`;
|
|
|
45907
46133
|
if (!Number.isInteger(version2) || version2 < 0) return null;
|
|
45908
46134
|
return { nodeId: rest.slice(0, sep15), version: version2 };
|
|
45909
46135
|
}
|
|
46136
|
+
function coordinatorGateBlockReason(gateId) {
|
|
46137
|
+
return `${GATE_BLOCK_PREFIX}${gateId}`;
|
|
46138
|
+
}
|
|
46139
|
+
function parseCoordinatorGateBlock(reason) {
|
|
46140
|
+
if (!reason || !reason.startsWith(GATE_BLOCK_PREFIX)) return null;
|
|
46141
|
+
const gateId = reason.slice(GATE_BLOCK_PREFIX.length);
|
|
46142
|
+
return gateId.length > 0 ? { gateId } : null;
|
|
46143
|
+
}
|
|
45910
46144
|
function registerMeshGraphQueueWakeHandler(handler) {
|
|
45911
46145
|
queueWakeHandler = handler;
|
|
45912
46146
|
}
|
|
@@ -46030,11 +46264,59 @@ ${blocks.join("\n\n")}`;
|
|
|
46030
46264
|
});
|
|
46031
46265
|
const nodes = graphStore.listNodes(node.graphId);
|
|
46032
46266
|
const byId = new Map(nodes.map((n) => [n.nodeId, n]));
|
|
46033
|
-
|
|
46034
|
-
|
|
46035
|
-
|
|
46036
|
-
|
|
46037
|
-
|
|
46267
|
+
const graph = graphStore.getGraph(node.graphId);
|
|
46268
|
+
const failurePolicy = projectGraphPublicPolicy(graph?.policyJson).on_dependency_failure;
|
|
46269
|
+
if ((terminal.status === "failed" || terminal.status === "cancelled") && failurePolicy === "cancel") {
|
|
46270
|
+
const edges = graphStore.listEdges(node.graphId);
|
|
46271
|
+
const cascade = applyGraphCancelCascade(
|
|
46272
|
+
store,
|
|
46273
|
+
nodes,
|
|
46274
|
+
edges,
|
|
46275
|
+
node,
|
|
46276
|
+
nowIso,
|
|
46277
|
+
(target, reason) => {
|
|
46278
|
+
graphStore.updateNodeState(target.graphId, target.nodeId, "cancelled", nowIso, {
|
|
46279
|
+
failureReason: reason
|
|
46280
|
+
});
|
|
46281
|
+
}
|
|
46282
|
+
);
|
|
46283
|
+
for (const taskId of cascade.cancelledTaskIds) {
|
|
46284
|
+
const entry = store.findQueueEntryById(node.meshId, taskId);
|
|
46285
|
+
if (entry && entry.status === "pending") {
|
|
46286
|
+
entry.status = "cancelled";
|
|
46287
|
+
entry.cancelledAt = nowIso;
|
|
46288
|
+
entry.cancelReason = `dependency_failed:${terminal.taskId}`;
|
|
46289
|
+
store.updateQueueEntry(entry);
|
|
46290
|
+
}
|
|
46291
|
+
}
|
|
46292
|
+
for (const nodeId of cascade.cancelledNodeIds) {
|
|
46293
|
+
const target = byId.get(nodeId);
|
|
46294
|
+
outbox.push({
|
|
46295
|
+
kind: "graph_node_cancelled",
|
|
46296
|
+
payload: {
|
|
46297
|
+
graphId: node.graphId,
|
|
46298
|
+
nodeId,
|
|
46299
|
+
ref: target?.ref,
|
|
46300
|
+
taskId: target?.queueTaskId,
|
|
46301
|
+
reason: `dependency_failed:${node.ref ?? node.nodeId}`
|
|
46302
|
+
}
|
|
46303
|
+
});
|
|
46304
|
+
}
|
|
46305
|
+
} else if (terminal.status === "failed" || terminal.status === "cancelled") {
|
|
46306
|
+
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");
|
|
46307
|
+
outbox.push({
|
|
46308
|
+
kind: "graph_derived_failure",
|
|
46309
|
+
payload: {
|
|
46310
|
+
graphId: node.graphId,
|
|
46311
|
+
nodeId: node.nodeId,
|
|
46312
|
+
ref: node.ref,
|
|
46313
|
+
taskId: terminal.taskId,
|
|
46314
|
+
outcome: terminal.status,
|
|
46315
|
+
policy: "block",
|
|
46316
|
+
dependentNodeIds: dependents.map((d) => d.nodeId),
|
|
46317
|
+
dependentTaskIds: dependents.map((d) => d.queueTaskId).filter(Boolean)
|
|
46318
|
+
}
|
|
46319
|
+
});
|
|
46038
46320
|
}
|
|
46039
46321
|
const materialized = [];
|
|
46040
46322
|
if (terminal.status === "completed") {
|
|
@@ -46047,7 +46329,12 @@ ${blocks.join("\n\n")}`;
|
|
|
46047
46329
|
);
|
|
46048
46330
|
for (const targetId of downstreamIds) {
|
|
46049
46331
|
const target = byId.get(targetId);
|
|
46050
|
-
if (!target
|
|
46332
|
+
if (!target) continue;
|
|
46333
|
+
if (target.kind === "coordinator_gate") {
|
|
46334
|
+
maybeOpenCoordinatorGate(store, target, edges, byId, nowIso);
|
|
46335
|
+
continue;
|
|
46336
|
+
}
|
|
46337
|
+
if (target.kind !== "worker_task" || !target.queueTaskId) continue;
|
|
46051
46338
|
if (target.state !== "declared" && target.state !== "blocked") continue;
|
|
46052
46339
|
const outcome = settleDownstreamNode(store, target, edges, byId, nowIso);
|
|
46053
46340
|
if (outcome.kind === "materialized") {
|
|
@@ -46091,10 +46378,12 @@ ${blocks.join("\n\n")}`;
|
|
|
46091
46378
|
}
|
|
46092
46379
|
frontier = nextFrontier;
|
|
46093
46380
|
}
|
|
46094
|
-
|
|
46095
|
-
|
|
46096
|
-
|
|
46097
|
-
|
|
46381
|
+
}
|
|
46382
|
+
const rolled = classifyGraphRollup(graphStore.listNodes(node.graphId));
|
|
46383
|
+
if (rolled && graph?.status !== rolled) {
|
|
46384
|
+
graphStore.updateGraphStatus(node.graphId, rolled, nowIso, true);
|
|
46385
|
+
const kind = rolled === "completed" ? "graph_completed" : rolled === "failed" ? "graph_failed" : "graph_cancelled";
|
|
46386
|
+
outbox.push({ kind, payload: { graphId: node.graphId, status: rolled } });
|
|
46098
46387
|
}
|
|
46099
46388
|
if (materialized.length > 0) {
|
|
46100
46389
|
outbox.push({ kind: "queue_wake", payload: { meshId: node.meshId, reason: "graph_materialization", graphId: node.graphId } });
|
|
@@ -46117,9 +46406,95 @@ ${blocks.join("\n\n")}`;
|
|
|
46117
46406
|
function isTerminalEquivalent(n) {
|
|
46118
46407
|
return n.state === "completed" || n.state === "released" || n.state === "skipped";
|
|
46119
46408
|
}
|
|
46409
|
+
function maybeOpenCoordinatorGate(store, gateNode, edges, byId, nowIso) {
|
|
46410
|
+
const graphStore = store.graphStore();
|
|
46411
|
+
if (gateNode.state !== "declared" && gateNode.state !== "blocked") return false;
|
|
46412
|
+
const gate = graphStore.findGateByNodeId(gateNode.graphId, gateNode.nodeId);
|
|
46413
|
+
if (!gate || gate.state !== "declared") return false;
|
|
46414
|
+
const incoming = edges.filter((e) => e.toNodeId === gateNode.nodeId);
|
|
46415
|
+
const satisfied = incoming.every((e) => {
|
|
46416
|
+
const sourceState = byId.get(e.fromNodeId)?.state;
|
|
46417
|
+
return sourceState === "completed" || sourceState === "released" || sourceState === "skipped" && e.omitOnSkip;
|
|
46418
|
+
});
|
|
46419
|
+
if (!satisfied) return false;
|
|
46420
|
+
const spec = safeParseJson(gateNode.baseSpecJson);
|
|
46421
|
+
const deadlineSeconds = typeof spec?.deadline_seconds === "number" ? spec.deadline_seconds : void 0;
|
|
46422
|
+
const deadlineAt = deadlineSeconds && deadlineSeconds > 0 ? new Date(Date.parse(nowIso) + deadlineSeconds * 1e3).toISOString() : void 0;
|
|
46423
|
+
graphStore.patchGate(gate.gateId, {
|
|
46424
|
+
state: "awaiting_coordinator",
|
|
46425
|
+
...deadlineAt ? { deadlineAt } : {}
|
|
46426
|
+
}, nowIso);
|
|
46427
|
+
graphStore.updateNodeState(gateNode.graphId, gateNode.nodeId, "awaiting_coordinator", nowIso);
|
|
46428
|
+
gateNode.state = "awaiting_coordinator";
|
|
46429
|
+
byId.set(gateNode.nodeId, gateNode);
|
|
46430
|
+
const block2 = coordinatorGateBlockReason(gate.gateId);
|
|
46431
|
+
for (const edge of edges.filter((e) => e.fromNodeId === gateNode.nodeId)) {
|
|
46432
|
+
const target = byId.get(edge.toNodeId);
|
|
46433
|
+
if (!target || target.kind !== "worker_task" || !target.queueTaskId) continue;
|
|
46434
|
+
if (target.state !== "declared" && target.state !== "blocked") continue;
|
|
46435
|
+
if (target.state !== "blocked") {
|
|
46436
|
+
graphStore.updateNodeState(target.graphId, target.nodeId, "blocked", nowIso);
|
|
46437
|
+
target.state = "blocked";
|
|
46438
|
+
byId.set(target.nodeId, target);
|
|
46439
|
+
}
|
|
46440
|
+
const entry = store.findQueueEntryById(target.meshId, target.queueTaskId);
|
|
46441
|
+
if (!entry || entry.status !== "pending") continue;
|
|
46442
|
+
const owned = !entry.blockedReason || parseGraphMaterializationBlock(entry.blockedReason) !== null || parseCoordinatorGateBlock(entry.blockedReason) !== null || entry.blockedReason.startsWith("materialization_error:");
|
|
46443
|
+
if (owned && entry.blockedReason !== block2) {
|
|
46444
|
+
entry.blockedReason = block2;
|
|
46445
|
+
store.updateQueueEntry(entry);
|
|
46446
|
+
}
|
|
46447
|
+
}
|
|
46448
|
+
graphStore.insertOutboxEvent({
|
|
46449
|
+
id: newMeshGraphOutboxId(),
|
|
46450
|
+
meshId: gateNode.meshId,
|
|
46451
|
+
graphId: gateNode.graphId,
|
|
46452
|
+
kind: "graph_gate_awaiting",
|
|
46453
|
+
payload: JSON.stringify({
|
|
46454
|
+
graphId: gateNode.graphId,
|
|
46455
|
+
gateId: gate.gateId,
|
|
46456
|
+
nodeId: gateNode.nodeId,
|
|
46457
|
+
ref: gate.ref ?? gateNode.ref,
|
|
46458
|
+
action: gate.action,
|
|
46459
|
+
instructions: gate.instructions,
|
|
46460
|
+
deadlineAt
|
|
46461
|
+
}),
|
|
46462
|
+
status: "pending",
|
|
46463
|
+
attemptCount: 0,
|
|
46464
|
+
createdAt: nowIso,
|
|
46465
|
+
updatedAt: nowIso
|
|
46466
|
+
});
|
|
46467
|
+
const graph = graphStore.getGraph(gateNode.graphId);
|
|
46468
|
+
if (graph?.status === "active") {
|
|
46469
|
+
graphStore.updateGraphStatus(gateNode.graphId, "waiting_gate", nowIso);
|
|
46470
|
+
}
|
|
46471
|
+
return true;
|
|
46472
|
+
}
|
|
46473
|
+
function gateOutcomeOutputForNode(graphStore, gateNode) {
|
|
46474
|
+
const gate = graphStore.findGateByNodeId(gateNode.graphId, gateNode.nodeId);
|
|
46475
|
+
if (!gate || gate.state !== "released") return null;
|
|
46476
|
+
const stored = typeof gate.releaseEvidenceJson === "string" ? safeParseJson(gate.releaseEvidenceJson) : void 0;
|
|
46477
|
+
return {
|
|
46478
|
+
ref: gateNode.ref ?? gate.ref ?? gateNode.nodeId,
|
|
46479
|
+
envelope: {
|
|
46480
|
+
gate_outcome: gate.releaseOutcome,
|
|
46481
|
+
action: gate.action,
|
|
46482
|
+
gate_id: gate.gateId,
|
|
46483
|
+
ref: gate.ref ?? gateNode.ref,
|
|
46484
|
+
result: stored?.result,
|
|
46485
|
+
evidence: stored?.evidence,
|
|
46486
|
+
evidence_digest: gate.releaseEvidenceDigest,
|
|
46487
|
+
released_at: gate.updatedAt
|
|
46488
|
+
}
|
|
46489
|
+
};
|
|
46490
|
+
}
|
|
46120
46491
|
function settleDownstreamNode(store, target, edges, byId, nowIso) {
|
|
46121
46492
|
const graphStore = store.graphStore();
|
|
46122
46493
|
const incoming = edges.filter((e) => e.toNodeId === target.nodeId && e.kind !== "gate");
|
|
46494
|
+
const gateIncoming = edges.filter((e) => e.toNodeId === target.nodeId && e.kind === "gate");
|
|
46495
|
+
for (const edge of gateIncoming) {
|
|
46496
|
+
if (byId.get(edge.fromNodeId)?.state !== "released") return { kind: "deferred" };
|
|
46497
|
+
}
|
|
46123
46498
|
const skippedSources = incoming.filter((e) => byId.get(e.fromNodeId)?.state === "skipped");
|
|
46124
46499
|
const propagatingSkip = skippedSources.find((e) => !e.omitOnSkip);
|
|
46125
46500
|
if (propagatingSkip) {
|
|
@@ -46132,6 +46507,12 @@ ${blocks.join("\n\n")}`;
|
|
|
46132
46507
|
const queueEntry = store.findQueueEntryById(target.meshId, target.queueTaskId);
|
|
46133
46508
|
if (!queueEntry) return { kind: "deferred" };
|
|
46134
46509
|
const outputsByRef = collectUpstreamOutputs(store, activeIncoming, byId);
|
|
46510
|
+
for (const edge of gateIncoming) {
|
|
46511
|
+
const gateNode = byId.get(edge.fromNodeId);
|
|
46512
|
+
if (!gateNode) continue;
|
|
46513
|
+
const output = gateOutcomeOutputForNode(graphStore, gateNode);
|
|
46514
|
+
if (output && !outputsByRef.has(output.ref)) outputsByRef.set(output.ref, output);
|
|
46515
|
+
}
|
|
46135
46516
|
const baseSpec = safeParseJson(target.baseSpecJson);
|
|
46136
46517
|
let conditionsHold = true;
|
|
46137
46518
|
let falseCondition;
|
|
@@ -46287,6 +46668,10 @@ ${blocks.join("\n\n")}`;
|
|
|
46287
46668
|
}
|
|
46288
46669
|
if (queueEntry.blockedReason?.startsWith("materialization_error:")) {
|
|
46289
46670
|
delete queueEntry.blockedReason;
|
|
46671
|
+
return;
|
|
46672
|
+
}
|
|
46673
|
+
if (parseCoordinatorGateBlock(queueEntry.blockedReason)) {
|
|
46674
|
+
delete queueEntry.blockedReason;
|
|
46290
46675
|
}
|
|
46291
46676
|
}
|
|
46292
46677
|
function syncNodeRowState(byId, target, state, patch) {
|
|
@@ -46375,6 +46760,7 @@ ${blocks.join("\n\n")}`;
|
|
|
46375
46760
|
return result.materialized;
|
|
46376
46761
|
}
|
|
46377
46762
|
var GRAPH_BLOCK_PREFIX;
|
|
46763
|
+
var GATE_BLOCK_PREFIX;
|
|
46378
46764
|
var queueWakeHandler;
|
|
46379
46765
|
var init_mesh_graph_transition_runner = __esm2({
|
|
46380
46766
|
"src/mesh/mesh-graph-transition-runner.ts"() {
|
|
@@ -46386,7 +46772,9 @@ ${blocks.join("\n\n")}`;
|
|
|
46386
46772
|
init_mesh_graph_types();
|
|
46387
46773
|
init_mesh_graph_input_binding();
|
|
46388
46774
|
init_mesh_graph_workspace_bind();
|
|
46775
|
+
init_mesh_graph_derived_failure();
|
|
46389
46776
|
GRAPH_BLOCK_PREFIX = "graph_materialization_pending:";
|
|
46777
|
+
GATE_BLOCK_PREFIX = "coordinator_gate:";
|
|
46390
46778
|
}
|
|
46391
46779
|
});
|
|
46392
46780
|
function formatMeshTaskModeViolations(result) {
|
|
@@ -48212,7 +48600,10 @@ ${blocks.join("\n\n")}`;
|
|
|
48212
48600
|
return record2 ? { ...record2, status: normalizeMissionStatus(record2.status), source: normalizeMissionSource(record2.source) } : null;
|
|
48213
48601
|
}
|
|
48214
48602
|
function summarizeMissionTasks(meshId, missionId) {
|
|
48215
|
-
const
|
|
48603
|
+
const queue = getQueue(meshId);
|
|
48604
|
+
const tasks = queue.filter((task) => task.missionId === missionId);
|
|
48605
|
+
const statusById = new Map(queue.map((task) => [task.id, task.status]));
|
|
48606
|
+
const depMetaById = new Map(queue.map((task) => [task.id, { blockedReason: task.blockedReason, cancelReason: task.cancelReason, status: task.status }]));
|
|
48216
48607
|
const aggregate = {
|
|
48217
48608
|
total: tasks.length,
|
|
48218
48609
|
pending: 0,
|
|
@@ -48229,7 +48620,9 @@ ${blocks.join("\n\n")}`;
|
|
|
48229
48620
|
else if (task.status === "completed") aggregate.completed += 1;
|
|
48230
48621
|
else if (task.status === "failed") aggregate.failed += 1;
|
|
48231
48622
|
else if (task.status === "cancelled") aggregate.cancelled += 1;
|
|
48232
|
-
if (task.status === "pending" && task.blockedReason
|
|
48623
|
+
if (task.status === "pending" && (task.blockedReason || deriveDependencyFailures(task.dependsOn, statusById, depMetaById).length > 0)) {
|
|
48624
|
+
aggregate.blocked += 1;
|
|
48625
|
+
}
|
|
48233
48626
|
if (task.updatedAt && (!aggregate.lastActivityAt || task.updatedAt > aggregate.lastActivityAt)) {
|
|
48234
48627
|
aggregate.lastActivityAt = task.updatedAt;
|
|
48235
48628
|
}
|
|
@@ -48425,6 +48818,7 @@ ${blocks.join("\n\n")}`;
|
|
|
48425
48818
|
init_logger();
|
|
48426
48819
|
init_mesh_runtime_store();
|
|
48427
48820
|
init_mesh_work_queue();
|
|
48821
|
+
init_mesh_graph_derived_failure();
|
|
48428
48822
|
init_mesh_task_stats();
|
|
48429
48823
|
init_mesh_ledger();
|
|
48430
48824
|
init_mesh_events_pending();
|
|
@@ -48857,13 +49251,14 @@ ${blocks.join("\n\n")}`;
|
|
|
48857
49251
|
function resolveDependencyFailurePolicy(meshId) {
|
|
48858
49252
|
try {
|
|
48859
49253
|
const policy = getMesh(meshId)?.policy ?? {};
|
|
48860
|
-
return policy.onDependencyFailure
|
|
49254
|
+
return resolveOnDependencyFailurePolicy(policy.onDependencyFailure ?? policy.on_dependency_failure);
|
|
48861
49255
|
} catch {
|
|
48862
49256
|
return "block";
|
|
48863
49257
|
}
|
|
48864
49258
|
}
|
|
48865
49259
|
function propagateDependencyFailure(meshId, failedTaskId) {
|
|
48866
49260
|
const policy = resolveDependencyFailurePolicy(meshId);
|
|
49261
|
+
if (policy !== "cancel") return [];
|
|
48867
49262
|
const store = MeshRuntimeStore.getInstance();
|
|
48868
49263
|
const cancelled = [];
|
|
48869
49264
|
const frontier = [failedTaskId];
|
|
@@ -48874,17 +49269,12 @@ ${blocks.join("\n\n")}`;
|
|
|
48874
49269
|
for (const dependent of dependents) {
|
|
48875
49270
|
if (seen.has(dependent.id)) continue;
|
|
48876
49271
|
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
|
-
}
|
|
49272
|
+
dependent.status = "cancelled";
|
|
49273
|
+
dependent.cancelledAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
49274
|
+
dependent.cancelReason = `dependency_failed:${currentId}`;
|
|
49275
|
+
store.updateQueueEntry(dependent);
|
|
49276
|
+
cancelled.push(dependent);
|
|
49277
|
+
frontier.push(dependent.id);
|
|
48888
49278
|
}
|
|
48889
49279
|
}
|
|
48890
49280
|
return cancelled;
|
|
@@ -49180,12 +49570,13 @@ ${blocks.join("\n\n")}`;
|
|
|
49180
49570
|
const deps = Array.isArray(entry.dependsOn) ? entry.dependsOn : [];
|
|
49181
49571
|
return deps.every((depId) => statusById.get(depId) === "completed");
|
|
49182
49572
|
}
|
|
49183
|
-
function describeTaskDependencyState(entry, statusById) {
|
|
49573
|
+
function describeTaskDependencyState(entry, statusById, depMetaById) {
|
|
49184
49574
|
const deps = Array.isArray(entry.dependsOn) ? entry.dependsOn : [];
|
|
49185
49575
|
const waitingOn = deps.filter((depId) => statusById.get(depId) !== "completed");
|
|
49186
49576
|
return {
|
|
49187
49577
|
waitingOn,
|
|
49188
|
-
dependenciesSatisfied: taskDependenciesSatisfied(entry, statusById)
|
|
49578
|
+
dependenciesSatisfied: taskDependenciesSatisfied(entry, statusById),
|
|
49579
|
+
dependencyFailures: deriveDependencyFailures(entry.dependsOn, statusById, depMetaById)
|
|
49189
49580
|
};
|
|
49190
49581
|
}
|
|
49191
49582
|
function getMeshQueueStats(meshId) {
|
|
@@ -49305,6 +49696,7 @@ ${blocks.join("\n\n")}`;
|
|
|
49305
49696
|
init_mesh_task_inflight();
|
|
49306
49697
|
init_mesh_turn_ledger();
|
|
49307
49698
|
init_mesh_graph_transition_runner();
|
|
49699
|
+
init_mesh_graph_derived_failure();
|
|
49308
49700
|
init_dist();
|
|
49309
49701
|
init_mesh_task_mode_guardrail();
|
|
49310
49702
|
init_mesh_task_mode_guardrail();
|
|
@@ -49520,10 +49912,12 @@ ${blocks.join("\n\n")}`;
|
|
|
49520
49912
|
CREATE INDEX IF NOT EXISTS idx_mesh_graph_outbox_graph
|
|
49521
49913
|
ON mesh_graph_outbox(graph_id, status);
|
|
49522
49914
|
`);
|
|
49915
|
+
migrateLegacyDependencyFailedQueueBlocks(db);
|
|
49523
49916
|
}
|
|
49524
49917
|
var init_mesh_graph_schema = __esm2({
|
|
49525
49918
|
"src/mesh/mesh-graph-schema.ts"() {
|
|
49526
49919
|
"use strict";
|
|
49920
|
+
init_mesh_graph_derived_failure();
|
|
49527
49921
|
}
|
|
49528
49922
|
});
|
|
49529
49923
|
function mapGraphRow(r) {
|
|
@@ -49704,6 +50098,39 @@ ${blocks.join("\n\n")}`;
|
|
|
49704
50098
|
).get(meshId, batchId);
|
|
49705
50099
|
return r ? mapGraphRow(r) : null;
|
|
49706
50100
|
}
|
|
50101
|
+
/**
|
|
50102
|
+
* Phase-E graph listing for the graph view / metrics (design :759-775). Newest
|
|
50103
|
+
* first so a view without an explicit graph id shows current work. `statuses`
|
|
50104
|
+
* narrows to e.g. the in-flight set.
|
|
50105
|
+
*/
|
|
50106
|
+
listGraphsByMesh(meshId, opts) {
|
|
50107
|
+
const limit = Math.max(1, Math.min(opts?.limit ?? 50, 500));
|
|
50108
|
+
const statuses = opts?.statuses;
|
|
50109
|
+
if (statuses && statuses.length > 0) {
|
|
50110
|
+
const placeholders = statuses.map(() => "?").join(",");
|
|
50111
|
+
const rows2 = this.db.prepare(
|
|
50112
|
+
`SELECT * FROM mesh_task_graphs WHERE mesh_id = ? AND status IN (${placeholders})
|
|
50113
|
+
ORDER BY created_at DESC LIMIT ?`
|
|
50114
|
+
).all(meshId, ...statuses, limit);
|
|
50115
|
+
return rows2.map(mapGraphRow);
|
|
50116
|
+
}
|
|
50117
|
+
const rows = this.db.prepare(
|
|
50118
|
+
`SELECT * FROM mesh_task_graphs WHERE mesh_id = ? ORDER BY created_at DESC LIMIT ?`
|
|
50119
|
+
).all(meshId, limit);
|
|
50120
|
+
return rows.map(mapGraphRow);
|
|
50121
|
+
}
|
|
50122
|
+
/**
|
|
50123
|
+
* Phase-E: replace the graph's normalized policy document. Used at plan-commit
|
|
50124
|
+
* time to attach the enqueue-decision provenance record next to the persisted
|
|
50125
|
+
* `on_dependency_failure` (design :697-710, :733-738). Deliberately a whole-
|
|
50126
|
+
* document write — the policy is normalized once at commit and never patched
|
|
50127
|
+
* field-by-field afterwards.
|
|
50128
|
+
*/
|
|
50129
|
+
updateGraphPolicyJson(graphId, policyJson, nowIso) {
|
|
50130
|
+
this.db.prepare(
|
|
50131
|
+
`UPDATE mesh_task_graphs SET policy_json = ?, updated_at = ? WHERE graph_id = ?`
|
|
50132
|
+
).run(policyJson, nowIso, graphId);
|
|
50133
|
+
}
|
|
49707
50134
|
/** Phase-B rollup: graph status transition once every node reached a terminal-equivalent state. */
|
|
49708
50135
|
updateGraphStatus(graphId, status, nowIso, terminal = false) {
|
|
49709
50136
|
this.db.prepare(`
|
|
@@ -49893,6 +50320,79 @@ ${blocks.join("\n\n")}`;
|
|
|
49893
50320
|
const r = this.db.prepare(`SELECT * FROM mesh_graph_gates WHERE gate_id = ?`).get(gateId);
|
|
49894
50321
|
return r ? mapGateRow(r) : null;
|
|
49895
50322
|
}
|
|
50323
|
+
/** Reverse lookup from a graph node (kind `coordinator_gate`) to its gate row (phase C2). */
|
|
50324
|
+
findGateByNodeId(graphId, nodeId) {
|
|
50325
|
+
const r = this.db.prepare(
|
|
50326
|
+
`SELECT * FROM mesh_graph_gates WHERE graph_id = ? AND node_id = ?`
|
|
50327
|
+
).get(graphId, nodeId);
|
|
50328
|
+
return r ? mapGateRow(r) : null;
|
|
50329
|
+
}
|
|
50330
|
+
listGatesByGraph(graphId) {
|
|
50331
|
+
const rows = this.db.prepare(
|
|
50332
|
+
`SELECT * FROM mesh_graph_gates WHERE graph_id = ? ORDER BY created_at ASC`
|
|
50333
|
+
).all(graphId);
|
|
50334
|
+
return rows.map(mapGateRow);
|
|
50335
|
+
}
|
|
50336
|
+
/** Phase-C2 timeout sweep: gates of one mesh, optionally narrowed to states (e.g. awaiting/claimed). */
|
|
50337
|
+
listGatesByMesh(meshId, states) {
|
|
50338
|
+
if (states && states.length > 0) {
|
|
50339
|
+
const placeholders = states.map(() => "?").join(",");
|
|
50340
|
+
const rows2 = this.db.prepare(
|
|
50341
|
+
`SELECT * FROM mesh_graph_gates WHERE mesh_id = ? AND state IN (${placeholders}) ORDER BY created_at ASC`
|
|
50342
|
+
).all(meshId, ...states);
|
|
50343
|
+
return rows2.map(mapGateRow);
|
|
50344
|
+
}
|
|
50345
|
+
const rows = this.db.prepare(
|
|
50346
|
+
`SELECT * FROM mesh_graph_gates WHERE mesh_id = ? ORDER BY created_at ASC`
|
|
50347
|
+
).all(meshId);
|
|
50348
|
+
return rows.map(mapGateRow);
|
|
50349
|
+
}
|
|
50350
|
+
/**
|
|
50351
|
+
* Row write for phase C2 (claim / release / deadline sweep). `undefined`
|
|
50352
|
+
* fields are left unchanged; explicit `null` clears a nullable field. The
|
|
50353
|
+
* optional generation CAS rejects a stale fenced writer: the update lands
|
|
50354
|
+
* only while the row still carries `cas.leaseGeneration`.
|
|
50355
|
+
*/
|
|
50356
|
+
patchGate(gateId, patch, nowIso, cas) {
|
|
50357
|
+
const current = this.getGate(gateId);
|
|
50358
|
+
if (!current) return false;
|
|
50359
|
+
if (cas && current.leaseGeneration !== cas.leaseGeneration) return false;
|
|
50360
|
+
const next = {
|
|
50361
|
+
state: patch.state ?? current.state,
|
|
50362
|
+
leaseOwnerSessionId: patch.leaseOwnerSessionId === void 0 ? current.leaseOwnerSessionId ?? null : patch.leaseOwnerSessionId,
|
|
50363
|
+
leaseGeneration: patch.leaseGeneration ?? current.leaseGeneration,
|
|
50364
|
+
fencingToken: patch.fencingToken === void 0 ? current.fencingToken ?? null : patch.fencingToken,
|
|
50365
|
+
leaseExpiresAt: patch.leaseExpiresAt === void 0 ? current.leaseExpiresAt ?? null : patch.leaseExpiresAt,
|
|
50366
|
+
deadlineAt: patch.deadlineAt === void 0 ? current.deadlineAt ?? null : patch.deadlineAt,
|
|
50367
|
+
releaseOutcome: patch.releaseOutcome === void 0 ? current.releaseOutcome ?? null : patch.releaseOutcome,
|
|
50368
|
+
releaseEvidenceJson: patch.releaseEvidenceJson === void 0 ? current.releaseEvidenceJson ?? null : patch.releaseEvidenceJson,
|
|
50369
|
+
releaseEvidenceDigest: patch.releaseEvidenceDigest === void 0 ? current.releaseEvidenceDigest ?? null : patch.releaseEvidenceDigest,
|
|
50370
|
+
releaseIdempotencyKey: patch.releaseIdempotencyKey === void 0 ? current.releaseIdempotencyKey ?? null : patch.releaseIdempotencyKey
|
|
50371
|
+
};
|
|
50372
|
+
const r = this.db.prepare(`
|
|
50373
|
+
UPDATE mesh_graph_gates
|
|
50374
|
+
SET state = ?, lease_owner_session_id = ?, lease_generation = ?, fencing_token = ?,
|
|
50375
|
+
lease_expires_at = ?, deadline_at = ?, release_outcome = ?,
|
|
50376
|
+
release_evidence_json = ?, release_evidence_digest = ?,
|
|
50377
|
+
release_idempotency_key = ?, updated_at = ?
|
|
50378
|
+
WHERE gate_id = ? AND lease_generation = ?
|
|
50379
|
+
`).run(
|
|
50380
|
+
next.state,
|
|
50381
|
+
next.leaseOwnerSessionId,
|
|
50382
|
+
next.leaseGeneration,
|
|
50383
|
+
next.fencingToken,
|
|
50384
|
+
next.leaseExpiresAt,
|
|
50385
|
+
next.deadlineAt,
|
|
50386
|
+
next.releaseOutcome,
|
|
50387
|
+
next.releaseEvidenceJson,
|
|
50388
|
+
next.releaseEvidenceDigest,
|
|
50389
|
+
next.releaseIdempotencyKey,
|
|
50390
|
+
nowIso,
|
|
50391
|
+
gateId,
|
|
50392
|
+
current.leaseGeneration
|
|
50393
|
+
);
|
|
50394
|
+
return r.changes > 0;
|
|
50395
|
+
}
|
|
49896
50396
|
// ── mesh_graph_workspace_intents ─────────────────────────────────────────
|
|
49897
50397
|
insertWorkspaceIntent(row) {
|
|
49898
50398
|
this.db.prepare(`
|
|
@@ -56325,6 +56825,10 @@ Default branch: \`${mesh.defaultBranch}\`` : ""}`);
|
|
|
56325
56825
|
if (policy.coordinatorIdlePushPolicy === "auto_silent_on_dispatch") {
|
|
56326
56826
|
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
56827
|
}
|
|
56828
|
+
const failurePolicy = policy.onDependencyFailure === "cancel" ? "cancel" : "block";
|
|
56829
|
+
rules.push(
|
|
56830
|
+
`- 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.`
|
|
56831
|
+
);
|
|
56328
56832
|
return `## Policy
|
|
56329
56833
|
${rules.join("\n")}`;
|
|
56330
56834
|
}
|
|
@@ -56401,8 +56905,11 @@ When you compose the task message you dispatch to a node, include this requireme
|
|
|
56401
56905
|
| \`mesh_status\` | Nodes' health, git state, sessions, branch convergence |
|
|
56402
56906
|
| \`mesh_list_nodes\` | List nodes with workspace paths |
|
|
56403
56907
|
| \`mesh_enqueue_task\` | Add a task to the pull-based queue; idle nodes auto-claim |
|
|
56404
|
-
| \`mesh_enqueue_batch\` | Atomically enqueue a dependency-wired task set; \`depends_on\` may name batch-local \`ref\`s (forward refs OK) |
|
|
56908
|
+
| \`mesh_enqueue_batch\` | Atomically enqueue a dependency-wired task set; \`depends_on\` may name batch-local \`ref\`s (forward refs OK). Also carries the full graph surface: \`inputs_from\`, \`run_if\`, \`gates\` + \`gated_by\`, \`workspaces\` + \`workspace_ref\` |
|
|
56405
56909
|
| \`mesh_view_queue\` | Queue status \u2014 pending/assigned/completed/failed/cancelled |
|
|
56910
|
+
| \`mesh_graph_view\` | Inspect orchestration graphs \u2014 node states, gates awaiting you, workspace sagas, why something is blocked |
|
|
56911
|
+
| \`mesh_graph_gate_claim\` | Take the lease on a gate awaiting a coordinator; returns the fencing token + generation a release needs |
|
|
56912
|
+
| \`mesh_graph_gate_release\` | Pass a gate you hold \u2014 the ONLY way through one (no timeout ever passes a gate) |
|
|
56406
56913
|
| \`mesh_queue_cancel\` | Cancel a queue task (audit history kept) |
|
|
56407
56914
|
| \`mesh_queue_requeue\` | Return a task to pending for retry |
|
|
56408
56915
|
| \`mesh_send_task\` | Push a task straight to a specific node/session |
|
|
@@ -59764,6 +60271,593 @@ ${rendered}`, "utf-8");
|
|
|
59764
60271
|
ACTIVE_SAGA_STATES = ["declared", "preparing", "compensating"];
|
|
59765
60272
|
}
|
|
59766
60273
|
});
|
|
60274
|
+
function readGateSpec(node) {
|
|
60275
|
+
if (!node) return {};
|
|
60276
|
+
try {
|
|
60277
|
+
const parsed = JSON.parse(node.baseSpecJson);
|
|
60278
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
60279
|
+
} catch {
|
|
60280
|
+
return {};
|
|
60281
|
+
}
|
|
60282
|
+
}
|
|
60283
|
+
function isoAfter(nowMs, seconds) {
|
|
60284
|
+
return new Date(nowMs + seconds * 1e3).toISOString();
|
|
60285
|
+
}
|
|
60286
|
+
function claimMeshGraphGate(input) {
|
|
60287
|
+
const store = MeshRuntimeStore.getInstance();
|
|
60288
|
+
const nowMs = input.nowMs ?? Date.now();
|
|
60289
|
+
const nowIso = new Date(nowMs).toISOString();
|
|
60290
|
+
const outboxEvents = [];
|
|
60291
|
+
const result = store.transaction(() => {
|
|
60292
|
+
const graphStore = store.graphStore();
|
|
60293
|
+
const gate = graphStore.getGate(input.gateId);
|
|
60294
|
+
if (!gate || gate.meshId !== input.meshId) return { claimed: false, reason: "gate_not_found" };
|
|
60295
|
+
if (gate.eligibleCoordinatorSessionId && gate.eligibleCoordinatorSessionId !== input.coordinatorSessionId) {
|
|
60296
|
+
return { claimed: false, reason: "gate_not_eligible" };
|
|
60297
|
+
}
|
|
60298
|
+
if (gate.state === "released" || gate.state === "cancelled") {
|
|
60299
|
+
return { claimed: false, reason: `gate_terminal:${gate.state}` };
|
|
60300
|
+
}
|
|
60301
|
+
if (gate.state === "declared") {
|
|
60302
|
+
return { claimed: false, reason: "gate_not_awaiting" };
|
|
60303
|
+
}
|
|
60304
|
+
const leaseLive = !!gate.leaseExpiresAt && gate.leaseExpiresAt > nowIso;
|
|
60305
|
+
const sameOwner = gate.leaseOwnerSessionId === input.coordinatorSessionId;
|
|
60306
|
+
const priorState = gate.state;
|
|
60307
|
+
let takeoverFromClaimed = false;
|
|
60308
|
+
if (priorState === "claimed" && leaseLive && !sameOwner) {
|
|
60309
|
+
return { claimed: false, reason: "gate_lease_held", gate };
|
|
60310
|
+
}
|
|
60311
|
+
if (priorState === "expired" && gate.onTimeout !== "hold") {
|
|
60312
|
+
return { claimed: false, reason: "gate_terminal:expired", gate };
|
|
60313
|
+
}
|
|
60314
|
+
const node = graphStore.getNode(gate.graphId, gate.nodeId);
|
|
60315
|
+
const spec = readGateSpec(node);
|
|
60316
|
+
const leaseSeconds = input.leaseSeconds ?? spec.lease_seconds ?? MESH_GATE_DEFAULT_LEASE_SECONDS;
|
|
60317
|
+
const leaseExpiresAt = isoAfter(nowMs, leaseSeconds);
|
|
60318
|
+
let generation;
|
|
60319
|
+
let fencingToken;
|
|
60320
|
+
if (priorState === "claimed" && sameOwner && leaseLive) {
|
|
60321
|
+
generation = gate.leaseGeneration;
|
|
60322
|
+
fencingToken = gate.fencingToken;
|
|
60323
|
+
} else {
|
|
60324
|
+
takeoverFromClaimed = priorState === "claimed";
|
|
60325
|
+
generation = gate.leaseGeneration + 1;
|
|
60326
|
+
fencingToken = (0, import_crypto13.randomUUID)();
|
|
60327
|
+
}
|
|
60328
|
+
const deadlineAt = input.extendDeadlineSeconds && input.extendDeadlineSeconds > 0 ? isoAfter(nowMs, input.extendDeadlineSeconds) : gate.deadlineAt;
|
|
60329
|
+
const won = graphStore.patchGate(gate.gateId, {
|
|
60330
|
+
state: "claimed",
|
|
60331
|
+
leaseOwnerSessionId: input.coordinatorSessionId,
|
|
60332
|
+
leaseGeneration: generation,
|
|
60333
|
+
fencingToken,
|
|
60334
|
+
leaseExpiresAt,
|
|
60335
|
+
...deadlineAt !== void 0 ? { deadlineAt } : {}
|
|
60336
|
+
}, nowIso, { leaseGeneration: gate.leaseGeneration });
|
|
60337
|
+
if (!won) return { claimed: false, reason: "gate_claim_race" };
|
|
60338
|
+
if (node && node.state === "expired") {
|
|
60339
|
+
graphStore.updateNodeState(gate.graphId, gate.nodeId, "awaiting_coordinator", nowIso);
|
|
60340
|
+
}
|
|
60341
|
+
if (takeoverFromClaimed) {
|
|
60342
|
+
outboxEvents.push({
|
|
60343
|
+
kind: "graph_gate_lease_expired",
|
|
60344
|
+
payload: {
|
|
60345
|
+
graphId: gate.graphId,
|
|
60346
|
+
gateId: gate.gateId,
|
|
60347
|
+
ref: gate.ref,
|
|
60348
|
+
previousOwnerSessionId: gate.leaseOwnerSessionId,
|
|
60349
|
+
previousGeneration: gate.leaseGeneration,
|
|
60350
|
+
newGeneration: generation
|
|
60351
|
+
}
|
|
60352
|
+
});
|
|
60353
|
+
}
|
|
60354
|
+
outboxEvents.push({
|
|
60355
|
+
kind: "graph_gate_claimed",
|
|
60356
|
+
payload: {
|
|
60357
|
+
graphId: gate.graphId,
|
|
60358
|
+
gateId: gate.gateId,
|
|
60359
|
+
ref: gate.ref,
|
|
60360
|
+
action: gate.action,
|
|
60361
|
+
ownerSessionId: input.coordinatorSessionId,
|
|
60362
|
+
generation,
|
|
60363
|
+
leaseExpiresAt
|
|
60364
|
+
}
|
|
60365
|
+
});
|
|
60366
|
+
for (const event of outboxEvents) {
|
|
60367
|
+
graphStore.insertOutboxEvent({
|
|
60368
|
+
id: newMeshGraphOutboxId(),
|
|
60369
|
+
meshId: gate.meshId,
|
|
60370
|
+
graphId: gate.graphId,
|
|
60371
|
+
kind: event.kind,
|
|
60372
|
+
payload: JSON.stringify(event.payload),
|
|
60373
|
+
status: "pending",
|
|
60374
|
+
attemptCount: 0,
|
|
60375
|
+
createdAt: nowIso,
|
|
60376
|
+
updatedAt: nowIso
|
|
60377
|
+
});
|
|
60378
|
+
}
|
|
60379
|
+
return {
|
|
60380
|
+
claimed: true,
|
|
60381
|
+
gate: graphStore.getGate(gate.gateId),
|
|
60382
|
+
leaseGeneration: generation,
|
|
60383
|
+
fencingToken,
|
|
60384
|
+
leaseExpiresAt,
|
|
60385
|
+
deadlineAt,
|
|
60386
|
+
ambiguousExternalOutcome: takeoverFromClaimed || priorState === "expired" && gate.leaseGeneration > 0,
|
|
60387
|
+
previousLeaseOwnerSessionId: gate.leaseOwnerSessionId
|
|
60388
|
+
};
|
|
60389
|
+
});
|
|
60390
|
+
if (result.claimed) {
|
|
60391
|
+
try {
|
|
60392
|
+
drainMeshGraphOutbox(input.meshId);
|
|
60393
|
+
} catch {
|
|
60394
|
+
}
|
|
60395
|
+
}
|
|
60396
|
+
return result;
|
|
60397
|
+
}
|
|
60398
|
+
function releaseMeshGraphGate(input) {
|
|
60399
|
+
const store = MeshRuntimeStore.getInstance();
|
|
60400
|
+
const nowMs = input.nowMs ?? Date.now();
|
|
60401
|
+
const nowIso = new Date(nowMs).toISOString();
|
|
60402
|
+
const result = store.transaction(() => {
|
|
60403
|
+
const graphStore = store.graphStore();
|
|
60404
|
+
const gate = graphStore.getGate(input.gateId);
|
|
60405
|
+
if (!gate || gate.meshId !== input.meshId) throw new Error(`gate_not_found: no gate '${input.gateId}'`);
|
|
60406
|
+
const releaseDigest = sha256Hex2(canonicalJson({
|
|
60407
|
+
outcome: input.outcome,
|
|
60408
|
+
result: input.result ?? null,
|
|
60409
|
+
evidence: input.evidence ?? null,
|
|
60410
|
+
patches: input.patches ?? []
|
|
60411
|
+
}));
|
|
60412
|
+
if (gate.releaseIdempotencyKey === input.idempotencyKey) {
|
|
60413
|
+
if (gate.releaseEvidenceDigest === releaseDigest) {
|
|
60414
|
+
return { released: true, duplicate: true, gate, materializedNodeIds: [] };
|
|
60415
|
+
}
|
|
60416
|
+
throw new Error(
|
|
60417
|
+
`gate_release_conflict: idempotency key '${input.idempotencyKey}' already committed with a different release digest (gate '${input.gateId}')`
|
|
60418
|
+
);
|
|
60419
|
+
}
|
|
60420
|
+
if (gate.state === "released") {
|
|
60421
|
+
throw new Error(`gate_already_released: gate '${input.gateId}' was released under a different idempotency key`);
|
|
60422
|
+
}
|
|
60423
|
+
if (gate.state !== "claimed") {
|
|
60424
|
+
throw new Error(`gate_not_claimed: gate '${input.gateId}' is '${gate.state}' \u2014 only a claimed gate can be released`);
|
|
60425
|
+
}
|
|
60426
|
+
if (input.fencingToken !== gate.fencingToken || input.leaseGeneration !== gate.leaseGeneration) {
|
|
60427
|
+
throw new Error(
|
|
60428
|
+
`stale_fence: gate '${input.gateId}' is at lease generation ${gate.leaseGeneration}; the presented token/generation (${input.leaseGeneration}) is stale`
|
|
60429
|
+
);
|
|
60430
|
+
}
|
|
60431
|
+
if (gate.leaseExpiresAt && gate.leaseExpiresAt <= nowIso) {
|
|
60432
|
+
throw new Error(
|
|
60433
|
+
`gate_lease_expired: gate '${input.gateId}' lease expired at ${gate.leaseExpiresAt} \u2014 reclaim the gate (higher generation) and reconcile external evidence before releasing`
|
|
60434
|
+
);
|
|
60435
|
+
}
|
|
60436
|
+
const node = graphStore.getNode(gate.graphId, gate.nodeId);
|
|
60437
|
+
if (!node) throw new Error(`graph_node_not_found: gate '${input.gateId}' backs a missing node '${gate.nodeId}'`);
|
|
60438
|
+
const nodes = graphStore.listNodes(gate.graphId);
|
|
60439
|
+
const edges = graphStore.listEdges(gate.graphId);
|
|
60440
|
+
const byId = new Map(nodes.map((n) => [n.nodeId, n]));
|
|
60441
|
+
const incoming = edges.filter((e) => e.toNodeId === node.nodeId);
|
|
60442
|
+
const upstreamSettled = incoming.every((e) => {
|
|
60443
|
+
const sourceState = byId.get(e.fromNodeId)?.state;
|
|
60444
|
+
return sourceState === "completed" || sourceState === "released" || sourceState === "skipped" && e.omitOnSkip;
|
|
60445
|
+
});
|
|
60446
|
+
if (!upstreamSettled) {
|
|
60447
|
+
throw new Error(`gate_upstream_unsettled: gate '${input.gateId}' has unsatisfied predecessors`);
|
|
60448
|
+
}
|
|
60449
|
+
const directDownstream = new Set(edges.filter((e) => e.fromNodeId === node.nodeId).map((e) => e.toNodeId));
|
|
60450
|
+
for (const patch of input.patches ?? []) {
|
|
60451
|
+
const target = nodes.find((n) => n.nodeId === patch.node || n.ref === patch.node);
|
|
60452
|
+
if (!target || !directDownstream.has(target.nodeId)) {
|
|
60453
|
+
throw new Error(`gate_patch_not_downstream: '${patch.node}' is not a direct downstream node of gate '${input.gateId}'`);
|
|
60454
|
+
}
|
|
60455
|
+
for (const key2 of Object.keys(patch.baseSpecPatch)) {
|
|
60456
|
+
if (!MESH_GATE_RELEASE_PATCH_KEYS.includes(key2)) {
|
|
60457
|
+
throw new Error(`gate_patch_forbidden: key '${key2}' is outside the permitted release patch surface (design :294-299)`);
|
|
60458
|
+
}
|
|
60459
|
+
}
|
|
60460
|
+
if (target.queueTaskId) {
|
|
60461
|
+
const entry = store.findQueueEntryById(target.meshId, target.queueTaskId);
|
|
60462
|
+
if (entry && entry.status !== "pending") {
|
|
60463
|
+
throw new Error(
|
|
60464
|
+
`task_already_claimed: gate release cannot patch node '${target.nodeId}' \u2014 queue task '${target.queueTaskId}' is '${entry.status}' and immutable (design :334)`
|
|
60465
|
+
);
|
|
60466
|
+
}
|
|
60467
|
+
}
|
|
60468
|
+
const rawSpec = (() => {
|
|
60469
|
+
try {
|
|
60470
|
+
return JSON.parse(target.baseSpecJson);
|
|
60471
|
+
} catch {
|
|
60472
|
+
return {};
|
|
60473
|
+
}
|
|
60474
|
+
})();
|
|
60475
|
+
graphStore.updateNodeBaseSpec(
|
|
60476
|
+
target.graphId,
|
|
60477
|
+
target.nodeId,
|
|
60478
|
+
JSON.stringify({ ...rawSpec && typeof rawSpec === "object" ? rawSpec : {}, ...patch.baseSpecPatch }),
|
|
60479
|
+
nowIso
|
|
60480
|
+
);
|
|
60481
|
+
const inMap = byId.get(target.nodeId);
|
|
60482
|
+
if (inMap) inMap.materializationVersion += 1;
|
|
60483
|
+
}
|
|
60484
|
+
const won = graphStore.patchGate(gate.gateId, {
|
|
60485
|
+
state: "released",
|
|
60486
|
+
releaseOutcome: input.outcome,
|
|
60487
|
+
releaseEvidenceJson: canonicalJson({ result: input.result ?? null, evidence: input.evidence ?? null }),
|
|
60488
|
+
releaseEvidenceDigest: releaseDigest,
|
|
60489
|
+
releaseIdempotencyKey: input.idempotencyKey
|
|
60490
|
+
}, nowIso, { leaseGeneration: gate.leaseGeneration });
|
|
60491
|
+
if (!won) throw new Error(`stale_fence: gate '${input.gateId}' moved under the release (generation race)`);
|
|
60492
|
+
graphStore.updateNodeState(gate.graphId, node.nodeId, "released", nowIso);
|
|
60493
|
+
const mapped = byId.get(node.nodeId);
|
|
60494
|
+
if (mapped) mapped.state = "released";
|
|
60495
|
+
const materializedNodeIds = advanceReleasedGateDownstream(store, gate, node, nowIso);
|
|
60496
|
+
const freshNodes = graphStore.listNodes(gate.graphId);
|
|
60497
|
+
const graph = graphStore.getGraph(gate.graphId);
|
|
60498
|
+
if (graph && freshNodes.every(isTerminalEquivalent)) {
|
|
60499
|
+
graphStore.updateGraphStatus(gate.graphId, "completed", nowIso, true);
|
|
60500
|
+
insertGateOutbox(graphStore, gate.meshId, gate.graphId, "graph_completed", { graphId: gate.graphId }, nowIso);
|
|
60501
|
+
} else if (graph?.status === "waiting_gate") {
|
|
60502
|
+
const stillWaiting = graphStore.listGatesByGraph(gate.graphId).some((g) => g.gateId !== gate.gateId && (g.state === "awaiting_coordinator" || g.state === "claimed"));
|
|
60503
|
+
if (!stillWaiting) graphStore.updateGraphStatus(gate.graphId, "active", nowIso);
|
|
60504
|
+
}
|
|
60505
|
+
insertGateOutbox(graphStore, gate.meshId, gate.graphId, "graph_gate_released", {
|
|
60506
|
+
graphId: gate.graphId,
|
|
60507
|
+
gateId: gate.gateId,
|
|
60508
|
+
nodeId: node.nodeId,
|
|
60509
|
+
ref: gate.ref,
|
|
60510
|
+
action: gate.action,
|
|
60511
|
+
outcome: input.outcome,
|
|
60512
|
+
releaseDigest,
|
|
60513
|
+
generation: gate.leaseGeneration,
|
|
60514
|
+
materializedNodeIds
|
|
60515
|
+
}, nowIso);
|
|
60516
|
+
if (materializedNodeIds.length > 0) {
|
|
60517
|
+
insertGateOutbox(graphStore, gate.meshId, gate.graphId, "queue_wake", {
|
|
60518
|
+
meshId: gate.meshId,
|
|
60519
|
+
reason: "graph_gate_released",
|
|
60520
|
+
graphId: gate.graphId,
|
|
60521
|
+
gateId: gate.gateId
|
|
60522
|
+
}, nowIso);
|
|
60523
|
+
}
|
|
60524
|
+
return { released: true, duplicate: false, gate: graphStore.getGate(gate.gateId), materializedNodeIds };
|
|
60525
|
+
});
|
|
60526
|
+
try {
|
|
60527
|
+
drainMeshGraphOutbox(input.meshId);
|
|
60528
|
+
} catch {
|
|
60529
|
+
}
|
|
60530
|
+
return result;
|
|
60531
|
+
}
|
|
60532
|
+
function advanceReleasedGateDownstream(store, gate, gateNode, nowIso) {
|
|
60533
|
+
const graphStore = store.graphStore();
|
|
60534
|
+
const nodes = graphStore.listNodes(gate.graphId);
|
|
60535
|
+
const edges = graphStore.listEdges(gate.graphId);
|
|
60536
|
+
const byId = new Map(nodes.map((n) => [n.nodeId, n]));
|
|
60537
|
+
const mappedGate = byId.get(gateNode.nodeId);
|
|
60538
|
+
if (mappedGate) mappedGate.state = "released";
|
|
60539
|
+
const materialized = [];
|
|
60540
|
+
let frontier = /* @__PURE__ */ new Set([gateNode.nodeId]);
|
|
60541
|
+
for (let pass = 0; pass < nodes.length + 1 && frontier.size > 0; pass += 1) {
|
|
60542
|
+
const nextFrontier = /* @__PURE__ */ new Set();
|
|
60543
|
+
const downstreamIds = new Set(edges.filter((e) => frontier.has(e.fromNodeId)).map((e) => e.toNodeId));
|
|
60544
|
+
for (const targetId of downstreamIds) {
|
|
60545
|
+
const target = byId.get(targetId);
|
|
60546
|
+
if (!target) continue;
|
|
60547
|
+
if (target.kind === "coordinator_gate") {
|
|
60548
|
+
maybeOpenCoordinatorGate(store, target, edges, byId, nowIso);
|
|
60549
|
+
continue;
|
|
60550
|
+
}
|
|
60551
|
+
if (target.kind !== "worker_task" || !target.queueTaskId) continue;
|
|
60552
|
+
if (target.state !== "declared" && target.state !== "blocked") continue;
|
|
60553
|
+
clearReleasedGateHold(store, target, gate.gateId);
|
|
60554
|
+
const outcome = settleDownstreamNode(store, target, edges, byId, nowIso);
|
|
60555
|
+
if (outcome.kind === "materialized") {
|
|
60556
|
+
materialized.push(target.nodeId);
|
|
60557
|
+
insertGateOutbox(graphStore, target.meshId, target.graphId, "graph_node_materialized", {
|
|
60558
|
+
graphId: target.graphId,
|
|
60559
|
+
nodeId: target.nodeId,
|
|
60560
|
+
ref: target.ref,
|
|
60561
|
+
taskId: target.queueTaskId,
|
|
60562
|
+
gateId: gate.gateId,
|
|
60563
|
+
materializationVersion: target.materializationVersion,
|
|
60564
|
+
digest: outcome.digest,
|
|
60565
|
+
receipts: outcome.receipts
|
|
60566
|
+
}, nowIso);
|
|
60567
|
+
} else if (outcome.kind === "skipped") {
|
|
60568
|
+
nextFrontier.add(target.nodeId);
|
|
60569
|
+
insertGateOutbox(graphStore, target.meshId, target.graphId, "graph_node_skipped", {
|
|
60570
|
+
graphId: target.graphId,
|
|
60571
|
+
nodeId: target.nodeId,
|
|
60572
|
+
ref: target.ref,
|
|
60573
|
+
taskId: target.queueTaskId,
|
|
60574
|
+
gateId: gate.gateId,
|
|
60575
|
+
reason: outcome.reason
|
|
60576
|
+
}, nowIso);
|
|
60577
|
+
} else if (outcome.kind === "error") {
|
|
60578
|
+
insertGateOutbox(graphStore, target.meshId, target.graphId, "graph_node_materialization_failed", {
|
|
60579
|
+
graphId: target.graphId,
|
|
60580
|
+
nodeId: target.nodeId,
|
|
60581
|
+
ref: target.ref,
|
|
60582
|
+
taskId: target.queueTaskId,
|
|
60583
|
+
gateId: gate.gateId,
|
|
60584
|
+
blockedReason: outcome.blockedReason
|
|
60585
|
+
}, nowIso);
|
|
60586
|
+
} else {
|
|
60587
|
+
const entry = store.findQueueEntryById(target.meshId, target.queueTaskId);
|
|
60588
|
+
if (entry && entry.status === "pending" && !entry.blockedReason) {
|
|
60589
|
+
entry.blockedReason = graphMaterializationBlockReason(target.nodeId, target.materializationVersion);
|
|
60590
|
+
store.updateQueueEntry(entry);
|
|
60591
|
+
}
|
|
60592
|
+
}
|
|
60593
|
+
}
|
|
60594
|
+
frontier = nextFrontier;
|
|
60595
|
+
}
|
|
60596
|
+
return materialized;
|
|
60597
|
+
}
|
|
60598
|
+
function clearReleasedGateHold(store, target, gateId) {
|
|
60599
|
+
if (!target.queueTaskId) return;
|
|
60600
|
+
const entry = store.findQueueEntryById(target.meshId, target.queueTaskId);
|
|
60601
|
+
if (!entry || entry.status !== "pending") return;
|
|
60602
|
+
const hold = parseCoordinatorGateBlock(entry.blockedReason);
|
|
60603
|
+
if (hold && hold.gateId === gateId) {
|
|
60604
|
+
delete entry.blockedReason;
|
|
60605
|
+
store.updateQueueEntry(entry);
|
|
60606
|
+
}
|
|
60607
|
+
}
|
|
60608
|
+
function sweepMeshGraphGateTimeouts(meshId, nowMs) {
|
|
60609
|
+
const store = MeshRuntimeStore.getInstance();
|
|
60610
|
+
const now = nowMs ?? Date.now();
|
|
60611
|
+
const nowIso = new Date(now).toISOString();
|
|
60612
|
+
const result = store.transaction(() => {
|
|
60613
|
+
const graphStore = store.graphStore();
|
|
60614
|
+
const candidates = graphStore.listGatesByMesh(meshId, ["awaiting_coordinator", "claimed"]);
|
|
60615
|
+
const expiredLeaseGateIds = candidates.filter((g) => g.state === "claimed" && g.leaseExpiresAt && g.leaseExpiresAt <= nowIso).map((g) => g.gateId);
|
|
60616
|
+
const expiredGateIds = [];
|
|
60617
|
+
for (const gate of candidates) {
|
|
60618
|
+
if (!gate.deadlineAt || gate.deadlineAt > nowIso) continue;
|
|
60619
|
+
const node = graphStore.getNode(gate.graphId, gate.nodeId);
|
|
60620
|
+
const won = graphStore.patchGate(gate.gateId, { state: "expired" }, nowIso, { leaseGeneration: gate.leaseGeneration });
|
|
60621
|
+
if (!won) continue;
|
|
60622
|
+
if (node && node.state !== "expired") {
|
|
60623
|
+
graphStore.updateNodeState(gate.graphId, gate.nodeId, "expired", nowIso);
|
|
60624
|
+
}
|
|
60625
|
+
let cancelledNodeIds = [];
|
|
60626
|
+
if (gate.onTimeout === "cancel_downstream" && node) {
|
|
60627
|
+
cancelledNodeIds = cancelGateDownstreamSubtree(store, node, nowIso);
|
|
60628
|
+
} else if (gate.onTimeout === "fail_graph") {
|
|
60629
|
+
graphStore.updateGraphStatus(gate.graphId, "failed", nowIso, true);
|
|
60630
|
+
}
|
|
60631
|
+
insertGateOutbox(graphStore, gate.meshId, gate.graphId, "graph_gate_expired", {
|
|
60632
|
+
graphId: gate.graphId,
|
|
60633
|
+
gateId: gate.gateId,
|
|
60634
|
+
nodeId: gate.nodeId,
|
|
60635
|
+
ref: gate.ref,
|
|
60636
|
+
action: gate.action,
|
|
60637
|
+
policy: gate.onTimeout,
|
|
60638
|
+
deadlineAt: gate.deadlineAt,
|
|
60639
|
+
urgent: true,
|
|
60640
|
+
...cancelledNodeIds.length > 0 ? { cancelledNodeIds } : {}
|
|
60641
|
+
}, nowIso);
|
|
60642
|
+
expiredGateIds.push(gate.gateId);
|
|
60643
|
+
}
|
|
60644
|
+
return { expiredGateIds, expiredLeaseGateIds };
|
|
60645
|
+
});
|
|
60646
|
+
if (result.expiredGateIds.length > 0) {
|
|
60647
|
+
try {
|
|
60648
|
+
drainMeshGraphOutbox(meshId);
|
|
60649
|
+
} catch {
|
|
60650
|
+
}
|
|
60651
|
+
}
|
|
60652
|
+
if (result.expiredGateIds.length > 0 || result.expiredLeaseGateIds.length > 0) {
|
|
60653
|
+
LOG2.info("MeshGraph", `Gate sweep for mesh ${meshId}: expired=[${result.expiredGateIds.join(",")}] lease-lapsed=[${result.expiredLeaseGateIds.join(",")}]`);
|
|
60654
|
+
}
|
|
60655
|
+
return result;
|
|
60656
|
+
}
|
|
60657
|
+
function isCancelExempt(state) {
|
|
60658
|
+
return state === "completed" || state === "released" || state === "skipped" || state === "cancelled" || state === "failed" || state === "expired";
|
|
60659
|
+
}
|
|
60660
|
+
function cancelGateDownstreamSubtree(store, gateNode, nowIso) {
|
|
60661
|
+
const graphStore = store.graphStore();
|
|
60662
|
+
const nodes = graphStore.listNodes(gateNode.graphId);
|
|
60663
|
+
const edges = graphStore.listEdges(gateNode.graphId);
|
|
60664
|
+
const byId = new Map(nodes.map((n) => [n.nodeId, n]));
|
|
60665
|
+
const cancelled = [];
|
|
60666
|
+
const visited = /* @__PURE__ */ new Set([gateNode.nodeId]);
|
|
60667
|
+
let frontier = [gateNode.nodeId];
|
|
60668
|
+
while (frontier.length > 0) {
|
|
60669
|
+
const next = [];
|
|
60670
|
+
for (const id of frontier) {
|
|
60671
|
+
for (const edge of edges.filter((e) => e.fromNodeId === id)) {
|
|
60672
|
+
if (visited.has(edge.toNodeId)) continue;
|
|
60673
|
+
visited.add(edge.toNodeId);
|
|
60674
|
+
const target = byId.get(edge.toNodeId);
|
|
60675
|
+
if (!target || isCancelExempt(target.state)) continue;
|
|
60676
|
+
graphStore.updateNodeState(target.graphId, target.nodeId, "cancelled", nowIso, {
|
|
60677
|
+
failureReason: `coordinator_gate_timeout:${gateNode.nodeId}`
|
|
60678
|
+
});
|
|
60679
|
+
if (target.queueTaskId) {
|
|
60680
|
+
const entry = store.findQueueEntryById(target.meshId, target.queueTaskId);
|
|
60681
|
+
if (entry && entry.status === "pending") {
|
|
60682
|
+
entry.status = "cancelled";
|
|
60683
|
+
entry.blockedReason = `coordinator_gate_timeout:${gateNode.nodeId}`;
|
|
60684
|
+
store.updateQueueEntry(entry);
|
|
60685
|
+
}
|
|
60686
|
+
}
|
|
60687
|
+
cancelled.push(target.nodeId);
|
|
60688
|
+
next.push(target.nodeId);
|
|
60689
|
+
}
|
|
60690
|
+
}
|
|
60691
|
+
frontier = next;
|
|
60692
|
+
}
|
|
60693
|
+
return cancelled;
|
|
60694
|
+
}
|
|
60695
|
+
function insertGateOutbox(graphStore, meshId, graphId, kind, payload, nowIso) {
|
|
60696
|
+
graphStore.insertOutboxEvent({
|
|
60697
|
+
id: newMeshGraphOutboxId(),
|
|
60698
|
+
meshId,
|
|
60699
|
+
graphId,
|
|
60700
|
+
kind,
|
|
60701
|
+
payload: JSON.stringify(payload),
|
|
60702
|
+
status: "pending",
|
|
60703
|
+
attemptCount: 0,
|
|
60704
|
+
createdAt: nowIso,
|
|
60705
|
+
updatedAt: nowIso
|
|
60706
|
+
});
|
|
60707
|
+
}
|
|
60708
|
+
var import_crypto13;
|
|
60709
|
+
var MESH_GATE_DEFAULT_LEASE_SECONDS;
|
|
60710
|
+
var MESH_GATE_NAMED_OUTCOMES;
|
|
60711
|
+
var MESH_GATE_RELEASE_PATCH_KEYS;
|
|
60712
|
+
var init_mesh_graph_gates = __esm2({
|
|
60713
|
+
"src/mesh/mesh-graph-gates.ts"() {
|
|
60714
|
+
"use strict";
|
|
60715
|
+
import_crypto13 = require("crypto");
|
|
60716
|
+
init_mesh_runtime_store();
|
|
60717
|
+
init_logger();
|
|
60718
|
+
init_mesh_graph_input_binding();
|
|
60719
|
+
init_mesh_graph_types();
|
|
60720
|
+
init_mesh_graph_transition_runner();
|
|
60721
|
+
init_mesh_graph_transition_runner();
|
|
60722
|
+
MESH_GATE_DEFAULT_LEASE_SECONDS = 900;
|
|
60723
|
+
MESH_GATE_NAMED_OUTCOMES = ["passed", "failed", "rejected"];
|
|
60724
|
+
MESH_GATE_RELEASE_PATCH_KEYS = ["run_if", "on_false", "inputs_from", "workspace_ref"];
|
|
60725
|
+
}
|
|
60726
|
+
});
|
|
60727
|
+
function normalizeOrchestrationDecision(raw, surface) {
|
|
60728
|
+
const source = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
60729
|
+
const readCount = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : void 0;
|
|
60730
|
+
const readyWorkerTasks = readCount(source.ready_worker_tasks ?? source.readyWorkerTasks);
|
|
60731
|
+
const knownGraphSteps = readCount(source.known_graph_steps ?? source.knownGraphSteps);
|
|
60732
|
+
const rawReason = source.single_reason ?? source.singleReason;
|
|
60733
|
+
const singleReason = typeof rawReason === "string" && rawReason.trim() ? rawReason.trim() : null;
|
|
60734
|
+
const rawBlockers = source.capability_blockers ?? source.capabilityBlockers;
|
|
60735
|
+
const blockers = Array.isArray(rawBlockers) ? rawBlockers.filter((b) => typeof b === "string" && b.trim().length > 0).map((b) => b.trim()) : [];
|
|
60736
|
+
const decision = {
|
|
60737
|
+
decision: surface,
|
|
60738
|
+
...readyWorkerTasks !== void 0 ? { ready_worker_tasks: readyWorkerTasks } : {},
|
|
60739
|
+
...knownGraphSteps !== void 0 ? { known_graph_steps: knownGraphSteps } : {},
|
|
60740
|
+
single_reason: surface === "single" ? singleReason : null,
|
|
60741
|
+
...blockers.length > 0 ? { capability_blockers: blockers } : {}
|
|
60742
|
+
};
|
|
60743
|
+
const result = { decision };
|
|
60744
|
+
if (surface !== "single") return result;
|
|
60745
|
+
const superseded = [singleReason, ...blockers].find(
|
|
60746
|
+
(value) => !!value && MESH_SUPERSEDED_SINGLE_REASONS.includes(value)
|
|
60747
|
+
);
|
|
60748
|
+
if (superseded) {
|
|
60749
|
+
result.batchCapabilityAvailable = {
|
|
60750
|
+
code: "batch_capability_available",
|
|
60751
|
+
reportedReason: superseded,
|
|
60752
|
+
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.`
|
|
60753
|
+
};
|
|
60754
|
+
}
|
|
60755
|
+
if (knownGraphSteps !== void 0 && knownGraphSteps >= 2) {
|
|
60756
|
+
result.declaredEligibleSingle = true;
|
|
60757
|
+
}
|
|
60758
|
+
return result;
|
|
60759
|
+
}
|
|
60760
|
+
function safeAppend(meshId, kind, payload, taskId) {
|
|
60761
|
+
try {
|
|
60762
|
+
appendLedgerEntry(meshId, { kind, ...taskId ? { taskId } : {}, payload });
|
|
60763
|
+
} catch (e) {
|
|
60764
|
+
LOG2.warn("MeshGraph", `Ledger provenance append (${kind}) failed: ${e?.message || e}`);
|
|
60765
|
+
}
|
|
60766
|
+
}
|
|
60767
|
+
function recordGraphEnqueueCommitted(meshId, p) {
|
|
60768
|
+
safeAppend(meshId, "graph_enqueue_committed", {
|
|
60769
|
+
graphId: p.graphId,
|
|
60770
|
+
batchId: p.batchId,
|
|
60771
|
+
enqueueSurface: p.enqueueSurface,
|
|
60772
|
+
schemaVersion: p.schemaVersion,
|
|
60773
|
+
planDigest: p.planDigest,
|
|
60774
|
+
...p.missionId ? { missionId: p.missionId } : {},
|
|
60775
|
+
...p.coordinatorSessionId ? { coordinatorSessionId: p.coordinatorSessionId } : {},
|
|
60776
|
+
taskCount: p.taskCount,
|
|
60777
|
+
gateCount: p.gateCount,
|
|
60778
|
+
workspaceCount: p.workspaceCount,
|
|
60779
|
+
dependencyEdgeCount: p.dependencyEdgeCount,
|
|
60780
|
+
onDependencyFailure: p.onDependencyFailure,
|
|
60781
|
+
...p.orchestrationDecision ? { orchestrationDecision: p.orchestrationDecision } : {},
|
|
60782
|
+
...p.replayed ? { replayed: true } : {}
|
|
60783
|
+
});
|
|
60784
|
+
}
|
|
60785
|
+
function recordGraphEnqueueValidationFailed(meshId, p) {
|
|
60786
|
+
safeAppend(meshId, "graph_enqueue_validation_failed", {
|
|
60787
|
+
code: p.code,
|
|
60788
|
+
...p.batchId ? { batchId: p.batchId } : {},
|
|
60789
|
+
...p.taskCount !== void 0 ? { taskCount: p.taskCount } : {},
|
|
60790
|
+
...p.gateCount !== void 0 ? { gateCount: p.gateCount } : {},
|
|
60791
|
+
...p.workspaceCount !== void 0 ? { workspaceCount: p.workspaceCount } : {},
|
|
60792
|
+
...p.orchestrationDecision ? { orchestrationDecision: p.orchestrationDecision } : {}
|
|
60793
|
+
});
|
|
60794
|
+
}
|
|
60795
|
+
function recordGraphEnqueueRolledBack(meshId, p) {
|
|
60796
|
+
safeAppend(meshId, "graph_enqueue_rolled_back", {
|
|
60797
|
+
code: p.code,
|
|
60798
|
+
...p.batchId ? { batchId: p.batchId } : {},
|
|
60799
|
+
...p.taskCount !== void 0 ? { taskCount: p.taskCount } : {},
|
|
60800
|
+
// The error STRING is a validation message authored by this codebase
|
|
60801
|
+
// (never worker output or a task message), so it is safe provenance.
|
|
60802
|
+
...p.error ? { error: p.error.slice(0, 500) } : {}
|
|
60803
|
+
});
|
|
60804
|
+
}
|
|
60805
|
+
function recordGraphGateClaimed(meshId, p) {
|
|
60806
|
+
safeAppend(meshId, "graph_gate_claimed", {
|
|
60807
|
+
graphId: p.graphId,
|
|
60808
|
+
gateId: p.gateId,
|
|
60809
|
+
...p.ref ? { ref: p.ref } : {},
|
|
60810
|
+
action: p.action,
|
|
60811
|
+
generation: p.generation,
|
|
60812
|
+
ownerSessionId: p.ownerSessionId,
|
|
60813
|
+
...p.leaseExpiresAt ? { leaseExpiresAt: p.leaseExpiresAt } : {},
|
|
60814
|
+
...p.ambiguousExternalOutcome ? { ambiguousExternalOutcome: true } : {},
|
|
60815
|
+
...p.previousLeaseOwnerSessionId ? { previousLeaseOwnerSessionId: p.previousLeaseOwnerSessionId } : {}
|
|
60816
|
+
});
|
|
60817
|
+
}
|
|
60818
|
+
function recordGraphGateReleased(meshId, p) {
|
|
60819
|
+
safeAppend(meshId, "graph_gate_released", {
|
|
60820
|
+
graphId: p.graphId,
|
|
60821
|
+
gateId: p.gateId,
|
|
60822
|
+
...p.ref ? { ref: p.ref } : {},
|
|
60823
|
+
action: p.action,
|
|
60824
|
+
outcome: p.outcome,
|
|
60825
|
+
generation: p.generation,
|
|
60826
|
+
...p.releaseDigest ? { releaseDigest: p.releaseDigest } : {},
|
|
60827
|
+
...p.materializedNodeIds?.length ? { materializedNodeIds: p.materializedNodeIds } : {},
|
|
60828
|
+
...p.duplicate ? { duplicate: true } : {}
|
|
60829
|
+
});
|
|
60830
|
+
}
|
|
60831
|
+
function recordGraphGateExpired(meshId, p) {
|
|
60832
|
+
safeAppend(meshId, "graph_gate_expired", {
|
|
60833
|
+
gateId: p.gateId,
|
|
60834
|
+
...p.graphId ? { graphId: p.graphId } : {},
|
|
60835
|
+
policy: p.policy,
|
|
60836
|
+
...p.deadlineAt ? { deadlineAt: p.deadlineAt } : {},
|
|
60837
|
+
...p.cancelledNodeIds?.length ? { cancelledNodeIds: p.cancelledNodeIds } : {}
|
|
60838
|
+
});
|
|
60839
|
+
}
|
|
60840
|
+
var MESH_VALID_SINGLE_REASONS;
|
|
60841
|
+
var MESH_SUPERSEDED_SINGLE_REASONS;
|
|
60842
|
+
var init_mesh_graph_provenance = __esm2({
|
|
60843
|
+
"src/mesh/mesh-graph-provenance.ts"() {
|
|
60844
|
+
"use strict";
|
|
60845
|
+
init_mesh_ledger();
|
|
60846
|
+
init_logger();
|
|
60847
|
+
MESH_VALID_SINGLE_REASONS = [
|
|
60848
|
+
"only_one_step_known",
|
|
60849
|
+
"future_step_not_specifiable",
|
|
60850
|
+
"same_session_continuation",
|
|
60851
|
+
"legacy_client",
|
|
60852
|
+
"operator_override"
|
|
60853
|
+
];
|
|
60854
|
+
MESH_SUPERSEDED_SINGLE_REASONS = [
|
|
60855
|
+
"output_needed",
|
|
60856
|
+
"workspace_unresolved",
|
|
60857
|
+
"coordinator_action_between"
|
|
60858
|
+
];
|
|
60859
|
+
}
|
|
60860
|
+
});
|
|
59767
60861
|
function turnProjectionActiveWorkStatus(meshId, taskId) {
|
|
59768
60862
|
const row = resolveTurnAttemptRow({ meshId, taskId });
|
|
59769
60863
|
if (!row) return null;
|
|
@@ -61805,15 +62899,15 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
61805
62899
|
}
|
|
61806
62900
|
return "";
|
|
61807
62901
|
}
|
|
61808
|
-
function extractFinalAssistantSummaryEvidence(messages, maxChars = DEFAULT_FINAL_SUMMARY_MAX_CHARS) {
|
|
61809
|
-
const turnEnd = selectFinalAssistantTurnEndMessage(messages);
|
|
62902
|
+
function extractFinalAssistantSummaryEvidence(messages, maxChars = DEFAULT_FINAL_SUMMARY_MAX_CHARS, opts) {
|
|
62903
|
+
const turnEnd = selectFinalAssistantTurnEndMessage(messages, opts);
|
|
61810
62904
|
if (!turnEnd) return { finalSummary: "" };
|
|
61811
62905
|
return {
|
|
61812
62906
|
finalSummary: flattenContent(turnEnd.content).trim().slice(0, maxChars),
|
|
61813
62907
|
transcriptMessageAt: readChatMessageTimestampIso(turnEnd)
|
|
61814
62908
|
};
|
|
61815
62909
|
}
|
|
61816
|
-
function selectFinalAssistantTurnEndMessage(messages) {
|
|
62910
|
+
function selectFinalAssistantTurnEndMessage(messages, opts) {
|
|
61817
62911
|
if (!Array.isArray(messages) || messages.length === 0) return null;
|
|
61818
62912
|
let chromeFallbacks = 0;
|
|
61819
62913
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
@@ -61828,6 +62922,13 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
61828
62922
|
chromeFallbacks++;
|
|
61829
62923
|
continue;
|
|
61830
62924
|
}
|
|
62925
|
+
const boundaryMs = opts?.turnStartedAtMs;
|
|
62926
|
+
if (typeof boundaryMs === "number" && Number.isFinite(boundaryMs)) {
|
|
62927
|
+
const msgAt = readChatMessageTimestampMs(msg);
|
|
62928
|
+
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) {
|
|
62929
|
+
return null;
|
|
62930
|
+
}
|
|
62931
|
+
}
|
|
61831
62932
|
return msg;
|
|
61832
62933
|
}
|
|
61833
62934
|
return null;
|
|
@@ -62164,6 +63265,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
62164
63265
|
return (Array.isArray(messages) ? messages : []).filter((message) => classifyChatMessageVisibility(message).surface === surface);
|
|
62165
63266
|
}
|
|
62166
63267
|
var DEFAULT_FINAL_SUMMARY_MAX_CHARS;
|
|
63268
|
+
var FINAL_ANSWER_MIN_TURN_AGE_MS;
|
|
62167
63269
|
var FINAL_SUMMARY_CHROME_FALLBACK_MAX_DEPTH;
|
|
62168
63270
|
var TRANSCRIPT_CHROME_HARD_LINE_PATTERNS;
|
|
62169
63271
|
var TRANSCRIPT_CHROME_PANEL_LINE_PATTERNS;
|
|
@@ -62187,6 +63289,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
62187
63289
|
"use strict";
|
|
62188
63290
|
init_contracts2();
|
|
62189
63291
|
DEFAULT_FINAL_SUMMARY_MAX_CHARS = 16e3;
|
|
63292
|
+
FINAL_ANSWER_MIN_TURN_AGE_MS = 3e4;
|
|
62190
63293
|
FINAL_SUMMARY_CHROME_FALLBACK_MAX_DEPTH = 2;
|
|
62191
63294
|
TRANSCRIPT_CHROME_HARD_LINE_PATTERNS = [
|
|
62192
63295
|
// Bottom status bar: "auto K3 thinking: high ~/Work/adhdev main [±]"
|
|
@@ -66765,7 +67868,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
66765
67868
|
const fingerprint = `${target}::${buildPendingEventFingerprint(fingerprintSource)}`;
|
|
66766
67869
|
try {
|
|
66767
67870
|
const inserted = store.insertPendingEvent({
|
|
66768
|
-
id: (0,
|
|
67871
|
+
id: (0, import_crypto15.randomUUID)(),
|
|
66769
67872
|
meshId: UNRESOLVED_FORWARD_OUTBOX_MESH_ID,
|
|
66770
67873
|
coordinatorDaemonId: target,
|
|
66771
67874
|
event,
|
|
@@ -66828,14 +67931,14 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
66828
67931
|
return 0;
|
|
66829
67932
|
}
|
|
66830
67933
|
}
|
|
66831
|
-
var
|
|
67934
|
+
var import_crypto15;
|
|
66832
67935
|
var UNRESOLVED_FORWARD_OUTBOX_MESH_ID;
|
|
66833
67936
|
var UNRESOLVED_FORWARD_MAX_AGE_MS;
|
|
66834
67937
|
var retryNudgeHandler;
|
|
66835
67938
|
var init_mesh_unresolved_forward_outbox = __esm2({
|
|
66836
67939
|
"src/mesh/mesh-unresolved-forward-outbox.ts"() {
|
|
66837
67940
|
"use strict";
|
|
66838
|
-
|
|
67941
|
+
import_crypto15 = require("crypto");
|
|
66839
67942
|
init_logger();
|
|
66840
67943
|
init_mesh_runtime_store();
|
|
66841
67944
|
init_mesh_events_pending();
|
|
@@ -69514,6 +70617,15 @@ ${cleanBody}`;
|
|
|
69514
70617
|
}
|
|
69515
70618
|
};
|
|
69516
70619
|
}
|
|
70620
|
+
if (admission.kind === "admit") {
|
|
70621
|
+
traceMeshEventStage(
|
|
70622
|
+
"terminal_admission_admitted",
|
|
70623
|
+
traceCtx,
|
|
70624
|
+
`${admission.evidenceLevel}:${admission.reason}`
|
|
70625
|
+
);
|
|
70626
|
+
} else {
|
|
70627
|
+
traceMeshEventStage("terminal_admission_not_enforced", traceCtx, admission.reason);
|
|
70628
|
+
}
|
|
69517
70629
|
}
|
|
69518
70630
|
if (!readNonEmptyString(args.metadataEvent.taskId) && !sessionHasActiveAssignment(args.meshId, eventSessionId) && !findRecentTerminalLedgerEvidence({ meshId: args.meshId, sessionId: eventSessionId, nodeId: eventNodeId || void 0 }) && isWeakCompletionEvidence(args.metadataEvent)) {
|
|
69519
70631
|
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 +70988,13 @@ ${cleanBody}`;
|
|
|
69876
70988
|
const emittedAfterFinalizationTimeout = completionDiagnostic?.emittedAfterFinalizationTimeout === true;
|
|
69877
70989
|
const weakEvidenceAtEntry = isWeakCompletionEvidence(args.metadataEvent);
|
|
69878
70990
|
const weakCompleted = outcome === "completed" && weakEvidenceAtEntry && !emittedAfterFinalizationTimeout;
|
|
69879
|
-
if (
|
|
70991
|
+
if (emittedAfterFinalizationTimeout && weakEvidenceAtEntry) {
|
|
69880
70992
|
args.metadataEvent.reviewRecommended = true;
|
|
69881
70993
|
args.metadataEvent.completionDiagnostic = {
|
|
69882
70994
|
...completionDiagnostic || {},
|
|
69883
70995
|
timedOutWithoutFinalAssistant: true
|
|
69884
70996
|
};
|
|
69885
|
-
LOG2.info("MeshQueue", `Completion for session ${sessionId} exhausted the finalization wait with no confirmed final assistant \u2014
|
|
70997
|
+
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
70998
|
}
|
|
69887
70999
|
let reducerAllowsFlip = true;
|
|
69888
71000
|
if (eventTaskId && !weakCompleted) {
|
|
@@ -70006,6 +71118,7 @@ ${cleanBody}`;
|
|
|
70006
71118
|
return task ? { id: task.id, taskMode: task.taskMode } : null;
|
|
70007
71119
|
}
|
|
70008
71120
|
let completedTaskForLedger = null;
|
|
71121
|
+
let forcedTimeoutNoResponse = false;
|
|
70009
71122
|
let directDispatchTaskIdForLedger;
|
|
70010
71123
|
let worktreeHasQueuedTask = false;
|
|
70011
71124
|
if (args.event === "agent:generating_completed") {
|
|
@@ -70015,7 +71128,8 @@ ${cleanBody}`;
|
|
|
70015
71128
|
if (sessionId) {
|
|
70016
71129
|
directDispatchTaskIdForLedger = readNonEmptyString(args.metadataEvent.taskId) || resolveActiveDirectDispatchTaskId(args.meshId, sessionId);
|
|
70017
71130
|
const isFalseIdle = isFalseIdleCompletion(args.metadataEvent);
|
|
70018
|
-
|
|
71131
|
+
forcedTimeoutNoResponse = isWeakCompletionEvidence(args.metadataEvent) && (args.metadataEvent.completionDiagnostic && typeof args.metadataEvent.completionDiagnostic === "object" && args.metadataEvent.completionDiagnostic.emittedAfterFinalizationTimeout === true) === true;
|
|
71132
|
+
completedTaskForLedger = markSessionTerminal(sessionId, forcedTimeoutNoResponse ? "failed" : "completed", eventTimestamp, { tentativeIfDirect: isFalseIdle });
|
|
70019
71133
|
if (nodeId && providerType) {
|
|
70020
71134
|
if (!isFalseIdle) {
|
|
70021
71135
|
sweepExpiredRemoteIdleSessions();
|
|
@@ -70038,7 +71152,7 @@ ${cleanBody}`;
|
|
|
70038
71152
|
}
|
|
70039
71153
|
}
|
|
70040
71154
|
const completedTaskId = completedTaskForLedger?.id;
|
|
70041
|
-
if (completedTaskId && hasPendingDependents(args.meshId, completedTaskId)) {
|
|
71155
|
+
if (completedTaskId && !forcedTimeoutNoResponse && hasPendingDependents(args.meshId, completedTaskId)) {
|
|
70042
71156
|
setImmediate(() => {
|
|
70043
71157
|
triggerMeshQueue(components, args.meshId).catch((e) => {
|
|
70044
71158
|
LOG2.warn("MeshQueue", `Dependent wake after task ${completedTaskId} failed: ${e?.message || e}`);
|
|
@@ -70264,7 +71378,7 @@ ${cleanBody}`;
|
|
|
70264
71378
|
});
|
|
70265
71379
|
});
|
|
70266
71380
|
}
|
|
70267
|
-
const ledgerKind = EVENT_TO_LEDGER_KIND[args.event];
|
|
71381
|
+
const ledgerKind = forcedTimeoutNoResponse ? "task_failed" : EVENT_TO_LEDGER_KIND[args.event];
|
|
70268
71382
|
if (ledgerKind) {
|
|
70269
71383
|
try {
|
|
70270
71384
|
const ledgerNodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0;
|
|
@@ -72466,6 +73580,20 @@ ${cleanBody}`;
|
|
|
72466
73580
|
}
|
|
72467
73581
|
return newest;
|
|
72468
73582
|
}
|
|
73583
|
+
function escapeTailFingerprint(messages) {
|
|
73584
|
+
let fp = "";
|
|
73585
|
+
for (const msg of messages) {
|
|
73586
|
+
if (!msg) continue;
|
|
73587
|
+
const ts2 = readChatMessageTimestampMs(msg) ?? 0;
|
|
73588
|
+
let len = -1;
|
|
73589
|
+
try {
|
|
73590
|
+
len = JSON.stringify(msg.content)?.length ?? -1;
|
|
73591
|
+
} catch {
|
|
73592
|
+
}
|
|
73593
|
+
fp += `${msg.role}:${ts2}:${len};`;
|
|
73594
|
+
}
|
|
73595
|
+
return fp;
|
|
73596
|
+
}
|
|
72469
73597
|
async function reconcileUnterminatedDirectDispatches(components, mesh, selfIds, localDaemonId) {
|
|
72470
73598
|
const dispatches = getActiveDirectDispatches(mesh.id);
|
|
72471
73599
|
rehydrateAckedHoldsForMesh(mesh.id);
|
|
@@ -72476,6 +73604,9 @@ ${cleanBody}`;
|
|
|
72476
73604
|
for (const key2 of heldKeys) {
|
|
72477
73605
|
if (!activeTaskKeys.has(key2)) deleteHoldState(key2, mesh.id);
|
|
72478
73606
|
}
|
|
73607
|
+
for (const key2 of [...nonIdleEscapeTracks.keys()]) {
|
|
73608
|
+
if (key2.startsWith(`${mesh.id}::`) && !activeTaskKeys.has(key2)) nonIdleEscapeTracks.delete(key2);
|
|
73609
|
+
}
|
|
72479
73610
|
if (dispatches.length === 0) return;
|
|
72480
73611
|
const dispatchMeshCommand = components.dispatchMeshCommand;
|
|
72481
73612
|
const nodeById = new Map(mesh.nodes.map((n) => [n.id, n]));
|
|
@@ -72540,23 +73671,43 @@ ${cleanBody}`;
|
|
|
72540
73671
|
consecutiveReadFailures: 0,
|
|
72541
73672
|
...priorHoldState?.transcriptIdleSinceMs !== void 0 ? { transcriptIdleSinceMs: priorHoldState.transcriptIdleSinceMs } : {}
|
|
72542
73673
|
});
|
|
72543
|
-
let nonIdleEscapeSinceAckMs = null;
|
|
72544
73674
|
const nowMs = Date.now();
|
|
72545
73675
|
if (readChatPayloadStatus(payload) !== "idle") {
|
|
72546
73676
|
if (isAcked && readChatPayloadStatus(payload) === "generating") {
|
|
72547
|
-
const
|
|
72548
|
-
const
|
|
72549
|
-
|
|
72550
|
-
|
|
73677
|
+
const escapeMessages = Array.isArray(payload.messages) ? payload.messages : [];
|
|
73678
|
+
const fingerprint = escapeTailFingerprint(escapeMessages);
|
|
73679
|
+
const prior = nonIdleEscapeTracks.get(synthKey);
|
|
73680
|
+
if (!prior || prior.tailFingerprint !== fingerprint) {
|
|
73681
|
+
nonIdleEscapeTracks.set(synthKey, { anchorMs: nowMs, tailFingerprint: fingerprint, candidateRecorded: false });
|
|
73682
|
+
if (prior) {
|
|
73683
|
+
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`);
|
|
73684
|
+
}
|
|
73685
|
+
} else {
|
|
73686
|
+
const staticMs = nowMs - prior.anchorMs;
|
|
73687
|
+
if (staticMs >= resolveAckedDeathDeadlineMs() && !prior.candidateRecorded) {
|
|
73688
|
+
prior.candidateRecorded = true;
|
|
73689
|
+
const candidateEvidence = extractFinalAssistantSummaryEvidence(escapeMessages);
|
|
73690
|
+
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`);
|
|
73691
|
+
traceMeshEventDrop("non_idle_escape_weak_candidate_held", {
|
|
73692
|
+
taskId,
|
|
73693
|
+
sessionId,
|
|
73694
|
+
nodeId,
|
|
73695
|
+
meshId: mesh.id,
|
|
73696
|
+
event: "agent:generating_completed"
|
|
73697
|
+
}, `staticTailMs=${staticMs} finalAssistant=${candidateEvidence.finalSummary ? "present" : "absent"} transcriptAt=${candidateEvidence.transcriptMessageAt ?? "n/a"}`);
|
|
73698
|
+
}
|
|
72551
73699
|
}
|
|
72552
73700
|
}
|
|
72553
|
-
|
|
72554
|
-
|
|
72555
|
-
continue;
|
|
72556
|
-
}
|
|
73701
|
+
setHoldState(synthKey, mesh.id, { liveConfirmedSinceAck: true, consecutiveReadFailures: 0 });
|
|
73702
|
+
continue;
|
|
72557
73703
|
}
|
|
72558
73704
|
const messages = Array.isArray(payload.messages) ? payload.messages : [];
|
|
72559
|
-
const
|
|
73705
|
+
const dispatchedAtMs = Date.parse(readNonEmptyString(dispatch.dispatchedAt));
|
|
73706
|
+
const evidence = extractFinalAssistantSummaryEvidence(
|
|
73707
|
+
messages,
|
|
73708
|
+
void 0,
|
|
73709
|
+
Number.isFinite(dispatchedAtMs) ? { turnStartedAtMs: dispatchedAtMs } : void 0
|
|
73710
|
+
);
|
|
72560
73711
|
if (hasTrailingToolActivityAfterFinalAssistant(messages)) {
|
|
72561
73712
|
setHoldState(synthKey, mesh.id, { liveConfirmedSinceAck: true, consecutiveReadFailures: 0 });
|
|
72562
73713
|
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 +73739,7 @@ ${cleanBody}`;
|
|
|
72588
73739
|
const deathDeadlineMs = resolveAckedDeathDeadlineMs();
|
|
72589
73740
|
const holdState = getHoldState(synthKey, mesh.id);
|
|
72590
73741
|
let fastTrackReady = false;
|
|
72591
|
-
if (
|
|
73742
|
+
if (evidence.finalSummary) {
|
|
72592
73743
|
const idleSinceMs = holdState?.transcriptIdleSinceMs ?? nowMs;
|
|
72593
73744
|
if (holdState && holdState.transcriptIdleSinceMs === void 0) {
|
|
72594
73745
|
setHoldState(synthKey, mesh.id, { ...holdState, transcriptIdleSinceMs: idleSinceMs });
|
|
@@ -72609,9 +73760,7 @@ ${cleanBody}`;
|
|
|
72609
73760
|
}
|
|
72610
73761
|
if (!fastTrackReady) {
|
|
72611
73762
|
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
|
-
}
|
|
73763
|
+
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
73764
|
}
|
|
72616
73765
|
}
|
|
72617
73766
|
if (realTerminalEmitPendingForTask(mesh.id, taskId)) {
|
|
@@ -72620,21 +73769,8 @@ ${cleanBody}`;
|
|
|
72620
73769
|
continue;
|
|
72621
73770
|
}
|
|
72622
73771
|
if (!evidence.finalSummary) continue;
|
|
72623
|
-
const dispatchedAtMs = Date.parse(readNonEmptyString(dispatch.dispatchedAt));
|
|
72624
73772
|
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) {
|
|
73773
|
+
if (Number.isFinite(dispatchedAtMs) && Number.isFinite(transcriptAtMs) && transcriptAtMs < dispatchedAtMs) {
|
|
72638
73774
|
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
73775
|
traceMeshEventDrop("reconcile_stale_summary_before_dispatch", {
|
|
72640
73776
|
taskId,
|
|
@@ -72645,15 +73781,12 @@ ${cleanBody}`;
|
|
|
72645
73781
|
}, `transcriptAt=${evidence.transcriptMessageAt} < dispatchedAt=${dispatch.dispatchedAt}`);
|
|
72646
73782
|
continue;
|
|
72647
73783
|
}
|
|
72648
|
-
const reprobeStatus =
|
|
73784
|
+
const reprobeStatus = await reprobeWorkerStatus(components, { isLocalNode, nodeDaemonId, readArgs });
|
|
72649
73785
|
if (reprobeStatus && reprobeStatus !== "idle") {
|
|
72650
73786
|
deleteHoldState(synthKey, mesh.id);
|
|
72651
73787
|
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
73788
|
continue;
|
|
72653
73789
|
}
|
|
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
73790
|
const providerSessionId = readNonEmptyString(payload.providerSessionId);
|
|
72658
73791
|
const workerSession = components.instanceManager.getInstance(sessionId);
|
|
72659
73792
|
const workerCoordinatorDaemonId = readNonEmptyString(
|
|
@@ -72814,8 +73947,12 @@ ${cleanBody}`;
|
|
|
72814
73947
|
if (!payload) return declined("chat_tail_unreadable", "worker transcript read returned no payload (offline/unreachable?)");
|
|
72815
73948
|
const payloadStatus = readChatPayloadProviderObservedStatus(payload);
|
|
72816
73949
|
const messages = Array.isArray(payload.messages) ? payload.messages : [];
|
|
72817
|
-
const evidence = extractFinalAssistantSummaryEvidence(messages);
|
|
72818
73950
|
const dispatchedAtMs = Date.parse(readNonEmptyString(row.dispatchTimestamp));
|
|
73951
|
+
const evidence = extractFinalAssistantSummaryEvidence(
|
|
73952
|
+
messages,
|
|
73953
|
+
void 0,
|
|
73954
|
+
Number.isFinite(dispatchedAtMs) ? { turnStartedAtMs: dispatchedAtMs } : void 0
|
|
73955
|
+
);
|
|
72819
73956
|
const transcriptAtMs = Date.parse(evidence.transcriptMessageAt ?? "");
|
|
72820
73957
|
if (Number.isFinite(dispatchedAtMs) && Number.isFinite(transcriptAtMs) && transcriptAtMs < dispatchedAtMs) {
|
|
72821
73958
|
return declined(
|
|
@@ -72861,7 +73998,10 @@ ${cleanBody}`;
|
|
|
72861
73998
|
`dispatchTimestamp=${row.dispatchTimestamp ?? "none"} transcriptMessageAt=${evidence.transcriptMessageAt ?? "none"} \u2014 shape-only evidence (${verdict.reason}) cannot complete an undated tail; reclaim owns it`
|
|
72862
73999
|
);
|
|
72863
74000
|
}
|
|
72864
|
-
const selectedFinalAssistant = finalAssistantPresent ? selectFinalAssistantTurnEndMessage(
|
|
74001
|
+
const selectedFinalAssistant = finalAssistantPresent ? selectFinalAssistantTurnEndMessage(
|
|
74002
|
+
messages,
|
|
74003
|
+
Number.isFinite(dispatchedAtMs) ? { turnStartedAtMs: dispatchedAtMs } : void 0
|
|
74004
|
+
) : null;
|
|
72865
74005
|
const selectedFinalAssistantIndex = selectedFinalAssistant ? messages.lastIndexOf(selectedFinalAssistant) : -1;
|
|
72866
74006
|
const admissionSnapshot = {
|
|
72867
74007
|
producer,
|
|
@@ -72897,6 +74037,7 @@ ${cleanBody}`;
|
|
|
72897
74037
|
admissionSnapshot
|
|
72898
74038
|
};
|
|
72899
74039
|
}
|
|
74040
|
+
var nonIdleEscapeTracks;
|
|
72900
74041
|
var init_mesh_completion_synthesis = __esm2({
|
|
72901
74042
|
"src/mesh/mesh-completion-synthesis.ts"() {
|
|
72902
74043
|
"use strict";
|
|
@@ -72917,6 +74058,7 @@ ${cleanBody}`;
|
|
|
72917
74058
|
init_mesh_reconcile_v2_backstop();
|
|
72918
74059
|
init_mesh_reconcile_acked_hold();
|
|
72919
74060
|
init_mesh_remote_event_pull();
|
|
74061
|
+
nonIdleEscapeTracks = /* @__PURE__ */ new Map();
|
|
72920
74062
|
}
|
|
72921
74063
|
});
|
|
72922
74064
|
function findLiveCoordinators(components) {
|
|
@@ -73527,6 +74669,53 @@ ${cleanBody}`;
|
|
|
73527
74669
|
producer: "early_idle_transcript_evidence"
|
|
73528
74670
|
});
|
|
73529
74671
|
if (terminalEvidence) {
|
|
74672
|
+
const weakCandidateKey = `${meshId}::${row.id}`;
|
|
74673
|
+
if (terminalEvidence.evidenceLevel === "weak") {
|
|
74674
|
+
const candidate = weakCompletionCandidateStreak.get(weakCandidateKey) ?? { count: 0 };
|
|
74675
|
+
if (candidate.finalAssistantAt !== void 0 && candidate.finalAssistantAt !== terminalEvidence.transcriptMessageAt) {
|
|
74676
|
+
candidate.count = 0;
|
|
74677
|
+
}
|
|
74678
|
+
candidate.finalAssistantAt = terminalEvidence.transcriptMessageAt;
|
|
74679
|
+
candidate.count += 1;
|
|
74680
|
+
weakCompletionCandidateStreak.set(weakCandidateKey, candidate);
|
|
74681
|
+
if (candidate.count === 1) {
|
|
74682
|
+
const nodeLabel = row.assignedNodeId ? `Node '${row.assignedNodeId}'` : "Remote agent";
|
|
74683
|
+
const metadataEvent = {
|
|
74684
|
+
evidenceLevel: "weak",
|
|
74685
|
+
finalSummary: terminalEvidence.finalSummary,
|
|
74686
|
+
taskId: row.id,
|
|
74687
|
+
providerType: readNonEmptyString(row.assignedProviderType) || terminalEvidence.providerType,
|
|
74688
|
+
providerSessionId: terminalEvidence.providerSessionId,
|
|
74689
|
+
source: "early_idle_transcript_evidence",
|
|
74690
|
+
targetSessionId: terminalEvidence.sessionId
|
|
74691
|
+
};
|
|
74692
|
+
try {
|
|
74693
|
+
queuePendingMeshCoordinatorEvent({
|
|
74694
|
+
event: "agent:generating_completed",
|
|
74695
|
+
meshId,
|
|
74696
|
+
nodeLabel,
|
|
74697
|
+
nodeId: row.assignedNodeId,
|
|
74698
|
+
metadataEvent,
|
|
74699
|
+
coordinatorMessage: buildMeshSystemMessage({ event: "agent:generating_completed", nodeLabel, metadataEvent }),
|
|
74700
|
+
queuedAt: nowMs
|
|
74701
|
+
});
|
|
74702
|
+
} catch {
|
|
74703
|
+
}
|
|
74704
|
+
}
|
|
74705
|
+
if (candidate.count < WEAK_COMPLETION_CANDIDATE_CONFIRM_TICKS) {
|
|
74706
|
+
traceMeshEventDrop("weak_completion_candidate_held", {
|
|
74707
|
+
taskId: row.id,
|
|
74708
|
+
sessionId: row.assignedSessionId,
|
|
74709
|
+
nodeId: row.assignedNodeId,
|
|
74710
|
+
meshId,
|
|
74711
|
+
event: "agent:generating_completed"
|
|
74712
|
+
}, `weak message-shape completion candidate ${candidate.count}/${WEAK_COMPLETION_CANDIDATE_CONFIRM_TICKS} (early-idle) \u2014 awaiting re-confirmation`);
|
|
74713
|
+
continue;
|
|
74714
|
+
}
|
|
74715
|
+
weakCompletionCandidateStreak.delete(weakCandidateKey);
|
|
74716
|
+
} else {
|
|
74717
|
+
weakCompletionCandidateStreak.delete(weakCandidateKey);
|
|
74718
|
+
}
|
|
73530
74719
|
const propagation = propagateWatchdogTranscriptCompletion(
|
|
73531
74720
|
components,
|
|
73532
74721
|
meshId,
|
|
@@ -74466,6 +75655,30 @@ ${cleanBody}`;
|
|
|
74466
75655
|
LOG2.warn("MeshReconcile", `Completion reconcile failed for mesh ${mesh.id}: ${e?.message || e}`);
|
|
74467
75656
|
}
|
|
74468
75657
|
}
|
|
75658
|
+
if (store) {
|
|
75659
|
+
for (const mesh of listMeshes()) {
|
|
75660
|
+
const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
|
|
75661
|
+
if (!daemonHostsMesh(mesh, selfIds)) continue;
|
|
75662
|
+
try {
|
|
75663
|
+
const gateStore = store.graphStore();
|
|
75664
|
+
const preSweep = new Map(
|
|
75665
|
+
gateStore.listGatesByMesh(mesh.id, ["awaiting_coordinator", "claimed"]).map((g) => [g.gateId, { graphId: g.graphId, onTimeout: g.onTimeout, deadlineAt: g.deadlineAt }])
|
|
75666
|
+
);
|
|
75667
|
+
const swept = sweepMeshGraphGateTimeouts(mesh.id);
|
|
75668
|
+
for (const gateId of swept.expiredGateIds) {
|
|
75669
|
+
const meta3 = preSweep.get(gateId);
|
|
75670
|
+
recordGraphGateExpired(mesh.id, {
|
|
75671
|
+
gateId,
|
|
75672
|
+
graphId: meta3?.graphId,
|
|
75673
|
+
policy: meta3?.onTimeout ?? "hold",
|
|
75674
|
+
deadlineAt: meta3?.deadlineAt
|
|
75675
|
+
});
|
|
75676
|
+
}
|
|
75677
|
+
} catch (e) {
|
|
75678
|
+
LOG2.warn("MeshReconcile", `Graph gate sweep failed for mesh ${mesh.id}: ${e?.message || e}`);
|
|
75679
|
+
}
|
|
75680
|
+
}
|
|
75681
|
+
}
|
|
74469
75682
|
if (store) {
|
|
74470
75683
|
for (const mesh of listMeshes()) {
|
|
74471
75684
|
const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
|
|
@@ -74768,6 +75981,8 @@ ${cleanBody}`;
|
|
|
74768
75981
|
init_mesh_reconcile_stranded_dispatch();
|
|
74769
75982
|
init_mesh_reconcile_unresolved_forward();
|
|
74770
75983
|
init_mesh_graph_workspace_saga();
|
|
75984
|
+
init_mesh_graph_gates();
|
|
75985
|
+
init_mesh_graph_provenance();
|
|
74771
75986
|
init_mesh_reconcile_v2_backstop();
|
|
74772
75987
|
init_mesh_reconcile_acked_hold();
|
|
74773
75988
|
init_mesh_reconcile_coordinator_drain();
|
|
@@ -77195,6 +78410,29 @@ ${cleanBody}`;
|
|
|
77195
78410
|
"use strict";
|
|
77196
78411
|
}
|
|
77197
78412
|
});
|
|
78413
|
+
function resolveRefineVitestMaxWorkers(env2 = process.env) {
|
|
78414
|
+
const raw = env2[REFINE_VITEST_MAX_WORKERS_ENV];
|
|
78415
|
+
if (raw === void 0 || raw.trim() === "") return DEFAULT_REFINE_VITEST_MAX_WORKERS;
|
|
78416
|
+
const parsed = Number.parseInt(raw, 10);
|
|
78417
|
+
if (!Number.isFinite(parsed)) return DEFAULT_REFINE_VITEST_MAX_WORKERS;
|
|
78418
|
+
if (parsed === 0) return null;
|
|
78419
|
+
return Math.max(1, parsed);
|
|
78420
|
+
}
|
|
78421
|
+
function refineGateChildEnv(env2 = process.env) {
|
|
78422
|
+
if (env2.VITEST_MAX_WORKERS) return {};
|
|
78423
|
+
const cap = resolveRefineVitestMaxWorkers(env2);
|
|
78424
|
+
if (cap === null) return {};
|
|
78425
|
+
return { VITEST_MAX_WORKERS: String(cap) };
|
|
78426
|
+
}
|
|
78427
|
+
var REFINE_VITEST_MAX_WORKERS_ENV;
|
|
78428
|
+
var DEFAULT_REFINE_VITEST_MAX_WORKERS;
|
|
78429
|
+
var init_mesh_refine_worker_cap = __esm2({
|
|
78430
|
+
"src/mesh/mesh-refine-worker-cap.ts"() {
|
|
78431
|
+
"use strict";
|
|
78432
|
+
REFINE_VITEST_MAX_WORKERS_ENV = "ADHDEV_REFINE_VITEST_MAX_WORKERS";
|
|
78433
|
+
DEFAULT_REFINE_VITEST_MAX_WORKERS = 2;
|
|
78434
|
+
}
|
|
78435
|
+
});
|
|
77198
78436
|
function truncateValidationOutput(value) {
|
|
77199
78437
|
const text = typeof value === "string" ? value : value == null ? "" : String(value);
|
|
77200
78438
|
if (text.length <= REFINE_VALIDATION_SUMMARY_CHARS) return text;
|
|
@@ -78673,7 +79911,7 @@ ${mergeTreeErr?.stderr || ""}`;
|
|
|
78673
79911
|
encoding: "utf8",
|
|
78674
79912
|
timeout,
|
|
78675
79913
|
maxBuffer: candidate.outputLimitBytes || REFINE_VALIDATION_OUTPUT_LIMIT_BYTES,
|
|
78676
|
-
env: { ...process.env, CI: process.env.CI || "1", ...candidate.env || {} },
|
|
79914
|
+
env: { ...process.env, CI: process.env.CI || "1", ...refineGateChildEnv(), ...candidate.env || {} },
|
|
78677
79915
|
...spawn7.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}
|
|
78678
79916
|
});
|
|
78679
79917
|
summary.bootstrapCommandsRun.push(commandRecord(candidate, cwd, startedAt, result, true, { exitCode: 0 }));
|
|
@@ -78718,7 +79956,7 @@ ${mergeTreeErr?.stderr || ""}`;
|
|
|
78718
79956
|
encoding: "utf8",
|
|
78719
79957
|
timeout,
|
|
78720
79958
|
maxBuffer: candidate.outputLimitBytes || REFINE_VALIDATION_OUTPUT_LIMIT_BYTES,
|
|
78721
|
-
env: { ...process.env, CI: process.env.CI || "1", ...candidate.env || {} },
|
|
79959
|
+
env: { ...process.env, CI: process.env.CI || "1", ...refineGateChildEnv(), ...candidate.env || {} },
|
|
78722
79960
|
...spawn7.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}
|
|
78723
79961
|
});
|
|
78724
79962
|
summary.commandsRun.push(commandRecord(candidate, cwd, startedAt, result, true, { exitCode: 0 }));
|
|
@@ -78784,6 +80022,7 @@ ${mergeTreeErr?.stderr || ""}`;
|
|
|
78784
80022
|
fs48 = __toESM2(require("fs"));
|
|
78785
80023
|
import_node_child_process11 = require("child_process");
|
|
78786
80024
|
init_resolve_executable();
|
|
80025
|
+
init_mesh_refine_worker_cap();
|
|
78787
80026
|
GIT4 = process.platform === "win32" ? resolveWin32Executable("git") : "git";
|
|
78788
80027
|
REFINE_VALIDATION_TIMEOUT_MS = 12e4;
|
|
78789
80028
|
REFINE_VALIDATION_OUTPUT_LIMIT_BYTES = 128 * 1024;
|
|
@@ -78869,6 +80108,45 @@ ${mergeTreeErr?.stderr || ""}`;
|
|
|
78869
80108
|
REFINE_INFLIGHT_FRESHNESS_MS = 30 * 6e4;
|
|
78870
80109
|
}
|
|
78871
80110
|
});
|
|
80111
|
+
function resolveRefineMaxConcurrentJobs(env2 = process.env) {
|
|
80112
|
+
const raw = env2[REFINE_MAX_CONCURRENT_JOBS_ENV];
|
|
80113
|
+
if (raw === void 0 || raw.trim() === "") return DEFAULT_REFINE_MAX_CONCURRENT_JOBS;
|
|
80114
|
+
const parsed = Number.parseInt(raw, 10);
|
|
80115
|
+
if (!Number.isFinite(parsed) || parsed < 1) return DEFAULT_REFINE_MAX_CONCURRENT_JOBS;
|
|
80116
|
+
return parsed;
|
|
80117
|
+
}
|
|
80118
|
+
async function runWithRefineExecutionSlot(label, task) {
|
|
80119
|
+
const limit = resolveRefineMaxConcurrentJobs();
|
|
80120
|
+
if (activeExecutions >= limit) {
|
|
80121
|
+
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.`);
|
|
80122
|
+
await new Promise((resolve30) => {
|
|
80123
|
+
waitQueue.push(resolve30);
|
|
80124
|
+
});
|
|
80125
|
+
LOG2.info("Mesh", `[Refinery] ${label} dequeued \u2014 starting now (${activeExecutions} other job(s) still executing).`);
|
|
80126
|
+
}
|
|
80127
|
+
activeExecutions += 1;
|
|
80128
|
+
try {
|
|
80129
|
+
return await task();
|
|
80130
|
+
} finally {
|
|
80131
|
+
activeExecutions -= 1;
|
|
80132
|
+
const next = waitQueue.shift();
|
|
80133
|
+
if (next) next();
|
|
80134
|
+
}
|
|
80135
|
+
}
|
|
80136
|
+
var REFINE_MAX_CONCURRENT_JOBS_ENV;
|
|
80137
|
+
var DEFAULT_REFINE_MAX_CONCURRENT_JOBS;
|
|
80138
|
+
var activeExecutions;
|
|
80139
|
+
var waitQueue;
|
|
80140
|
+
var init_mesh_refine_concurrency = __esm2({
|
|
80141
|
+
"src/mesh/mesh-refine-concurrency.ts"() {
|
|
80142
|
+
"use strict";
|
|
80143
|
+
init_logger();
|
|
80144
|
+
REFINE_MAX_CONCURRENT_JOBS_ENV = "ADHDEV_REFINE_MAX_CONCURRENT_JOBS";
|
|
80145
|
+
DEFAULT_REFINE_MAX_CONCURRENT_JOBS = 1;
|
|
80146
|
+
activeExecutions = 0;
|
|
80147
|
+
waitQueue = [];
|
|
80148
|
+
}
|
|
80149
|
+
});
|
|
78872
80150
|
function resolveRefineResumeDispatchGraceMs() {
|
|
78873
80151
|
return resolveTunedReconcileMs("MESH_REFINE_RESUME_DISPATCH_GRACE_MS", 6e4, 0, 10 * 6e4);
|
|
78874
80152
|
}
|
|
@@ -80807,7 +82085,10 @@ ${e?.stderr || ""}`;
|
|
|
80807
82085
|
await appendRefineBatchJobLedger(self, "task_dispatched", handle);
|
|
80808
82086
|
queueRefineBatchJobEvent(self, "refine:accepted", handle);
|
|
80809
82087
|
setImmediate(() => {
|
|
80810
|
-
void
|
|
82088
|
+
void runWithRefineExecutionSlot(
|
|
82089
|
+
`batch ${handle.jobId} (mesh ${meshId})`,
|
|
82090
|
+
() => finishMeshRefineBatchJob(self, handle, orderedNodes, ordering, args)
|
|
82091
|
+
);
|
|
80811
82092
|
});
|
|
80812
82093
|
return {
|
|
80813
82094
|
...handle,
|
|
@@ -81006,7 +82287,10 @@ ${e?.stderr || ""}`;
|
|
|
81006
82287
|
queueRefineJobEvent(self, "refine:accepted", handle);
|
|
81007
82288
|
setImmediate(() => {
|
|
81008
82289
|
void recordRefineAcceptBaseDivergence(self, handle, node);
|
|
81009
|
-
void
|
|
82290
|
+
void runWithRefineExecutionSlot(
|
|
82291
|
+
`job ${handle.jobId} (node ${handle.targetNodeId})`,
|
|
82292
|
+
() => finishMeshRefineJob(self, handle, args)
|
|
82293
|
+
);
|
|
81010
82294
|
});
|
|
81011
82295
|
return handle;
|
|
81012
82296
|
}
|
|
@@ -81035,6 +82319,7 @@ ${e?.stderr || ""}`;
|
|
|
81035
82319
|
init_git_locale();
|
|
81036
82320
|
init_mesh_node_identity();
|
|
81037
82321
|
init_mesh_refine_gates();
|
|
82322
|
+
init_mesh_refine_concurrency();
|
|
81038
82323
|
init_router_refine_resume();
|
|
81039
82324
|
RETRYABLE_BASE_MOVEMENT_CODES = /* @__PURE__ */ new Set(["base_moved", "base_locked"]);
|
|
81040
82325
|
}
|
|
@@ -81110,6 +82395,9 @@ ${e?.stderr || ""}`;
|
|
|
81110
82395
|
MAX_SIZE_ROTATION_GENERATIONS: () => MAX_SIZE_ROTATION_GENERATIONS,
|
|
81111
82396
|
MESH_CONVERGE_FAST_FORWARD_TAG: () => MESH_CONVERGE_FAST_FORWARD_TAG,
|
|
81112
82397
|
MESH_CONVERGE_REFINE_TAG: () => MESH_CONVERGE_REFINE_TAG,
|
|
82398
|
+
MESH_GATE_DEFAULT_LEASE_SECONDS: () => MESH_GATE_DEFAULT_LEASE_SECONDS,
|
|
82399
|
+
MESH_GATE_NAMED_OUTCOMES: () => MESH_GATE_NAMED_OUTCOMES,
|
|
82400
|
+
MESH_GATE_RELEASE_PATCH_KEYS: () => MESH_GATE_RELEASE_PATCH_KEYS,
|
|
81113
82401
|
MESH_JSON_CONFIG_LOCATIONS: () => MESH_JSON_CONFIG_LOCATIONS,
|
|
81114
82402
|
MESH_JSON_CONFIG_SCHEMA: () => MESH_JSON_CONFIG_SCHEMA,
|
|
81115
82403
|
MESH_JSON_PROVIDER_DEFAULTS_EXAMPLE: () => MESH_JSON_PROVIDER_DEFAULTS_EXAMPLE,
|
|
@@ -81119,16 +82407,21 @@ ${e?.stderr || ""}`;
|
|
|
81119
82407
|
MESH_MISSION_LIST_STATUS_LIMIT: () => MESH_MISSION_LIST_STATUS_LIMIT,
|
|
81120
82408
|
MESH_MISSION_STATUSES: () => MESH_MISSION_STATUSES,
|
|
81121
82409
|
MESH_NODE_LIVE_TRUTH_MARKER: () => MESH_NODE_LIVE_TRUTH_MARKER,
|
|
82410
|
+
MESH_ON_DEPENDENCY_FAILURE_PUBLIC_TEXT: () => MESH_ON_DEPENDENCY_FAILURE_PUBLIC_TEXT,
|
|
81122
82411
|
MESH_REFINE_CONFIG_LOCATIONS: () => MESH_REFINE_CONFIG_LOCATIONS,
|
|
81123
82412
|
MESH_REFINE_CONFIG_SCHEMA: () => MESH_REFINE_CONFIG_SCHEMA,
|
|
81124
82413
|
MESH_SCHEDULING_STRATEGIES: () => MESH_SCHEDULING_STRATEGIES,
|
|
82414
|
+
MESH_SUPERSEDED_SINGLE_REASONS: () => MESH_SUPERSEDED_SINGLE_REASONS,
|
|
81125
82415
|
MESH_TASK_GRAPH_MAX_TASKS: () => MESH_TASK_GRAPH_MAX_TASKS,
|
|
81126
82416
|
MESH_TASK_PRIORITIES: () => MESH_TASK_PRIORITIES,
|
|
82417
|
+
MESH_VALID_SINGLE_REASONS: () => MESH_VALID_SINGLE_REASONS,
|
|
81127
82418
|
MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS: () => MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS,
|
|
81128
82419
|
MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA: () => MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA,
|
|
81129
82420
|
MIN_GIT_WORKSPACE_POLL_INTERVAL_MS: () => MIN_GIT_WORKSPACE_POLL_INTERVAL_MS,
|
|
81130
82421
|
MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS: () => MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS2,
|
|
81131
82422
|
MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS: () => MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS2,
|
|
82423
|
+
MeshGraphPlanError: () => MeshGraphPlanError,
|
|
82424
|
+
MeshGraphPolicyError: () => MeshGraphPolicyError,
|
|
81132
82425
|
NOT_BEFORE_RELATIVE_THRESHOLD_MS: () => NOT_BEFORE_RELATIVE_THRESHOLD_MS,
|
|
81133
82426
|
NodePtyTransportFactory: () => NodePtyTransportFactory,
|
|
81134
82427
|
OPERATING_NOTE_DEDUPE_WINDOW: () => OPERATING_NOTE_DEDUPE_WINDOW,
|
|
@@ -81185,6 +82478,7 @@ ${e?.stderr || ""}`;
|
|
|
81185
82478
|
buildMeshActiveWork: () => buildMeshActiveWork,
|
|
81186
82479
|
buildMeshActiveWorkSummary: () => buildMeshActiveWorkSummary,
|
|
81187
82480
|
buildMeshAsyncRefineJobs: () => buildMeshAsyncRefineJobs,
|
|
82481
|
+
buildMeshGraphViews: () => buildMeshGraphViews,
|
|
81188
82482
|
buildMeshHostRequiredFailure: () => buildMeshHostRequiredFailure,
|
|
81189
82483
|
buildMeshJsonConfigScaffold: () => buildMeshJsonConfigScaffold,
|
|
81190
82484
|
buildMeshLedgerReconciliationEvidence: () => buildMeshLedgerReconciliationEvidence,
|
|
@@ -81211,6 +82505,7 @@ ${e?.stderr || ""}`;
|
|
|
81211
82505
|
buildUserChatMessage: () => buildUserChatMessage,
|
|
81212
82506
|
cancelTask: () => cancelTask,
|
|
81213
82507
|
canonicalDaemonId: () => canonicalDaemonId,
|
|
82508
|
+
claimMeshGraphGate: () => claimMeshGraphGate,
|
|
81214
82509
|
claimNextTask: () => claimNextTask,
|
|
81215
82510
|
classifyChatMessageVisibility: () => classifyChatMessageVisibility,
|
|
81216
82511
|
classifyDuplicateMeshDispatch: () => classifyDuplicateMeshDispatch,
|
|
@@ -81225,14 +82520,17 @@ ${e?.stderr || ""}`;
|
|
|
81225
82520
|
clearPendingMeshCoordinatorEvents: () => clearPendingMeshCoordinatorEvents,
|
|
81226
82521
|
collectPendingApprovals: () => collectPendingApprovals,
|
|
81227
82522
|
collectSyncTargetTypes: () => collectSyncTargetTypes,
|
|
82523
|
+
commitMeshGraphPlan: () => commitMeshGraphPlan,
|
|
81228
82524
|
compareGitSnapshots: () => compareGitSnapshots,
|
|
81229
82525
|
compensateWorkspaceIntent: () => compensateWorkspaceIntent,
|
|
82526
|
+
computeMeshGraphPlanDigest: () => computeMeshGraphPlanDigest,
|
|
81230
82527
|
computeMeshMissionStats: () => computeMeshMissionStats,
|
|
81231
82528
|
computeMeshTaskStats: () => computeMeshTaskStats,
|
|
81232
82529
|
computeProviderTreeDigest: () => computeProviderTreeDigest,
|
|
81233
82530
|
configureDebugTraceStore: () => configureDebugTraceStore,
|
|
81234
82531
|
connectCdpManager: () => connectCdpManager,
|
|
81235
82532
|
consoleSymbols: () => consoleSymbols,
|
|
82533
|
+
coordinatorGateBlockReason: () => coordinatorGateBlockReason,
|
|
81236
82534
|
coordinatorIdentityFromEmitFields: () => coordinatorIdentityFromEmitFields,
|
|
81237
82535
|
createDebugTraceStore: () => createDebugTraceStore,
|
|
81238
82536
|
createDefaultGitCommandServices: () => createDefaultGitCommandServices,
|
|
@@ -81252,6 +82550,7 @@ ${e?.stderr || ""}`;
|
|
|
81252
82550
|
delegatedWorkerAutoApproveSettings: () => delegatedWorkerAutoApproveSettings,
|
|
81253
82551
|
deleteDirectDispatchesByTaskId: () => deleteDirectDispatchesByTaskId,
|
|
81254
82552
|
deleteMesh: () => deleteMesh,
|
|
82553
|
+
deriveDependencyFailures: () => deriveDependencyFailures,
|
|
81255
82554
|
deriveMeshNodeHealthFromGit: () => deriveMeshNodeHealthFromGit,
|
|
81256
82555
|
deriveMeshReviewInboxItems: () => deriveMeshReviewInboxItems,
|
|
81257
82556
|
deriveWorkspaceBranchIdentity: () => deriveWorkspaceBranchIdentity,
|
|
@@ -81352,6 +82651,7 @@ ${e?.stderr || ""}`;
|
|
|
81352
82651
|
installGlobalInterceptor: () => installGlobalInterceptor,
|
|
81353
82652
|
interactivePromptFromClaudeAskUserQuestion: () => interactivePromptFromClaudeAskUserQuestion,
|
|
81354
82653
|
isActivityChatMessage: () => isActivityChatMessage,
|
|
82654
|
+
isAdvancedGraphTask: () => isAdvancedGraphTask,
|
|
81355
82655
|
isAlwaysOnTraceCategory: () => isAlwaysOnTraceCategory,
|
|
81356
82656
|
isBuiltinChatMessageKind: () => isBuiltinChatMessageKind,
|
|
81357
82657
|
isCdpConnected: () => isCdpConnected,
|
|
@@ -81437,11 +82737,13 @@ ${e?.stderr || ""}`;
|
|
|
81437
82737
|
normalizeMeshTaskPriority: () => normalizeMeshTaskPriority,
|
|
81438
82738
|
normalizeMeshWorkerResult: () => normalizeMeshWorkerResult,
|
|
81439
82739
|
normalizeMessageParts: () => normalizeMessageParts,
|
|
82740
|
+
normalizeOrchestrationDecision: () => normalizeOrchestrationDecision,
|
|
81440
82741
|
normalizeRepoIdentity: () => normalizeRepoIdentity,
|
|
81441
82742
|
normalizeRepoMeshDeclarativeConfig: () => normalizeRepoMeshDeclarativeConfig,
|
|
81442
82743
|
normalizeSessionModalFields: () => normalizeSessionModalFields,
|
|
81443
82744
|
notifyCoordinatorOfOrphanedPins: () => notifyCoordinatorOfOrphanedPins,
|
|
81444
82745
|
otherTrackConfigDir: () => otherTrackConfigDir,
|
|
82746
|
+
parseOnDependencyFailurePolicy: () => parseOnDependencyFailurePolicy,
|
|
81445
82747
|
parsePorcelainV2Status: () => parsePorcelainV2Status,
|
|
81446
82748
|
parseProviderSourceConfigUpdate: () => parseProviderSourceConfigUpdate,
|
|
81447
82749
|
parseWorktreeListOutput: () => parseWorktreeListOutput,
|
|
@@ -81458,6 +82760,7 @@ ${e?.stderr || ""}`;
|
|
|
81458
82760
|
printQuota: () => printQuota2,
|
|
81459
82761
|
printQuotaInstallError: () => printQuotaInstallError2,
|
|
81460
82762
|
probeCdpPort: () => probeCdpPort,
|
|
82763
|
+
projectGraphPublicPolicy: () => projectGraphPublicPolicy,
|
|
81461
82764
|
pruneOperatingNotes: () => pruneOperatingNotes,
|
|
81462
82765
|
pruneStaleDirectDispatches: () => pruneStaleDirectDispatches,
|
|
81463
82766
|
queuePendingMeshCoordinatorEvent: () => queuePendingMeshCoordinatorEvent,
|
|
@@ -81481,15 +82784,23 @@ ${e?.stderr || ""}`;
|
|
|
81481
82784
|
reconcileDirectDispatchCompletionFromTranscript: () => reconcileDirectDispatchCompletionFromTranscript,
|
|
81482
82785
|
recordDebugTrace: () => recordDebugTrace,
|
|
81483
82786
|
recordDirectDispatchTask: () => recordDirectDispatchTask,
|
|
82787
|
+
recordGraphEnqueueCommitted: () => recordGraphEnqueueCommitted,
|
|
82788
|
+
recordGraphEnqueueRolledBack: () => recordGraphEnqueueRolledBack,
|
|
82789
|
+
recordGraphEnqueueValidationFailed: () => recordGraphEnqueueValidationFailed,
|
|
82790
|
+
recordGraphGateClaimed: () => recordGraphGateClaimed,
|
|
82791
|
+
recordGraphGateExpired: () => recordGraphGateExpired,
|
|
82792
|
+
recordGraphGateReleased: () => recordGraphGateReleased,
|
|
81484
82793
|
recordMeshToolCall: () => recordMeshToolCall,
|
|
81485
82794
|
recordMissingSessionAttempt: () => recordMissingSessionAttempt2,
|
|
81486
82795
|
recordSessionUsage: () => recordSessionUsage,
|
|
81487
82796
|
recoverExpiredWorkspaceSagas: () => recoverExpiredWorkspaceSagas,
|
|
81488
82797
|
registerExtensionProviders: () => registerExtensionProviders,
|
|
81489
82798
|
registerMeshCoordinator: () => registerMeshCoordinator,
|
|
82799
|
+
releaseMeshGraphGate: () => releaseMeshGraphGate,
|
|
81490
82800
|
removeMagiKindPanel: () => removeMagiKindPanel,
|
|
81491
82801
|
removeNode: () => removeNode,
|
|
81492
82802
|
removeWorktree: () => removeWorktree,
|
|
82803
|
+
requestUsesGraphV2: () => requestUsesGraphV2,
|
|
81493
82804
|
requeueHeldMeshCoordinatorEvents: () => requeueHeldMeshCoordinatorEvents,
|
|
81494
82805
|
requeueTask: () => requeueTask,
|
|
81495
82806
|
requireMeshHostQueueOwner: () => requireMeshHostQueueOwner,
|
|
@@ -81524,6 +82835,7 @@ ${e?.stderr || ""}`;
|
|
|
81524
82835
|
resolveNodeSchedulingPriority: () => resolveNodeSchedulingPriority,
|
|
81525
82836
|
resolveNotBefore: () => resolveNotBefore,
|
|
81526
82837
|
resolveNpmPublishedVersion: () => resolveNpmPublishedVersion,
|
|
82838
|
+
resolveOnDependencyFailurePolicy: () => resolveOnDependencyFailurePolicy,
|
|
81527
82839
|
resolveProviderChannel: () => resolveProviderChannel,
|
|
81528
82840
|
resolveProviderMaxParallel: () => resolveProviderMaxParallel,
|
|
81529
82841
|
resolveScopedMeshId: () => resolveScopedMeshId,
|
|
@@ -81568,6 +82880,7 @@ ${e?.stderr || ""}`;
|
|
|
81568
82880
|
summarizeMeshUsage: () => summarizeMeshUsage,
|
|
81569
82881
|
summarizeMissionTasks: () => summarizeMissionTasks,
|
|
81570
82882
|
supportsUnicodeSymbols: () => supportsUnicodeSymbols,
|
|
82883
|
+
sweepMeshGraphGateTimeouts: () => sweepMeshGraphGateTimeouts,
|
|
81571
82884
|
taskDependenciesSatisfied: () => taskDependenciesSatisfied,
|
|
81572
82885
|
tombstoneOperatingNote: () => tombstoneOperatingNote,
|
|
81573
82886
|
totalTokens: () => totalTokens,
|
|
@@ -84066,10 +85379,658 @@ ${e?.stderr || ""}`;
|
|
|
84066
85379
|
};
|
|
84067
85380
|
}
|
|
84068
85381
|
init_mesh_work_queue();
|
|
85382
|
+
init_mesh_graph_derived_failure();
|
|
84069
85383
|
init_mesh_graph_workspace_saga();
|
|
84070
85384
|
init_mesh_graph_workspace_safety();
|
|
84071
85385
|
init_mesh_graph_workspace_identity();
|
|
84072
85386
|
init_mesh_graph_workspace_ports();
|
|
85387
|
+
init_mesh_graph_gates();
|
|
85388
|
+
var import_crypto14 = require("crypto");
|
|
85389
|
+
init_logger();
|
|
85390
|
+
init_mesh_runtime_store();
|
|
85391
|
+
init_mesh_graph_input_binding();
|
|
85392
|
+
init_mesh_graph_types();
|
|
85393
|
+
init_mesh_graph_derived_failure();
|
|
85394
|
+
init_mesh_graph_transition_runner();
|
|
85395
|
+
init_mesh_graph_workspace_saga();
|
|
85396
|
+
init_mesh_work_queue();
|
|
85397
|
+
var MeshGraphPlanError = class extends Error {
|
|
85398
|
+
constructor(code, message, extra) {
|
|
85399
|
+
super(`${code}: ${message}`);
|
|
85400
|
+
this.code = code;
|
|
85401
|
+
this.extra = extra;
|
|
85402
|
+
this.name = "MeshGraphPlanError";
|
|
85403
|
+
}
|
|
85404
|
+
};
|
|
85405
|
+
function isAdvancedGraphTask(spec) {
|
|
85406
|
+
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;
|
|
85407
|
+
}
|
|
85408
|
+
function requestUsesGraphV2(req) {
|
|
85409
|
+
if (Array.isArray(req.gates) && req.gates.length > 0) return true;
|
|
85410
|
+
if (Array.isArray(req.workspaces) && req.workspaces.length > 0) return true;
|
|
85411
|
+
return (req.tasks ?? []).some(isAdvancedGraphTask);
|
|
85412
|
+
}
|
|
85413
|
+
function normalizeRef(value, label) {
|
|
85414
|
+
const ref = typeof value === "string" ? value.trim() : "";
|
|
85415
|
+
if (!ref) throw new MeshGraphPlanError("invalid_graph_ref", `${label} requires a non-empty ref`);
|
|
85416
|
+
return ref;
|
|
85417
|
+
}
|
|
85418
|
+
function pickEnum(value, allowed, fallback, code, label) {
|
|
85419
|
+
if (value === void 0 || value === null || value === "") return fallback;
|
|
85420
|
+
if (typeof value === "string" && allowed.includes(value)) return value;
|
|
85421
|
+
throw new MeshGraphPlanError(code, `${label} must be one of ${allowed.join(" | ")} (got ${JSON.stringify(value)})`);
|
|
85422
|
+
}
|
|
85423
|
+
function computeMeshGraphPlanDigest(req) {
|
|
85424
|
+
const normalized = {
|
|
85425
|
+
tasks: (req.tasks ?? []).map((t) => ({
|
|
85426
|
+
ref: typeof t.ref === "string" ? t.ref.trim() : null,
|
|
85427
|
+
message: t.message,
|
|
85428
|
+
depends_on: [...t.dependsOn ?? []].sort(),
|
|
85429
|
+
gated_by: [...t.gated_by ?? []].sort(),
|
|
85430
|
+
inputs_from: t.inputs_from ?? null,
|
|
85431
|
+
run_if: t.run_if ?? null,
|
|
85432
|
+
on_false: t.on_false ?? null,
|
|
85433
|
+
on_upstream_skip: t.on_upstream_skip ?? null,
|
|
85434
|
+
workspace_ref: t.workspace_ref ?? null,
|
|
85435
|
+
target_node_id: t.targetNodeId ?? null,
|
|
85436
|
+
task_mode: t.taskMode ?? null,
|
|
85437
|
+
difficulty: t.difficulty ?? null
|
|
85438
|
+
})),
|
|
85439
|
+
gates: (req.gates ?? []).map((g) => ({
|
|
85440
|
+
ref: g.ref,
|
|
85441
|
+
action: g.action ?? "custom",
|
|
85442
|
+
depends_on: [...g.depends_on ?? []].sort(),
|
|
85443
|
+
on_timeout: g.on_timeout ?? "hold",
|
|
85444
|
+
lease_seconds: g.lease_seconds ?? null,
|
|
85445
|
+
deadline_seconds: g.deadline_seconds ?? null
|
|
85446
|
+
})),
|
|
85447
|
+
workspaces: (req.workspaces ?? []).map((w) => ({
|
|
85448
|
+
ref: w.ref,
|
|
85449
|
+
source_node_id: w.source_node_id ?? null,
|
|
85450
|
+
base_revision: w.base_revision ?? null,
|
|
85451
|
+
desired_path: w.desired_path ?? null,
|
|
85452
|
+
cleanup_on_graph_failure: w.cleanup_on_graph_failure === true
|
|
85453
|
+
})),
|
|
85454
|
+
on_dependency_failure: parseOnDependencyFailurePolicy(req.onDependencyFailure)
|
|
85455
|
+
};
|
|
85456
|
+
return (0, import_crypto14.createHash)("sha256").update(canonicalJson(normalized)).digest("hex");
|
|
85457
|
+
}
|
|
85458
|
+
function commitMeshGraphPlan(req) {
|
|
85459
|
+
const store = MeshRuntimeStore.getInstance();
|
|
85460
|
+
const tasks = Array.isArray(req.tasks) ? req.tasks : [];
|
|
85461
|
+
if (tasks.length === 0) {
|
|
85462
|
+
throw new MeshGraphPlanError("empty_task_graph", "a graph plan requires at least one task");
|
|
85463
|
+
}
|
|
85464
|
+
const gates = Array.isArray(req.gates) ? req.gates : [];
|
|
85465
|
+
const workspaces = Array.isArray(req.workspaces) ? req.workspaces : [];
|
|
85466
|
+
const policy = { on_dependency_failure: parseOnDependencyFailurePolicy(req.onDependencyFailure) };
|
|
85467
|
+
const planDigest = computeMeshGraphPlanDigest(req);
|
|
85468
|
+
const batchId = typeof req.batchId === "string" && req.batchId.trim() ? req.batchId.trim() : newMeshGraphId();
|
|
85469
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
85470
|
+
const taskRefs = /* @__PURE__ */ new Map();
|
|
85471
|
+
tasks.forEach((t, i) => {
|
|
85472
|
+
const ref = typeof t.ref === "string" ? t.ref.trim() : "";
|
|
85473
|
+
if (!ref) return;
|
|
85474
|
+
if (taskRefs.has(ref)) {
|
|
85475
|
+
throw new MeshGraphPlanError("duplicate_task_ref", `ref '${ref}' is used by more than one task in this batch`);
|
|
85476
|
+
}
|
|
85477
|
+
taskRefs.set(ref, i);
|
|
85478
|
+
});
|
|
85479
|
+
const gateRefs = /* @__PURE__ */ new Map();
|
|
85480
|
+
gates.forEach((g, i) => {
|
|
85481
|
+
const ref = normalizeRef(g.ref, `gates[${i}]`);
|
|
85482
|
+
if (taskRefs.has(ref) || gateRefs.has(ref)) {
|
|
85483
|
+
throw new MeshGraphPlanError(
|
|
85484
|
+
"duplicate_graph_ref",
|
|
85485
|
+
`ref '${ref}' is declared more than once \u2014 tasks, gates and workspaces share ONE ref namespace`
|
|
85486
|
+
);
|
|
85487
|
+
}
|
|
85488
|
+
gateRefs.set(ref, i);
|
|
85489
|
+
});
|
|
85490
|
+
const workspaceRefs = /* @__PURE__ */ new Set();
|
|
85491
|
+
workspaces.forEach((w, i) => {
|
|
85492
|
+
const ref = normalizeRef(w.ref, `workspaces[${i}]`);
|
|
85493
|
+
if (taskRefs.has(ref) || gateRefs.has(ref) || workspaceRefs.has(ref)) {
|
|
85494
|
+
throw new MeshGraphPlanError(
|
|
85495
|
+
"duplicate_graph_ref",
|
|
85496
|
+
`ref '${ref}' is declared more than once \u2014 tasks, gates and workspaces share ONE ref namespace`
|
|
85497
|
+
);
|
|
85498
|
+
}
|
|
85499
|
+
workspaceRefs.add(ref);
|
|
85500
|
+
});
|
|
85501
|
+
tasks.forEach((t, i) => {
|
|
85502
|
+
for (const dep of t.dependsOn ?? []) {
|
|
85503
|
+
if (gateRefs.has(dep)) {
|
|
85504
|
+
throw new MeshGraphPlanError(
|
|
85505
|
+
"gate_ref_in_depends_on",
|
|
85506
|
+
`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)`
|
|
85507
|
+
);
|
|
85508
|
+
}
|
|
85509
|
+
}
|
|
85510
|
+
for (const gateRef of t.gated_by ?? []) {
|
|
85511
|
+
if (!gateRefs.has(gateRef)) {
|
|
85512
|
+
throw new MeshGraphPlanError(
|
|
85513
|
+
"unknown_gate_ref",
|
|
85514
|
+
`task ${t.ref ? `'${t.ref}'` : `#${i}`} is gated_by '${gateRef}', which is not a gate in this batch` + (gateRefs.size ? ` (gates: ${[...gateRefs.keys()].join(", ")})` : "")
|
|
85515
|
+
);
|
|
85516
|
+
}
|
|
85517
|
+
}
|
|
85518
|
+
const ws = typeof t.workspace_ref === "string" ? t.workspace_ref.trim() : "";
|
|
85519
|
+
if (ws && !workspaceRefs.has(ws)) {
|
|
85520
|
+
throw new MeshGraphPlanError(
|
|
85521
|
+
"unknown_workspace_ref",
|
|
85522
|
+
`task ${t.ref ? `'${t.ref}'` : `#${i}`} names workspace_ref '${ws}', which is not declared in this batch`
|
|
85523
|
+
);
|
|
85524
|
+
}
|
|
85525
|
+
});
|
|
85526
|
+
gates.forEach((g, i) => {
|
|
85527
|
+
for (const dep of g.depends_on ?? []) {
|
|
85528
|
+
if (!taskRefs.has(dep) && !gateRefs.has(dep)) {
|
|
85529
|
+
throw new MeshGraphPlanError(
|
|
85530
|
+
"unknown_gate_dependency",
|
|
85531
|
+
`gate '${g.ref}' depends on '${dep}', which is not a task or gate ref in this batch`
|
|
85532
|
+
);
|
|
85533
|
+
}
|
|
85534
|
+
}
|
|
85535
|
+
pickEnum(g.action, MESH_GRAPH_GATE_ACTIONS, "custom", "invalid_gate_action", `gates[${i}].action`);
|
|
85536
|
+
pickEnum(g.on_timeout, MESH_GRAPH_GATE_TIMEOUT_POLICIES, "hold", "invalid_gate_on_timeout", `gates[${i}].on_timeout`);
|
|
85537
|
+
});
|
|
85538
|
+
tasks.forEach((t, i) => {
|
|
85539
|
+
pickEnum(t.on_upstream_skip, MESH_GRAPH_ON_UPSTREAM_SKIP_POLICIES, "skip", "invalid_on_upstream_skip", `tasks[${i}].on_upstream_skip`);
|
|
85540
|
+
});
|
|
85541
|
+
const result = store.transaction(() => {
|
|
85542
|
+
const graphStore = store.graphStore();
|
|
85543
|
+
const existing = graphStore.getGraphByBatchId(req.meshId, batchId);
|
|
85544
|
+
if (existing) {
|
|
85545
|
+
if (existing.planDigest !== planDigest) {
|
|
85546
|
+
throw new MeshGraphPlanError(
|
|
85547
|
+
"batch_id_conflict",
|
|
85548
|
+
`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`,
|
|
85549
|
+
{ graphId: existing.graphId }
|
|
85550
|
+
);
|
|
85551
|
+
}
|
|
85552
|
+
const existingNodes = graphStore.listNodes(existing.graphId);
|
|
85553
|
+
const workerNodes = existingNodes.filter((n) => n.kind === "worker_task");
|
|
85554
|
+
const replayTasks = workerNodes.map((n) => n.queueTaskId ? store.findQueueEntryById(req.meshId, n.queueTaskId) : null).filter((e) => !!e);
|
|
85555
|
+
return {
|
|
85556
|
+
graphId: existing.graphId,
|
|
85557
|
+
batchId,
|
|
85558
|
+
tasks: replayTasks,
|
|
85559
|
+
nodeIdByIndex: workerNodes.map((n) => n.nodeId),
|
|
85560
|
+
gates: graphStore.listGatesByGraph(existing.graphId).map((g) => ({
|
|
85561
|
+
ref: g.ref ?? g.gateId,
|
|
85562
|
+
gateId: g.gateId,
|
|
85563
|
+
nodeId: g.nodeId,
|
|
85564
|
+
action: g.action
|
|
85565
|
+
})),
|
|
85566
|
+
workspaces: graphStore.listWorkspaceIntents(existing.graphId).map((w) => ({
|
|
85567
|
+
ref: w.workspaceRef,
|
|
85568
|
+
sagaState: w.sagaState,
|
|
85569
|
+
branchIdentity: w.branchIdentity
|
|
85570
|
+
})),
|
|
85571
|
+
dependencyEdgeCount: existing.dependencyEdgeCount,
|
|
85572
|
+
planDigest,
|
|
85573
|
+
replayed: true,
|
|
85574
|
+
workspacePreparation: existing.workspaceCount > 0 ? "pending" : "not_requested",
|
|
85575
|
+
heldNodeIds: []
|
|
85576
|
+
};
|
|
85577
|
+
}
|
|
85578
|
+
const queueSpecs = tasks.map((t) => {
|
|
85579
|
+
const {
|
|
85580
|
+
inputs_from: _inputsFrom,
|
|
85581
|
+
run_if: _runIf,
|
|
85582
|
+
on_false: _onFalse,
|
|
85583
|
+
on_upstream_skip: _onUpstreamSkip,
|
|
85584
|
+
workspace_ref: _workspaceRef,
|
|
85585
|
+
gated_by: _gatedBy,
|
|
85586
|
+
...queueSpec
|
|
85587
|
+
} = t;
|
|
85588
|
+
return {
|
|
85589
|
+
...queueSpec,
|
|
85590
|
+
missionId: queueSpec.missionId ?? req.missionId,
|
|
85591
|
+
...req.sourceCoordinatorSessionId ? { sourceCoordinatorSessionId: req.sourceCoordinatorSessionId } : {}
|
|
85592
|
+
};
|
|
85593
|
+
});
|
|
85594
|
+
const inserted = enqueueTaskGraph(req.meshId, queueSpecs);
|
|
85595
|
+
const graphId = newMeshGraphId();
|
|
85596
|
+
const nodeIdByIndex = tasks.map(() => newMeshGraphNodeId());
|
|
85597
|
+
const gateNodeIdByRef = /* @__PURE__ */ new Map();
|
|
85598
|
+
const gateIdByRef = /* @__PURE__ */ new Map();
|
|
85599
|
+
gates.forEach((g) => {
|
|
85600
|
+
gateNodeIdByRef.set(g.ref.trim(), newMeshGraphNodeId());
|
|
85601
|
+
gateIdByRef.set(g.ref.trim(), newMeshGraphGateId());
|
|
85602
|
+
});
|
|
85603
|
+
const nodeIdByRef = /* @__PURE__ */ new Map();
|
|
85604
|
+
tasks.forEach((t, i) => {
|
|
85605
|
+
const ref = typeof t.ref === "string" ? t.ref.trim() : "";
|
|
85606
|
+
if (ref) nodeIdByRef.set(ref, nodeIdByIndex[i]);
|
|
85607
|
+
});
|
|
85608
|
+
for (const [ref, nodeId] of gateNodeIdByRef) nodeIdByRef.set(ref, nodeId);
|
|
85609
|
+
const edges = [];
|
|
85610
|
+
const addEdge = (fromNodeId, toNodeId, kind, omitOnSkip) => {
|
|
85611
|
+
edges.push({ graphId, meshId: req.meshId, fromNodeId, toNodeId, kind, omitOnSkip, createdAt: nowIso });
|
|
85612
|
+
};
|
|
85613
|
+
tasks.forEach((t, i) => {
|
|
85614
|
+
const nodeId = nodeIdByIndex[i];
|
|
85615
|
+
const ref = typeof t.ref === "string" ? t.ref.trim() : void 0;
|
|
85616
|
+
const baseSpec = { message: t.message };
|
|
85617
|
+
if (t.inputs_from !== void 0) baseSpec.inputs_from = t.inputs_from;
|
|
85618
|
+
if (t.run_if !== void 0) baseSpec.run_if = t.run_if;
|
|
85619
|
+
if (t.on_false !== void 0) baseSpec.on_false = t.on_false;
|
|
85620
|
+
if (typeof t.workspace_ref === "string" && t.workspace_ref.trim()) {
|
|
85621
|
+
baseSpec.workspace_ref = t.workspace_ref.trim();
|
|
85622
|
+
}
|
|
85623
|
+
const node = {
|
|
85624
|
+
graphId,
|
|
85625
|
+
nodeId,
|
|
85626
|
+
meshId: req.meshId,
|
|
85627
|
+
...ref ? { ref } : {},
|
|
85628
|
+
kind: "worker_task",
|
|
85629
|
+
queueTaskId: inserted[i].id,
|
|
85630
|
+
state: "declared",
|
|
85631
|
+
baseSpecJson: JSON.stringify(baseSpec),
|
|
85632
|
+
materializationVersion: 0,
|
|
85633
|
+
createdAt: nowIso,
|
|
85634
|
+
updatedAt: nowIso
|
|
85635
|
+
};
|
|
85636
|
+
graphStore.insertNode(node);
|
|
85637
|
+
});
|
|
85638
|
+
gates.forEach((g, i) => {
|
|
85639
|
+
const ref = g.ref.trim();
|
|
85640
|
+
const nodeId = gateNodeIdByRef.get(ref);
|
|
85641
|
+
const gateSpec = {};
|
|
85642
|
+
if (typeof g.lease_seconds === "number" && g.lease_seconds > 0) gateSpec.lease_seconds = g.lease_seconds;
|
|
85643
|
+
if (typeof g.deadline_seconds === "number" && g.deadline_seconds > 0) gateSpec.deadline_seconds = g.deadline_seconds;
|
|
85644
|
+
graphStore.insertNode({
|
|
85645
|
+
graphId,
|
|
85646
|
+
nodeId,
|
|
85647
|
+
meshId: req.meshId,
|
|
85648
|
+
ref,
|
|
85649
|
+
kind: "coordinator_gate",
|
|
85650
|
+
state: "declared",
|
|
85651
|
+
baseSpecJson: JSON.stringify(gateSpec),
|
|
85652
|
+
materializationVersion: 0,
|
|
85653
|
+
createdAt: nowIso,
|
|
85654
|
+
updatedAt: nowIso
|
|
85655
|
+
});
|
|
85656
|
+
graphStore.insertGate({
|
|
85657
|
+
gateId: gateIdByRef.get(ref),
|
|
85658
|
+
graphId,
|
|
85659
|
+
nodeId,
|
|
85660
|
+
meshId: req.meshId,
|
|
85661
|
+
ref,
|
|
85662
|
+
state: "declared",
|
|
85663
|
+
action: pickEnum(g.action, MESH_GRAPH_GATE_ACTIONS, "custom", "invalid_gate_action", `gates[${i}].action`),
|
|
85664
|
+
instructions: typeof g.instructions === "string" ? g.instructions : void 0,
|
|
85665
|
+
eligibleCoordinatorSessionId: typeof g.eligible_coordinator_session_id === "string" ? g.eligible_coordinator_session_id : void 0,
|
|
85666
|
+
leaseGeneration: 0,
|
|
85667
|
+
onTimeout: pickEnum(g.on_timeout, MESH_GRAPH_GATE_TIMEOUT_POLICIES, "hold", "invalid_gate_on_timeout", `gates[${i}].on_timeout`),
|
|
85668
|
+
createdAt: nowIso,
|
|
85669
|
+
updatedAt: nowIso
|
|
85670
|
+
});
|
|
85671
|
+
});
|
|
85672
|
+
tasks.forEach((t, i) => {
|
|
85673
|
+
const omitOnSkip = pickEnum(
|
|
85674
|
+
t.on_upstream_skip,
|
|
85675
|
+
MESH_GRAPH_ON_UPSTREAM_SKIP_POLICIES,
|
|
85676
|
+
"skip",
|
|
85677
|
+
"invalid_on_upstream_skip",
|
|
85678
|
+
`tasks[${i}].on_upstream_skip`
|
|
85679
|
+
) === "omit_dependency";
|
|
85680
|
+
for (const dep of t.dependsOn ?? []) {
|
|
85681
|
+
const fromNodeId = nodeIdByRef.get(dep);
|
|
85682
|
+
if (!fromNodeId) continue;
|
|
85683
|
+
addEdge(fromNodeId, nodeIdByIndex[i], "requires", omitOnSkip);
|
|
85684
|
+
}
|
|
85685
|
+
for (const sourceRef of collectRunIfSourceRefs(t.run_if)) {
|
|
85686
|
+
const fromNodeId = nodeIdByRef.get(sourceRef);
|
|
85687
|
+
if (!fromNodeId || fromNodeId === nodeIdByIndex[i]) continue;
|
|
85688
|
+
if (edges.some((e) => e.fromNodeId === fromNodeId && e.toNodeId === nodeIdByIndex[i])) continue;
|
|
85689
|
+
addEdge(fromNodeId, nodeIdByIndex[i], "conditional", omitOnSkip);
|
|
85690
|
+
}
|
|
85691
|
+
for (const sourceRef of collectInputSourceRefs(t.inputs_from)) {
|
|
85692
|
+
const fromNodeId = nodeIdByRef.get(sourceRef);
|
|
85693
|
+
if (!fromNodeId || fromNodeId === nodeIdByIndex[i]) continue;
|
|
85694
|
+
if (edges.some((e) => e.fromNodeId === fromNodeId && e.toNodeId === nodeIdByIndex[i] && e.kind === "requires")) continue;
|
|
85695
|
+
addEdge(fromNodeId, nodeIdByIndex[i], "requires", omitOnSkip);
|
|
85696
|
+
}
|
|
85697
|
+
for (const gateRef of t.gated_by ?? []) {
|
|
85698
|
+
const fromNodeId = gateNodeIdByRef.get(gateRef.trim());
|
|
85699
|
+
if (!fromNodeId) continue;
|
|
85700
|
+
addEdge(fromNodeId, nodeIdByIndex[i], "gate", false);
|
|
85701
|
+
}
|
|
85702
|
+
});
|
|
85703
|
+
gates.forEach((g) => {
|
|
85704
|
+
const toNodeId = gateNodeIdByRef.get(g.ref.trim());
|
|
85705
|
+
for (const dep of g.depends_on ?? []) {
|
|
85706
|
+
const fromNodeId = nodeIdByRef.get(dep);
|
|
85707
|
+
if (!fromNodeId) continue;
|
|
85708
|
+
addEdge(fromNodeId, toNodeId, "requires", false);
|
|
85709
|
+
}
|
|
85710
|
+
});
|
|
85711
|
+
for (const edge of edges) graphStore.insertEdge(edge);
|
|
85712
|
+
graphStore.insertGraph({
|
|
85713
|
+
graphId,
|
|
85714
|
+
meshId: req.meshId,
|
|
85715
|
+
batchId,
|
|
85716
|
+
missionId: req.missionId,
|
|
85717
|
+
sourceCoordinatorSessionId: req.sourceCoordinatorSessionId,
|
|
85718
|
+
enqueueSurface: req.enqueueSurface ?? "batch",
|
|
85719
|
+
schemaVersion: MESH_GRAPH_SCHEMA_VERSION,
|
|
85720
|
+
status: "active",
|
|
85721
|
+
taskCount: tasks.length,
|
|
85722
|
+
gateCount: gates.length,
|
|
85723
|
+
workspaceCount: workspaces.length,
|
|
85724
|
+
dependencyEdgeCount: edges.length,
|
|
85725
|
+
planDigest,
|
|
85726
|
+
// ★ E-2: `on_dependency_failure` is now PERSISTED on the graph row
|
|
85727
|
+
// (C3 only validated and echoed it). The C3 derived-failure path reads
|
|
85728
|
+
// it back through parseGraphPolicy/projectGraphPublicPolicy.
|
|
85729
|
+
policyJson: serializeGraphPolicy(policy),
|
|
85730
|
+
createdAt: nowIso,
|
|
85731
|
+
activatedAt: nowIso,
|
|
85732
|
+
updatedAt: nowIso
|
|
85733
|
+
});
|
|
85734
|
+
if (req.orchestrationDecision) {
|
|
85735
|
+
const merged = JSON.stringify({
|
|
85736
|
+
...JSON.parse(serializeGraphPolicy(policy)),
|
|
85737
|
+
orchestration_decision: req.orchestrationDecision
|
|
85738
|
+
});
|
|
85739
|
+
graphStore.updateGraphPolicyJson(graphId, merged, nowIso);
|
|
85740
|
+
}
|
|
85741
|
+
const heldNodeIds = [];
|
|
85742
|
+
tasks.forEach((t, i) => {
|
|
85743
|
+
if (!isAdvancedGraphTask(t)) return;
|
|
85744
|
+
const nodeId = nodeIdByIndex[i];
|
|
85745
|
+
const entry = store.findQueueEntryById(req.meshId, inserted[i].id);
|
|
85746
|
+
if (!entry || entry.status !== "pending" || entry.blockedReason) return;
|
|
85747
|
+
entry.blockedReason = graphMaterializationBlockReason(nodeId, 0);
|
|
85748
|
+
store.updateQueueEntry(entry);
|
|
85749
|
+
graphStore.updateNodeState(graphId, nodeId, "blocked", nowIso);
|
|
85750
|
+
heldNodeIds.push(nodeId);
|
|
85751
|
+
});
|
|
85752
|
+
return {
|
|
85753
|
+
graphId,
|
|
85754
|
+
batchId,
|
|
85755
|
+
tasks: inserted,
|
|
85756
|
+
nodeIdByIndex,
|
|
85757
|
+
gates: gates.map((g) => ({
|
|
85758
|
+
ref: g.ref.trim(),
|
|
85759
|
+
gateId: gateIdByRef.get(g.ref.trim()),
|
|
85760
|
+
nodeId: gateNodeIdByRef.get(g.ref.trim()),
|
|
85761
|
+
action: pickEnum(g.action, MESH_GRAPH_GATE_ACTIONS, "custom", "invalid_gate_action", "gate action")
|
|
85762
|
+
})),
|
|
85763
|
+
workspaces: [],
|
|
85764
|
+
dependencyEdgeCount: edges.length,
|
|
85765
|
+
planDigest,
|
|
85766
|
+
replayed: false,
|
|
85767
|
+
workspacePreparation: workspaces.length > 0 ? "pending" : "not_requested",
|
|
85768
|
+
heldNodeIds
|
|
85769
|
+
};
|
|
85770
|
+
});
|
|
85771
|
+
if (!result.replayed && workspaces.length > 0) {
|
|
85772
|
+
try {
|
|
85773
|
+
const declared = declareWorkspaceIntents({
|
|
85774
|
+
graphId: result.graphId,
|
|
85775
|
+
meshId: req.meshId,
|
|
85776
|
+
workspaces
|
|
85777
|
+
});
|
|
85778
|
+
result.workspaces = declared.map((w) => ({
|
|
85779
|
+
ref: w.workspaceRef,
|
|
85780
|
+
sagaState: w.sagaState,
|
|
85781
|
+
branchIdentity: w.branchIdentity
|
|
85782
|
+
}));
|
|
85783
|
+
} catch (e) {
|
|
85784
|
+
LOG2.warn("MeshGraph", `Workspace intent declaration failed for graph ${result.graphId}: ${e?.message || e}`);
|
|
85785
|
+
}
|
|
85786
|
+
}
|
|
85787
|
+
return result;
|
|
85788
|
+
}
|
|
85789
|
+
function collectRunIfSourceRefs(runIf, depth = 0) {
|
|
85790
|
+
if (!runIf || typeof runIf !== "object" || depth > 8) return [];
|
|
85791
|
+
const out = [];
|
|
85792
|
+
const node = runIf;
|
|
85793
|
+
if (typeof node.from === "string" && node.from.trim()) out.push(node.from.trim());
|
|
85794
|
+
for (const key2 of ["all", "any", "not", "conditions"]) {
|
|
85795
|
+
const child = node[key2];
|
|
85796
|
+
if (Array.isArray(child)) {
|
|
85797
|
+
for (const item of child) out.push(...collectRunIfSourceRefs(item, depth + 1));
|
|
85798
|
+
} else if (child && typeof child === "object") {
|
|
85799
|
+
out.push(...collectRunIfSourceRefs(child, depth + 1));
|
|
85800
|
+
}
|
|
85801
|
+
}
|
|
85802
|
+
return [...new Set(out)];
|
|
85803
|
+
}
|
|
85804
|
+
function collectInputSourceRefs(inputsFrom) {
|
|
85805
|
+
if (!Array.isArray(inputsFrom)) return [];
|
|
85806
|
+
const out = [];
|
|
85807
|
+
for (const item of inputsFrom) {
|
|
85808
|
+
if (item && typeof item === "object" && typeof item.from === "string") {
|
|
85809
|
+
const from = item.from.trim();
|
|
85810
|
+
if (from) out.push(from);
|
|
85811
|
+
}
|
|
85812
|
+
}
|
|
85813
|
+
return [...new Set(out)];
|
|
85814
|
+
}
|
|
85815
|
+
init_mesh_runtime_store();
|
|
85816
|
+
init_mesh_graph_derived_failure();
|
|
85817
|
+
init_mesh_graph_transition_runner();
|
|
85818
|
+
var IN_FLIGHT_GRAPH_STATUSES = [
|
|
85819
|
+
"preparing",
|
|
85820
|
+
"active",
|
|
85821
|
+
"waiting_gate",
|
|
85822
|
+
"compensation_required"
|
|
85823
|
+
];
|
|
85824
|
+
function buildMeshGraphViews(meshId, opts = {}) {
|
|
85825
|
+
const store = MeshRuntimeStore.getInstance();
|
|
85826
|
+
const graphStore = store.graphStore();
|
|
85827
|
+
const nowIso = new Date(opts.nowMs ?? Date.now()).toISOString();
|
|
85828
|
+
let graphs;
|
|
85829
|
+
if (opts.graphId) {
|
|
85830
|
+
const one = graphStore.getGraph(opts.graphId);
|
|
85831
|
+
graphs = one && one.meshId === meshId ? [one] : [];
|
|
85832
|
+
} else if (opts.batchId) {
|
|
85833
|
+
const one = graphStore.getGraphByBatchId(meshId, opts.batchId);
|
|
85834
|
+
graphs = one ? [one] : [];
|
|
85835
|
+
} else {
|
|
85836
|
+
graphs = graphStore.listGraphsByMesh(meshId, {
|
|
85837
|
+
...opts.activeOnly === false ? {} : { statuses: IN_FLIGHT_GRAPH_STATUSES },
|
|
85838
|
+
limit: opts.limit ?? 20
|
|
85839
|
+
});
|
|
85840
|
+
}
|
|
85841
|
+
return graphs.map((graph) => {
|
|
85842
|
+
const nodes = graphStore.listNodes(graph.graphId);
|
|
85843
|
+
const edges = graphStore.listEdges(graph.graphId);
|
|
85844
|
+
const gates = graphStore.listGatesByGraph(graph.graphId);
|
|
85845
|
+
const workspaces = graphStore.listWorkspaceIntents(graph.graphId);
|
|
85846
|
+
const byId = new Map(nodes.map((n) => [n.nodeId, n]));
|
|
85847
|
+
const policy = projectGraphPublicPolicy(graph.policyJson);
|
|
85848
|
+
const decision = readOrchestrationDecision(graph.policyJson);
|
|
85849
|
+
const queueByTaskId = /* @__PURE__ */ new Map();
|
|
85850
|
+
for (const node of nodes) {
|
|
85851
|
+
if (!node.queueTaskId) continue;
|
|
85852
|
+
const entry = store.findQueueEntryById(meshId, node.queueTaskId);
|
|
85853
|
+
if (entry) {
|
|
85854
|
+
queueByTaskId.set(node.queueTaskId, {
|
|
85855
|
+
status: entry.status,
|
|
85856
|
+
blockedReason: entry.blockedReason,
|
|
85857
|
+
cancelReason: entry.cancelReason,
|
|
85858
|
+
dependsOn: entry.dependsOn
|
|
85859
|
+
});
|
|
85860
|
+
}
|
|
85861
|
+
}
|
|
85862
|
+
const statusById = /* @__PURE__ */ new Map();
|
|
85863
|
+
const depMetaById = /* @__PURE__ */ new Map();
|
|
85864
|
+
for (const [taskId, row] of queueByTaskId) {
|
|
85865
|
+
statusById.set(taskId, row.status);
|
|
85866
|
+
depMetaById.set(taskId, { status: row.status, blockedReason: row.blockedReason, cancelReason: row.cancelReason });
|
|
85867
|
+
}
|
|
85868
|
+
for (const row of queueByTaskId.values()) {
|
|
85869
|
+
for (const depId of row.dependsOn ?? []) {
|
|
85870
|
+
if (statusById.has(depId)) continue;
|
|
85871
|
+
const dep = store.findQueueEntryById(meshId, depId);
|
|
85872
|
+
if (!dep) continue;
|
|
85873
|
+
statusById.set(depId, dep.status);
|
|
85874
|
+
depMetaById.set(depId, {
|
|
85875
|
+
status: dep.status,
|
|
85876
|
+
blockedReason: dep.blockedReason,
|
|
85877
|
+
cancelReason: dep.cancelReason
|
|
85878
|
+
});
|
|
85879
|
+
}
|
|
85880
|
+
}
|
|
85881
|
+
const gateByNodeId = new Map(gates.map((g) => [g.nodeId, g]));
|
|
85882
|
+
const nodeViews = nodes.map((node) => {
|
|
85883
|
+
const queue = node.queueTaskId ? queueByTaskId.get(node.queueTaskId) : void 0;
|
|
85884
|
+
const gateBlock = parseCoordinatorGateBlock(queue?.blockedReason);
|
|
85885
|
+
const failures = queue?.dependsOn?.length ? deriveDependencyFailures(queue.dependsOn, statusById, depMetaById) : [];
|
|
85886
|
+
return {
|
|
85887
|
+
nodeId: node.nodeId,
|
|
85888
|
+
...node.ref ? { ref: node.ref } : {},
|
|
85889
|
+
kind: node.kind,
|
|
85890
|
+
state: node.state,
|
|
85891
|
+
...node.queueTaskId ? { taskId: node.queueTaskId } : {},
|
|
85892
|
+
...queue ? { taskStatus: queue.status } : {},
|
|
85893
|
+
...queue?.blockedReason ? { blockedReason: queue.blockedReason } : {},
|
|
85894
|
+
...gateBlock ? { blockedByGateId: gateBlock.gateId } : {},
|
|
85895
|
+
materializationVersion: node.materializationVersion,
|
|
85896
|
+
...node.materializedDigest ? { materializedDigest: node.materializedDigest } : {},
|
|
85897
|
+
...node.skipReason ? { skipReason: node.skipReason } : {},
|
|
85898
|
+
...node.failureReason ? { failureReason: node.failureReason } : {},
|
|
85899
|
+
...failures.length > 0 ? { dependencyFailures: failures } : {},
|
|
85900
|
+
...describeNodeFeatures(node.baseSpecJson).length > 0 ? { features: describeNodeFeatures(node.baseSpecJson) } : {}
|
|
85901
|
+
};
|
|
85902
|
+
});
|
|
85903
|
+
const edgeViews = edges.map((edge) => {
|
|
85904
|
+
const source = byId.get(edge.fromNodeId);
|
|
85905
|
+
const active = !(source?.state === "skipped" && edge.omitOnSkip);
|
|
85906
|
+
return {
|
|
85907
|
+
from: source?.ref ?? edge.fromNodeId,
|
|
85908
|
+
to: byId.get(edge.toNodeId)?.ref ?? edge.toNodeId,
|
|
85909
|
+
kind: edge.kind,
|
|
85910
|
+
omitOnSkip: edge.omitOnSkip,
|
|
85911
|
+
active
|
|
85912
|
+
};
|
|
85913
|
+
});
|
|
85914
|
+
const gateViews = gates.map((gate) => {
|
|
85915
|
+
const blocking = nodeViews.filter((n) => n.blockedByGateId === gate.gateId).map((n) => n.ref ?? n.nodeId);
|
|
85916
|
+
const leaseExpired = gate.state === "claimed" && !!gate.leaseExpiresAt && gate.leaseExpiresAt <= nowIso;
|
|
85917
|
+
return {
|
|
85918
|
+
gateId: gate.gateId,
|
|
85919
|
+
...gate.ref ? { ref: gate.ref } : {},
|
|
85920
|
+
nodeId: gate.nodeId,
|
|
85921
|
+
state: gate.state,
|
|
85922
|
+
action: gate.action,
|
|
85923
|
+
...gate.instructions ? { instructions: gate.instructions } : {},
|
|
85924
|
+
onTimeout: gate.onTimeout,
|
|
85925
|
+
leaseGeneration: gate.leaseGeneration,
|
|
85926
|
+
...gate.leaseOwnerSessionId ? { leaseOwnerSessionId: gate.leaseOwnerSessionId } : {},
|
|
85927
|
+
...gate.leaseExpiresAt ? { leaseExpiresAt: gate.leaseExpiresAt } : {},
|
|
85928
|
+
...leaseExpired ? { leaseExpired: true } : {},
|
|
85929
|
+
...gate.deadlineAt ? { deadlineAt: gate.deadlineAt } : {},
|
|
85930
|
+
...gate.releaseOutcome ? { releaseOutcome: gate.releaseOutcome } : {},
|
|
85931
|
+
...gate.eligibleCoordinatorSessionId ? { eligibleCoordinatorSessionId: gate.eligibleCoordinatorSessionId } : {},
|
|
85932
|
+
...blocking.length > 0 ? { blocking } : {}
|
|
85933
|
+
};
|
|
85934
|
+
});
|
|
85935
|
+
const nodeStates = {};
|
|
85936
|
+
for (const node of nodes) nodeStates[node.state] = (nodeStates[node.state] ?? 0) + 1;
|
|
85937
|
+
const actions = [];
|
|
85938
|
+
for (const gate of gateViews) {
|
|
85939
|
+
if (gate.state === "awaiting_coordinator") {
|
|
85940
|
+
actions.push({
|
|
85941
|
+
kind: "gate_awaiting",
|
|
85942
|
+
gateId: gate.gateId,
|
|
85943
|
+
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.`
|
|
85944
|
+
});
|
|
85945
|
+
} else if (gate.state === "claimed" && gate.leaseExpired) {
|
|
85946
|
+
actions.push({
|
|
85947
|
+
kind: "gate_reclaim",
|
|
85948
|
+
gateId: gate.gateId,
|
|
85949
|
+
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.`
|
|
85950
|
+
});
|
|
85951
|
+
} else if (gate.state === "claimed") {
|
|
85952
|
+
actions.push({
|
|
85953
|
+
kind: "gate_claimed",
|
|
85954
|
+
gateId: gate.gateId,
|
|
85955
|
+
detail: `Gate '${gate.ref ?? gate.gateId}' is claimed by ${gate.leaseOwnerSessionId ?? "a coordinator"} until ${gate.leaseExpiresAt ?? "lease expiry"}; release it to advance the graph.`
|
|
85956
|
+
});
|
|
85957
|
+
} else if (gate.state === "expired" && gate.onTimeout === "hold") {
|
|
85958
|
+
actions.push({
|
|
85959
|
+
kind: "gate_reclaim",
|
|
85960
|
+
gateId: gate.gateId,
|
|
85961
|
+
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.`
|
|
85962
|
+
});
|
|
85963
|
+
}
|
|
85964
|
+
}
|
|
85965
|
+
for (const ws of workspaces) {
|
|
85966
|
+
if (ws.sagaState === "compensation_required") {
|
|
85967
|
+
actions.push({
|
|
85968
|
+
kind: "workspace_compensation",
|
|
85969
|
+
workspaceRef: ws.workspaceRef,
|
|
85970
|
+
detail: `Workspace '${ws.workspaceRef}' needs manual compensation: ${ws.lastError ?? "compensation refused"}.`
|
|
85971
|
+
});
|
|
85972
|
+
}
|
|
85973
|
+
}
|
|
85974
|
+
return {
|
|
85975
|
+
graphId: graph.graphId,
|
|
85976
|
+
batchId: graph.batchId,
|
|
85977
|
+
status: graph.status,
|
|
85978
|
+
...graph.missionId ? { missionId: graph.missionId } : {},
|
|
85979
|
+
enqueueSurface: graph.enqueueSurface,
|
|
85980
|
+
schemaVersion: graph.schemaVersion,
|
|
85981
|
+
createdAt: graph.createdAt,
|
|
85982
|
+
...graph.terminalAt ? { terminalAt: graph.terminalAt } : {},
|
|
85983
|
+
onDependencyFailure: policy.on_dependency_failure,
|
|
85984
|
+
...policy.policyInvalid ? { policyInvalid: policy.policyInvalid } : {},
|
|
85985
|
+
...decision ? { orchestrationDecision: decision } : {},
|
|
85986
|
+
counts: {
|
|
85987
|
+
tasks: graph.taskCount,
|
|
85988
|
+
gates: graph.gateCount,
|
|
85989
|
+
workspaces: graph.workspaceCount,
|
|
85990
|
+
edges: graph.dependencyEdgeCount
|
|
85991
|
+
},
|
|
85992
|
+
nodeStates,
|
|
85993
|
+
nodes: nodeViews,
|
|
85994
|
+
edges: edgeViews,
|
|
85995
|
+
gates: gateViews,
|
|
85996
|
+
workspaces: workspaces.map((w) => ({
|
|
85997
|
+
ref: w.workspaceRef,
|
|
85998
|
+
sagaState: w.sagaState,
|
|
85999
|
+
...w.branchIdentity ? { branchIdentity: w.branchIdentity } : {},
|
|
86000
|
+
...w.createdNodeId ? { createdNodeId: w.createdNodeId } : {},
|
|
86001
|
+
...w.createdWorktreePath ? { createdWorktreePath: w.createdWorktreePath } : {},
|
|
86002
|
+
...w.lastError ? { lastError: w.lastError } : {}
|
|
86003
|
+
})),
|
|
86004
|
+
...actions.length > 0 ? { nextCoordinatorAction: actions } : {}
|
|
86005
|
+
};
|
|
86006
|
+
});
|
|
86007
|
+
}
|
|
86008
|
+
function describeNodeFeatures(baseSpecJson) {
|
|
86009
|
+
let spec;
|
|
86010
|
+
try {
|
|
86011
|
+
const parsed = JSON.parse(baseSpecJson);
|
|
86012
|
+
spec = parsed && typeof parsed === "object" ? parsed : void 0;
|
|
86013
|
+
} catch {
|
|
86014
|
+
return ["invalid_base_spec"];
|
|
86015
|
+
}
|
|
86016
|
+
if (!spec) return [];
|
|
86017
|
+
const features = [];
|
|
86018
|
+
if (Array.isArray(spec.inputs_from) && spec.inputs_from.length > 0) features.push("inputs_from");
|
|
86019
|
+
if (spec.run_if !== void 0) features.push("run_if");
|
|
86020
|
+
if (typeof spec.workspace_ref === "string" && spec.workspace_ref) features.push("workspace_ref");
|
|
86021
|
+
return features;
|
|
86022
|
+
}
|
|
86023
|
+
function readOrchestrationDecision(policyJson) {
|
|
86024
|
+
if (!policyJson) return void 0;
|
|
86025
|
+
try {
|
|
86026
|
+
const parsed = JSON.parse(policyJson);
|
|
86027
|
+
const decision = parsed?.orchestration_decision;
|
|
86028
|
+
return decision && typeof decision === "object" && !Array.isArray(decision) ? decision : void 0;
|
|
86029
|
+
} catch {
|
|
86030
|
+
return void 0;
|
|
86031
|
+
}
|
|
86032
|
+
}
|
|
86033
|
+
init_mesh_graph_provenance();
|
|
84073
86034
|
init_mesh_node_identity();
|
|
84074
86035
|
init_mesh_active_work();
|
|
84075
86036
|
init_mesh_idle_reminder();
|
|
@@ -111685,7 +113646,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
111685
113646
|
var path47 = __toESM2(require("path"));
|
|
111686
113647
|
var fs41 = __toESM2(require("fs"));
|
|
111687
113648
|
var path46 = __toESM2(require("path"));
|
|
111688
|
-
var
|
|
113649
|
+
var import_crypto16 = require("crypto");
|
|
111689
113650
|
var TREE_DIGEST_ALGORITHM = "adhdev-provider-tree-sha256-v1";
|
|
111690
113651
|
function computeProviderTreeDigest(rootDir, providerType) {
|
|
111691
113652
|
const relPaths = [];
|
|
@@ -111698,7 +113659,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
111698
113659
|
);
|
|
111699
113660
|
}
|
|
111700
113661
|
relPaths.sort();
|
|
111701
|
-
const hash2 = (0,
|
|
113662
|
+
const hash2 = (0, import_crypto16.createHash)("sha256");
|
|
111702
113663
|
for (const relPath of relPaths) {
|
|
111703
113664
|
const absPath = path46.join(rootDir, ...relPath.split("/"));
|
|
111704
113665
|
const bytes = fs41.readFileSync(absPath);
|
|
@@ -115775,11 +117736,11 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
|
|
|
115775
117736
|
let node;
|
|
115776
117737
|
const { migrateProviderRolesToSlots: migrateProviderRolesToSlots2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
115777
117738
|
if (meshRecord.inline) {
|
|
115778
|
-
const { randomUUID:
|
|
117739
|
+
const { randomUUID: randomUUID20 } = await import("crypto");
|
|
115779
117740
|
const clonedPolicy = { ...sourceNode.policy || {} };
|
|
115780
117741
|
migrateProviderRolesToSlots2(clonedPolicy);
|
|
115781
117742
|
node = {
|
|
115782
|
-
id: `node_${
|
|
117743
|
+
id: `node_${randomUUID20().replace(/-/g, "")}`,
|
|
115783
117744
|
workspace: result.worktreePath,
|
|
115784
117745
|
repoRoot: result.worktreePath,
|
|
115785
117746
|
daemonId: sourceNode.daemonId,
|
|
@@ -116311,8 +118272,10 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
|
|
|
116311
118272
|
const { getMeshQueueStats: getMeshQueueStats2, getQueue: getQueue2, describeTaskDependencyState: describeTaskDependencyState2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
|
|
116312
118273
|
const status = Array.isArray(args?.status) ? args.status.map((s2) => typeof s2 === "string" ? s2.trim() : "").filter(Boolean) : void 0;
|
|
116313
118274
|
const rawQueue = getQueue2(meshId, { status });
|
|
116314
|
-
const
|
|
116315
|
-
const
|
|
118275
|
+
const allQueue = getQueue2(meshId);
|
|
118276
|
+
const statusById = new Map(allQueue.map((task) => [task.id, task.status]));
|
|
118277
|
+
const depMetaById = new Map(allQueue.map((task) => [task.id, task]));
|
|
118278
|
+
const queue = rawQueue.map((task) => Array.isArray(task.dependsOn) && task.dependsOn.length > 0 ? { ...task, ...describeTaskDependencyState2(task, statusById, depMetaById) } : task);
|
|
116316
118279
|
const summary = getMeshQueueStats2(meshId);
|
|
116317
118280
|
return {
|
|
116318
118281
|
success: true,
|
|
@@ -127391,7 +129354,7 @@ data: ${JSON.stringify(msg.data)}
|
|
|
127391
129354
|
});
|
|
127392
129355
|
}
|
|
127393
129356
|
};
|
|
127394
|
-
var
|
|
129357
|
+
var import_crypto17 = require("crypto");
|
|
127395
129358
|
var import_session_host_core12 = require_dist();
|
|
127396
129359
|
var BASE_KEY_SEQUENCES = {
|
|
127397
129360
|
enter: "\r",
|
|
@@ -127489,7 +129452,7 @@ data: ${JSON.stringify(msg.data)}
|
|
|
127489
129452
|
const sessionId = String(options.sessionId || "").trim();
|
|
127490
129453
|
if (!sessionId) throw new Error("sessionId is required");
|
|
127491
129454
|
const mode = options.mode || "read";
|
|
127492
|
-
const clientId = options.clientId || `raw-terminal-${process.pid}-${(0,
|
|
129455
|
+
const clientId = options.clientId || `raw-terminal-${process.pid}-${(0, import_crypto17.randomUUID)().slice(0, 8)}`;
|
|
127493
129456
|
const client = options.client || new import_session_host_core12.SessionHostClient({ endpoint: options.endpoint });
|
|
127494
129457
|
await client.connect();
|
|
127495
129458
|
const attachResponse = await client.request({
|
|
@@ -128253,7 +130216,7 @@ data: ${JSON.stringify(msg.data)}
|
|
|
128253
130216
|
init_mesh_reconcile_loop();
|
|
128254
130217
|
var import_fs19 = require("fs");
|
|
128255
130218
|
var import_path20 = require("path");
|
|
128256
|
-
var
|
|
130219
|
+
var import_crypto18 = require("crypto");
|
|
128257
130220
|
init_logger();
|
|
128258
130221
|
init_mesh_ledger();
|
|
128259
130222
|
init_mesh_runtime_store();
|
|
@@ -128287,7 +130250,7 @@ data: ${JSON.stringify(msg.data)}
|
|
|
128287
130250
|
try {
|
|
128288
130251
|
const fingerprint = buildPendingEventFingerprint(event);
|
|
128289
130252
|
MeshRuntimeStore.getInstance().insertPendingEvent({
|
|
128290
|
-
id: (0,
|
|
130253
|
+
id: (0, import_crypto18.randomUUID)(),
|
|
128291
130254
|
meshId: event.meshId,
|
|
128292
130255
|
coordinatorDaemonId: event.targetCoordinatorDaemonId ?? null,
|
|
128293
130256
|
event: event.event,
|
|
@@ -128408,6 +130371,73 @@ data: ${JSON.stringify(msg.data)}
|
|
|
128408
130371
|
}
|
|
128409
130372
|
return newlyFrozen;
|
|
128410
130373
|
}
|
|
130374
|
+
var import_node_perf_hooks = require("perf_hooks");
|
|
130375
|
+
init_logger();
|
|
130376
|
+
var EVENT_LOOP_MONITOR_INTERVAL_ENV = "ADHDEV_EVENT_LOOP_MONITOR_INTERVAL_MS";
|
|
130377
|
+
var EVENT_LOOP_LAG_WARN_ENV = "ADHDEV_EVENT_LOOP_LAG_WARN_MS";
|
|
130378
|
+
var DEFAULT_EVENT_LOOP_MONITOR_INTERVAL_MS = 1e4;
|
|
130379
|
+
var DEFAULT_EVENT_LOOP_LAG_WARN_MS = 5e3;
|
|
130380
|
+
function parseMs(raw, fallback) {
|
|
130381
|
+
if (raw === void 0 || raw.trim() === "") return fallback;
|
|
130382
|
+
const parsed = Number.parseInt(raw, 10);
|
|
130383
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
|
|
130384
|
+
}
|
|
130385
|
+
function resolveEventLoopMonitorIntervalMs(env2 = process.env) {
|
|
130386
|
+
return parseMs(env2[EVENT_LOOP_MONITOR_INTERVAL_ENV], DEFAULT_EVENT_LOOP_MONITOR_INTERVAL_MS);
|
|
130387
|
+
}
|
|
130388
|
+
function resolveEventLoopLagWarnMs(env2 = process.env) {
|
|
130389
|
+
return parseMs(env2[EVENT_LOOP_LAG_WARN_ENV], DEFAULT_EVENT_LOOP_LAG_WARN_MS);
|
|
130390
|
+
}
|
|
130391
|
+
function defaultLogFn(level, message) {
|
|
130392
|
+
if (level === "warn") LOG2.warn("EventLoop", message);
|
|
130393
|
+
else LOG2.debug("EventLoop", message);
|
|
130394
|
+
}
|
|
130395
|
+
function startEventLoopMonitor(opts) {
|
|
130396
|
+
const intervalMs = opts?.intervalMs ?? resolveEventLoopMonitorIntervalMs();
|
|
130397
|
+
const warnMs = opts?.warnMs ?? resolveEventLoopLagWarnMs();
|
|
130398
|
+
const logFn = opts?.logFn ?? defaultLogFn;
|
|
130399
|
+
if (intervalMs <= 0) return { stop: () => {
|
|
130400
|
+
} };
|
|
130401
|
+
let histogram;
|
|
130402
|
+
try {
|
|
130403
|
+
histogram = (0, import_node_perf_hooks.monitorEventLoopDelay)({ resolution: 20 });
|
|
130404
|
+
histogram.enable();
|
|
130405
|
+
} catch {
|
|
130406
|
+
return { stop: () => {
|
|
130407
|
+
} };
|
|
130408
|
+
}
|
|
130409
|
+
let expectedTickAt = null;
|
|
130410
|
+
const timer = setInterval(() => {
|
|
130411
|
+
try {
|
|
130412
|
+
const now = Date.now();
|
|
130413
|
+
const driftMs = expectedTickAt === null ? 0 : Math.max(0, now - expectedTickAt);
|
|
130414
|
+
expectedTickAt = now + intervalMs;
|
|
130415
|
+
const maxMs = histogram.max / 1e6;
|
|
130416
|
+
const p99Ms = histogram.percentile(99) / 1e6;
|
|
130417
|
+
const meanMs = histogram.mean / 1e6;
|
|
130418
|
+
histogram.reset();
|
|
130419
|
+
const worstMs = Math.max(driftMs, maxMs);
|
|
130420
|
+
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`;
|
|
130421
|
+
if (worstMs >= warnMs) {
|
|
130422
|
+
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.`);
|
|
130423
|
+
} else {
|
|
130424
|
+
logFn("debug", message);
|
|
130425
|
+
}
|
|
130426
|
+
} catch {
|
|
130427
|
+
}
|
|
130428
|
+
}, intervalMs);
|
|
130429
|
+
expectedTickAt = Date.now() + intervalMs;
|
|
130430
|
+
timer.unref?.();
|
|
130431
|
+
return {
|
|
130432
|
+
stop: () => {
|
|
130433
|
+
clearInterval(timer);
|
|
130434
|
+
try {
|
|
130435
|
+
histogram.disable();
|
|
130436
|
+
} catch {
|
|
130437
|
+
}
|
|
130438
|
+
}
|
|
130439
|
+
};
|
|
130440
|
+
}
|
|
128411
130441
|
init_require_whitelist();
|
|
128412
130442
|
async function initDaemonComponents2(config2) {
|
|
128413
130443
|
applyProcessHardening();
|
|
@@ -128707,6 +130737,7 @@ ${upgradeFailureNotice.notice}${supersededHint}`);
|
|
|
128707
130737
|
refreshQuotaCacheOnBoot(isQuotaProviderEnabled);
|
|
128708
130738
|
});
|
|
128709
130739
|
setImmediate(() => void router.resumePendingRefineJobsOnStartup());
|
|
130740
|
+
components.eventLoopMonitor = startEventLoopMonitor();
|
|
128710
130741
|
setImmediate(() => router.resumeDeferredRestartsOnStartup());
|
|
128711
130742
|
return components;
|
|
128712
130743
|
}
|
|
@@ -128736,10 +130767,15 @@ ${upgradeFailureNotice.notice}${supersededHint}`);
|
|
|
128736
130767
|
meshReconcileLoop,
|
|
128737
130768
|
quotaRefreshLoop,
|
|
128738
130769
|
quotaEventRefresh,
|
|
128739
|
-
providerStalenessProbe
|
|
130770
|
+
providerStalenessProbe,
|
|
130771
|
+
eventLoopMonitor
|
|
128740
130772
|
} = components;
|
|
128741
130773
|
poller.stop();
|
|
128742
130774
|
cdpInitializer.stop();
|
|
130775
|
+
try {
|
|
130776
|
+
eventLoopMonitor?.stop();
|
|
130777
|
+
} catch {
|
|
130778
|
+
}
|
|
128743
130779
|
try {
|
|
128744
130780
|
meshReconcileLoop?.stop();
|
|
128745
130781
|
} catch {
|