@adhdev/daemon-standalone 1.0.45-rc.11 → 1.0.45-rc.12
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 +110 -50
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/public/assets/index-s_TifdFn.js +116 -0
- package/public/index.html +1 -1
- package/vendor/mcp-server/index.js +79 -9
- package/vendor/mcp-server/index.js.map +1 -1
- package/public/assets/index-digx-PVW.js +0 -116
package/public/index.html
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<meta name="description" content="ADHDev self-hosted dashboard for controlling AI agents" />
|
|
8
8
|
<link rel="icon" href="/otter-logo.png" />
|
|
9
9
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet" />
|
|
10
|
-
<script type="module" crossorigin src="/assets/index-
|
|
10
|
+
<script type="module" crossorigin src="/assets/index-s_TifdFn.js"></script>
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/vendor-DgFmqOGd.js">
|
|
12
12
|
<link rel="stylesheet" crossorigin href="/assets/index-TrEtD2r5.css">
|
|
13
13
|
</head>
|
|
@@ -996,7 +996,7 @@ var MESH_ENQUEUE_TASK_TOOL = {
|
|
|
996
996
|
priority: { type: "string", enum: ["low", "normal", "high"], description: "G6 (task-level scheduling priority). Within the claim tier a high task is pulled ahead of an older normal/low task (created_at is the tie-break); low is pulled last. Defaults to normal. This is the TASK priority (which task a node pulls first) \u2014 distinct from a node's schedulingPriority (which node work goes to). Use high to jump an urgent fix ahead of a backlog without cancelling the queue." },
|
|
997
997
|
model: { type: "string", description: "Optional model override for the agent that runs this task, e.g. opus, sonnet, haiku. Best-effort: applied at launch for providers that support a model flag (claude-cli --model, ACP setConfigOption); ignored by providers that cannot honor it. Use a cheaper model for simple tasks to save tokens, a stronger one for hard work. Blank = the provider default." },
|
|
998
998
|
thinkingLevel: { type: "string", enum: ["low", "medium", "high"], description: "Optional reasoning-effort level for this task. Best-effort: applied at launch for providers that support it (claude-cli --effort, codex-cli reasoning effort, ACP thought_level); ignored otherwise. Use low for simple tasks (fewer tokens), high for hard reasoning." },
|
|
999
|
-
difficulty: { type: "string", enum: ["easy", "medium", "difficult", "freeform"], description: "
|
|
999
|
+
difficulty: { type: "string", enum: ["easy", "medium", "difficult", "freeform"], description: "REQUIRED task execution difficulty \u2014 a ROUTING HINT, not a model selector. It is matched against each node's capability slots so the task lands on a slot configured for that difficulty, and THAT SLOT's own model + thinkingLevel are what launch. It does not by itself mean a cheaper or stronger model: to change what a difficulty runs on, edit the node's slots (mesh_node_slots_set) rather than picking a different difficulty. Classify each task by how hard the work actually is. An explicit model/thinkingLevel above always wins." },
|
|
1000
1000
|
notBefore: { type: "number", description: "CamelCase alias for not_before. Also accepts an ISO-8601 timestamp string." },
|
|
1001
1001
|
max_retries: { type: "number", description: "P3 (retry cap). Max automatic requeue attempts before the task auto-fails instead of returning to pending. When requeueCount reaches this, mesh_queue_requeue auto-fails the task unless force=true. Omit to use the mesh policy default (maxTaskRetries, typically 1)." },
|
|
1002
1002
|
maxRetries: { type: "number", description: "CamelCase alias for max_retries." },
|
|
@@ -1005,7 +1005,7 @@ var MESH_ENQUEUE_TASK_TOOL = {
|
|
|
1005
1005
|
allow_duplicate: { type: "boolean", description: "G4. Set true to skip duplicate detection entirely (no warning, no block) for an intentional re-enqueue of the same instruction." },
|
|
1006
1006
|
allowDuplicate: { type: "boolean", description: "CamelCase alias for allow_duplicate." }
|
|
1007
1007
|
},
|
|
1008
|
-
required: ["message"]
|
|
1008
|
+
required: ["message", "difficulty"]
|
|
1009
1009
|
}
|
|
1010
1010
|
};
|
|
1011
1011
|
var MESH_VIEW_QUEUE_TOOL = {
|
|
@@ -1072,9 +1072,10 @@ var MESH_SEND_TASK_TOOL = {
|
|
|
1072
1072
|
readonly: { type: "boolean", description: "Optional read-only axis (orthogonal to task_mode). When true the task runs without write isolation, is counted under the read-only cap, and rejects write/commit/push/deploy/destructive instructions like live_debug_readonly. Composable with any task_mode." },
|
|
1073
1073
|
read_only: { type: "boolean", description: "Snake-case alias for readonly." },
|
|
1074
1074
|
mission_id: { type: "string", description: "Mission this task belongs to (mesh_mission record id). When set, the directly dispatched task is attributed to the mission task aggregates exactly like mesh_enqueue_task, including terminal completion. Omit for an unattributed direct dispatch." },
|
|
1075
|
-
missionId: { type: "string", description: "CamelCase alias for mission_id." }
|
|
1075
|
+
missionId: { type: "string", description: "CamelCase alias for mission_id." },
|
|
1076
|
+
difficulty: { type: "string", enum: ["easy", "medium", "difficult", "freeform"], description: "REQUIRED task execution difficulty. Classify each task by how hard the work actually is. On a direct dispatch the target node/session is already chosen, so difficulty is not used to ROUTE \u2014 it is recorded on the task so scheduling analytics, mission aggregates and (critically) failure-recovery relaunch all see the same axis a queued task carries. A recovery relaunch inherits this value from the ledger, so an unclassified direct dispatch would silently downgrade its own retry." }
|
|
1076
1077
|
},
|
|
1077
|
-
required: ["node_id", "session_id", "message"]
|
|
1078
|
+
required: ["node_id", "session_id", "message", "difficulty"]
|
|
1078
1079
|
}
|
|
1079
1080
|
};
|
|
1080
1081
|
var MESH_READ_CHAT_TOOL = {
|
|
@@ -3344,10 +3345,10 @@ async function collectRelatedRepoStatuses(ctx, node) {
|
|
|
3344
3345
|
return results;
|
|
3345
3346
|
}
|
|
3346
3347
|
function readProviderPriority(policy) {
|
|
3348
|
+
const fromSlots = deriveProviderPriorityFromSlots(policy?.slots);
|
|
3349
|
+
if (fromSlots.length) return fromSlots;
|
|
3347
3350
|
const raw = policy?.providerPriority;
|
|
3348
|
-
|
|
3349
|
-
if (explicit.length) return explicit;
|
|
3350
|
-
return deriveProviderPriorityFromSlots(policy?.slots);
|
|
3351
|
+
return Array.isArray(raw) ? raw.map((type) => typeof type === "string" ? type.trim() : "").filter(Boolean) : [];
|
|
3351
3352
|
}
|
|
3352
3353
|
function readNodeSupportedProviders(policy) {
|
|
3353
3354
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -6242,6 +6243,22 @@ Target: ${args.target}` : ""}`,
|
|
|
6242
6243
|
const task = (0, import_daemon_core5.enqueueTask)(ctx.mesh.id, prompt, {
|
|
6243
6244
|
readonly: true,
|
|
6244
6245
|
taskMode: "live_debug_readonly",
|
|
6246
|
+
// DIFFICULTY-REQUIRED (MAGI decision): a fixed 'freeform' sentinel, NOT an
|
|
6247
|
+
// exemption from the guard. MAGI routes on a different axis entirely — each
|
|
6248
|
+
// replica is already hard-pinned to a (node, provider) slot by the kind-panel
|
|
6249
|
+
// via requiredTags (`provider=<X>`) and often an explicit targetNodeId, and
|
|
6250
|
+
// its model comes from that slot. Difficulty exists to MATCH a task against
|
|
6251
|
+
// node capability slots at assignment time; here the slot is already chosen,
|
|
6252
|
+
// so any difficulty we stamped would be inert at best and would fight the
|
|
6253
|
+
// panel's own slot selection at worst.
|
|
6254
|
+
//
|
|
6255
|
+
// 'freeform' is the correct sentinel rather than a guard bypass: it is a real
|
|
6256
|
+
// member of the axis meaning "no difficulty-based constraint", so the fan-out
|
|
6257
|
+
// satisfies the required-difficulty invariant honestly instead of carving out
|
|
6258
|
+
// a hole that a future non-MAGI caller could slip through. Deliberately NOT
|
|
6259
|
+
// caller-configurable — exposing a difficulty knob on mesh_magi_review would
|
|
6260
|
+
// imply it influences replica placement, which it does not.
|
|
6261
|
+
difficulty: "freeform",
|
|
6245
6262
|
requiredTags: replica.requiredTags,
|
|
6246
6263
|
missionId: mission.id,
|
|
6247
6264
|
consensusGroupId,
|
|
@@ -7043,6 +7060,7 @@ async function meshNodeSlotsPropose(ctx, args = {}) {
|
|
|
7043
7060
|
}
|
|
7044
7061
|
|
|
7045
7062
|
// src/tools/mesh-tools-session.ts
|
|
7063
|
+
var import_daemon_core7 = require("@adhdev/daemon-core");
|
|
7046
7064
|
function computeIdleDispatchAckRisk(sessionWasIdle, dispatchPreRecorded, sessionId) {
|
|
7047
7065
|
if (!sessionWasIdle || dispatchPreRecorded) return {};
|
|
7048
7066
|
return {
|
|
@@ -7111,6 +7129,16 @@ async function meshSendTask(ctx, args) {
|
|
|
7111
7129
|
const requestedTaskMode = readString(args.task_mode) || readString(args.taskMode);
|
|
7112
7130
|
const readonly = args.readonly === true || args.read_only === true;
|
|
7113
7131
|
const missionId = readString(args.missionId) || readString(args.mission_id) || void 0;
|
|
7132
|
+
const difficultyRaw = readString(args.difficulty);
|
|
7133
|
+
if (!difficultyRaw || !isMeshTaskDifficulty(difficultyRaw)) {
|
|
7134
|
+
return JSON.stringify({
|
|
7135
|
+
success: false,
|
|
7136
|
+
code: difficultyRaw ? "invalid_difficulty" : "missing_difficulty",
|
|
7137
|
+
error: difficultyRaw ? `mesh_send_task received an unrecognized \`difficulty\` value '${difficultyRaw}'. Allowed: ${MESH_TASK_DIFFICULTIES.join(" | ")}.` : `mesh_send_task requires a \`difficulty\`. Allowed: ${MESH_TASK_DIFFICULTIES.join(" | ")}. Classify the task by how hard the work actually is.`,
|
|
7138
|
+
allowedDifficulties: MESH_TASK_DIFFICULTIES
|
|
7139
|
+
});
|
|
7140
|
+
}
|
|
7141
|
+
const difficulty = difficultyRaw;
|
|
7114
7142
|
const modeValidation = (0, import_daemon_core5.validateMeshTaskModeRequest)(requestedTaskMode, message, readonly);
|
|
7115
7143
|
if (!modeValidation.valid) {
|
|
7116
7144
|
return JSON.stringify({
|
|
@@ -7260,6 +7288,7 @@ async function meshSendTask(ctx, args) {
|
|
|
7260
7288
|
assignedNodeId: args.node_id,
|
|
7261
7289
|
assignedSessionId: dispatchedSessionId,
|
|
7262
7290
|
taskMode,
|
|
7291
|
+
difficulty,
|
|
7263
7292
|
...readonly ? { readonly: true } : {},
|
|
7264
7293
|
dispatchedAt
|
|
7265
7294
|
});
|
|
@@ -7362,6 +7391,7 @@ async function meshSendTask(ctx, args) {
|
|
|
7362
7391
|
targetNodeId: args.node_id,
|
|
7363
7392
|
targetSessionId: args.session_id,
|
|
7364
7393
|
taskMode,
|
|
7394
|
+
difficulty,
|
|
7365
7395
|
...readonly ? { readonly: true } : {},
|
|
7366
7396
|
...missionId ? { missionId } : {},
|
|
7367
7397
|
...ctx.coordinatorSessionId ? { sourceCoordinatorSessionId: ctx.coordinatorSessionId } : {}
|
|
@@ -7465,6 +7495,7 @@ async function meshSendTask(ctx, args) {
|
|
|
7465
7495
|
assignedNodeId: args.node_id,
|
|
7466
7496
|
assignedSessionId: args.session_id,
|
|
7467
7497
|
taskMode,
|
|
7498
|
+
difficulty,
|
|
7468
7499
|
...readonly ? { readonly: true } : {},
|
|
7469
7500
|
dispatchedAt
|
|
7470
7501
|
});
|
|
@@ -7508,6 +7539,7 @@ async function meshSendTask(ctx, args) {
|
|
|
7508
7539
|
targetNodeId: args.node_id,
|
|
7509
7540
|
targetSessionId: args.session_id,
|
|
7510
7541
|
taskMode,
|
|
7542
|
+
difficulty,
|
|
7511
7543
|
...readonly ? { readonly: true } : {},
|
|
7512
7544
|
...missionId ? { missionId } : {},
|
|
7513
7545
|
...ctx.coordinatorSessionId ? { sourceCoordinatorSessionId: ctx.coordinatorSessionId } : {}
|
|
@@ -7702,6 +7734,7 @@ async function meshLaunchSession(ctx, args) {
|
|
|
7702
7734
|
{
|
|
7703
7735
|
const requestedType = typeof args.type === "string" && args.type.trim() ? args.type.trim() : "";
|
|
7704
7736
|
let resolvedProviderType = requestedType;
|
|
7737
|
+
let explicitTypeQuotaWarning = null;
|
|
7705
7738
|
if (requestedType) {
|
|
7706
7739
|
const slotProviders = normalizeNodeCapabilitySlots(node.policy?.slots).map((s) => s.provider);
|
|
7707
7740
|
if (slotProviders.length && !slotProviders.includes(requestedType)) {
|
|
@@ -7714,6 +7747,19 @@ async function meshLaunchSession(ctx, args) {
|
|
|
7714
7747
|
supportedProviders: slotProviders
|
|
7715
7748
|
}, null, 2);
|
|
7716
7749
|
}
|
|
7750
|
+
const explicitBlock = (0, import_daemon_core7.evaluateProviderQuotaGate)(node, requestedType, ctx.mesh.policy?.quotaRouting ?? null);
|
|
7751
|
+
if (explicitBlock) {
|
|
7752
|
+
explicitTypeQuotaWarning = {
|
|
7753
|
+
quotaWarning: `Provider '${requestedType}' on node '${args.node_id}' is quota-gated (${explicitBlock.reason}; ${explicitBlock.window} window at ${explicitBlock.remainingPercent}% remaining, threshold ${explicitBlock.thresholdPercent}%). Launching anyway because the type was requested explicitly \u2014 the session may fail immediately if the provider rejects on quota.`,
|
|
7754
|
+
quotaBlock: {
|
|
7755
|
+
providerType: requestedType,
|
|
7756
|
+
reason: explicitBlock.reason,
|
|
7757
|
+
window: explicitBlock.window,
|
|
7758
|
+
remainingPercent: explicitBlock.remainingPercent,
|
|
7759
|
+
thresholdPercent: explicitBlock.thresholdPercent
|
|
7760
|
+
}
|
|
7761
|
+
};
|
|
7762
|
+
}
|
|
7717
7763
|
}
|
|
7718
7764
|
if (!resolvedProviderType) {
|
|
7719
7765
|
const providerPriority = readProviderPriority(node.policy);
|
|
@@ -7721,6 +7767,7 @@ async function meshLaunchSession(ctx, args) {
|
|
|
7721
7767
|
return JSON.stringify({ success: false, error: missingProviderPriorityMessage(args.node_id) });
|
|
7722
7768
|
}
|
|
7723
7769
|
const failed = [];
|
|
7770
|
+
const detectedCandidates = [];
|
|
7724
7771
|
let unreachableError = null;
|
|
7725
7772
|
for (const providerType of providerPriority) {
|
|
7726
7773
|
let detectedPayload;
|
|
@@ -7732,11 +7779,33 @@ async function meshLaunchSession(ctx, args) {
|
|
|
7732
7779
|
break;
|
|
7733
7780
|
}
|
|
7734
7781
|
if (detectedPayload?.success && detectedPayload?.detected) {
|
|
7735
|
-
|
|
7736
|
-
|
|
7782
|
+
if (!detectedCandidates.includes(providerType)) detectedCandidates.push(providerType);
|
|
7783
|
+
continue;
|
|
7737
7784
|
}
|
|
7738
7785
|
failed.push(`${providerType}: ${detectedPayload?.error || "not detected"}`);
|
|
7739
7786
|
}
|
|
7787
|
+
if (detectedCandidates.length) {
|
|
7788
|
+
const ranked = (0, import_daemon_core7.rankProvidersByQuotaGate)(node, detectedCandidates, ctx.mesh.policy?.quotaRouting ?? null);
|
|
7789
|
+
if (ranked.clear.length) {
|
|
7790
|
+
resolvedProviderType = ranked.clear[0];
|
|
7791
|
+
} else {
|
|
7792
|
+
const detail = ranked.gated.map((g) => `${g.providerType}: ${g.block.reason}`).join("; ");
|
|
7793
|
+
return JSON.stringify({
|
|
7794
|
+
success: false,
|
|
7795
|
+
code: "mesh_all_providers_quota_gated",
|
|
7796
|
+
error: `Every detected provider on node '${args.node_id}' is quota-gated (${detail}). This is a WAIT, not a misconfiguration \u2014 the quota windows reset on their own.`,
|
|
7797
|
+
nodeId: args.node_id,
|
|
7798
|
+
gated: ranked.gated.map((g) => ({
|
|
7799
|
+
providerType: g.providerType,
|
|
7800
|
+
reason: g.block.reason,
|
|
7801
|
+
window: g.block.window,
|
|
7802
|
+
remainingPercent: g.block.remainingPercent,
|
|
7803
|
+
thresholdPercent: g.block.thresholdPercent
|
|
7804
|
+
})),
|
|
7805
|
+
nextAction: `Enqueue the work (mesh_enqueue_task) so the drain claims it when a window resets, or launch on a node whose providers still have quota. Retrying mesh_launch_session immediately will hit the same gate.`
|
|
7806
|
+
}, null, 2);
|
|
7807
|
+
}
|
|
7808
|
+
}
|
|
7740
7809
|
if (!resolvedProviderType) {
|
|
7741
7810
|
if (unreachableError) {
|
|
7742
7811
|
return JSON.stringify({ success: false, error: `Node '${args.node_id}' is unreachable \u2014 cannot detect a provider (${unreachableError}). The node's daemon may be offline; retry once it reconnects.` });
|
|
@@ -7865,6 +7934,7 @@ async function meshLaunchSession(ctx, args) {
|
|
|
7865
7934
|
...launchPayload,
|
|
7866
7935
|
resolvedProviderType,
|
|
7867
7936
|
...providerSessionId ? { providerSessionId } : {},
|
|
7937
|
+
...explicitTypeQuotaWarning ?? {},
|
|
7868
7938
|
queueTrigger,
|
|
7869
7939
|
...buildQueueTriggerGuidance(queueTrigger)
|
|
7870
7940
|
}, null, 2);
|