@adhdev/daemon-standalone 1.0.56-rc.4 → 1.0.56-rc.6
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 +225 -90
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/vendor/mcp-server/index.js +86 -14
- package/vendor/mcp-server/index.js.map +1 -1
package/dist/index.js
CHANGED
|
@@ -36620,10 +36620,10 @@ var require_dist3 = __commonJS({
|
|
|
36620
36620
|
}
|
|
36621
36621
|
function getDaemonBuildInfo() {
|
|
36622
36622
|
if (cached2) return cached2;
|
|
36623
|
-
const commit = readInjected(true ? "
|
|
36624
|
-
const commitShort = readInjected(true ? "
|
|
36625
|
-
const version2 = readInjected(true ? "1.0.56-rc.
|
|
36626
|
-
const builtAt = readInjected(true ? "2026-08-
|
|
36623
|
+
const commit = readInjected(true ? "d143fc7369d10b184b4cad79ce3a4240bc811faf" : void 0) ?? "unknown";
|
|
36624
|
+
const commitShort = readInjected(true ? "d143fc73" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
36625
|
+
const version2 = readInjected(true ? "1.0.56-rc.6" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
36626
|
+
const builtAt = readInjected(true ? "2026-08-19T09:46:30.077Z" : void 0);
|
|
36627
36627
|
cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
|
|
36628
36628
|
return cached2;
|
|
36629
36629
|
}
|
|
@@ -42852,8 +42852,9 @@ ${error48.message || ""}`;
|
|
|
42852
42852
|
CANONICAL_MESH_TOOL_NAMES = [
|
|
42853
42853
|
"mesh_status",
|
|
42854
42854
|
"mesh_list_nodes",
|
|
42855
|
-
|
|
42855
|
+
// GRAPH-ORCHESTRATION Phase F — batch before task, mirroring ALL_MESH_TOOLS.
|
|
42856
42856
|
"mesh_enqueue_batch",
|
|
42857
|
+
"mesh_enqueue_task",
|
|
42857
42858
|
"mesh_view_queue",
|
|
42858
42859
|
// GRAPH-ORCHESTRATION Phase E — the coordinator gate + graph view surface.
|
|
42859
42860
|
"mesh_graph_view",
|
|
@@ -52427,62 +52428,6 @@ ${blocks.join("\n\n")}`;
|
|
|
52427
52428
|
MAX_CLONE_CHAIN = 16;
|
|
52428
52429
|
}
|
|
52429
52430
|
});
|
|
52430
|
-
function loadDatabaseCtor() {
|
|
52431
|
-
if (DatabaseCtor) return DatabaseCtor;
|
|
52432
|
-
DatabaseCtor = loadBetterSqlite3();
|
|
52433
|
-
return DatabaseCtor;
|
|
52434
|
-
}
|
|
52435
|
-
function safeMeshId(meshId) {
|
|
52436
|
-
return meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
52437
|
-
}
|
|
52438
|
-
function legacyQueuePath(meshId) {
|
|
52439
|
-
return (0, import_path6.join)(getLedgerDir2(), `${safeMeshId(meshId)}.queue.json`);
|
|
52440
|
-
}
|
|
52441
|
-
function cleanupStrayRootRuntimeDb(canonicalPath2) {
|
|
52442
|
-
try {
|
|
52443
|
-
const strayPath = (0, import_path6.join)(getConfigDir2(), "mesh-runtime.db");
|
|
52444
|
-
if (strayPath === canonicalPath2) return;
|
|
52445
|
-
if (!(0, import_fs6.existsSync)(strayPath)) return;
|
|
52446
|
-
if ((0, import_fs6.statSync)(strayPath).size !== 0) return;
|
|
52447
|
-
(0, import_fs6.unlinkSync)(strayPath);
|
|
52448
|
-
if (!loggedStrayCleanup) {
|
|
52449
|
-
loggedStrayCleanup = true;
|
|
52450
|
-
LOG2.info("MeshRuntimeStore", `Removed stray 0-byte root mesh-runtime.db at ${strayPath}`);
|
|
52451
|
-
}
|
|
52452
|
-
} catch (err) {
|
|
52453
|
-
if (!loggedStrayCleanup) {
|
|
52454
|
-
loggedStrayCleanup = true;
|
|
52455
|
-
LOG2.warn("MeshRuntimeStore", `Stray root mesh-runtime.db cleanup failed (ignored): ${err?.message || err}`);
|
|
52456
|
-
}
|
|
52457
|
-
}
|
|
52458
|
-
}
|
|
52459
|
-
function meshRuntimeStorePath() {
|
|
52460
|
-
const dir = getLedgerDir2();
|
|
52461
|
-
const nextPath = (0, import_path6.join)(dir, "mesh-runtime.db");
|
|
52462
|
-
cleanupStrayRootRuntimeDb(nextPath);
|
|
52463
|
-
if ((0, import_fs6.existsSync)(nextPath)) return nextPath;
|
|
52464
|
-
const legacyPath = (0, import_path6.join)(dir, "beads.db");
|
|
52465
|
-
if (!(0, import_fs6.existsSync)(legacyPath)) return nextPath;
|
|
52466
|
-
try {
|
|
52467
|
-
(0, import_fs6.renameSync)(legacyPath, nextPath);
|
|
52468
|
-
for (const suffix of ["-wal", "-shm"]) {
|
|
52469
|
-
const legacyCompanion = `${legacyPath}${suffix}`;
|
|
52470
|
-
if ((0, import_fs6.existsSync)(legacyCompanion)) {
|
|
52471
|
-
(0, import_fs6.renameSync)(legacyCompanion, `${nextPath}${suffix}`);
|
|
52472
|
-
}
|
|
52473
|
-
}
|
|
52474
|
-
} catch (err) {
|
|
52475
|
-
if (!loggedMigrationFailure) {
|
|
52476
|
-
loggedMigrationFailure = true;
|
|
52477
|
-
LOG2.warn(
|
|
52478
|
-
"MeshRuntimeStore",
|
|
52479
|
-
`Legacy beads.db\u2192mesh-runtime.db migration failed; using existing DB in-place to avoid data loss: ${err?.message || err}`
|
|
52480
|
-
);
|
|
52481
|
-
}
|
|
52482
|
-
return (0, import_fs6.existsSync)(nextPath) ? nextPath : legacyPath;
|
|
52483
|
-
}
|
|
52484
|
-
return nextPath;
|
|
52485
|
-
}
|
|
52486
52431
|
function meshTurnAttemptFromRow(r) {
|
|
52487
52432
|
return {
|
|
52488
52433
|
attemptId: r.attempt_id,
|
|
@@ -52540,15 +52485,82 @@ ${blocks.join("\n\n")}`;
|
|
|
52540
52485
|
return { ledger: 0, toolCalls: 0, terminalQueue: 0, sessionDelivery: 0 };
|
|
52541
52486
|
}
|
|
52542
52487
|
}
|
|
52488
|
+
var MESH_EVENT_LEDGER_RETENTION_MS;
|
|
52489
|
+
var MESH_TOOL_CALL_LOG_RETENTION_MS;
|
|
52490
|
+
var MESH_TERMINAL_QUEUE_RETENTION_MS;
|
|
52491
|
+
var init_mesh_runtime_store_turn_rows = __esm2({
|
|
52492
|
+
"src/mesh/mesh-runtime-store-turn-rows.ts"() {
|
|
52493
|
+
"use strict";
|
|
52494
|
+
init_logger();
|
|
52495
|
+
init_mesh_retention_config();
|
|
52496
|
+
init_mesh_runtime_store();
|
|
52497
|
+
MESH_EVENT_LEDGER_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
52498
|
+
MESH_TOOL_CALL_LOG_RETENTION_MS = 14 * 24 * 60 * 60 * 1e3;
|
|
52499
|
+
MESH_TERMINAL_QUEUE_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
52500
|
+
}
|
|
52501
|
+
});
|
|
52502
|
+
function loadDatabaseCtor() {
|
|
52503
|
+
if (DatabaseCtor) return DatabaseCtor;
|
|
52504
|
+
DatabaseCtor = loadBetterSqlite3();
|
|
52505
|
+
return DatabaseCtor;
|
|
52506
|
+
}
|
|
52507
|
+
function safeMeshId(meshId) {
|
|
52508
|
+
return meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
52509
|
+
}
|
|
52510
|
+
function legacyQueuePath(meshId) {
|
|
52511
|
+
return (0, import_path6.join)(getLedgerDir2(), `${safeMeshId(meshId)}.queue.json`);
|
|
52512
|
+
}
|
|
52513
|
+
function cleanupStrayRootRuntimeDb(canonicalPath2) {
|
|
52514
|
+
try {
|
|
52515
|
+
const strayPath = (0, import_path6.join)(getConfigDir2(), "mesh-runtime.db");
|
|
52516
|
+
if (strayPath === canonicalPath2) return;
|
|
52517
|
+
if (!(0, import_fs6.existsSync)(strayPath)) return;
|
|
52518
|
+
if ((0, import_fs6.statSync)(strayPath).size !== 0) return;
|
|
52519
|
+
(0, import_fs6.unlinkSync)(strayPath);
|
|
52520
|
+
if (!loggedStrayCleanup) {
|
|
52521
|
+
loggedStrayCleanup = true;
|
|
52522
|
+
LOG2.info("MeshRuntimeStore", `Removed stray 0-byte root mesh-runtime.db at ${strayPath}`);
|
|
52523
|
+
}
|
|
52524
|
+
} catch (err) {
|
|
52525
|
+
if (!loggedStrayCleanup) {
|
|
52526
|
+
loggedStrayCleanup = true;
|
|
52527
|
+
LOG2.warn("MeshRuntimeStore", `Stray root mesh-runtime.db cleanup failed (ignored): ${err?.message || err}`);
|
|
52528
|
+
}
|
|
52529
|
+
}
|
|
52530
|
+
}
|
|
52531
|
+
function meshRuntimeStorePath() {
|
|
52532
|
+
const dir = getLedgerDir2();
|
|
52533
|
+
const nextPath = (0, import_path6.join)(dir, "mesh-runtime.db");
|
|
52534
|
+
cleanupStrayRootRuntimeDb(nextPath);
|
|
52535
|
+
if ((0, import_fs6.existsSync)(nextPath)) return nextPath;
|
|
52536
|
+
const legacyPath = (0, import_path6.join)(dir, "beads.db");
|
|
52537
|
+
if (!(0, import_fs6.existsSync)(legacyPath)) return nextPath;
|
|
52538
|
+
try {
|
|
52539
|
+
(0, import_fs6.renameSync)(legacyPath, nextPath);
|
|
52540
|
+
for (const suffix of ["-wal", "-shm"]) {
|
|
52541
|
+
const legacyCompanion = `${legacyPath}${suffix}`;
|
|
52542
|
+
if ((0, import_fs6.existsSync)(legacyCompanion)) {
|
|
52543
|
+
(0, import_fs6.renameSync)(legacyCompanion, `${nextPath}${suffix}`);
|
|
52544
|
+
}
|
|
52545
|
+
}
|
|
52546
|
+
} catch (err) {
|
|
52547
|
+
if (!loggedMigrationFailure) {
|
|
52548
|
+
loggedMigrationFailure = true;
|
|
52549
|
+
LOG2.warn(
|
|
52550
|
+
"MeshRuntimeStore",
|
|
52551
|
+
`Legacy beads.db\u2192mesh-runtime.db migration failed; using existing DB in-place to avoid data loss: ${err?.message || err}`
|
|
52552
|
+
);
|
|
52553
|
+
}
|
|
52554
|
+
return (0, import_fs6.existsSync)(nextPath) ? nextPath : legacyPath;
|
|
52555
|
+
}
|
|
52556
|
+
return nextPath;
|
|
52557
|
+
}
|
|
52543
52558
|
var import_fs6;
|
|
52544
52559
|
var import_path6;
|
|
52545
52560
|
var DatabaseCtor;
|
|
52546
52561
|
var loggedMigrationFailure;
|
|
52547
52562
|
var loggedStrayCleanup;
|
|
52548
52563
|
var MeshRuntimeStore;
|
|
52549
|
-
var MESH_EVENT_LEDGER_RETENTION_MS;
|
|
52550
|
-
var MESH_TOOL_CALL_LOG_RETENTION_MS;
|
|
52551
|
-
var MESH_TERMINAL_QUEUE_RETENTION_MS;
|
|
52552
52564
|
var init_mesh_runtime_store = __esm2({
|
|
52553
52565
|
"src/mesh/mesh-runtime-store.ts"() {
|
|
52554
52566
|
"use strict";
|
|
@@ -52558,13 +52570,14 @@ ${blocks.join("\n\n")}`;
|
|
|
52558
52570
|
init_load_better_sqlite3();
|
|
52559
52571
|
init_config();
|
|
52560
52572
|
init_mesh_ledger();
|
|
52561
|
-
init_mesh_retention_config();
|
|
52562
52573
|
init_mesh_work_queue();
|
|
52563
52574
|
init_mesh_graph_schema();
|
|
52564
52575
|
init_mesh_graph_store();
|
|
52565
52576
|
init_slot_model_enforcement();
|
|
52566
52577
|
init_mesh_daemon_slot_axis();
|
|
52567
52578
|
init_dist();
|
|
52579
|
+
init_mesh_runtime_store_turn_rows();
|
|
52580
|
+
init_mesh_runtime_store_turn_rows();
|
|
52568
52581
|
loggedMigrationFailure = false;
|
|
52569
52582
|
loggedStrayCleanup = false;
|
|
52570
52583
|
MeshRuntimeStore = class _MeshRuntimeStore {
|
|
@@ -52760,6 +52773,7 @@ ${blocks.join("\n\n")}`;
|
|
|
52760
52773
|
mesh_id TEXT NOT NULL,
|
|
52761
52774
|
tool TEXT NOT NULL,
|
|
52762
52775
|
session_id TEXT,
|
|
52776
|
+
caller_role TEXT,
|
|
52763
52777
|
called_at INTEGER NOT NULL
|
|
52764
52778
|
);
|
|
52765
52779
|
|
|
@@ -53089,6 +53103,10 @@ ${blocks.join("\n\n")}`;
|
|
|
53089
53103
|
ON mesh_event_ledger(mesh_id, task_id, timestamp)
|
|
53090
53104
|
WHERE task_id IS NOT NULL
|
|
53091
53105
|
`);
|
|
53106
|
+
const toolCallCols = this.tableColumns("mesh_tool_call_log");
|
|
53107
|
+
if (!toolCallCols.has("caller_role")) {
|
|
53108
|
+
this.db.exec(`ALTER TABLE mesh_tool_call_log ADD COLUMN caller_role TEXT`);
|
|
53109
|
+
}
|
|
53092
53110
|
} catch (err) {
|
|
53093
53111
|
if (!loggedMigrationFailure) {
|
|
53094
53112
|
loggedMigrationFailure = true;
|
|
@@ -54022,16 +54040,23 @@ ${blocks.join("\n\n")}`;
|
|
|
54022
54040
|
* Returns a rate-limit advisory string when the call rate is too high, null otherwise.
|
|
54023
54041
|
* windowMs: sliding window size in ms (default 10s)
|
|
54024
54042
|
* maxCalls: max allowed calls within the window (default 5)
|
|
54043
|
+
*
|
|
54044
|
+
* `callerRole` is a diagnostic, not an auth boundary: it reflects whether
|
|
54045
|
+
* ADHDEV_COORDINATOR_SESSION_ID was present in this process's env at call
|
|
54046
|
+
* time, and a process can set that env var on itself. It exists to answer
|
|
54047
|
+
* "was this call made by a coordinator-launched process or not" for the
|
|
54048
|
+
* mesh-tool-call-caller-instrumentation investigation, not to gate access —
|
|
54049
|
+
* do not wire it into any block/allow decision.
|
|
54025
54050
|
*/
|
|
54026
54051
|
recordMeshToolCall(opts) {
|
|
54027
|
-
const { meshId, tool, sessionId = null } = opts;
|
|
54052
|
+
const { meshId, tool, sessionId = null, callerRole = null } = opts;
|
|
54028
54053
|
const windowMs = opts.windowMs ?? 1e4;
|
|
54029
54054
|
const maxCalls = opts.maxCalls ?? 5;
|
|
54030
54055
|
const now = Date.now();
|
|
54031
54056
|
const windowStart = now - windowMs;
|
|
54032
54057
|
this.db.prepare(
|
|
54033
|
-
"INSERT INTO mesh_tool_call_log (mesh_id, tool, session_id, called_at) VALUES (?, ?, ?, ?)"
|
|
54034
|
-
).run(meshId, tool, sessionId, now);
|
|
54058
|
+
"INSERT INTO mesh_tool_call_log (mesh_id, tool, session_id, caller_role, called_at) VALUES (?, ?, ?, ?, ?)"
|
|
54059
|
+
).run(meshId, tool, sessionId, callerRole, now);
|
|
54035
54060
|
const row = this.db.prepare(
|
|
54036
54061
|
"SELECT COUNT(*) as cnt FROM mesh_tool_call_log WHERE mesh_id = ? AND tool = ? AND called_at >= ?"
|
|
54037
54062
|
).get(meshId, tool, windowStart);
|
|
@@ -54057,6 +54082,18 @@ ${blocks.join("\n\n")}`;
|
|
|
54057
54082
|
pruneToolCallLog(olderThanMs) {
|
|
54058
54083
|
return this.db.prepare("DELETE FROM mesh_tool_call_log WHERE called_at < ?").run(Date.now() - olderThanMs).changes;
|
|
54059
54084
|
}
|
|
54085
|
+
/**
|
|
54086
|
+
* Read back recent mesh_tool_call_log rows for one mesh, most recent first.
|
|
54087
|
+
* Diagnostic reader for the MESH-TOOL-CALL-CALLER-INSTRUMENTATION 1단계
|
|
54088
|
+
* investigation (was session_id/caller_role actually getting recorded, and
|
|
54089
|
+
* does the coordinator/unknown split hold up) — not on any hot path.
|
|
54090
|
+
*/
|
|
54091
|
+
getRecentToolCalls(meshId, limit = 100) {
|
|
54092
|
+
const rows = this.db.prepare(
|
|
54093
|
+
"SELECT tool, session_id, caller_role, called_at FROM mesh_tool_call_log WHERE mesh_id = ? ORDER BY called_at DESC LIMIT ?"
|
|
54094
|
+
).all(meshId, limit);
|
|
54095
|
+
return rows.map((r) => ({ tool: r.tool, sessionId: r.session_id, callerRole: r.caller_role, calledAt: r.called_at }));
|
|
54096
|
+
}
|
|
54060
54097
|
/**
|
|
54061
54098
|
* Retention prune for mesh_event_ledger (SoT 1-11 (b)). The ledger is append-only
|
|
54062
54099
|
* with NO lifecycle GC of its own, so lifecycle events accumulate without bound
|
|
@@ -55199,9 +55236,6 @@ ${blocks.join("\n\n")}`;
|
|
|
55199
55236
|
return res.changes > 0;
|
|
55200
55237
|
}
|
|
55201
55238
|
};
|
|
55202
|
-
MESH_EVENT_LEDGER_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
55203
|
-
MESH_TOOL_CALL_LOG_RETENTION_MS = 14 * 24 * 60 * 60 * 1e3;
|
|
55204
|
-
MESH_TERMINAL_QUEUE_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
55205
55239
|
}
|
|
55206
55240
|
});
|
|
55207
55241
|
var mesh_ledger_exports = {};
|
|
@@ -56755,7 +56789,7 @@ Default branch: \`${mesh.defaultBranch}\`` : ""}`);
|
|
|
56755
56789
|
const lines = [
|
|
56756
56790
|
"## Task difficulty",
|
|
56757
56791
|
"",
|
|
56758
|
-
"Pass `difficulty` on `mesh_enqueue_task` (`easy` / `medium` / `difficult` / `freeform`)
|
|
56792
|
+
"Pass `difficulty` on every worker entry in `mesh_enqueue_batch`, or on `mesh_enqueue_task` for the single-task fallback. The values (`easy` / `medium` / `difficult` / `freeform`) describe how hard the work is. It is a ROUTING HINT: it is matched against each node's capability slots, so a task goes to a slot configured for that difficulty.",
|
|
56759
56793
|
"",
|
|
56760
56794
|
'**The slot decides the model and thinking level \u2014 not the difficulty.** `difficulty: "difficult"` does not mean "use opus"; it means "route to a slot that handles difficult work", and that slot\'s own model/thinking is what launches. So classify honestly by how hard the task is, and change what a difficulty RUNS ON by editing the node\'s slots (`mesh_node_slots_set`), never by picking a different difficulty. Passing an explicit `model`/`thinkingLevel` still overrides everything for one task.'
|
|
56761
56795
|
];
|
|
@@ -56839,11 +56873,12 @@ ${rules.join("\n")}`;
|
|
|
56839
56873
|
return `## Rules
|
|
56840
56874
|
|
|
56841
56875
|
- **Route, don't implement.** Delegate all code reading, analysis, and execution to node agents. Never read source files or run commands in the coordinator \u2014 keep context lean. See also: **Never use local sub-agents** below.${destructiveGitRule}
|
|
56842
|
-
- **Never use local sub-agents.** Do NOT spawn your runtime's own sub-agents (e.g. Claude Code's Task/Explore/Agent tools, or any equivalent in-process agent-spawning tool) to read code, investigate, run RCA, or implement. Such sub-agents execute on the coordinator's machine, outside the mesh \u2014 they escape mesh parallelism, the ledger/audit trail, node capability profiles, and worktree isolation, and leave no \`mesh_task_history\` record. ALL code reading, analysis, RCA, and implementation must be delegated
|
|
56843
|
-
- **Front-load task
|
|
56876
|
+
- **Never use local sub-agents.** Do NOT spawn your runtime's own sub-agents (e.g. Claude Code's Task/Explore/Agent tools, or any equivalent in-process agent-spawning tool) to read code, investigate, run RCA, or implement. Such sub-agents execute on the coordinator's machine, outside the mesh \u2014 they escape mesh parallelism, the ledger/audit trail, node capability profiles, and worktree isolation, and leave no \`mesh_task_history\` record. ALL code reading, analysis, RCA, and implementation must be delegated through \`mesh_enqueue_batch\` (the default \u2014 see Workflow 3.a), falling back to \`mesh_enqueue_task\` only for a terminal single step, to \`mesh_send_task\` for a same-session continuation (use \`task_mode: "live_debug_readonly"\` for read-only investigation), or cross-verified via \`mesh_magi_review\` for read-only fan-out. The coordinator's own actions are limited to \`mesh_*\` tool orchestration and synthesizing results.
|
|
56877
|
+
- **Front-load immutable task instructions.** Include everything the agent needs (files, problem, expected fix) in whichever dispatch surface Workflow 3.a selects (\`mesh_enqueue_batch\` by default; \`mesh_enqueue_task\` / \`mesh_send_task\` in their narrower cases). Put predecessor-produced data in explicit \`inputs_from\` bindings and coordinator decisions in gates; never copy untrusted worker output into a new instruction by hand when a binding can preserve provenance. Append a structured result request at the end: ask the worker to conclude with a JSON block containing \`status\`, \`changedFiles\`, \`gitStatus\`, \`validationResults\`, \`errors\`, \`nextAction\`. The daemon parses this automatically; you can read it from \`mesh_task_history\`.
|
|
56844
56878
|
- **Reuse idle sessions.** For follow-up, retry, commit/push, or cleanup on the same issue, send only the delta to the existing idle session. Start a fresh session only when: (a) branch/worktree isolation is required, (b) the existing session had a dispatch failure or provider mismatch, (c) the transcript/runtime is contaminated or interrupted, (d) the user explicitly asks for a different provider/session, or (e) **the delta is a genuinely NEW subject rather than a continuation** \u2014 a new topic appended to an existing session can be dropped or re-run as the previous task, so give it its own task even when a session sits idle. Continuation of the same issue in an already-idle session is allowed and preferred \u2014 this rule blocks concurrent unrelated work interleaved into a live (still-generating) session, not sequential same-issue follow-ups. The test is subject continuity, not timing: carrying an investigation forward into its own fix is the SAME subject and belongs in that session (Workflow 3f), while an unrelated bug is a new subject even if the same session just went idle.
|
|
56845
56879
|
- **Nodes are separate machines with separate checkouts \u2014 not interchangeable execution slots.** Each node is a different physical computer with its own clone of the repo. Work done on another node must be committed, pushed, and pulled back before this machine sees it, and since RELEASE/DEPLOY runs on the coordinator's own machine, sending a code change elsewhere buys a round trip out and another one back. So **default to this coordinator's own machine for code changes** \u2014 its local node (base or a worktree cloned from it). Routing to a DIFFERENT machine is the exception and needs a reason, of which there are exactly two: (a) **platform-specific verification** that cannot be done here \u2014 win32 PATH/registry, a clean install/uninstall on that OS, that machine's package-manager state; or (b) **parallelizing read-only investigation** across machines. "That node is idle" is not a reason. If you catch yourself dispatching a fix to another machine without (a) or (b), route it here instead.
|
|
56846
56880
|
- **Don't split investigation from the fix.** When a task will plainly end in a code change, dispatch it as \`code_change\` from the start \u2014 the in-session handoff and split criteria live in Workflow 3f. Split only when the fix genuinely belongs on another machine for reason (a) above; redoing an investigator's context in a fresh session (worse, on another machine) is pure loss.
|
|
56881
|
+
- **Batch is the default enqueue surface.** Apply Workflow 3.a: before every enqueue, ask whether you will read this task's result and then dispatch more work. If yes, that successor is declarable \u2014 submit the whole plan as one \`mesh_enqueue_batch\` with \`inputs_from\` / \`run_if\` / \`gated_by\`, rather than enqueueing a step, waiting, and enqueueing the next by hand. \`mesh_enqueue_task\` is the fallback for a genuinely terminal single step, and it takes an \`orchestration_decision\` stating which \`single_reason\` applies.
|
|
56847
56882
|
- **Base nodes are reserved for environment-specific testing.** Apply Workflow 3.b0: only work that verifies a machine's physical environment runs on a base node (pinned with \`required_tags\`/\`target_node_id\`); every ordinary \`code_change\` gets its own cloned worktree. Node availability is not branch isolation.
|
|
56848
56883
|
- **Worktree affinity.** Apply Workflow 3.b1: route a branch's follow-ups back to its own worktree node (\`required_tags: ["worktree=<branch>"]\` or \`target_node_id\`, taken from the \`mesh_clone_node\` result or a live \`mesh_status\`); only \`convergence\` (merge/push) runs base-side.
|
|
56849
56884
|
- **Classify task difficulty honestly.** Judge each task's real difficulty (\`easy\`/\`medium\`/\`difficult\`/\`freeform\`) per the Task difficulty section above \u2014 it is a routing hint, and the matched slot's own model/thinking is what launches. Never bend difficulty to chase a model; retune slots instead (\`mesh_node_slots_set\`).
|
|
@@ -56904,12 +56939,12 @@ When you compose the task message you dispatch to a node, include this requireme
|
|
|
56904
56939
|
|------|---------|
|
|
56905
56940
|
| \`mesh_status\` | Nodes' health, git state, sessions, branch convergence |
|
|
56906
56941
|
| \`mesh_list_nodes\` | List nodes with workspace paths |
|
|
56907
|
-
| \`
|
|
56908
|
-
| \`
|
|
56942
|
+
| \`mesh_enqueue_batch\` | **DEFAULT enqueue surface** for a plan with two or more known graph steps. Atomically enqueues a dependency-wired task set; \`depends_on\` may name batch-local \`ref\`s (forward refs OK). Carries the full graph surface: \`inputs_from\`, \`run_if\`, \`gates\` + \`gated_by\`, \`workspaces\` + \`workspace_ref\` |
|
|
56943
|
+
| \`mesh_enqueue_task\` | SINGLE-TASK FALLBACK \u2014 one ready task with no declarable downstream step; idle nodes auto-claim |
|
|
56909
56944
|
| \`mesh_view_queue\` | Queue status \u2014 pending/assigned/completed/failed/cancelled |
|
|
56910
56945
|
| \`mesh_graph_view\` | Inspect orchestration graphs \u2014 node states, gates awaiting you, workspace sagas, why something is blocked |
|
|
56911
56946
|
| \`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) |
|
|
56947
|
+
| \`mesh_graph_gate_release\` | Pass a gate you hold \u2014 the ONLY way through one (no timeout ever passes a gate). **A gate and its dependents are one unit**: a gate earns its keep only when some task names it in \`gated_by\`, because releasing it is what dispatches that task. A gate nothing depends on opens nothing and is pure claim/release overhead \u2014 declare the follower in the same batch, or skip the gate |
|
|
56913
56948
|
| \`mesh_queue_cancel\` | Cancel a queue task (audit history kept) |
|
|
56914
56949
|
| \`mesh_queue_requeue\` | Return a task to pending for retry |
|
|
56915
56950
|
| \`mesh_send_task\` | Push a task straight to a specific node/session |
|
|
@@ -56962,6 +56997,8 @@ When you compose the task message you dispatch to a node, include this requireme
|
|
|
56962
56997
|
| \`mesh_coordinator_prompt_append_set\` | Write/clear that APPEND (append-only; base prompt is not replaceable) |`;
|
|
56963
56998
|
TOOL_EXPOSURE_PREFLIGHT_SECTION = `## Tool Exposure Preflight
|
|
56964
56999
|
|
|
57000
|
+
Before searching for an enqueue tool, classify the whole currently known work frontier. For every new delegation search, include \`mesh_enqueue_batch\` by exact name; never search for or load only \`mesh_enqueue_task\`. Load \`mesh_enqueue_task\` only as the single-task fallback after the batch eligibility check below fails.
|
|
57001
|
+
|
|
56965
57002
|
Before doing any coordinator work, confirm that the actual callable tool list includes \`mesh_status\` and the other \`mesh_*\` tools from the table above. If this Repo Mesh coordinator prompt is present but the callable \`mesh_*\` tools are missing, the MCP server/tool manifest is stale or not injected yet. Do not substitute terminal/file/git tools, do not inspect or edit the repository directly, and do not continue as a non-mesh local coding agent. Stop immediately and tell the user to run \`/reload-mcp\` or start a fresh coordinator session so ADHDev can reconnect \`adhdev-mesh\`.`;
|
|
56966
57003
|
QUOTA_SECTION = `## Provider Quota
|
|
56967
57004
|
|
|
@@ -56972,9 +57009,24 @@ Before doing any coordinator work, confirm that the actual callable tool list in
|
|
|
56972
57009
|
WORKFLOW_SECTION = `## Orchestration Workflow
|
|
56973
57010
|
|
|
56974
57011
|
1. **Assess** \u2014 Call \`mesh_status\` to see which nodes are healthy and available. Check \`mesh_task_history\` to understand what has already been done in this mesh \u2014 previous delegations, completions, and failures.
|
|
56975
|
-
2. **Plan** \u2014 Decompose the user's request into independent tasks for parallel execution, or sequential tasks when dependencies exist. If \`mesh_task_history\` shows a recent failure for a task, decide whether to retry or reassign. **For multi-task work, create a mission first**: call \`mesh_mission_upsert\` with a title and goal, then
|
|
57012
|
+
2. **Plan** \u2014 Decompose the user's request into independent tasks for parallel execution, or sequential tasks when dependencies exist. If \`mesh_task_history\` shows a recent failure for a task, decide whether to retry or reassign. **For multi-task work, create a mission first**: call \`mesh_mission_upsert\` with a title and goal, then submit the plan as one \`mesh_enqueue_batch\` carrying that \`mission_id\` (a top-level \`mission_id\` applies to every entry). Express "B after A" ordering with \`depends_on\` instead of waiting and polling \u2014 the system claims dependents automatically when their dependencies complete. When the mission's outcome is decided, update its status (\`completed\`/\`abandoned\`) via \`mesh_mission_upsert\`. If the prompt already shows an **Active Mission**, continue it from its current task state \u2014 do not re-enqueue tasks that already exist.
|
|
56976
57013
|
3. **Queue / Delegate** \u2014 The Mesh uses an autonomous pull-based Work Queue:
|
|
56977
|
-
a. **
|
|
57014
|
+
a. **Batch-first rule.** Before enqueueing anything, answer ONE question: *when this task reports back, will I read its result and then dispatch more work \u2014 or does the result go to the user with nothing behind it?*
|
|
57015
|
+
- **You will act on the result \u2192 \`mesh_enqueue_batch\`.** If you already intend to read the output and dispatch a next step, that next step is a step you can declare. Declare it: bind the evidence with \`inputs_from\`, branch on it with \`run_if\`, or put your own decision behind a coordinator gate and point the follower at it with \`gated_by\`. "I need to see the result first" is HOW a graph edge is expressed, not a reason to skip the graph \u2014 that is exactly what \`inputs_from\` and gates are for. An investigation you plan to act on is, by this test, a declarable two-step plan.
|
|
57016
|
+
- **The result goes to the user and nothing follows \u2192 \`mesh_enqueue_task\`.** One task, no successor you intend to dispatch yourself. Same-subject continuation of an already-running session is \`mesh_send_task\`, not a new enqueue either way.
|
|
57017
|
+
- Submit the whole materializable plan in ONE batch. A batch may mix worker tasks, \`inputs_from\` bindings, \`run_if\` branches, delayed \`workspace_ref\` preparation, and coordinator gates for approval, Refinery, CI waiting, publish, or deployment. Do not enqueue one known step, wait for it, and then enqueue the next known step by hand.
|
|
57018
|
+
- **A mesh with idle nodes or a short plan does not remove this requirement.** "It is only two steps" and "I will just enqueue the next one when this finishes" are the two ways the graph gets skipped; neither is a reason. Two declarable steps is precisely the case the batch surface exists for.
|
|
57019
|
+
- **"The only thing I am sure of right now is this one task" is not evidence of a single.** That feeling is about your confidence, not about how many steps are settled; two steps are routinely settled at once while it still feels that way. Count the steps you would actually dispatch. If that count is two or more, it is a batch no matter how the plan feels.
|
|
57020
|
+
- **"I cannot choose the next step until I see this result" states that the next step EXISTS.** What the result decides is *which branch you take*, not *whether* there is a follow-up \u2014 and a branch that turns on a predecessor's output is what \`run_if\` selects, what \`inputs_from\` feeds, and what a coordinator gate holds until you decide. Needing to look at evidence before choosing is the ordinary case for a graph edge, not an exception to it.
|
|
57021
|
+
- **A gate or a batch is not overhead when something follows it.** The extra call buys the dispatch of everything behind it, so it costs one call and saves a whole round-trip through you. The only genuinely wasteful gate is one nothing depends on \u2014 a gate no task names in \`gated_by\` opens nothing when released, which the release response tells you about; that case is answered by declaring the follower, not by abandoning the batch.
|
|
57022
|
+
|
|
57023
|
+
When you do use \`mesh_enqueue_task\`, pass \`orchestration_decision\` recording why a single was right (\`single_reason\`: \`only_one_step_known\` / \`future_step_not_specifiable\` / \`same_session_continuation\` / \`legacy_client\` / \`operator_override\`). Omitting it is recorded as \`decision_missing\`.
|
|
57024
|
+
|
|
57025
|
+
Do not invent speculative downstream instructions merely to form a batch \u2014 a step you would not actually dispatch does not belong in the graph. But check the three declaration surfaces before concluding a step is unstatable: \`inputs_from\` when the instruction is stable and only needs predecessor evidence, a coordinator gate when a human/coordinator decision or external side effect must come first, \`run_if\` when the branch turns on a structured result predicate. Only when none of the three can state the future step faithfully is single-task enqueue correct.
|
|
57026
|
+
|
|
57027
|
+
**The line between the two failures.** The rebuttals above are aimed at declining to declare a step you already intend to dispatch; this guard is aimed at fabricating one you do not. They never both apply to the same step: the test is whether the step exists in your plan, not whether its details are settled. A step you intend to run but whose exact branch depends on evidence is KNOWN and belongs in the batch \u2014 that dependency is what the three surfaces express. A step you would have to make up to fill the batch is UNKNOWN and belongs nowhere. When you genuinely cannot tell which one you are looking at, enqueue the single and record \`future_step_not_specifiable\`; that is the honest answer and it is measured.
|
|
57028
|
+
|
|
57029
|
+
Either way, idle node agents automatically pull tasks from the queue and begin working. A batch inserts atomically (a mid-batch error such as a cycle or bad difficulty rolls the whole set back, so no half-registered chain), and each task's \`depends_on\` may name sibling tasks by their batch-local \`ref\` label, forward references included.
|
|
56978
57030
|
b. **Node Preparation**: Reuse an existing idle session on the correct node/provider before launching a new chat/session. Call \`mesh_launch_session\` only when no suitable session exists, when the user explicitly asks for a fresh provider/session, or when branch/worktree isolation requires it. **A node is not limited to one live session for read-only work** \u2014 \`readonly\`/\`live_debug_readonly\` tasks are exempt from the one-active-per-node invariant, so the SAME node can auto-launch multiple concurrent read-only sessions with no worktree needed. Cloning a worktree costs roughly 10 seconds, so it is cheap enough to create one whenever write work needs a free node; use it for branch isolation, for parallel write tasks (one active write per node), or when a node's read-only queue is deep enough that a second node would clearly finish faster \u2014 call \`mesh_clone_node\` to create the worktree node first.
|
|
56979
57031
|
b0. **Base nodes are for environment-specific testing, not for general code changes.** Before dispatching any write task, answer ONE question: *does this task verify the physical environment of a specific machine or OS, or does it only change code?*
|
|
56980
57032
|
- **Physical-environment task \u2192 base node, targeted.** Pin it with \`required_tags\` (e.g. \`["os=win32"]\`) or \`target_node_id\`. Examples that genuinely require the real machine: verifying a win32 \`PATH\`/registry/installer layout, a clean-install or uninstall on a specific OS, Homebrew or package-manager state on one particular machine, an OS-dependent runtime behavior (path separators, process spawn, native bindings), or reproducing a bug reported only on that node. A worktree CANNOT substitute for these \u2014 the point is the machine itself.
|
|
@@ -60495,9 +60547,11 @@ ${rendered}`, "utf-8");
|
|
|
60495
60547
|
const materializedNodeIds = advanceReleasedGateDownstream(store, gate, node, nowIso);
|
|
60496
60548
|
const freshNodes = graphStore.listNodes(gate.graphId);
|
|
60497
60549
|
const graph = graphStore.getGraph(gate.graphId);
|
|
60550
|
+
let graphCompleted = false;
|
|
60498
60551
|
if (graph && freshNodes.every(isTerminalEquivalent)) {
|
|
60499
60552
|
graphStore.updateGraphStatus(gate.graphId, "completed", nowIso, true);
|
|
60500
60553
|
insertGateOutbox(graphStore, gate.meshId, gate.graphId, "graph_completed", { graphId: gate.graphId }, nowIso);
|
|
60554
|
+
graphCompleted = true;
|
|
60501
60555
|
} else if (graph?.status === "waiting_gate") {
|
|
60502
60556
|
const stillWaiting = graphStore.listGatesByGraph(gate.graphId).some((g) => g.gateId !== gate.gateId && (g.state === "awaiting_coordinator" || g.state === "claimed"));
|
|
60503
60557
|
if (!stillWaiting) graphStore.updateGraphStatus(gate.graphId, "active", nowIso);
|
|
@@ -60521,7 +60575,16 @@ ${rendered}`, "utf-8");
|
|
|
60521
60575
|
gateId: gate.gateId
|
|
60522
60576
|
}, nowIso);
|
|
60523
60577
|
}
|
|
60524
|
-
return {
|
|
60578
|
+
return {
|
|
60579
|
+
released: true,
|
|
60580
|
+
duplicate: false,
|
|
60581
|
+
gate: graphStore.getGate(gate.gateId),
|
|
60582
|
+
materializedNodeIds,
|
|
60583
|
+
// Read from the edge set this release already walked — a gate with an empty
|
|
60584
|
+
// directDownstream is terminal, and terminal gates materialize nothing by design.
|
|
60585
|
+
downstreamNodeCount: directDownstream.size,
|
|
60586
|
+
graphCompleted
|
|
60587
|
+
};
|
|
60525
60588
|
});
|
|
60526
60589
|
try {
|
|
60527
60590
|
drainMeshGraphOutbox(input.meshId);
|
|
@@ -60782,6 +60845,18 @@ ${rendered}`, "utf-8");
|
|
|
60782
60845
|
...p.replayed ? { replayed: true } : {}
|
|
60783
60846
|
});
|
|
60784
60847
|
}
|
|
60848
|
+
function recordSingleEnqueueDecision(meshId, p) {
|
|
60849
|
+
safeAppend(meshId, "single_enqueue_decision", {
|
|
60850
|
+
taskId: p.taskId,
|
|
60851
|
+
enqueueSurface: "single",
|
|
60852
|
+
...p.missionId ? { missionId: p.missionId } : {},
|
|
60853
|
+
...p.coordinatorSessionId ? { coordinatorSessionId: p.coordinatorSessionId } : {},
|
|
60854
|
+
orchestrationDecision: p.decision,
|
|
60855
|
+
...p.decisionMissing ? { decisionMissing: true } : {},
|
|
60856
|
+
...p.declaredEligibleSingle ? { declaredEligibleSingle: true } : {},
|
|
60857
|
+
...p.batchCapabilityAvailable ? { batchCapabilityAvailable: p.batchCapabilityAvailable } : {}
|
|
60858
|
+
}, p.taskId);
|
|
60859
|
+
}
|
|
60785
60860
|
function recordGraphEnqueueValidationFailed(meshId, p) {
|
|
60786
60861
|
safeAppend(meshId, "graph_enqueue_validation_failed", {
|
|
60787
60862
|
code: p.code,
|
|
@@ -60839,6 +60914,7 @@ ${rendered}`, "utf-8");
|
|
|
60839
60914
|
}
|
|
60840
60915
|
var MESH_VALID_SINGLE_REASONS;
|
|
60841
60916
|
var MESH_SUPERSEDED_SINGLE_REASONS;
|
|
60917
|
+
var MESH_DECLARED_ELIGIBLE_SINGLE_HINT;
|
|
60842
60918
|
var init_mesh_graph_provenance = __esm2({
|
|
60843
60919
|
"src/mesh/mesh-graph-provenance.ts"() {
|
|
60844
60920
|
"use strict";
|
|
@@ -60856,6 +60932,7 @@ ${rendered}`, "utf-8");
|
|
|
60856
60932
|
"workspace_unresolved",
|
|
60857
60933
|
"coordinator_action_between"
|
|
60858
60934
|
];
|
|
60935
|
+
MESH_DECLARED_ELIGIBLE_SINGLE_HINT = "You declared known_graph_steps >= 2 but used the single-task surface. Submit the known steps as one mesh_enqueue_batch \u2014 its input bindings, conditions and coordinator gates declare the ones that need predecessor evidence, a condition, or a coordinator action \u2014 instead of enqueueing them one at a time.";
|
|
60859
60936
|
}
|
|
60860
60937
|
});
|
|
60861
60938
|
function turnProjectionActiveWorkStatus(meshId, taskId) {
|
|
@@ -61706,8 +61783,28 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
61706
61783
|
}
|
|
61707
61784
|
};
|
|
61708
61785
|
}
|
|
61709
|
-
function
|
|
61710
|
-
|
|
61786
|
+
function liveLocalProviderEnablementForRouting(loader2, isLocalNode) {
|
|
61787
|
+
if (!loader2) return null;
|
|
61788
|
+
const verdicts = /* @__PURE__ */ new Map();
|
|
61789
|
+
return {
|
|
61790
|
+
isMachineProviderEnabled: (providerType) => loader2.isMachineProviderEnabled(providerType),
|
|
61791
|
+
isMachineQuotaEnabled: (providerType) => loader2.isMachineQuotaEnabled ? loader2.isMachineQuotaEnabled(providerType) : true,
|
|
61792
|
+
isLocalNode(node) {
|
|
61793
|
+
let verdict = verdicts.get(node);
|
|
61794
|
+
if (verdict === void 0) {
|
|
61795
|
+
verdict = !!node && isLocalNode(node) === true;
|
|
61796
|
+
verdicts.set(node, verdict);
|
|
61797
|
+
}
|
|
61798
|
+
return verdict;
|
|
61799
|
+
}
|
|
61800
|
+
};
|
|
61801
|
+
}
|
|
61802
|
+
function quotaFactsContextForLiveRouting(mesh, isLocalNode, providerLoader) {
|
|
61803
|
+
return {
|
|
61804
|
+
nodes: mesh?.nodes,
|
|
61805
|
+
liveLocalQuota: liveLocalQuotaForRouting(isLocalNode),
|
|
61806
|
+
providerEnablement: liveLocalProviderEnablementForRouting(providerLoader, isLocalNode)
|
|
61807
|
+
};
|
|
61711
61808
|
}
|
|
61712
61809
|
function directQuotaEntryFor(node, providerType) {
|
|
61713
61810
|
const facts = node?.nodeFacts;
|
|
@@ -61764,6 +61861,28 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
61764
61861
|
const ageText = Number.isFinite(ageMs2) ? `${Math.round(ageMs2 / 6e4)}m` : "unparseable";
|
|
61765
61862
|
LOG2.info("MeshQuota", `QUOTA GATE: provider '${providerType}' on node ${nodeId} fails open \u2014 snapshot age ${ageText} exceeds the ${Math.round(staleAfterMs / 6e4)}m freshness threshold`);
|
|
61766
61863
|
}
|
|
61864
|
+
function classifyAbsentQuotaReason(node, providerType, context) {
|
|
61865
|
+
const enablement = context?.providerEnablement;
|
|
61866
|
+
if (!enablement) return "unclassified_remote";
|
|
61867
|
+
const sourceNode = cloneSourceNodeFor(node, context);
|
|
61868
|
+
if (!enablement.isLocalNode(node) && !(sourceNode !== void 0 && enablement.isLocalNode(sourceNode))) {
|
|
61869
|
+
return "unclassified_remote";
|
|
61870
|
+
}
|
|
61871
|
+
if (!enablement.isMachineProviderEnabled(providerType)) return "provider_disabled";
|
|
61872
|
+
if (!enablement.isMachineQuotaEnabled(providerType)) return "probe_disabled";
|
|
61873
|
+
return "not_measured";
|
|
61874
|
+
}
|
|
61875
|
+
function logAbsentQuotaFailOpen(node, providerType, policy, now, context) {
|
|
61876
|
+
const staleAfterMs = resolveQuotaRoutingPolicy(policy).staleAfterMs;
|
|
61877
|
+
const nodeId = typeof node?.nodeId === "string" && node.nodeId ? node.nodeId : typeof node?.id === "string" && node.id ? node.id : "unknown";
|
|
61878
|
+
const key2 = `${nodeId} ${providerType}`;
|
|
61879
|
+
const last = absentFailOpenLoggedAt.get(key2);
|
|
61880
|
+
if (last !== void 0 && now - last < staleAfterMs) return;
|
|
61881
|
+
absentFailOpenLoggedAt.set(key2, now);
|
|
61882
|
+
const reason = classifyAbsentQuotaReason(node, providerType, context);
|
|
61883
|
+
const reasonText = reason === "probe_disabled" ? "quota probing is disabled for this provider on this machine (quotaEnabled: false \u2014 an intended opt-out, not a fault)" : reason === "provider_disabled" ? "this provider is not enabled on this machine" : reason === "not_measured" ? "no snapshot has been measured yet" : "this daemon cannot read that node's machineProviders config (remote node, or its own daemon has not injected one) \u2014 cannot tell not-yet-measured from an opt-out; check the owning node's config directly";
|
|
61884
|
+
LOG2.info("MeshQuota", `QUOTA GATE: provider '${providerType}' on node ${nodeId} fails open \u2014 no quota entry (${reason}: ${reasonText})`);
|
|
61885
|
+
}
|
|
61767
61886
|
function isSessionResetImminent(session, facts, quota, imminentMs, now) {
|
|
61768
61887
|
const resetsAt = Number(session?.resetsAt);
|
|
61769
61888
|
if (!Number.isFinite(resetsAt) || resetsAt <= 0) return false;
|
|
@@ -61785,7 +61904,10 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
61785
61904
|
}
|
|
61786
61905
|
function evaluateProviderQuotaGate(node, providerType, policy, now = Date.now(), context) {
|
|
61787
61906
|
const entry = quotaEntryFor(node, providerType, context, now);
|
|
61788
|
-
if (!entry)
|
|
61907
|
+
if (!entry) {
|
|
61908
|
+
logAbsentQuotaFailOpen(node, providerType, policy, now, context);
|
|
61909
|
+
return null;
|
|
61910
|
+
}
|
|
61789
61911
|
const { facts, quota } = entry;
|
|
61790
61912
|
let sessionTrustworthy = true;
|
|
61791
61913
|
let weeklyTrustworthy = true;
|
|
@@ -61947,6 +62069,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
61947
62069
|
var PROVIDER_QUOTA_EXHAUSTED_SKIP_REASON;
|
|
61948
62070
|
var ALL_PROVIDERS_QUOTA_GATED_SKIP_REASON;
|
|
61949
62071
|
var staleFailOpenLoggedAt;
|
|
62072
|
+
var absentFailOpenLoggedAt;
|
|
61950
62073
|
var DEFAULT_WEEKLY_WINDOW_MINUTES;
|
|
61951
62074
|
var DEFAULT_SESSION_WINDOW_MINUTES;
|
|
61952
62075
|
var lastQuotaRankingByNode;
|
|
@@ -61962,6 +62085,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
61962
62085
|
PROVIDER_QUOTA_EXHAUSTED_SKIP_REASON = "provider_quota_exhausted";
|
|
61963
62086
|
ALL_PROVIDERS_QUOTA_GATED_SKIP_REASON = "all_providers_quota_gated";
|
|
61964
62087
|
staleFailOpenLoggedAt = /* @__PURE__ */ new Map();
|
|
62088
|
+
absentFailOpenLoggedAt = /* @__PURE__ */ new Map();
|
|
61965
62089
|
DEFAULT_WEEKLY_WINDOW_MINUTES = 7 * 24 * 60;
|
|
61966
62090
|
DEFAULT_SESSION_WINDOW_MINUTES = 5 * 60;
|
|
61967
62091
|
lastQuotaRankingByNode = /* @__PURE__ */ new Map();
|
|
@@ -66498,7 +66622,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
66498
66622
|
LOG2.info("MeshQueue", `Deferring queue claim for node ${nodeId} (${sessionId}): an auto fast-forward is mutating its workspace \u2014 task left pending, claim re-fires next tick`);
|
|
66499
66623
|
return false;
|
|
66500
66624
|
}
|
|
66501
|
-
const quotaClaimBlock = evaluateProviderQuotaGate(node, providerType, mesh?.policy?.quotaRouting ?? null, Date.now(), quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode));
|
|
66625
|
+
const quotaClaimBlock = evaluateProviderQuotaGate(node, providerType, mesh?.policy?.quotaRouting ?? null, Date.now(), quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, components.providerLoader));
|
|
66502
66626
|
if (quotaClaimTrace) quotaClaimTrace.evaluated += 1;
|
|
66503
66627
|
if (quotaClaimBlock) {
|
|
66504
66628
|
const observation = { nodeId, sessionId, providerType, block: quotaClaimBlock };
|
|
@@ -67236,7 +67360,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
67236
67360
|
// pass it through for the 'fitness' strategy's task→slot ranking. The
|
|
67237
67361
|
// mesh's quotaRouting thresholds ride along so the fitness score can
|
|
67238
67362
|
// include the quota-headroom spread bonus (fail-open when unset).
|
|
67239
|
-
{ bumpCursor: true, task: { difficulty: task.difficulty, requiredTags: task.requiredTags }, quotaRouting: mesh?.policy?.quotaRouting ?? null, quotaFactsContext: quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode) }
|
|
67363
|
+
{ bumpCursor: true, task: { difficulty: task.difficulty, requiredTags: task.requiredTags }, quotaRouting: mesh?.policy?.quotaRouting ?? null, quotaFactsContext: quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, components.providerLoader) }
|
|
67240
67364
|
).map((c) => c.node);
|
|
67241
67365
|
const skippedCandidates = [];
|
|
67242
67366
|
const SKIPPED_CANDIDATES_MAX = 5;
|
|
@@ -67295,7 +67419,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
67295
67419
|
}
|
|
67296
67420
|
autoLaunchInProgress.add(launchKey);
|
|
67297
67421
|
try {
|
|
67298
|
-
const resolved = await resolveUsableProvider(components, nodeId, node, meshId, task.requiredTags, { difficulty: task.difficulty, requiredTags: task.requiredTags }, mesh?.policy?.quotaRouting ?? null, quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode), task.id);
|
|
67422
|
+
const resolved = await resolveUsableProvider(components, nodeId, node, meshId, task.requiredTags, { difficulty: task.difficulty, requiredTags: task.requiredTags }, mesh?.policy?.quotaRouting ?? null, quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, components.providerLoader), task.id);
|
|
67299
67423
|
if (!resolved.providerType) {
|
|
67300
67424
|
markSkip(nodeId, resolved.reason || "provider_unusable");
|
|
67301
67425
|
continue;
|
|
@@ -67436,7 +67560,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
67436
67560
|
task: { difficulty: task.difficulty, requiredTags: task.requiredTags },
|
|
67437
67561
|
resolved,
|
|
67438
67562
|
quotaRouting: mesh?.policy?.quotaRouting ?? null,
|
|
67439
|
-
quotaFactsContext: quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode),
|
|
67563
|
+
quotaFactsContext: quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, components.providerLoader),
|
|
67440
67564
|
skippedCandidates,
|
|
67441
67565
|
requiredTagsResult: {
|
|
67442
67566
|
required: requiredTags,
|
|
@@ -71489,7 +71613,7 @@ ${cleanBody}`;
|
|
|
71489
71613
|
// daemon's live quota cache, not the git_status-stamped
|
|
71490
71614
|
// nodeFacts copy (mesh-quota-routing). Remote nodes keep
|
|
71491
71615
|
// reading their reported nodeFacts.
|
|
71492
|
-
quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode)
|
|
71616
|
+
quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, components.providerLoader)
|
|
71493
71617
|
) : { action: "block" };
|
|
71494
71618
|
const relaunchProviderType = relaunch.action === "block" ? null : relaunch.providerType;
|
|
71495
71619
|
if (node) {
|
|
@@ -82395,6 +82519,7 @@ ${e?.stderr || ""}`;
|
|
|
82395
82519
|
MAX_SIZE_ROTATION_GENERATIONS: () => MAX_SIZE_ROTATION_GENERATIONS,
|
|
82396
82520
|
MESH_CONVERGE_FAST_FORWARD_TAG: () => MESH_CONVERGE_FAST_FORWARD_TAG,
|
|
82397
82521
|
MESH_CONVERGE_REFINE_TAG: () => MESH_CONVERGE_REFINE_TAG,
|
|
82522
|
+
MESH_DECLARED_ELIGIBLE_SINGLE_HINT: () => MESH_DECLARED_ELIGIBLE_SINGLE_HINT,
|
|
82398
82523
|
MESH_GATE_DEFAULT_LEASE_SECONDS: () => MESH_GATE_DEFAULT_LEASE_SECONDS,
|
|
82399
82524
|
MESH_GATE_NAMED_OUTCOMES: () => MESH_GATE_NAMED_OUTCOMES,
|
|
82400
82525
|
MESH_GATE_RELEASE_PATCH_KEYS: () => MESH_GATE_RELEASE_PATCH_KEYS,
|
|
@@ -82793,6 +82918,7 @@ ${e?.stderr || ""}`;
|
|
|
82793
82918
|
recordMeshToolCall: () => recordMeshToolCall,
|
|
82794
82919
|
recordMissingSessionAttempt: () => recordMissingSessionAttempt2,
|
|
82795
82920
|
recordSessionUsage: () => recordSessionUsage,
|
|
82921
|
+
recordSingleEnqueueDecision: () => recordSingleEnqueueDecision,
|
|
82796
82922
|
recoverExpiredWorkspaceSagas: () => recoverExpiredWorkspaceSagas,
|
|
82797
82923
|
registerExtensionProviders: () => registerExtensionProviders,
|
|
82798
82924
|
registerMeshCoordinator: () => registerMeshCoordinator,
|
|
@@ -85682,20 +85808,29 @@ ${e?.stderr || ""}`;
|
|
|
85682
85808
|
if (!fromNodeId) continue;
|
|
85683
85809
|
addEdge(fromNodeId, nodeIdByIndex[i], "requires", omitOnSkip);
|
|
85684
85810
|
}
|
|
85811
|
+
const gateRefsForNode = new Set((t.gated_by ?? []).map((r) => r.trim()).filter(Boolean));
|
|
85685
85812
|
for (const sourceRef of collectRunIfSourceRefs(t.run_if)) {
|
|
85686
85813
|
const fromNodeId = nodeIdByRef.get(sourceRef);
|
|
85687
85814
|
if (!fromNodeId || fromNodeId === nodeIdByIndex[i]) continue;
|
|
85815
|
+
if (gateNodeIdByRef.has(sourceRef)) {
|
|
85816
|
+
gateRefsForNode.add(sourceRef);
|
|
85817
|
+
continue;
|
|
85818
|
+
}
|
|
85688
85819
|
if (edges.some((e) => e.fromNodeId === fromNodeId && e.toNodeId === nodeIdByIndex[i])) continue;
|
|
85689
85820
|
addEdge(fromNodeId, nodeIdByIndex[i], "conditional", omitOnSkip);
|
|
85690
85821
|
}
|
|
85691
85822
|
for (const sourceRef of collectInputSourceRefs(t.inputs_from)) {
|
|
85692
85823
|
const fromNodeId = nodeIdByRef.get(sourceRef);
|
|
85693
85824
|
if (!fromNodeId || fromNodeId === nodeIdByIndex[i]) continue;
|
|
85825
|
+
if (gateNodeIdByRef.has(sourceRef)) {
|
|
85826
|
+
gateRefsForNode.add(sourceRef);
|
|
85827
|
+
continue;
|
|
85828
|
+
}
|
|
85694
85829
|
if (edges.some((e) => e.fromNodeId === fromNodeId && e.toNodeId === nodeIdByIndex[i] && e.kind === "requires")) continue;
|
|
85695
85830
|
addEdge(fromNodeId, nodeIdByIndex[i], "requires", omitOnSkip);
|
|
85696
85831
|
}
|
|
85697
|
-
for (const gateRef of
|
|
85698
|
-
const fromNodeId = gateNodeIdByRef.get(gateRef
|
|
85832
|
+
for (const gateRef of gateRefsForNode) {
|
|
85833
|
+
const fromNodeId = gateNodeIdByRef.get(gateRef);
|
|
85699
85834
|
if (!fromNodeId) continue;
|
|
85700
85835
|
addEdge(fromNodeId, nodeIdByIndex[i], "gate", false);
|
|
85701
85836
|
}
|