agenthub-multiagent-mcp 1.77.0 → 1.78.0
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/channel.js +26 -4
- package/dist/channel.js.map +1 -1
- package/dist/client.d.ts +37 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +18 -0
- package/dist/client.js.map +1 -1
- package/dist/commands/setup-shell.js +11 -11
- package/dist/daemon.js +42 -1
- package/dist/daemon.js.map +1 -1
- package/dist/daemonPolicy.d.ts +23 -0
- package/dist/daemonPolicy.d.ts.map +1 -1
- package/dist/daemonPolicy.js +22 -0
- package/dist/daemonPolicy.js.map +1 -1
- package/dist/daemonPolicy.test.js +54 -1
- package/dist/daemonPolicy.test.js.map +1 -1
- package/dist/inboxHookCli.d.ts +1 -1
- package/dist/inboxHookCli.d.ts.map +1 -1
- package/dist/inboxHookCli.js +16 -3
- package/dist/inboxHookCli.js.map +1 -1
- package/dist/inboxHookCli.test.js +23 -0
- package/dist/inboxHookCli.test.js.map +1 -1
- package/dist/lib/brainstormInvites.d.ts +68 -0
- package/dist/lib/brainstormInvites.d.ts.map +1 -0
- package/dist/lib/brainstormInvites.js +143 -0
- package/dist/lib/brainstormInvites.js.map +1 -0
- package/dist/lib/brainstormInvites.test.d.ts +8 -0
- package/dist/lib/brainstormInvites.test.d.ts.map +1 -0
- package/dist/lib/brainstormInvites.test.js +102 -0
- package/dist/lib/brainstormInvites.test.js.map +1 -0
- package/dist/lib/pending-items.d.ts +7 -0
- package/dist/lib/pending-items.d.ts.map +1 -1
- package/dist/lib/pending-items.js +3 -2
- package/dist/lib/pending-items.js.map +1 -1
- package/dist/skillPackInstaller.d.ts +1 -1
- package/dist/skillPackInstaller.d.ts.map +1 -1
- package/dist/skillPackInstaller.js +56 -2
- package/dist/skillPackInstaller.js.map +1 -1
- package/dist/skillPackInstaller.test.js +113 -0
- package/dist/skillPackInstaller.test.js.map +1 -1
- package/dist/tools/brainLessons.test.d.ts +6 -0
- package/dist/tools/brainLessons.test.d.ts.map +1 -0
- package/dist/tools/brainLessons.test.js +61 -0
- package/dist/tools/brainLessons.test.js.map +1 -0
- package/dist/tools/brainstormInvites.test.d.ts +7 -0
- package/dist/tools/brainstormInvites.test.d.ts.map +1 -0
- package/dist/tools/brainstormInvites.test.js +139 -0
- package/dist/tools/brainstormInvites.test.js.map +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +224 -12
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/tools.test.js +122 -2
- package/dist/tools/tools.test.js.map +1 -1
- package/package.json +79 -79
- package/skills/catalog.json +6 -4
- package/skills/manifest.json +4 -2
- package/skills/skills/inbox/SKILL.md +25 -3
- package/skills/skills/multiagent-brainstorm/SKILL.md +26 -6
package/dist/tools/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MCP tool definitions and handlers
|
|
3
3
|
*/
|
|
4
|
+
import { AgentHubApiError, } from "../client.js";
|
|
4
5
|
import { resolveAgentIdentity, missingStateGuidance, credentialIsDead, } from "../agentIdentity.js";
|
|
5
6
|
import { boundInbox, resolveInboxLimit, CHECK_INBOX_DEFAULT_LIMIT, CHECK_INBOX_MAX_LIMIT, } from "./inboxBounds.js";
|
|
6
7
|
import * as state from "../state.js";
|
|
@@ -20,6 +21,7 @@ import { isWhatsAppTool, runWhatsAppTool, whatsappToolDefinitions } from "./what
|
|
|
20
21
|
import { installPackFromArchive } from "../skillPackInstaller.js";
|
|
21
22
|
import { getServerTime, useTimeInjection } from "../lib/server-time.js";
|
|
22
23
|
import { getPendingItems } from "../lib/pending-items.js";
|
|
24
|
+
import { brainstormAutoAcceptEnabled, inviteGuidance, } from "../lib/brainstormInvites.js";
|
|
23
25
|
import { ENG_IC_CHARTER } from "../employee.js";
|
|
24
26
|
import { promises as fsp } from "node:fs";
|
|
25
27
|
import * as os from "node:os";
|
|
@@ -84,6 +86,19 @@ function backgroundSync(client, agentId, workingDir) {
|
|
|
84
86
|
}
|
|
85
87
|
})();
|
|
86
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* The invite fields every pending-items block carries (check_inbox and the
|
|
91
|
+
* tool wrapper). The guidance rides along only when there is an invite, so an
|
|
92
|
+
* empty inbox costs no extra text.
|
|
93
|
+
*/
|
|
94
|
+
function inviteFields(invites) {
|
|
95
|
+
const list = invites ?? [];
|
|
96
|
+
return {
|
|
97
|
+
pending_brainstorm_invites: list,
|
|
98
|
+
pending_brainstorm_invites_count: list.length,
|
|
99
|
+
...(list.length > 0 ? { brainstorm_invite_guidance: inviteGuidance(brainstormAutoAcceptEnabled()) } : {}),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
87
102
|
/**
|
|
88
103
|
* Wraps a tool response with pending messages and tasks
|
|
89
104
|
* This enables automatic delivery without extra API calls
|
|
@@ -161,6 +176,7 @@ async function wrapWithPendingItems(client, agentId, result, context) {
|
|
|
161
176
|
pending_messages_count: messages.length,
|
|
162
177
|
pending_tasks: tasks,
|
|
163
178
|
pending_tasks_count: tasks.length,
|
|
179
|
+
...inviteFields(cachedItems.invites),
|
|
164
180
|
urgent_action: urgentAction,
|
|
165
181
|
};
|
|
166
182
|
if (useTimeInjection()) {
|
|
@@ -977,7 +993,7 @@ export function registerTools() {
|
|
|
977
993
|
},
|
|
978
994
|
{
|
|
979
995
|
name: "skill_pack_install",
|
|
980
|
-
description: "Install an opt-in skill pack into ~/.claude/skills/. Hash-safe (never overwrites files you've edited unless force=true). Destructive packs (e.g. cybersecurity, which ships offensive PoC scripts) require the skill-pack:<name> capability attached to this agent. Returns a per-file install report.",
|
|
996
|
+
description: "Install an opt-in skill pack into ~/.claude/skills/. Hash-safe (never overwrites files you've edited unless force=true). On upgrade, files the new version dropped are removed if unedited (removed) and kept if you edited them (kept_modified). Destructive packs (e.g. cybersecurity, which ships offensive PoC scripts) require the skill-pack:<name> capability attached to this agent. Returns a per-file install report.",
|
|
981
997
|
inputSchema: {
|
|
982
998
|
type: "object",
|
|
983
999
|
properties: {
|
|
@@ -990,6 +1006,52 @@ export function registerTools() {
|
|
|
990
1006
|
required: ["pack"],
|
|
991
1007
|
},
|
|
992
1008
|
},
|
|
1009
|
+
// add-product-lifecycle-pack §2.1–2.2 — product-lifecycle hard gates.
|
|
1010
|
+
// 503 lifecycle_gates_disabled while AGENTHUB_LIFECYCLE_GATES_ENABLED is off.
|
|
1011
|
+
{
|
|
1012
|
+
name: "lifecycle_gate_request",
|
|
1013
|
+
description: "Open a product-lifecycle hard gate (stage 3, 6, 9 or dvt) on the stage's gate task. Creates a blocking lifecycle_gate review that only an org owner or admin can sign, on the dashboard (/reviews) — not an agent, an API key or a chat button. Returns the review id and status; poll lifecycle_gate_list until it is decided. A second pending gate on the same task is refused (gate_pending). For a re-gate after a stale or revised one, pass parent_review_id.",
|
|
1014
|
+
inputSchema: {
|
|
1015
|
+
type: "object",
|
|
1016
|
+
properties: {
|
|
1017
|
+
project_id: { type: "string", description: "Project id (defaults to the project this agent is bound to)" },
|
|
1018
|
+
task_id: { type: "string", description: "The stage's gate task id (must be in project_id)" },
|
|
1019
|
+
stage: { type: "string", enum: ["3", "6", "9", "dvt"], description: "Hard-gate stage" },
|
|
1020
|
+
title: { type: "string", description: "Gate title" },
|
|
1021
|
+
content: { type: "string", description: "The gate pack, as markdown" },
|
|
1022
|
+
evidence_hash: { type: "string", description: "sha256 of the evidence, 64 lowercase hex characters" },
|
|
1023
|
+
items: {
|
|
1024
|
+
type: "array",
|
|
1025
|
+
description: "The stage's tasks and their verdicts",
|
|
1026
|
+
items: {
|
|
1027
|
+
type: "object",
|
|
1028
|
+
properties: {
|
|
1029
|
+
task_id: { type: "string" },
|
|
1030
|
+
status: { type: "string" },
|
|
1031
|
+
verdict: { type: "string" },
|
|
1032
|
+
refs: { type: "array", items: { type: "string" }, description: "Evidence refs (PRs, docs, runs)" },
|
|
1033
|
+
},
|
|
1034
|
+
required: ["task_id", "status", "verdict"],
|
|
1035
|
+
},
|
|
1036
|
+
},
|
|
1037
|
+
parent_review_id: { type: "string", description: "Earlier lifecycle_gate on the same task this one supersedes" },
|
|
1038
|
+
},
|
|
1039
|
+
required: ["task_id", "stage", "title", "content", "evidence_hash", "items"],
|
|
1040
|
+
},
|
|
1041
|
+
},
|
|
1042
|
+
{
|
|
1043
|
+
name: "lifecycle_gate_list",
|
|
1044
|
+
description: "List a project's product-lifecycle hard gates, newest first, with their parsed metadata (stage, evidence hash, items) and status (pending/approved/denied/…). Filter by stage, task_id or status.",
|
|
1045
|
+
inputSchema: {
|
|
1046
|
+
type: "object",
|
|
1047
|
+
properties: {
|
|
1048
|
+
project_id: { type: "string", description: "Project id (defaults to the project this agent is bound to)" },
|
|
1049
|
+
stage: { type: "string", enum: ["3", "6", "9", "dvt"], description: "Only this stage" },
|
|
1050
|
+
task_id: { type: "string", description: "Only gates on this task" },
|
|
1051
|
+
status: { type: "string", description: "Only this review status, e.g. pending" },
|
|
1052
|
+
},
|
|
1053
|
+
},
|
|
1054
|
+
},
|
|
993
1055
|
{
|
|
994
1056
|
name: "api_search",
|
|
995
1057
|
description: "Search the API knowledge graph — endpoints (REST/MCP/GraphQL/gRPC) extracted from indexed repos across the org. Use this BEFORE building a new endpoint to check if it already exists or to match a sibling project's contract. Org-wide read.",
|
|
@@ -1141,6 +1203,21 @@ export function registerTools() {
|
|
|
1141
1203
|
required: ["channel"],
|
|
1142
1204
|
},
|
|
1143
1205
|
},
|
|
1206
|
+
{
|
|
1207
|
+
name: "room_history",
|
|
1208
|
+
description: "Read a room's (channel's) earlier posts, oldest first, paged — e.g. after joining late. " +
|
|
1209
|
+
"Pass next_since from the previous page as `since` to read on. Reading marks nothing read. " +
|
|
1210
|
+
"Rooms belong to your organization; another org's room is 'not found'.",
|
|
1211
|
+
inputSchema: {
|
|
1212
|
+
type: "object",
|
|
1213
|
+
properties: {
|
|
1214
|
+
room: { type: "string", description: "Room (channel) name" },
|
|
1215
|
+
since: { type: "number", description: "Return posts after this message seq (default 0: from the start)" },
|
|
1216
|
+
limit: { type: "number", description: "Posts per page (default 50, at most 200)" },
|
|
1217
|
+
},
|
|
1218
|
+
required: ["room"],
|
|
1219
|
+
},
|
|
1220
|
+
},
|
|
1144
1221
|
{
|
|
1145
1222
|
name: "leave_channel",
|
|
1146
1223
|
description: "Unsubscribe from a channel",
|
|
@@ -1779,13 +1856,18 @@ export function registerTools() {
|
|
|
1779
1856
|
required: ["topic"],
|
|
1780
1857
|
},
|
|
1781
1858
|
},
|
|
1859
|
+
{
|
|
1860
|
+
name: "brainstorm_invites",
|
|
1861
|
+
description: "List your pending brainstorm invites (session id, display number, topic, initiator). An invite is not chat: bring it to your human before brainstorm_accept unless they asked for that brainstorm or AGENTHUB_BRAINSTORM_AUTO_ACCEPT=true. The same list rides on check_inbox and every tool response as pending_brainstorm_invites. (requires agent_register first)",
|
|
1862
|
+
inputSchema: { type: "object", properties: {} },
|
|
1863
|
+
},
|
|
1782
1864
|
{
|
|
1783
1865
|
name: "brainstorm_accept",
|
|
1784
|
-
description: "Accept a brainstorm invite (transition the session to active). Idempotent — a redelivered invite can't double-join. You are the responder. (requires agent_register first)",
|
|
1866
|
+
description: "Accept a brainstorm invite (transition the session to active). Idempotent — a redelivered invite can't double-join. You are the responder. Unless your human asked for this brainstorm or AGENTHUB_BRAINSTORM_AUTO_ACCEPT=true, ask them first. (requires agent_register first)",
|
|
1785
1867
|
inputSchema: {
|
|
1786
1868
|
type: "object",
|
|
1787
1869
|
properties: {
|
|
1788
|
-
session_id: { type: "string", description: "The session id from
|
|
1870
|
+
session_id: { type: "string", description: "The session id, from pending_brainstorm_invites / brainstorm_invites (or a legacy invite message)." },
|
|
1789
1871
|
},
|
|
1790
1872
|
required: ["session_id"],
|
|
1791
1873
|
},
|
|
@@ -2372,10 +2454,14 @@ export function registerTools() {
|
|
|
2372
2454
|
query: { type: "string", description: "Search query" },
|
|
2373
2455
|
category: {
|
|
2374
2456
|
type: "string",
|
|
2375
|
-
enum: ["decision", "convention", "architecture", "blocker", "alert", "insight"],
|
|
2457
|
+
enum: ["decision", "convention", "architecture", "blocker", "alert", "insight", "lesson"],
|
|
2376
2458
|
description: "Filter by knowledge category",
|
|
2377
2459
|
},
|
|
2378
2460
|
project: { type: "string", description: "Filter by project" },
|
|
2461
|
+
tag: {
|
|
2462
|
+
type: "string",
|
|
2463
|
+
description: "Exact tag filter: matches a row only when one of its tags equals this string (e.g. `stage:6` does not match `stage:60`)",
|
|
2464
|
+
},
|
|
2379
2465
|
limit: { type: "number", description: "Max results (default 20)" },
|
|
2380
2466
|
},
|
|
2381
2467
|
required: ["query"],
|
|
@@ -2383,20 +2469,38 @@ export function registerTools() {
|
|
|
2383
2469
|
},
|
|
2384
2470
|
{
|
|
2385
2471
|
name: "brain_write_knowledge",
|
|
2386
|
-
description: "Write an org knowledge fact (decision, convention, architecture insight, blocker, or alert) to the shared org brain. The result carries `check`: `created` for a new row (with its `id`), or `deduped` when an identical active fact already exists — then `canonical_id` names the existing row
|
|
2472
|
+
description: "Write an org knowledge fact (decision, convention, architecture insight, blocker, or alert) to the shared org brain. The result carries `check`: `created` for a new row (with its `id`), or `deduped` when an identical active fact already exists — then `canonical_id` names the existing row. Dedup matches only your organization's own facts: identical text held by another organization is stored as your own new row. A refusal (missing fact, unknown category) is an error.",
|
|
2387
2473
|
inputSchema: {
|
|
2388
2474
|
type: "object",
|
|
2389
2475
|
properties: {
|
|
2390
2476
|
category: {
|
|
2391
2477
|
type: "string",
|
|
2392
|
-
enum: ["decision", "convention", "architecture", "blocker", "alert", "insight"],
|
|
2478
|
+
enum: ["decision", "convention", "architecture", "blocker", "alert", "insight", "lesson"],
|
|
2393
2479
|
description: "Knowledge category",
|
|
2394
2480
|
},
|
|
2395
|
-
fact: { type: "string", description: "The fact or decision (concise)" },
|
|
2481
|
+
fact: { type: "string", description: "The fact or decision (concise). Required except for a lesson, whose fact defaults to `LESSON s<stage>: <rule>`." },
|
|
2396
2482
|
detail: { type: "string", description: "Extended context or rationale" },
|
|
2397
2483
|
project: { type: "string", description: "Project this applies to (optional)" },
|
|
2484
|
+
tags: {
|
|
2485
|
+
type: "array",
|
|
2486
|
+
items: { type: "string" },
|
|
2487
|
+
description: "Tags for the exact tag filter (e.g. [\"lesson\",\"stage:6\"]). A lesson always also gets lesson, stage:<n>, track:<t>, product:<p>.",
|
|
2488
|
+
},
|
|
2489
|
+
lesson: {
|
|
2490
|
+
type: "object",
|
|
2491
|
+
description: "Required for category=lesson (400 invalid_lesson otherwise). All six fields are non-empty strings.",
|
|
2492
|
+
properties: {
|
|
2493
|
+
stage: { type: "string", description: "\"0\"–\"10\", \"HW-POC\", \"HW-EVT\", \"HW-DVT\", \"HW-PVT\" or \"HW-CERT\"" },
|
|
2494
|
+
product: { type: "string" },
|
|
2495
|
+
track: { type: "string", enum: ["gov", "commercial", "hardware", "any"] },
|
|
2496
|
+
rule: { type: "string", description: "One imperative sentence" },
|
|
2497
|
+
evidence: { type: "string", description: "A verifiable reference" },
|
|
2498
|
+
gate_kind: { type: "string", enum: ["gate item", "template field", "stage instruction"] },
|
|
2499
|
+
},
|
|
2500
|
+
required: ["stage", "product", "track", "rule", "evidence", "gate_kind"],
|
|
2501
|
+
},
|
|
2398
2502
|
},
|
|
2399
|
-
required: ["category"
|
|
2503
|
+
required: ["category"],
|
|
2400
2504
|
},
|
|
2401
2505
|
},
|
|
2402
2506
|
{
|
|
@@ -3233,6 +3337,23 @@ function brainstormError(tool, result) {
|
|
|
3233
3337
|
message: result.message || `${tool} failed: ${result.error} (HTTP ${result.status})`,
|
|
3234
3338
|
};
|
|
3235
3339
|
}
|
|
3340
|
+
// lifecycleGateProject resolves a lifecycle gate tool's project: the argument,
|
|
3341
|
+
// else the project this agent is bound to (add-product-lifecycle-pack §2.1).
|
|
3342
|
+
function lifecycleGateProject(args, context) {
|
|
3343
|
+
if (typeof args.project_id === "string" && args.project_id !== "")
|
|
3344
|
+
return args.project_id;
|
|
3345
|
+
return state.loadState(context.getWorkingDir())?.project_id ?? "";
|
|
3346
|
+
}
|
|
3347
|
+
// lifecycleGateError passes a server refusal through with its code and status,
|
|
3348
|
+
// and says in plain words what a 503 lifecycle_gates_disabled means.
|
|
3349
|
+
function lifecycleGateError(err) {
|
|
3350
|
+
if (!(err instanceof AgentHubApiError))
|
|
3351
|
+
throw err;
|
|
3352
|
+
const message = err.code === "lifecycle_gates_disabled"
|
|
3353
|
+
? "Lifecycle gates are switched off on this AgentHub server (AGENTHUB_LIFECYCLE_GATES_ENABLED is not true). No gate can be opened or listed until an operator turns it on; existing gates stay readable and decidable on /reviews."
|
|
3354
|
+
: err.message;
|
|
3355
|
+
return { ok: false, error: err.code || "request_failed", status: err.status, message };
|
|
3356
|
+
}
|
|
3236
3357
|
export async function handleToolCall(name, args, client, context) {
|
|
3237
3358
|
const agentId = context.getCurrentAgentId();
|
|
3238
3359
|
// Session steering + takeover (add-multiplayer-sessions §4.7, §5.9). Handled
|
|
@@ -3972,7 +4093,7 @@ export async function handleToolCall(name, args, client, context) {
|
|
|
3972
4093
|
throw new Error("Not registered. Call agent_register first.");
|
|
3973
4094
|
const inboxLimit = resolveInboxLimit(args.limit);
|
|
3974
4095
|
const raw = await client.getInbox(agentId, args.unread_only, inboxLimit);
|
|
3975
|
-
return boundInbox(raw);
|
|
4096
|
+
return { ...boundInbox(raw), ...inviteFields(raw.pending_brainstorm_invites) };
|
|
3976
4097
|
}
|
|
3977
4098
|
case "mark_read": {
|
|
3978
4099
|
const result = await client.markRead(args.message_id);
|
|
@@ -4118,6 +4239,12 @@ export async function handleToolCall(name, args, client, context) {
|
|
|
4118
4239
|
const result = await client.joinChannel(args.channel, agentId);
|
|
4119
4240
|
return wrapWithPendingItems(client, agentId, result, context);
|
|
4120
4241
|
}
|
|
4242
|
+
case "room_history": {
|
|
4243
|
+
if (!agentId)
|
|
4244
|
+
throw new Error("Not registered. Call agent_register first.");
|
|
4245
|
+
const result = await client.roomHistory(args.room, args.since, args.limit);
|
|
4246
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
4247
|
+
}
|
|
4121
4248
|
case "leave_channel": {
|
|
4122
4249
|
if (!agentId)
|
|
4123
4250
|
throw new Error("Not registered. Call agent_register first.");
|
|
@@ -4545,6 +4672,55 @@ export async function handleToolCall(name, args, client, context) {
|
|
|
4545
4672
|
message: `Filed bug ${result.key} (${result.severity}/${result.bug_state}): ${args.title}`,
|
|
4546
4673
|
};
|
|
4547
4674
|
}
|
|
4675
|
+
case "lifecycle_gate_request": {
|
|
4676
|
+
const projectId = lifecycleGateProject(args, context);
|
|
4677
|
+
if (!projectId) {
|
|
4678
|
+
return {
|
|
4679
|
+
ok: false,
|
|
4680
|
+
error: "invalid_args",
|
|
4681
|
+
message: "project_id is required (or bind this agent to a project with agent_register).",
|
|
4682
|
+
};
|
|
4683
|
+
}
|
|
4684
|
+
const body = { project_id: projectId };
|
|
4685
|
+
for (const k of ["task_id", "stage", "title", "content", "evidence_hash", "items", "parent_review_id"]) {
|
|
4686
|
+
if (args[k] !== undefined)
|
|
4687
|
+
body[k] = args[k];
|
|
4688
|
+
}
|
|
4689
|
+
try {
|
|
4690
|
+
const review = await client.post("/lifecycle/gates", body);
|
|
4691
|
+
return {
|
|
4692
|
+
ok: true,
|
|
4693
|
+
review_id: review.id,
|
|
4694
|
+
status: review.status,
|
|
4695
|
+
review,
|
|
4696
|
+
message: `Opened lifecycle gate ${review.id} (stage ${String(args.stage)}, ${review.status}). An org owner or admin signs it on /reviews.`,
|
|
4697
|
+
};
|
|
4698
|
+
}
|
|
4699
|
+
catch (err) {
|
|
4700
|
+
return lifecycleGateError(err);
|
|
4701
|
+
}
|
|
4702
|
+
}
|
|
4703
|
+
case "lifecycle_gate_list": {
|
|
4704
|
+
const projectId = lifecycleGateProject(args, context);
|
|
4705
|
+
if (!projectId) {
|
|
4706
|
+
return {
|
|
4707
|
+
ok: false,
|
|
4708
|
+
error: "invalid_args",
|
|
4709
|
+
message: "project_id is required (or bind this agent to a project with agent_register).",
|
|
4710
|
+
};
|
|
4711
|
+
}
|
|
4712
|
+
const params = new URLSearchParams({ project_id: projectId });
|
|
4713
|
+
for (const k of ["stage", "task_id", "status"]) {
|
|
4714
|
+
if (typeof args[k] === "string" && args[k] !== "")
|
|
4715
|
+
params.set(k, args[k]);
|
|
4716
|
+
}
|
|
4717
|
+
try {
|
|
4718
|
+
return await client.get(`/lifecycle/gates?${params.toString()}`);
|
|
4719
|
+
}
|
|
4720
|
+
catch (err) {
|
|
4721
|
+
return lifecycleGateError(err);
|
|
4722
|
+
}
|
|
4723
|
+
}
|
|
4548
4724
|
case "skill_pack_list": {
|
|
4549
4725
|
try {
|
|
4550
4726
|
const { packs } = await client.listSkillPacks();
|
|
@@ -4612,7 +4788,8 @@ export async function handleToolCall(name, args, client, context) {
|
|
|
4612
4788
|
const c = report.counts;
|
|
4613
4789
|
const summary = `${pack} v${manifest.version}: ${c.installed} installed, ${c.updated} updated, ` +
|
|
4614
4790
|
`${c.skipped_current} unchanged, ${c.skipped_user_modified} skipped (user-modified), ` +
|
|
4615
|
-
`${c.overwritten_force} overwritten, ${c.
|
|
4791
|
+
`${c.overwritten_force} overwritten, ${c.removed} removed (dropped by this version), ` +
|
|
4792
|
+
`${c.kept_modified} kept (dropped but user-modified), ${c.error} errors.`;
|
|
4616
4793
|
return {
|
|
4617
4794
|
ok: c.error === 0,
|
|
4618
4795
|
pack,
|
|
@@ -4707,7 +4884,37 @@ export async function handleToolCall(name, args, client, context) {
|
|
|
4707
4884
|
invites: result.data.invites,
|
|
4708
4885
|
mirror_thread_id: result.data.mirror_thread_id,
|
|
4709
4886
|
mirror_warning: result.data.mirror_warning,
|
|
4710
|
-
|
|
4887
|
+
// separate-agent-chat-from-brainstorms §1.4: event | message |
|
|
4888
|
+
// event+message | none. "none" means recorded but the invitee is not
|
|
4889
|
+
// connected — it will see the invite in pending_brainstorm_invites.
|
|
4890
|
+
invite_delivery: result.data.invite_delivery,
|
|
4891
|
+
warning: result.data.warning,
|
|
4892
|
+
message: `Brainstorm ${result.data.display_number} opened with ${invitees}. ${result.data.invite_delivered === false
|
|
4893
|
+
? result.data.warning
|
|
4894
|
+
? "Invite delivery FAILED."
|
|
4895
|
+
: "Invite recorded; the invitee is not connected and will see it in pending_brainstorm_invites."
|
|
4896
|
+
: "Invite delivered."}`,
|
|
4897
|
+
};
|
|
4898
|
+
}
|
|
4899
|
+
case "brainstorm_invites": {
|
|
4900
|
+
// separate-agent-chat-from-brainstorms §2.1. Not wrapped: the wrapper
|
|
4901
|
+
// would repeat this same list as pending_brainstorm_invites.
|
|
4902
|
+
if (!agentId)
|
|
4903
|
+
throw new Error("Not registered. Call agent_register first.");
|
|
4904
|
+
const result = await client.brainstormInvites();
|
|
4905
|
+
if (!result.ok)
|
|
4906
|
+
return brainstormError("brainstorm_invites", result);
|
|
4907
|
+
const invites = Array.isArray(result.data.invites) ? result.data.invites : [];
|
|
4908
|
+
const autoAccept = brainstormAutoAcceptEnabled();
|
|
4909
|
+
return {
|
|
4910
|
+
ok: true,
|
|
4911
|
+
count: invites.length,
|
|
4912
|
+
invites,
|
|
4913
|
+
auto_accept: autoAccept,
|
|
4914
|
+
guidance: inviteGuidance(autoAccept),
|
|
4915
|
+
message: invites.length === 0
|
|
4916
|
+
? "No pending brainstorm invites."
|
|
4917
|
+
: `${invites.length} pending brainstorm invite(s). ${autoAccept ? "Auto-accept is on." : "Ask your human before accepting any you were not asked to join."}`,
|
|
4711
4918
|
};
|
|
4712
4919
|
}
|
|
4713
4920
|
case "brainstorm_accept": {
|
|
@@ -5266,6 +5473,8 @@ export async function handleToolCall(name, args, client, context) {
|
|
|
5266
5473
|
brainParams.set("category", args.category);
|
|
5267
5474
|
if (args.project)
|
|
5268
5475
|
brainParams.set("project", args.project);
|
|
5476
|
+
if (args.tag)
|
|
5477
|
+
brainParams.set("tag", args.tag);
|
|
5269
5478
|
if (args.limit)
|
|
5270
5479
|
brainParams.set("limit", String(args.limit));
|
|
5271
5480
|
return client.get(`/brain/knowledge?${brainParams.toString()}`);
|
|
@@ -5275,10 +5484,13 @@ export async function handleToolCall(name, args, client, context) {
|
|
|
5275
5484
|
throw new Error("Not registered. Call agent_register first.");
|
|
5276
5485
|
return client.createBrainKnowledge({
|
|
5277
5486
|
category: args.category,
|
|
5278
|
-
fact: args.fact,
|
|
5487
|
+
fact: args.fact || "",
|
|
5279
5488
|
detail: args.detail || "",
|
|
5280
5489
|
source_agent: agentId,
|
|
5281
5490
|
project: args.project,
|
|
5491
|
+
// The server stores tags as a JSON array string.
|
|
5492
|
+
...(Array.isArray(args.tags) ? { tags: JSON.stringify(args.tags) } : {}),
|
|
5493
|
+
...(args.lesson ? { lesson: args.lesson } : {}),
|
|
5282
5494
|
});
|
|
5283
5495
|
}
|
|
5284
5496
|
case "brain_get_context": {
|