@adhdev/daemon-standalone 0.9.82-rc.380 → 0.9.82-rc.381
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 +215 -59
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/public/assets/index-3wtc5vDj.js +113 -0
- package/public/assets/index-BirH1m9z.css +1 -0
- package/public/index.html +2 -2
- package/vendor/mcp-server/index.js +62 -6
- package/vendor/mcp-server/index.js.map +1 -1
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-3wtc5vDj.js"></script>
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/vendor-BHUMCOj6.js">
|
|
12
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BirH1m9z.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<!-- Apply theme immediately to prevent FOIT (Flash of Incorrect Theme) -->
|
|
@@ -685,8 +685,10 @@ var MESH_ENQUEUE_TASK_TOOL = {
|
|
|
685
685
|
taskMode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "CamelCase alias for task_mode." },
|
|
686
686
|
requiredTags: { type: "array", items: { type: "string" }, description: "Optional capability tags that every eligible node must have, e.g. os=darwin, provider=codex-cli, gpu." },
|
|
687
687
|
required_tags: { type: "array", items: { type: "string" }, description: "Snake_case alias for requiredTags." },
|
|
688
|
-
target_node_id: { type: "string", description: "Optional:
|
|
688
|
+
target_node_id: { type: "string", description: "Optional HARD constraint: ONLY this node may claim the task. No other node (especially a different machine) will ever claim it \u2014 if the target node has no idle session the task stays pending until it does. Use to route a queued task to a specific (e.g. freshly cloned) worktree node instead of letting the first idle base node claim it. Takes priority over prefer_worktree. An unresolvable target id is rejected at enqueue (no silent unpin)." },
|
|
689
689
|
targetNodeId: { type: "string", description: "CamelCase alias for target_node_id." },
|
|
690
|
+
target_node: { type: "string", description: "Alias for target_node_id." },
|
|
691
|
+
targetNode: { type: "string", description: "CamelCase alias for target_node_id." },
|
|
690
692
|
prefer_worktree: { type: "boolean", description: "Optional: when true, route this task to the most recently cloned idle worktree node (avoids the main/base workspace preemptively claiming an isolated task). No-op if no worktree node exists; resolves to a target_node_id when one does." },
|
|
691
693
|
preferWorktree: { type: "boolean", description: "CamelCase alias for prefer_worktree." },
|
|
692
694
|
depends_on: { type: "array", items: { type: "string" }, description: "Task ids that must complete before this task becomes claimable. Cycles are rejected at enqueue." },
|
|
@@ -2930,6 +2932,8 @@ async function meshStatus(ctx, args = {}) {
|
|
|
2930
2932
|
await refreshMeshFromDaemon(ctx);
|
|
2931
2933
|
const { mesh, transport } = ctx;
|
|
2932
2934
|
let ledgerSummary = (0, import_daemon_core3.getLedgerSummary)(mesh.id);
|
|
2935
|
+
const schedulingRuntime = (0, import_daemon_core3.buildMeshSchedulingRuntime)(mesh, (0, import_daemon_core3.getQueue)(mesh.id));
|
|
2936
|
+
const schedulingByNode = new Map(schedulingRuntime.nodes.map((n) => [n.nodeId, n]));
|
|
2933
2937
|
const results = await Promise.all(mesh.nodes.map(async (node) => {
|
|
2934
2938
|
const entry = {
|
|
2935
2939
|
nodeId: node.id,
|
|
@@ -2940,6 +2944,11 @@ async function meshStatus(ctx, args = {}) {
|
|
|
2940
2944
|
...getNodeLaunchReadiness(node),
|
|
2941
2945
|
...buildNodeCapabilityExposure(node)
|
|
2942
2946
|
};
|
|
2947
|
+
const nodeScheduling = schedulingByNode.get(node.id);
|
|
2948
|
+
if (nodeScheduling) {
|
|
2949
|
+
const { nodeId: _omit, ...rest } = nodeScheduling;
|
|
2950
|
+
entry.scheduling = compact ? { load: rest.load, capReached: rest.capReached } : rest;
|
|
2951
|
+
}
|
|
2943
2952
|
let liveTruthProbed = false;
|
|
2944
2953
|
try {
|
|
2945
2954
|
const autoDiscover = node.policy?.autoDiscoverSubmodules !== false;
|
|
@@ -3220,6 +3229,18 @@ async function meshStatus(ctx, args = {}) {
|
|
|
3220
3229
|
meshName: mesh.name,
|
|
3221
3230
|
repoIdentity: mesh.repoIdentity,
|
|
3222
3231
|
policy: mesh.policy,
|
|
3232
|
+
// Mesh-level scheduling rollup (strategy + global cap consumption). Per-node
|
|
3233
|
+
// detail (load/priority/provider caps/claim-block reasons) lives on each
|
|
3234
|
+
// nodes[].scheduling; the node array is dropped here to avoid duplicating it.
|
|
3235
|
+
scheduling: {
|
|
3236
|
+
strategy: schedulingRuntime.strategy,
|
|
3237
|
+
maxParallelTasks: schedulingRuntime.maxParallelTasks,
|
|
3238
|
+
maxReadonlyParallelTasks: schedulingRuntime.maxReadonlyParallelTasks,
|
|
3239
|
+
activeWriteAssigned: schedulingRuntime.activeWriteAssigned,
|
|
3240
|
+
activeReadonlyAssigned: schedulingRuntime.activeReadonlyAssigned,
|
|
3241
|
+
globalWriteCapReached: schedulingRuntime.globalWriteCapReached,
|
|
3242
|
+
globalReadonlyCapReached: schedulingRuntime.globalReadonlyCapReached
|
|
3243
|
+
},
|
|
3223
3244
|
payloadMode: compact ? "compact" : "full",
|
|
3224
3245
|
refreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3225
3246
|
sourceOfTruth: {
|
|
@@ -3364,9 +3385,24 @@ async function meshEnqueueTask(ctx, args) {
|
|
|
3364
3385
|
const requiredTags = (0, import_daemon_core3.normalizeMeshCapabilityTags)(Array.isArray(args.requiredTags) ? args.requiredTags : args.required_tags);
|
|
3365
3386
|
const dependsOn = Array.isArray(args.dependsOn) ? args.dependsOn : Array.isArray(args.depends_on) ? args.depends_on : void 0;
|
|
3366
3387
|
const missionId = readString(args.missionId) || readString(args.mission_id) || void 0;
|
|
3367
|
-
const
|
|
3388
|
+
const explicitTargetRaw = readString(args.targetNodeId) || readString(args.target_node_id) || readString(args.targetNode) || readString(args.target_node) || void 0;
|
|
3368
3389
|
const preferWorktree = args.preferWorktree === true || args.prefer_worktree === true;
|
|
3369
|
-
|
|
3390
|
+
let targetNodeId;
|
|
3391
|
+
if (explicitTargetRaw) {
|
|
3392
|
+
const matched = ctx.mesh.nodes.find((n) => (0, import_daemon_core3.meshNodeIdMatches)(n, explicitTargetRaw));
|
|
3393
|
+
if (!matched) {
|
|
3394
|
+
return JSON.stringify({
|
|
3395
|
+
success: false,
|
|
3396
|
+
code: "target_node_not_found",
|
|
3397
|
+
error: `target node '${explicitTargetRaw}' is not a member of this mesh \u2014 refusing to enqueue an unpinned task (it could be claimed by any node, including a different machine). Use mesh_list_nodes to get a valid node id.`,
|
|
3398
|
+
targetNodeId: explicitTargetRaw,
|
|
3399
|
+
availableNodeIds: ctx.mesh.nodes.map((n) => n.id).filter(Boolean)
|
|
3400
|
+
});
|
|
3401
|
+
}
|
|
3402
|
+
targetNodeId = readString(matched.id) || explicitTargetRaw;
|
|
3403
|
+
} else if (preferWorktree) {
|
|
3404
|
+
targetNodeId = resolvePreferredWorktreeNodeId(ctx) || void 0;
|
|
3405
|
+
}
|
|
3370
3406
|
try {
|
|
3371
3407
|
const task = (0, import_daemon_core3.enqueueTask)(ctx.mesh.id, args.message, { taskMode, requiredTags, dependsOn, missionId, targetNodeId, ...ctx.coordinatorSessionId ? { sourceCoordinatorSessionId: ctx.coordinatorSessionId } : {} });
|
|
3372
3408
|
if (!(ctx.transport instanceof IpcTransport)) {
|
|
@@ -3379,7 +3415,7 @@ async function meshEnqueueTask(ctx, args) {
|
|
|
3379
3415
|
taskMode: task.taskMode,
|
|
3380
3416
|
requiredTags: task.requiredTags,
|
|
3381
3417
|
...targetNodeId ? { targetNodeId } : {},
|
|
3382
|
-
...preferWorktree && !
|
|
3418
|
+
...preferWorktree && !explicitTargetRaw && !targetNodeId ? { preferWorktreeNoOp: true } : {},
|
|
3383
3419
|
queueTrigger,
|
|
3384
3420
|
...buildQueueTriggerGuidance(queueTrigger)
|
|
3385
3421
|
});
|
|
@@ -3446,7 +3482,7 @@ async function meshEnqueueTask(ctx, args) {
|
|
|
3446
3482
|
taskMode: task.taskMode,
|
|
3447
3483
|
requiredTags: task.requiredTags,
|
|
3448
3484
|
...targetNodeId ? { targetNodeId } : {},
|
|
3449
|
-
...preferWorktree && !
|
|
3485
|
+
...preferWorktree && !explicitTargetRaw && !targetNodeId ? { preferWorktreeNoOp: true } : {},
|
|
3450
3486
|
queueTrigger,
|
|
3451
3487
|
...buildQueueTriggerGuidance(queueTrigger)
|
|
3452
3488
|
});
|
|
@@ -3591,11 +3627,31 @@ async function meshQueueCancel(ctx, args) {
|
|
|
3591
3627
|
try {
|
|
3592
3628
|
const taskId = (args.task_id || args.taskId || "").trim();
|
|
3593
3629
|
if (!taskId) return JSON.stringify({ success: false, error: "task_id required" });
|
|
3630
|
+
const preCancel = (0, import_daemon_core3.getQueue)(ctx.mesh.id).find((t) => t?.id === taskId);
|
|
3631
|
+
const wasAssigned = preCancel?.status === "assigned";
|
|
3632
|
+
const assignedSessionId = readString(preCancel?.assignedSessionId) || void 0;
|
|
3633
|
+
const assignedNodeId = readString(preCancel?.assignedNodeId) || void 0;
|
|
3634
|
+
const assignedProviderType = readString(preCancel?.assignedProviderType) || void 0;
|
|
3594
3635
|
const task = (0, import_daemon_core3.cancelTask)(ctx.mesh.id, taskId, { reason: args.reason });
|
|
3595
3636
|
if (!task) return JSON.stringify({ success: false, error: `Queue task '${taskId}' not found` });
|
|
3596
3637
|
ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
|
|
3597
3638
|
});
|
|
3598
|
-
|
|
3639
|
+
let workerStop = { attempted: false };
|
|
3640
|
+
if (wasAssigned && assignedSessionId && assignedSessionId !== ctx.coordinatorSessionId && assignedProviderType) {
|
|
3641
|
+
workerStop = { attempted: true, sessionId: assignedSessionId, nodeId: assignedNodeId };
|
|
3642
|
+
ctx.transport.command("agent_command", {
|
|
3643
|
+
targetSessionId: assignedSessionId,
|
|
3644
|
+
cliType: assignedProviderType,
|
|
3645
|
+
agentType: assignedProviderType,
|
|
3646
|
+
action: "stop",
|
|
3647
|
+
...assignedNodeId ? { meshContext: { meshId: ctx.mesh.id, nodeId: assignedNodeId, taskId } } : {}
|
|
3648
|
+
}).catch((e) => {
|
|
3649
|
+
workerStop.reason = e?.message || String(e);
|
|
3650
|
+
});
|
|
3651
|
+
} else if (wasAssigned && assignedSessionId === ctx.coordinatorSessionId) {
|
|
3652
|
+
workerStop = { attempted: false, reason: "assigned_session_is_coordinator_self \u2014 stop suppressed" };
|
|
3653
|
+
}
|
|
3654
|
+
return JSON.stringify({ success: true, task, workerStop }, null, 2);
|
|
3599
3655
|
} catch (e) {
|
|
3600
3656
|
return JSON.stringify({ success: false, error: e.message });
|
|
3601
3657
|
}
|