agenthub-multiagent-mcp 1.76.1 → 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/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/session-capture.sh +21 -0
- package/dist/sessionCaptureScript.test.js +6 -5
- package/dist/sessionCaptureScript.test.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 +239 -12
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/tools.test.js +125 -3
- package/dist/tools/tools.test.js.map +1 -1
- package/dist/tools/whatsapp.d.ts +73 -0
- package/dist/tools/whatsapp.d.ts.map +1 -0
- package/dist/tools/whatsapp.js +278 -0
- package/dist/tools/whatsapp.js.map +1 -0
- package/dist/tools/whatsapp.test.d.ts +2 -0
- package/dist/tools/whatsapp.test.d.ts.map +1 -0
- package/dist/tools/whatsapp.test.js +139 -0
- package/dist/tools/whatsapp.test.js.map +1 -0
- package/package.json +1 -1
- package/skills/catalog.json +106 -104
- package/skills/manifest.json +69 -67
- 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";
|
|
@@ -16,9 +17,11 @@ import { handleEmployeeTrainingToolCall, isEmployeeTrainingTool, registerEmploye
|
|
|
16
17
|
import { registerSessionControlTools, handleSessionControlToolCall, isSessionControlTool, } from "./sessionControl.js";
|
|
17
18
|
import { registerGoshtiReadTools, handleGoshtiReadToolCall, isGoshtiReadTool, } from "./goshtiRead.js";
|
|
18
19
|
import { registerRunTools, handleRunToolCall, isRunTool } from "./runs.js";
|
|
20
|
+
import { isWhatsAppTool, runWhatsAppTool, whatsappToolDefinitions } from "./whatsapp.js";
|
|
19
21
|
import { installPackFromArchive } from "../skillPackInstaller.js";
|
|
20
22
|
import { getServerTime, useTimeInjection } from "../lib/server-time.js";
|
|
21
23
|
import { getPendingItems } from "../lib/pending-items.js";
|
|
24
|
+
import { brainstormAutoAcceptEnabled, inviteGuidance, } from "../lib/brainstormInvites.js";
|
|
22
25
|
import { ENG_IC_CHARTER } from "../employee.js";
|
|
23
26
|
import { promises as fsp } from "node:fs";
|
|
24
27
|
import * as os from "node:os";
|
|
@@ -83,6 +86,19 @@ function backgroundSync(client, agentId, workingDir) {
|
|
|
83
86
|
}
|
|
84
87
|
})();
|
|
85
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
|
+
}
|
|
86
102
|
/**
|
|
87
103
|
* Wraps a tool response with pending messages and tasks
|
|
88
104
|
* This enables automatic delivery without extra API calls
|
|
@@ -160,6 +176,7 @@ async function wrapWithPendingItems(client, agentId, result, context) {
|
|
|
160
176
|
pending_messages_count: messages.length,
|
|
161
177
|
pending_tasks: tasks,
|
|
162
178
|
pending_tasks_count: tasks.length,
|
|
179
|
+
...inviteFields(cachedItems.invites),
|
|
163
180
|
urgent_action: urgentAction,
|
|
164
181
|
};
|
|
165
182
|
if (useTimeInjection()) {
|
|
@@ -976,7 +993,7 @@ export function registerTools() {
|
|
|
976
993
|
},
|
|
977
994
|
{
|
|
978
995
|
name: "skill_pack_install",
|
|
979
|
-
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.",
|
|
980
997
|
inputSchema: {
|
|
981
998
|
type: "object",
|
|
982
999
|
properties: {
|
|
@@ -989,6 +1006,52 @@ export function registerTools() {
|
|
|
989
1006
|
required: ["pack"],
|
|
990
1007
|
},
|
|
991
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
|
+
},
|
|
992
1055
|
{
|
|
993
1056
|
name: "api_search",
|
|
994
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.",
|
|
@@ -1140,6 +1203,21 @@ export function registerTools() {
|
|
|
1140
1203
|
required: ["channel"],
|
|
1141
1204
|
},
|
|
1142
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
|
+
},
|
|
1143
1221
|
{
|
|
1144
1222
|
name: "leave_channel",
|
|
1145
1223
|
description: "Unsubscribe from a channel",
|
|
@@ -1778,13 +1856,18 @@ export function registerTools() {
|
|
|
1778
1856
|
required: ["topic"],
|
|
1779
1857
|
},
|
|
1780
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
|
+
},
|
|
1781
1864
|
{
|
|
1782
1865
|
name: "brainstorm_accept",
|
|
1783
|
-
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)",
|
|
1784
1867
|
inputSchema: {
|
|
1785
1868
|
type: "object",
|
|
1786
1869
|
properties: {
|
|
1787
|
-
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)." },
|
|
1788
1871
|
},
|
|
1789
1872
|
required: ["session_id"],
|
|
1790
1873
|
},
|
|
@@ -2222,6 +2305,9 @@ export function registerTools() {
|
|
|
2222
2305
|
...registerEmployeeTrainingTools(),
|
|
2223
2306
|
// Reading Goshti as the agent's owner (add-agent-goshti-read §3.1)
|
|
2224
2307
|
...registerGoshtiReadTools(),
|
|
2308
|
+
// Reading the owner's WhatsApp through the local reader on this PC
|
|
2309
|
+
// (add-whatsapp-personal-reader §3). Never touches the AgentHub server.
|
|
2310
|
+
...whatsappToolDefinitions(),
|
|
2225
2311
|
// Orchestrated runs for the dashboard Run view (add-live-animated-views §1.8)
|
|
2226
2312
|
...registerRunTools(),
|
|
2227
2313
|
// OpenSpec Visualizer tools
|
|
@@ -2368,10 +2454,14 @@ export function registerTools() {
|
|
|
2368
2454
|
query: { type: "string", description: "Search query" },
|
|
2369
2455
|
category: {
|
|
2370
2456
|
type: "string",
|
|
2371
|
-
enum: ["decision", "convention", "architecture", "blocker", "alert", "insight"],
|
|
2457
|
+
enum: ["decision", "convention", "architecture", "blocker", "alert", "insight", "lesson"],
|
|
2372
2458
|
description: "Filter by knowledge category",
|
|
2373
2459
|
},
|
|
2374
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
|
+
},
|
|
2375
2465
|
limit: { type: "number", description: "Max results (default 20)" },
|
|
2376
2466
|
},
|
|
2377
2467
|
required: ["query"],
|
|
@@ -2379,20 +2469,38 @@ export function registerTools() {
|
|
|
2379
2469
|
},
|
|
2380
2470
|
{
|
|
2381
2471
|
name: "brain_write_knowledge",
|
|
2382
|
-
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.",
|
|
2383
2473
|
inputSchema: {
|
|
2384
2474
|
type: "object",
|
|
2385
2475
|
properties: {
|
|
2386
2476
|
category: {
|
|
2387
2477
|
type: "string",
|
|
2388
|
-
enum: ["decision", "convention", "architecture", "blocker", "alert", "insight"],
|
|
2478
|
+
enum: ["decision", "convention", "architecture", "blocker", "alert", "insight", "lesson"],
|
|
2389
2479
|
description: "Knowledge category",
|
|
2390
2480
|
},
|
|
2391
|
-
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>`." },
|
|
2392
2482
|
detail: { type: "string", description: "Extended context or rationale" },
|
|
2393
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
|
+
},
|
|
2394
2502
|
},
|
|
2395
|
-
required: ["category"
|
|
2503
|
+
required: ["category"],
|
|
2396
2504
|
},
|
|
2397
2505
|
},
|
|
2398
2506
|
{
|
|
@@ -3229,6 +3337,23 @@ function brainstormError(tool, result) {
|
|
|
3229
3337
|
message: result.message || `${tool} failed: ${result.error} (HTTP ${result.status})`,
|
|
3230
3338
|
};
|
|
3231
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
|
+
}
|
|
3232
3357
|
export async function handleToolCall(name, args, client, context) {
|
|
3233
3358
|
const agentId = context.getCurrentAgentId();
|
|
3234
3359
|
// Session steering + takeover (add-multiplayer-sessions §4.7, §5.9). Handled
|
|
@@ -3252,6 +3377,17 @@ export async function handleToolCall(name, args, client, context) {
|
|
|
3252
3377
|
if (isGoshtiReadTool(name)) {
|
|
3253
3378
|
return handleGoshtiReadToolCall(name, args, client);
|
|
3254
3379
|
}
|
|
3380
|
+
// WhatsApp reads (add-whatsapp-personal-reader §3). Local only: no agent
|
|
3381
|
+
// registration needed, no server call, refused unless the session is private.
|
|
3382
|
+
if (isWhatsAppTool(name)) {
|
|
3383
|
+
const out = await runWhatsAppTool(name, args, {
|
|
3384
|
+
env: process.env,
|
|
3385
|
+
homeDir: os.homedir(),
|
|
3386
|
+
workingDir: context.getWorkingDir(),
|
|
3387
|
+
fetch: (url, init) => fetch(url, init),
|
|
3388
|
+
});
|
|
3389
|
+
return { content: [{ type: "text", text: JSON.stringify(out, null, 2) }] };
|
|
3390
|
+
}
|
|
3255
3391
|
// Orchestrated runs (add-live-animated-views §1.8). Same module pattern. The
|
|
3256
3392
|
// run id run_open returns is kept in .agenthub/state, so the other run tools
|
|
3257
3393
|
// (and sub-agents sharing the checkout's state) default to it.
|
|
@@ -3957,7 +4093,7 @@ export async function handleToolCall(name, args, client, context) {
|
|
|
3957
4093
|
throw new Error("Not registered. Call agent_register first.");
|
|
3958
4094
|
const inboxLimit = resolveInboxLimit(args.limit);
|
|
3959
4095
|
const raw = await client.getInbox(agentId, args.unread_only, inboxLimit);
|
|
3960
|
-
return boundInbox(raw);
|
|
4096
|
+
return { ...boundInbox(raw), ...inviteFields(raw.pending_brainstorm_invites) };
|
|
3961
4097
|
}
|
|
3962
4098
|
case "mark_read": {
|
|
3963
4099
|
const result = await client.markRead(args.message_id);
|
|
@@ -4103,6 +4239,12 @@ export async function handleToolCall(name, args, client, context) {
|
|
|
4103
4239
|
const result = await client.joinChannel(args.channel, agentId);
|
|
4104
4240
|
return wrapWithPendingItems(client, agentId, result, context);
|
|
4105
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
|
+
}
|
|
4106
4248
|
case "leave_channel": {
|
|
4107
4249
|
if (!agentId)
|
|
4108
4250
|
throw new Error("Not registered. Call agent_register first.");
|
|
@@ -4530,6 +4672,55 @@ export async function handleToolCall(name, args, client, context) {
|
|
|
4530
4672
|
message: `Filed bug ${result.key} (${result.severity}/${result.bug_state}): ${args.title}`,
|
|
4531
4673
|
};
|
|
4532
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
|
+
}
|
|
4533
4724
|
case "skill_pack_list": {
|
|
4534
4725
|
try {
|
|
4535
4726
|
const { packs } = await client.listSkillPacks();
|
|
@@ -4597,7 +4788,8 @@ export async function handleToolCall(name, args, client, context) {
|
|
|
4597
4788
|
const c = report.counts;
|
|
4598
4789
|
const summary = `${pack} v${manifest.version}: ${c.installed} installed, ${c.updated} updated, ` +
|
|
4599
4790
|
`${c.skipped_current} unchanged, ${c.skipped_user_modified} skipped (user-modified), ` +
|
|
4600
|
-
`${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.`;
|
|
4601
4793
|
return {
|
|
4602
4794
|
ok: c.error === 0,
|
|
4603
4795
|
pack,
|
|
@@ -4692,7 +4884,37 @@ export async function handleToolCall(name, args, client, context) {
|
|
|
4692
4884
|
invites: result.data.invites,
|
|
4693
4885
|
mirror_thread_id: result.data.mirror_thread_id,
|
|
4694
4886
|
mirror_warning: result.data.mirror_warning,
|
|
4695
|
-
|
|
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."}`,
|
|
4696
4918
|
};
|
|
4697
4919
|
}
|
|
4698
4920
|
case "brainstorm_accept": {
|
|
@@ -5251,6 +5473,8 @@ export async function handleToolCall(name, args, client, context) {
|
|
|
5251
5473
|
brainParams.set("category", args.category);
|
|
5252
5474
|
if (args.project)
|
|
5253
5475
|
brainParams.set("project", args.project);
|
|
5476
|
+
if (args.tag)
|
|
5477
|
+
brainParams.set("tag", args.tag);
|
|
5254
5478
|
if (args.limit)
|
|
5255
5479
|
brainParams.set("limit", String(args.limit));
|
|
5256
5480
|
return client.get(`/brain/knowledge?${brainParams.toString()}`);
|
|
@@ -5260,10 +5484,13 @@ export async function handleToolCall(name, args, client, context) {
|
|
|
5260
5484
|
throw new Error("Not registered. Call agent_register first.");
|
|
5261
5485
|
return client.createBrainKnowledge({
|
|
5262
5486
|
category: args.category,
|
|
5263
|
-
fact: args.fact,
|
|
5487
|
+
fact: args.fact || "",
|
|
5264
5488
|
detail: args.detail || "",
|
|
5265
5489
|
source_agent: agentId,
|
|
5266
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 } : {}),
|
|
5267
5494
|
});
|
|
5268
5495
|
}
|
|
5269
5496
|
case "brain_get_context": {
|