@adhdev/daemon-standalone 1.0.27 → 1.0.28-rc.10
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 +7539 -7132
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/public/assets/{index-B51nIfEq.css → index-8TNNoifN.css} +1 -1
- package/public/assets/index-BD5OPONs.js +121 -0
- package/public/assets/{terminal-BZj_X5zy.js → terminal-DiXj2eIR.js} +1 -1
- package/public/assets/{vendor-cL4V2vaO.js → vendor-DyCWA2YZ.js} +57 -49
- package/public/index.html +3 -3
- package/vendor/mcp-server/index.js +183 -5
- package/vendor/mcp-server/index.js.map +1 -1
- package/vendor/session-host-daemon/index.js +0 -0
- package/vendor/session-host-daemon/index.mjs +0 -0
- package/public/assets/index-Bfkec2FE.js +0 -121
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-
|
|
11
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-
|
|
12
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
10
|
+
<script type="module" crossorigin src="/assets/index-BD5OPONs.js"></script>
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-DyCWA2YZ.js">
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/assets/index-8TNNoifN.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<!-- Apply theme immediately to prevent FOIT (Flash of Incorrect Theme) -->
|
|
@@ -393,6 +393,8 @@ var CANONICAL_MESH_TOOL_NAMES = [
|
|
|
393
393
|
"mesh_approve",
|
|
394
394
|
"mesh_answer_question",
|
|
395
395
|
"mesh_list_pending_approvals",
|
|
396
|
+
"mesh_create",
|
|
397
|
+
"mesh_add_node",
|
|
396
398
|
"mesh_clone_node",
|
|
397
399
|
"mesh_remove_node",
|
|
398
400
|
"mesh_refine_node",
|
|
@@ -1116,6 +1118,41 @@ var MESH_LIST_PENDING_APPROVALS_TOOL = {
|
|
|
1116
1118
|
properties: {}
|
|
1117
1119
|
}
|
|
1118
1120
|
};
|
|
1121
|
+
var MESH_CREATE_TOOL = {
|
|
1122
|
+
name: "mesh_create",
|
|
1123
|
+
description: "Bootstrap a brand-new mesh for a Git repository \u2014 the first step for an MCP-only agent that has no mesh yet. Mirrors `adhdev mesh create <name>`. A mesh groups one repo's workspaces/nodes so the coordinator can delegate work across them. You MUST supply the repo identity: pass repo_remote_url (the git remote URL, e.g. git@github.com:user/repo.git or https://github.com/user/repo.git \u2014 the identity is derived from it) OR repo_identity (an explicit normalized identity like github.com/user/repo). At least one is required; there is no auto-detection here (the CLI auto-detects from the current dir, but the MCP server may run elsewhere). Set add_current:true to also register a node in the same call (uses workspace if given, else the daemon's current working directory) \u2014 handy when the daemon runs in the repo you want as the base node. BOOT-GATE / WHEN CALLABLE: this tool is reachable in STANDARD mode (adhdev mcp, no --repo-mesh) \u2014 the bootstrap context where no mesh exists \u2014 and also in mesh mode (where it creates a SEPARATE additional mesh). It is NOT reachable before any mesh exists via mesh mode, because `adhdev mcp --repo-mesh <id>` refuses to start without an existing meshId. So the intended flow is: run standard-mode MCP \u2192 mesh_create \u2192 mesh_add_node \u2192 then relaunch as `adhdev mcp --repo-mesh <returned mesh_id>`. Returns mesh_id (and node_id when add_current is used); use mesh_id for the follow-up mesh_add_node call and to launch mesh mode.",
|
|
1124
|
+
inputSchema: {
|
|
1125
|
+
type: "object",
|
|
1126
|
+
properties: {
|
|
1127
|
+
name: { type: "string", description: 'Human-readable mesh name (e.g. "adhdev-main"). Trimmed, max 100 chars.' },
|
|
1128
|
+
repo_remote_url: { type: "string", description: "Git remote URL of the repo (e.g. git@github.com:user/repo.git). The repo identity is normalized from this. Provide this OR repo_identity." },
|
|
1129
|
+
repo_identity: { type: "string", description: "Explicit normalized repo identity (e.g. github.com/user/repo). Provide this OR repo_remote_url. Wins over repo_remote_url when both are given." },
|
|
1130
|
+
default_branch: { type: "string", description: 'Default branch for the repo (e.g. "main"). Optional; used as the merge/convergence target.' },
|
|
1131
|
+
add_current: { type: "boolean", description: "Also register a node in this same call (parity with CLI --add-current). Uses `workspace` if provided, otherwise the daemon's current working directory." },
|
|
1132
|
+
workspace: { type: "string", description: "Absolute workspace path to register when add_current:true. Ignored unless add_current is true. Defaults to the daemon's cwd." }
|
|
1133
|
+
},
|
|
1134
|
+
required: ["name"]
|
|
1135
|
+
}
|
|
1136
|
+
};
|
|
1137
|
+
var MESH_ADD_NODE_TOOL = {
|
|
1138
|
+
name: "mesh_add_node",
|
|
1139
|
+
description: "Register a workspace as a node in an EXISTING mesh \u2014 the second bootstrap step after mesh_create (or to add more nodes later). Mirrors `adhdev mesh add-node <mesh_id>` with --workspace / --read-only / --provider-priority. A node is a repo checkout on a daemon that the coordinator can launch agents on and delegate tasks to. mesh_id is REQUIRED in standard mode (pass the id returned by mesh_create); in mesh mode it defaults to the active mesh. workspace is the absolute path to the repo checkout ON THE DAEMON that owns it \u2014 the local base node is added by the daemon that created the mesh. NOTE: this registers an EXISTING directory as a node (including marking one as a worktree via is_worktree). To CREATE a fresh git worktree + branch for isolated parallel work, use mesh_clone_node instead \u2014 that runs the actual `git worktree add`. Returns node_id + workspace so you can immediately target the node with mesh_launch_session / mesh_send_task / mesh_enqueue_task.",
|
|
1140
|
+
inputSchema: {
|
|
1141
|
+
type: "object",
|
|
1142
|
+
properties: {
|
|
1143
|
+
mesh_id: { type: "string", description: "Target mesh id (from mesh_create / mesh_status). Required in standard mode; defaults to the active mesh in mesh mode." },
|
|
1144
|
+
workspace: { type: "string", description: "Absolute path to the repo checkout on the owning daemon (e.g. /Users/me/work/repo). Must be unique within the mesh." },
|
|
1145
|
+
read_only: { type: "boolean", description: "Mark the node read-only (no launches/mutations targeted here). Parity with CLI --read-only." },
|
|
1146
|
+
provider_priority: {
|
|
1147
|
+
type: "array",
|
|
1148
|
+
items: { type: "string" },
|
|
1149
|
+
description: 'Ordered provider types this node prefers when mesh_launch_session omits an explicit type (e.g. ["claude-cli","codex"]). Parity with CLI --provider-priority. A comma-separated string is also accepted.'
|
|
1150
|
+
},
|
|
1151
|
+
is_worktree: { type: "boolean", description: "Mark this workspace as an existing local git worktree (parity with CLI --worktree). This only tags an already-present worktree dir; it does NOT create one \u2014 use mesh_clone_node to create a worktree+branch." }
|
|
1152
|
+
},
|
|
1153
|
+
required: ["workspace"]
|
|
1154
|
+
}
|
|
1155
|
+
};
|
|
1119
1156
|
var MESH_CLONE_NODE_TOOL = {
|
|
1120
1157
|
name: "mesh_clone_node",
|
|
1121
1158
|
description: "Create a new worktree-based node from an existing node for isolated parallel work. Creates a git worktree on a new branch so multiple tasks can run on separate branches simultaneously.",
|
|
@@ -1406,7 +1443,6 @@ var MESH_MAGI_REVIEW_TOOL = {
|
|
|
1406
1443
|
n: { type: "number", description: "Global replica override per slot (clamped by the total-replica guard cap, default 12)." },
|
|
1407
1444
|
task_kind: { type: "string", enum: ["claim_audit", "rca", "design", "freeform"], description: "REQUIRED. Selects (1) the SINGLE output schema injected into each replica prompt and the strict parser used at collection (no schema-on-schema conflict), AND (2) the user-configured kind-panel binding that supplies the fan-out slots (mesh settings \u2192 magiKindPanels; errors magi_kind_not_configured if that kind has no configured slots \u2014 no named-panel/inline/preset fallback). claim_audit: {claims[],top_findings[],open_questions[]}. rca: {rootCause,failsAt,mechanism,evidence[],fixDirection,confidence}. design: {recommendation,rationale,alternatives[],tradeoffs[],risks[],evidence[],confidence}. freeform: no schema \u2014 natural-language answer, parsing/evidence checks waived, cross-verification is weak. Every kind except freeform requires non-empty evidence[]; an empty-evidence or schema-invalid answer triggers ONE delta re-request before being dropped as unparseable. Do NOT also embed an output-format schema in the question \u2014 it collides with this contract (a warning is surfaced if detected)." },
|
|
1408
1445
|
mode: { type: "string", enum: ["rca", "investigation", "claim_audit", "design_review", "code_audit"], description: "Synthesis emphasis hint \u2014 affects labels only, never the agent count or schema. Distinct from task_kind (which selects the output schema)." },
|
|
1409
|
-
use_judge: { type: "boolean", description: "Default false (clustering synthesis). STUB: judge synthesis is not yet implemented \u2014 passing true currently falls back to clustering with a warning. Reserved interface only." },
|
|
1410
1446
|
require_independent_evidence: { type: "boolean", description: "Default true \u2014 high-impact claims with no file:line/source evidence are routed to needs_verification." },
|
|
1411
1447
|
include_stale: { type: "boolean", description: "Default false. By default, panel slots whose node HEAD commit differs from the coordinator reference commit are EXCLUDED (they would investigate different code). Set true to fan out to them anyway \u2014 results will be git-skewed and a warning is surfaced. If exclusion drops the panel below 2 independent targets the call errors rather than degrading to N=1; include_stale=true is one way to recover." },
|
|
1412
1448
|
wait: { type: "boolean", description: "Default true \u2014 collect replica outputs and return the synthesis. Set false to dispatch async and return a consensusGroupId handle; collect later with mesh_magi_collect." },
|
|
@@ -1543,6 +1579,8 @@ var ALL_MESH_TOOLS = [
|
|
|
1543
1579
|
MESH_APPROVE_TOOL,
|
|
1544
1580
|
MESH_ANSWER_QUESTION_TOOL,
|
|
1545
1581
|
MESH_LIST_PENDING_APPROVALS_TOOL,
|
|
1582
|
+
MESH_CREATE_TOOL,
|
|
1583
|
+
MESH_ADD_NODE_TOOL,
|
|
1546
1584
|
MESH_CLONE_NODE_TOOL,
|
|
1547
1585
|
MESH_REMOVE_NODE_TOOL,
|
|
1548
1586
|
MESH_REFINE_NODE_TOOL,
|
|
@@ -2044,7 +2082,19 @@ function buildDirectTaskPayload(message, via, opts) {
|
|
|
2044
2082
|
...opts.targetSessionId ? { targetSessionId: opts.targetSessionId } : {},
|
|
2045
2083
|
...opts.dispatchedToIdleSession !== void 0 ? { dispatchedToIdleSession: opts.dispatchedToIdleSession } : {},
|
|
2046
2084
|
...opts.coordinatorSessionId ? { coordinatorSessionId: opts.coordinatorSessionId } : {},
|
|
2047
|
-
...opts.coordinatorDaemonId ? { coordinatorDaemonId: opts.coordinatorDaemonId } : {}
|
|
2085
|
+
...opts.coordinatorDaemonId ? { coordinatorDaemonId: opts.coordinatorDaemonId } : {},
|
|
2086
|
+
// Uniform routing rationale (mirrors the queue-claim task_dispatched shape) so both
|
|
2087
|
+
// paths render identically in mesh_task_history / the dashboard. The legacy top-level
|
|
2088
|
+
// `source`/`via`/`providerType` fields above are preserved verbatim for existing
|
|
2089
|
+
// consumers (mesh-active-work / mesh-events-stale key on payload.source === 'direct').
|
|
2090
|
+
routingDecision: {
|
|
2091
|
+
source: "direct",
|
|
2092
|
+
via,
|
|
2093
|
+
...opts.selectedNodeId ? { selectedNodeId: opts.selectedNodeId } : {},
|
|
2094
|
+
...opts.providerType ? { resolvedProviderType: opts.providerType } : {},
|
|
2095
|
+
...opts.resolvedModel ? { resolvedModel: opts.resolvedModel } : {},
|
|
2096
|
+
...opts.resolvedThinkingLevel ? { resolvedThinkingLevel: opts.resolvedThinkingLevel } : {}
|
|
2097
|
+
}
|
|
2048
2098
|
};
|
|
2049
2099
|
}
|
|
2050
2100
|
function findNode(mesh, nodeId) {
|
|
@@ -2809,11 +2859,27 @@ function isGitStatusDirty(status) {
|
|
|
2809
2859
|
if (Array.isArray(status?.submodules) && status.submodules.some((submodule) => submodule?.dirty || submodule?.outOfSync || submodule?.error)) return true;
|
|
2810
2860
|
return countUncommittedChanges(status) > 0;
|
|
2811
2861
|
}
|
|
2862
|
+
var ROUTING_SKIPPED_COMPACT_MAX = 5;
|
|
2863
|
+
function compactRoutingDecision(routing) {
|
|
2864
|
+
const out = {};
|
|
2865
|
+
for (const [k, v] of Object.entries(routing)) {
|
|
2866
|
+
if (k === "skippedCandidates" && Array.isArray(v)) {
|
|
2867
|
+
const kept = v.slice(0, ROUTING_SKIPPED_COMPACT_MAX);
|
|
2868
|
+
out[k] = kept;
|
|
2869
|
+
if (v.length > kept.length) out.skippedCandidatesDropped = v.length - kept.length;
|
|
2870
|
+
} else {
|
|
2871
|
+
out[k] = v;
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
return out;
|
|
2875
|
+
}
|
|
2812
2876
|
function slimLedgerPayload(payload) {
|
|
2813
2877
|
const slim = {};
|
|
2814
2878
|
for (const [k, v] of Object.entries(payload)) {
|
|
2815
2879
|
if (k === "message" || k === "taskSummary") {
|
|
2816
2880
|
slim[k] = typeof v === "string" && v.length > 200 ? v.slice(0, 200) + "\u2026" : v;
|
|
2881
|
+
} else if (k === "routingDecision" && v && typeof v === "object" && !Array.isArray(v)) {
|
|
2882
|
+
slim[k] = compactRoutingDecision(v);
|
|
2817
2883
|
} else if (k === "evidence" || k === "workerResult" || k === "gitStatus" || k === "validationResults") {
|
|
2818
2884
|
} else if (k === "finalSummary") {
|
|
2819
2885
|
slim[k] = typeof v === "string" && v.length > 300 ? v.slice(0, 300) + "\u2026" : v;
|
|
@@ -5532,8 +5598,6 @@ async function meshMagiReview(ctx, args) {
|
|
|
5532
5598
|
}, null, 2);
|
|
5533
5599
|
}
|
|
5534
5600
|
const questionSchemaWarning = detectQuestionOutputSchemaConflict(question);
|
|
5535
|
-
const useJudge = (args.use_judge ?? args.useJudge) === true;
|
|
5536
|
-
const judgeWarning = useJudge ? "use_judge=true requested, but judge synthesis is not yet implemented \u2014 falling back to clustering synthesis." : null;
|
|
5537
5601
|
await refreshMeshFromDaemon(ctx);
|
|
5538
5602
|
const referenceCommit = resolveMagiReferenceCommit(ctx);
|
|
5539
5603
|
const referenceSubmoduleKey = resolveMagiReferenceSubmoduleKey(ctx);
|
|
@@ -5639,7 +5703,6 @@ Target: ${args.target}` : ""}`,
|
|
|
5639
5703
|
panel: panelName,
|
|
5640
5704
|
taskKind,
|
|
5641
5705
|
...questionSchemaWarning ? { questionSchemaWarning } : {},
|
|
5642
|
-
...judgeWarning ? { judgeWarning } : {},
|
|
5643
5706
|
question,
|
|
5644
5707
|
replicaCount: replicaRecords.length,
|
|
5645
5708
|
replicas: replicaRecords.map((r) => ({ taskId: r.taskId, provider: r.provider, targetNodeId: r.targetNodeId })),
|
|
@@ -6488,6 +6551,7 @@ async function meshSendTask(ctx, args) {
|
|
|
6488
6551
|
taskMode,
|
|
6489
6552
|
providerType,
|
|
6490
6553
|
targetSessionId: dispatchedSessionId,
|
|
6554
|
+
...args.node_id ? { selectedNodeId: args.node_id } : {},
|
|
6491
6555
|
...ctx.coordinatorSessionId ? { coordinatorSessionId: ctx.coordinatorSessionId } : {},
|
|
6492
6556
|
// COORD-EVENT-MISROUTE: persist the dispatching coordinator daemon anchor
|
|
6493
6557
|
// (same value stamped into meshContext above) so a transcript-reconcile
|
|
@@ -6650,6 +6714,7 @@ async function meshSendTask(ctx, args) {
|
|
|
6650
6714
|
providerType: resolvedProviderType,
|
|
6651
6715
|
targetSessionId: args.session_id,
|
|
6652
6716
|
dispatchedToIdleSession: sessionWasIdle,
|
|
6717
|
+
...args.node_id ? { selectedNodeId: args.node_id } : {},
|
|
6653
6718
|
...ctx.coordinatorSessionId ? { coordinatorSessionId: ctx.coordinatorSessionId } : {},
|
|
6654
6719
|
// COORD-EVENT-MISROUTE: persist the dispatching coordinator daemon anchor so a
|
|
6655
6720
|
// transcript-reconcile synth recovers it from the ledger rather than stamping
|
|
@@ -7391,6 +7456,100 @@ async function meshRemoveNode(ctx, args) {
|
|
|
7391
7456
|
return JSON.stringify({ ...result || {}, ...transportFallback ? { transportFallback } : {} }, null, 2);
|
|
7392
7457
|
}
|
|
7393
7458
|
|
|
7459
|
+
// src/tools/mesh-tools-crud.ts
|
|
7460
|
+
async function meshCreate(transport, args) {
|
|
7461
|
+
const name = typeof args?.name === "string" ? args.name.trim() : "";
|
|
7462
|
+
if (!name) {
|
|
7463
|
+
return JSON.stringify({ success: false, error: "name required" }, null, 2);
|
|
7464
|
+
}
|
|
7465
|
+
const repoRemoteUrl = typeof args?.repo_remote_url === "string" ? args.repo_remote_url.trim() : "";
|
|
7466
|
+
const repoIdentity = typeof args?.repo_identity === "string" ? args.repo_identity.trim() : "";
|
|
7467
|
+
const defaultBranch = typeof args?.default_branch === "string" ? args.default_branch.trim() : "";
|
|
7468
|
+
if (!repoRemoteUrl && !repoIdentity) {
|
|
7469
|
+
return JSON.stringify({
|
|
7470
|
+
success: false,
|
|
7471
|
+
error: "Either repo_remote_url or repo_identity is required. Pass the repo's git remote URL (e.g. git@github.com:user/repo.git) or an explicit identity (e.g. github.com/user/repo)."
|
|
7472
|
+
}, null, 2);
|
|
7473
|
+
}
|
|
7474
|
+
const createResult = await transport.command("create_mesh", {
|
|
7475
|
+
name,
|
|
7476
|
+
...repoRemoteUrl ? { repoRemoteUrl } : {},
|
|
7477
|
+
...repoIdentity ? { repoIdentity } : {},
|
|
7478
|
+
...defaultBranch ? { defaultBranch } : {}
|
|
7479
|
+
});
|
|
7480
|
+
const createPayload = unwrapCommandPayload(createResult);
|
|
7481
|
+
const mesh = createPayload?.mesh;
|
|
7482
|
+
if (!createPayload?.success || !mesh?.id) {
|
|
7483
|
+
return JSON.stringify({
|
|
7484
|
+
success: false,
|
|
7485
|
+
error: createPayload?.error || "create_mesh failed",
|
|
7486
|
+
raw: createPayload ?? createResult
|
|
7487
|
+
}, null, 2);
|
|
7488
|
+
}
|
|
7489
|
+
const out = {
|
|
7490
|
+
success: true,
|
|
7491
|
+
mesh_id: mesh.id,
|
|
7492
|
+
name: mesh.name,
|
|
7493
|
+
repo_identity: mesh.repoIdentity,
|
|
7494
|
+
default_branch: mesh.defaultBranch,
|
|
7495
|
+
node_count: Array.isArray(mesh.nodes) ? mesh.nodes.length : 0,
|
|
7496
|
+
next_step: `Add the base node with mesh_add_node (mesh_id: "${mesh.id}", workspace: <repo path>), then start mesh mode with: adhdev mcp --repo-mesh ${mesh.id}`
|
|
7497
|
+
};
|
|
7498
|
+
if (args?.add_current === true) {
|
|
7499
|
+
const workspace = typeof args?.workspace === "string" && args.workspace.trim() ? args.workspace.trim() : "";
|
|
7500
|
+
const addResult = await transport.command("add_mesh_node", {
|
|
7501
|
+
meshId: mesh.id,
|
|
7502
|
+
...workspace ? { workspace } : {},
|
|
7503
|
+
inlineMesh: mesh
|
|
7504
|
+
});
|
|
7505
|
+
const addPayload = unwrapCommandPayload(addResult);
|
|
7506
|
+
if (addPayload?.success && addPayload?.node?.id) {
|
|
7507
|
+
out.node_id = addPayload.node.id;
|
|
7508
|
+
out.node_workspace = addPayload.node.workspace;
|
|
7509
|
+
} else {
|
|
7510
|
+
out.add_current_error = addPayload?.error || "add_mesh_node failed (mesh was still created)";
|
|
7511
|
+
}
|
|
7512
|
+
}
|
|
7513
|
+
return JSON.stringify(out, null, 2);
|
|
7514
|
+
}
|
|
7515
|
+
async function meshAddNode(transport, args, defaultMeshId) {
|
|
7516
|
+
const meshId = typeof args?.mesh_id === "string" && args.mesh_id.trim() ? args.mesh_id.trim() : typeof defaultMeshId === "string" ? defaultMeshId.trim() : "";
|
|
7517
|
+
const workspace = typeof args?.workspace === "string" ? args.workspace.trim() : "";
|
|
7518
|
+
if (!meshId) {
|
|
7519
|
+
return JSON.stringify({ success: false, error: "mesh_id required (create one first with mesh_create)." }, null, 2);
|
|
7520
|
+
}
|
|
7521
|
+
if (!workspace) {
|
|
7522
|
+
return JSON.stringify({ success: false, error: "workspace required (absolute path to the repo checkout on the target daemon)." }, null, 2);
|
|
7523
|
+
}
|
|
7524
|
+
const providerPriority = Array.isArray(args?.provider_priority) ? args.provider_priority.map((v) => typeof v === "string" ? v.trim() : "").filter(Boolean) : typeof args?.provider_priority === "string" ? args.provider_priority.split(",").map((v) => v.trim()).filter(Boolean) : [];
|
|
7525
|
+
const addResult = await transport.command("add_mesh_node", {
|
|
7526
|
+
meshId,
|
|
7527
|
+
workspace,
|
|
7528
|
+
...args?.read_only === true ? { readOnly: true } : {},
|
|
7529
|
+
...providerPriority.length ? { providerPriority } : {},
|
|
7530
|
+
...args?.is_worktree === true ? { isLocalWorktree: true } : {},
|
|
7531
|
+
...args?.inline_mesh ? { inlineMesh: args.inline_mesh } : {}
|
|
7532
|
+
});
|
|
7533
|
+
const addPayload = unwrapCommandPayload(addResult);
|
|
7534
|
+
if (!addPayload?.success || !addPayload?.node?.id) {
|
|
7535
|
+
return JSON.stringify({
|
|
7536
|
+
success: false,
|
|
7537
|
+
error: addPayload?.error || "add_mesh_node failed",
|
|
7538
|
+
code: addPayload?.code,
|
|
7539
|
+
raw: addPayload ?? addResult
|
|
7540
|
+
}, null, 2);
|
|
7541
|
+
}
|
|
7542
|
+
return JSON.stringify({
|
|
7543
|
+
success: true,
|
|
7544
|
+
mesh_id: meshId,
|
|
7545
|
+
node_id: addPayload.node.id,
|
|
7546
|
+
workspace: addPayload.node.workspace,
|
|
7547
|
+
read_only: addPayload.node.policy?.readOnly === true,
|
|
7548
|
+
provider_priority: addPayload.node.policy?.providerPriority,
|
|
7549
|
+
next_step: `Node registered. Launch an agent on it with mesh_launch_session (node_id: "${addPayload.node.id}") or delegate work with mesh_send_task / mesh_enqueue_task.`
|
|
7550
|
+
}, null, 2);
|
|
7551
|
+
}
|
|
7552
|
+
|
|
7394
7553
|
// src/tools/mesh-tools-refine.ts
|
|
7395
7554
|
async function meshRefineConfigSchema(ctx) {
|
|
7396
7555
|
const node = resolveRefineConfigNode(ctx);
|
|
@@ -8770,6 +8929,12 @@ async function startMcpServer(opts) {
|
|
|
8770
8929
|
case "mesh_list_pending_approvals":
|
|
8771
8930
|
text = await meshListPendingApprovals(meshCtx, a);
|
|
8772
8931
|
break;
|
|
8932
|
+
case "mesh_create":
|
|
8933
|
+
text = await meshCreate(meshCtx.transport, a);
|
|
8934
|
+
break;
|
|
8935
|
+
case "mesh_add_node":
|
|
8936
|
+
text = await meshAddNode(meshCtx.transport, a, meshCtx.mesh.id);
|
|
8937
|
+
break;
|
|
8773
8938
|
case "mesh_clone_node":
|
|
8774
8939
|
text = await meshCloneNode(meshCtx, a);
|
|
8775
8940
|
break;
|
|
@@ -8907,6 +9072,11 @@ async function startMcpServer(opts) {
|
|
|
8907
9072
|
GIT_DIFF_TOOL,
|
|
8908
9073
|
GIT_CHECKPOINT_TOOL,
|
|
8909
9074
|
GIT_PUSH_TOOL,
|
|
9075
|
+
// Mesh bootstrap: create a mesh + register its first node from an MCP-only agent.
|
|
9076
|
+
// Exposed in standard mode precisely because this is the no-mesh-yet context —
|
|
9077
|
+
// mesh mode refuses to boot without an existing meshId (see the mesh-mode block above).
|
|
9078
|
+
MESH_CREATE_TOOL,
|
|
9079
|
+
MESH_ADD_NODE_TOOL,
|
|
8910
9080
|
...isLocal ? [SCREENSHOT_TOOL] : []
|
|
8911
9081
|
];
|
|
8912
9082
|
const server = new import_server.Server(
|
|
@@ -8996,6 +9166,14 @@ async function startMcpServer(opts) {
|
|
|
8996
9166
|
const text = await checkPending(transport, { format: a.format });
|
|
8997
9167
|
return { content: [{ type: "text", text }] };
|
|
8998
9168
|
}
|
|
9169
|
+
case "mesh_create": {
|
|
9170
|
+
const text = await meshCreate(transport, a);
|
|
9171
|
+
return { content: [{ type: "text", text }] };
|
|
9172
|
+
}
|
|
9173
|
+
case "mesh_add_node": {
|
|
9174
|
+
const text = await meshAddNode(transport, a);
|
|
9175
|
+
return { content: [{ type: "text", text }] };
|
|
9176
|
+
}
|
|
8999
9177
|
default:
|
|
9000
9178
|
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
9001
9179
|
}
|