@adhdev/daemon-standalone 1.0.45-rc.9 → 1.0.47
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 +1618 -1199
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/public/assets/index-CEPCfMWL.css +1 -0
- package/public/assets/index-D3K4NyAl.js +116 -0
- package/public/index.html +2 -2
- package/vendor/mcp-server/index.js +89 -9
- package/vendor/mcp-server/index.js.map +1 -1
- package/public/assets/index-TrEtD2r5.css +0 -1
- package/public/assets/index-digx-PVW.js +0 -116
package/public/index.html
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
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-D3K4NyAl.js"></script>
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/vendor-DgFmqOGd.js">
|
|
12
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CEPCfMWL.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<!-- Apply theme immediately to prevent FOIT (Flash of Incorrect Theme) -->
|
|
@@ -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 = {
|
|
@@ -3159,6 +3160,8 @@ async function ipcDispatchToRemoteAgent(ctx, node, args) {
|
|
|
3159
3160
|
cliType: resolvedProviderType,
|
|
3160
3161
|
action: "send_chat",
|
|
3161
3162
|
message: args.message,
|
|
3163
|
+
// DISPATCH-SOURCE-TRACE: call-site tag echoed in the worker daemon log.
|
|
3164
|
+
dispatchSource: "mesh-tools-internal:ipcDispatchToRemoteAgent",
|
|
3162
3165
|
// WTCLAIM (B): carry the node workspace so a sessionless dispatch can be
|
|
3163
3166
|
// scoped to THIS node's session on the worker (findAdapter dir match /
|
|
3164
3167
|
// findMeshNodeAdapter). Without it, a worker hosting both a base node and a
|
|
@@ -3342,10 +3345,10 @@ async function collectRelatedRepoStatuses(ctx, node) {
|
|
|
3342
3345
|
return results;
|
|
3343
3346
|
}
|
|
3344
3347
|
function readProviderPriority(policy) {
|
|
3348
|
+
const fromSlots = deriveProviderPriorityFromSlots(policy?.slots);
|
|
3349
|
+
if (fromSlots.length) return fromSlots;
|
|
3345
3350
|
const raw = policy?.providerPriority;
|
|
3346
|
-
|
|
3347
|
-
if (explicit.length) return explicit;
|
|
3348
|
-
return deriveProviderPriorityFromSlots(policy?.slots);
|
|
3351
|
+
return Array.isArray(raw) ? raw.map((type) => typeof type === "string" ? type.trim() : "").filter(Boolean) : [];
|
|
3349
3352
|
}
|
|
3350
3353
|
function readNodeSupportedProviders(policy) {
|
|
3351
3354
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -3929,6 +3932,10 @@ async function meshStatus(ctx, args = {}) {
|
|
|
3929
3932
|
const { nodeId: _omit, ...rest } = nodeScheduling;
|
|
3930
3933
|
entry.scheduling = compact ? { load: rest.load, capReached: rest.capReached } : rest;
|
|
3931
3934
|
}
|
|
3935
|
+
const lastQuotaRanking = (0, import_daemon_core5.getLastQuotaRanking)(node.id);
|
|
3936
|
+
if (lastQuotaRanking) {
|
|
3937
|
+
entry.scheduling = { ...entry.scheduling ?? {}, lastQuotaRanking };
|
|
3938
|
+
}
|
|
3932
3939
|
let liveTruthProbed = false;
|
|
3933
3940
|
try {
|
|
3934
3941
|
const autoDiscover = node.policy?.autoDiscoverSubmodules !== false;
|
|
@@ -6236,6 +6243,22 @@ Target: ${args.target}` : ""}`,
|
|
|
6236
6243
|
const task = (0, import_daemon_core5.enqueueTask)(ctx.mesh.id, prompt, {
|
|
6237
6244
|
readonly: true,
|
|
6238
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",
|
|
6239
6262
|
requiredTags: replica.requiredTags,
|
|
6240
6263
|
missionId: mission.id,
|
|
6241
6264
|
consensusGroupId,
|
|
@@ -6686,6 +6709,8 @@ ${magiOutputContractFor(kind)}`;
|
|
|
6686
6709
|
agentType: task.assignedProviderType,
|
|
6687
6710
|
action: "send_chat",
|
|
6688
6711
|
message,
|
|
6712
|
+
// DISPATCH-SOURCE-TRACE: call-site tag echoed in the worker daemon log.
|
|
6713
|
+
dispatchSource: "mesh-tools-magi:sendKindRetry",
|
|
6689
6714
|
meshContext: {
|
|
6690
6715
|
meshId: ctx.mesh.id,
|
|
6691
6716
|
nodeId: task.assignedNodeId,
|
|
@@ -7035,6 +7060,7 @@ async function meshNodeSlotsPropose(ctx, args = {}) {
|
|
|
7035
7060
|
}
|
|
7036
7061
|
|
|
7037
7062
|
// src/tools/mesh-tools-session.ts
|
|
7063
|
+
var import_daemon_core7 = require("@adhdev/daemon-core");
|
|
7038
7064
|
function computeIdleDispatchAckRisk(sessionWasIdle, dispatchPreRecorded, sessionId) {
|
|
7039
7065
|
if (!sessionWasIdle || dispatchPreRecorded) return {};
|
|
7040
7066
|
return {
|
|
@@ -7103,6 +7129,16 @@ async function meshSendTask(ctx, args) {
|
|
|
7103
7129
|
const requestedTaskMode = readString(args.task_mode) || readString(args.taskMode);
|
|
7104
7130
|
const readonly = args.readonly === true || args.read_only === true;
|
|
7105
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;
|
|
7106
7142
|
const modeValidation = (0, import_daemon_core5.validateMeshTaskModeRequest)(requestedTaskMode, message, readonly);
|
|
7107
7143
|
if (!modeValidation.valid) {
|
|
7108
7144
|
return JSON.stringify({
|
|
@@ -7252,6 +7288,7 @@ async function meshSendTask(ctx, args) {
|
|
|
7252
7288
|
assignedNodeId: args.node_id,
|
|
7253
7289
|
assignedSessionId: dispatchedSessionId,
|
|
7254
7290
|
taskMode,
|
|
7291
|
+
difficulty,
|
|
7255
7292
|
...readonly ? { readonly: true } : {},
|
|
7256
7293
|
dispatchedAt
|
|
7257
7294
|
});
|
|
@@ -7354,6 +7391,7 @@ async function meshSendTask(ctx, args) {
|
|
|
7354
7391
|
targetNodeId: args.node_id,
|
|
7355
7392
|
targetSessionId: args.session_id,
|
|
7356
7393
|
taskMode,
|
|
7394
|
+
difficulty,
|
|
7357
7395
|
...readonly ? { readonly: true } : {},
|
|
7358
7396
|
...missionId ? { missionId } : {},
|
|
7359
7397
|
...ctx.coordinatorSessionId ? { sourceCoordinatorSessionId: ctx.coordinatorSessionId } : {}
|
|
@@ -7423,6 +7461,8 @@ async function meshSendTask(ctx, args) {
|
|
|
7423
7461
|
providerType: resolvedProviderType,
|
|
7424
7462
|
action: "send_chat",
|
|
7425
7463
|
message,
|
|
7464
|
+
// DISPATCH-SOURCE-TRACE: call-site tag echoed in the worker daemon log.
|
|
7465
|
+
dispatchSource: "mesh-tools-session:mesh_send_task:direct",
|
|
7426
7466
|
meshContext: {
|
|
7427
7467
|
meshId: ctx.mesh.id,
|
|
7428
7468
|
nodeId: args.node_id,
|
|
@@ -7455,6 +7495,7 @@ async function meshSendTask(ctx, args) {
|
|
|
7455
7495
|
assignedNodeId: args.node_id,
|
|
7456
7496
|
assignedSessionId: args.session_id,
|
|
7457
7497
|
taskMode,
|
|
7498
|
+
difficulty,
|
|
7458
7499
|
...readonly ? { readonly: true } : {},
|
|
7459
7500
|
dispatchedAt
|
|
7460
7501
|
});
|
|
@@ -7498,6 +7539,7 @@ async function meshSendTask(ctx, args) {
|
|
|
7498
7539
|
targetNodeId: args.node_id,
|
|
7499
7540
|
targetSessionId: args.session_id,
|
|
7500
7541
|
taskMode,
|
|
7542
|
+
difficulty,
|
|
7501
7543
|
...readonly ? { readonly: true } : {},
|
|
7502
7544
|
...missionId ? { missionId } : {},
|
|
7503
7545
|
...ctx.coordinatorSessionId ? { sourceCoordinatorSessionId: ctx.coordinatorSessionId } : {}
|
|
@@ -7692,6 +7734,7 @@ async function meshLaunchSession(ctx, args) {
|
|
|
7692
7734
|
{
|
|
7693
7735
|
const requestedType = typeof args.type === "string" && args.type.trim() ? args.type.trim() : "";
|
|
7694
7736
|
let resolvedProviderType = requestedType;
|
|
7737
|
+
let explicitTypeQuotaWarning = null;
|
|
7695
7738
|
if (requestedType) {
|
|
7696
7739
|
const slotProviders = normalizeNodeCapabilitySlots(node.policy?.slots).map((s) => s.provider);
|
|
7697
7740
|
if (slotProviders.length && !slotProviders.includes(requestedType)) {
|
|
@@ -7704,6 +7747,19 @@ async function meshLaunchSession(ctx, args) {
|
|
|
7704
7747
|
supportedProviders: slotProviders
|
|
7705
7748
|
}, null, 2);
|
|
7706
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
|
+
}
|
|
7707
7763
|
}
|
|
7708
7764
|
if (!resolvedProviderType) {
|
|
7709
7765
|
const providerPriority = readProviderPriority(node.policy);
|
|
@@ -7711,6 +7767,7 @@ async function meshLaunchSession(ctx, args) {
|
|
|
7711
7767
|
return JSON.stringify({ success: false, error: missingProviderPriorityMessage(args.node_id) });
|
|
7712
7768
|
}
|
|
7713
7769
|
const failed = [];
|
|
7770
|
+
const detectedCandidates = [];
|
|
7714
7771
|
let unreachableError = null;
|
|
7715
7772
|
for (const providerType of providerPriority) {
|
|
7716
7773
|
let detectedPayload;
|
|
@@ -7722,11 +7779,33 @@ async function meshLaunchSession(ctx, args) {
|
|
|
7722
7779
|
break;
|
|
7723
7780
|
}
|
|
7724
7781
|
if (detectedPayload?.success && detectedPayload?.detected) {
|
|
7725
|
-
|
|
7726
|
-
|
|
7782
|
+
if (!detectedCandidates.includes(providerType)) detectedCandidates.push(providerType);
|
|
7783
|
+
continue;
|
|
7727
7784
|
}
|
|
7728
7785
|
failed.push(`${providerType}: ${detectedPayload?.error || "not detected"}`);
|
|
7729
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
|
+
}
|
|
7730
7809
|
if (!resolvedProviderType) {
|
|
7731
7810
|
if (unreachableError) {
|
|
7732
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.` });
|
|
@@ -7855,6 +7934,7 @@ async function meshLaunchSession(ctx, args) {
|
|
|
7855
7934
|
...launchPayload,
|
|
7856
7935
|
resolvedProviderType,
|
|
7857
7936
|
...providerSessionId ? { providerSessionId } : {},
|
|
7937
|
+
...explicitTypeQuotaWarning ?? {},
|
|
7858
7938
|
queueTrigger,
|
|
7859
7939
|
...buildQueueTriggerGuidance(queueTrigger)
|
|
7860
7940
|
}, null, 2);
|