agenthub-multiagent-mcp 1.30.0 → 1.32.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/client.d.ts +71 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +67 -2
- package/dist/client.js.map +1 -1
- package/dist/client.unit.test.js +40 -0
- package/dist/client.unit.test.js.map +1 -1
- package/dist/hooks/brainCapture.d.ts +6 -2
- package/dist/hooks/brainCapture.d.ts.map +1 -1
- package/dist/hooks/brainCapture.js +3 -1
- package/dist/hooks/brainCapture.js.map +1 -1
- package/dist/index.js +96 -87
- package/dist/index.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +372 -7
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/tools.test.js +164 -2
- package/dist/tools/tools.test.js.map +1 -1
- package/package.json +4 -1
- package/skills/catalog.json +11 -2
- package/skills/commands/close-session.md +12 -6
- package/skills/manifest.json +8 -1
- package/skills/skills/code-brain-hook/SKILL.md +61 -0
package/dist/tools/index.js
CHANGED
|
@@ -239,6 +239,14 @@ export function registerTools() {
|
|
|
239
239
|
properties: {},
|
|
240
240
|
},
|
|
241
241
|
},
|
|
242
|
+
{
|
|
243
|
+
name: "brain_flush_session",
|
|
244
|
+
description: "Flush this session's accumulated activity into the local searchable brain (.mv2) and the server brain_sessions table WITHOUT disconnecting the agent. Call this at session close (e.g. from /close-session) so the brain is current even where the on-process-exit flush is unreliable (notably Windows, where SIGTERM never fires). Idempotent and best-effort.",
|
|
245
|
+
inputSchema: {
|
|
246
|
+
type: "object",
|
|
247
|
+
properties: {},
|
|
248
|
+
},
|
|
249
|
+
},
|
|
242
250
|
// Time orientation (add-time-aware-agents §1.3)
|
|
243
251
|
{
|
|
244
252
|
name: "now",
|
|
@@ -715,6 +723,64 @@ export function registerTools() {
|
|
|
715
723
|
required: ["id"],
|
|
716
724
|
},
|
|
717
725
|
},
|
|
726
|
+
{
|
|
727
|
+
name: "calendar_schedule_meeting",
|
|
728
|
+
description: "Schedule a Google Calendar meeting with an auto-generated Google Meet link. Idempotent on dedupe_key (same key returns the same event). Requires the calendar capability to be enabled on the server.",
|
|
729
|
+
inputSchema: {
|
|
730
|
+
type: "object",
|
|
731
|
+
properties: {
|
|
732
|
+
title: { type: "string", description: "Meeting title" },
|
|
733
|
+
start: { type: "string", description: "Start time (RFC3339, e.g. 2026-06-02T10:00:00Z)" },
|
|
734
|
+
end: { type: "string", description: "End time (RFC3339); must be after start" },
|
|
735
|
+
attendee_emails: {
|
|
736
|
+
type: "array",
|
|
737
|
+
items: { type: "string" },
|
|
738
|
+
description: "Invitee email addresses",
|
|
739
|
+
},
|
|
740
|
+
description: { type: "string", description: "Optional meeting description" },
|
|
741
|
+
timezone: { type: "string", description: "Optional IANA timezone; defaults to the server default (UTC)" },
|
|
742
|
+
recurrence: { type: "string", description: "Optional RRULE, e.g. RRULE:FREQ=WEEKLY;BYDAY=MO" },
|
|
743
|
+
dedupe_key: { type: "string", description: "Idempotency key; same key returns the same event" },
|
|
744
|
+
source: {
|
|
745
|
+
type: "object",
|
|
746
|
+
description: "Optional linkage to a blocker/task; a task source resolves the assignee as an attendee",
|
|
747
|
+
properties: {
|
|
748
|
+
kind: { type: "string", enum: ["blocker", "task", "manual"] },
|
|
749
|
+
id: { type: "string" },
|
|
750
|
+
},
|
|
751
|
+
},
|
|
752
|
+
},
|
|
753
|
+
required: ["title", "start", "end", "dedupe_key"],
|
|
754
|
+
},
|
|
755
|
+
},
|
|
756
|
+
{
|
|
757
|
+
name: "email_send",
|
|
758
|
+
description: "Send an email via Gmail to an allowlisted recipient (external partners not in Slack). Recipient must pass the allowlist; per-org daily cap applies. Supports templates (onboarding_invite, task_assignment, digest) or raw body_text/body_html.",
|
|
759
|
+
inputSchema: {
|
|
760
|
+
type: "object",
|
|
761
|
+
properties: {
|
|
762
|
+
to: {
|
|
763
|
+
type: "array",
|
|
764
|
+
items: { type: "string" },
|
|
765
|
+
description: "Recipient email addresses (each must pass the allowlist)",
|
|
766
|
+
},
|
|
767
|
+
subject: { type: "string", description: "Email subject (required for ad-hoc sends)" },
|
|
768
|
+
body_text: { type: "string", description: "Plain-text body (required for ad-hoc sends)" },
|
|
769
|
+
body_html: { type: "string", description: "Optional HTML body" },
|
|
770
|
+
template: {
|
|
771
|
+
type: "string",
|
|
772
|
+
enum: ["onboarding_invite", "task_assignment", "digest"],
|
|
773
|
+
description: "Optional template; when set, body_* are derived from template_vars",
|
|
774
|
+
},
|
|
775
|
+
template_vars: {
|
|
776
|
+
type: "object",
|
|
777
|
+
description: "Variables for the chosen template (e.g. {name, login_url})",
|
|
778
|
+
},
|
|
779
|
+
dedupe_key: { type: "string", description: "Optional idempotency key; a repeat is a no-op" },
|
|
780
|
+
},
|
|
781
|
+
required: ["to"],
|
|
782
|
+
},
|
|
783
|
+
},
|
|
718
784
|
{
|
|
719
785
|
name: "list_channels",
|
|
720
786
|
description: "List all available channels",
|
|
@@ -736,6 +802,143 @@ export function registerTools() {
|
|
|
736
802
|
},
|
|
737
803
|
},
|
|
738
804
|
},
|
|
805
|
+
{
|
|
806
|
+
name: "api_search",
|
|
807
|
+
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.",
|
|
808
|
+
inputSchema: {
|
|
809
|
+
type: "object",
|
|
810
|
+
properties: {
|
|
811
|
+
query: { type: "string", description: "Free-text match over endpoint identity + description" },
|
|
812
|
+
project_id: { type: "string", description: "Filter to one project (optional)" },
|
|
813
|
+
surface: { type: "string", description: "Filter by surface: rest | mcp | graphql | grpc (optional)" },
|
|
814
|
+
},
|
|
815
|
+
},
|
|
816
|
+
},
|
|
817
|
+
{
|
|
818
|
+
name: "api_get_endpoint",
|
|
819
|
+
description: "Get one API endpoint's full contract (handler ref, schemas, auth, description) plus its edges, by identity (e.g. \"POST /api/x\").",
|
|
820
|
+
inputSchema: {
|
|
821
|
+
type: "object",
|
|
822
|
+
properties: {
|
|
823
|
+
identity: { type: "string", description: "Endpoint identity, e.g. \"POST /api/x\" (REST) or a tool name (MCP)" },
|
|
824
|
+
project_id: { type: "string", description: "Project to look in (optional; org-wide if omitted)" },
|
|
825
|
+
},
|
|
826
|
+
required: ["identity"],
|
|
827
|
+
},
|
|
828
|
+
},
|
|
829
|
+
{
|
|
830
|
+
name: "api_endpoint_impact",
|
|
831
|
+
description: "Show an endpoint's blast radius before you change it: the consumers, models, and database tables it touches (plus linked openspec changes / feature-graph nodes).",
|
|
832
|
+
inputSchema: {
|
|
833
|
+
type: "object",
|
|
834
|
+
properties: {
|
|
835
|
+
identity: { type: "string", description: "Endpoint identity, e.g. \"POST /api/x\"" },
|
|
836
|
+
project_id: { type: "string", description: "Project to look in (optional)" },
|
|
837
|
+
},
|
|
838
|
+
required: ["identity"],
|
|
839
|
+
},
|
|
840
|
+
},
|
|
841
|
+
{
|
|
842
|
+
name: "api_graph",
|
|
843
|
+
description: "Return a project's API endpoint subgraph (endpoints with their edges) — for an overview of what a project exposes.",
|
|
844
|
+
inputSchema: {
|
|
845
|
+
type: "object",
|
|
846
|
+
properties: {
|
|
847
|
+
project_id: { type: "string", description: "Project to render (optional; org-wide if omitted)" },
|
|
848
|
+
surface: { type: "string", description: "Filter by surface: rest | mcp | graphql | grpc (optional)" },
|
|
849
|
+
},
|
|
850
|
+
},
|
|
851
|
+
},
|
|
852
|
+
{
|
|
853
|
+
name: "action_item_create",
|
|
854
|
+
description: "Create a lightweight action item — a tracked to-do assigned to a person (e.g. a task captured from chat). Distinct from the openspec-linked ticket hierarchy; promote to a ticket task when it becomes real dev work. Requires the deployment to have action items enabled.",
|
|
855
|
+
inputSchema: {
|
|
856
|
+
type: "object",
|
|
857
|
+
properties: {
|
|
858
|
+
title: { type: "string", description: "Short description of the action item" },
|
|
859
|
+
assignee_user_id: {
|
|
860
|
+
type: "string",
|
|
861
|
+
description: "AgentHub user id to assign to (optional; created unassigned if omitted)",
|
|
862
|
+
},
|
|
863
|
+
details: { type: "string", description: "Optional longer details" },
|
|
864
|
+
due_at: { type: "string", description: "Optional due timestamp (RFC3339)" },
|
|
865
|
+
source_kind: {
|
|
866
|
+
type: "string",
|
|
867
|
+
description: "Origin: manual | slack_mention | slack_command | slack_shortcut | slack_reaction | slack_autoparse (default manual)",
|
|
868
|
+
},
|
|
869
|
+
source_ref: { type: "string", description: "Optional provenance link (e.g. a Slack permalink)" },
|
|
870
|
+
},
|
|
871
|
+
required: ["title"],
|
|
872
|
+
},
|
|
873
|
+
},
|
|
874
|
+
{
|
|
875
|
+
name: "action_item_list",
|
|
876
|
+
description: "List action items in your org, optionally filtered by assignee user id and/or status (open | snoozed | done | cancelled).",
|
|
877
|
+
inputSchema: {
|
|
878
|
+
type: "object",
|
|
879
|
+
properties: {
|
|
880
|
+
assignee: { type: "string", description: "Filter by assignee AgentHub user id" },
|
|
881
|
+
status: {
|
|
882
|
+
type: "string",
|
|
883
|
+
description: "Filter by status: open | snoozed | done | cancelled",
|
|
884
|
+
},
|
|
885
|
+
},
|
|
886
|
+
},
|
|
887
|
+
},
|
|
888
|
+
{
|
|
889
|
+
name: "action_item_complete",
|
|
890
|
+
description: "Mark an action item done.",
|
|
891
|
+
inputSchema: {
|
|
892
|
+
type: "object",
|
|
893
|
+
properties: {
|
|
894
|
+
id: { type: "string", description: "Action item id" },
|
|
895
|
+
},
|
|
896
|
+
required: ["id"],
|
|
897
|
+
},
|
|
898
|
+
},
|
|
899
|
+
{
|
|
900
|
+
name: "action_item_snooze",
|
|
901
|
+
description: "Snooze an action item until a given time (RFC3339).",
|
|
902
|
+
inputSchema: {
|
|
903
|
+
type: "object",
|
|
904
|
+
properties: {
|
|
905
|
+
id: { type: "string", description: "Action item id" },
|
|
906
|
+
until: { type: "string", description: "RFC3339 timestamp to snooze until" },
|
|
907
|
+
},
|
|
908
|
+
required: ["id", "until"],
|
|
909
|
+
},
|
|
910
|
+
},
|
|
911
|
+
{
|
|
912
|
+
name: "action_item_promote",
|
|
913
|
+
description: "Promote an action item into a tracked ticket task under a story (when an ad-hoc item turns out to be real dev work). Links the two; completing the ticket later marks the action item done.",
|
|
914
|
+
inputSchema: {
|
|
915
|
+
type: "object",
|
|
916
|
+
properties: {
|
|
917
|
+
id: { type: "string", description: "Action item id to promote" },
|
|
918
|
+
story_id: { type: "string", description: "Target story id the new task will live under" },
|
|
919
|
+
},
|
|
920
|
+
required: ["id", "story_id"],
|
|
921
|
+
},
|
|
922
|
+
},
|
|
923
|
+
{
|
|
924
|
+
name: "notify_prefs_set",
|
|
925
|
+
description: "Set your action-item digest + reminder preferences (the authenticated user's). Controls which channel the daily digest is delivered on and whether overdue reminders are sent.",
|
|
926
|
+
inputSchema: {
|
|
927
|
+
type: "object",
|
|
928
|
+
properties: {
|
|
929
|
+
digest_channel: {
|
|
930
|
+
type: "string",
|
|
931
|
+
description: "Where to deliver the digest: auto (Slack DM if linked, else email) | slack | email | both",
|
|
932
|
+
},
|
|
933
|
+
digest_hour_local: {
|
|
934
|
+
type: "number",
|
|
935
|
+
description: "Hour of day (0-23, local to tz) to send the daily digest",
|
|
936
|
+
},
|
|
937
|
+
tz: { type: "string", description: "IANA timezone name (e.g. Asia/Kolkata); default UTC" },
|
|
938
|
+
reminders_enabled: { type: "boolean", description: "Whether to send overdue reminders" },
|
|
939
|
+
},
|
|
940
|
+
},
|
|
941
|
+
},
|
|
739
942
|
{
|
|
740
943
|
name: "join_channel",
|
|
741
944
|
description: "Subscribe to a channel to receive its messages",
|
|
@@ -1683,7 +1886,29 @@ export function registerTools() {
|
|
|
1683
1886
|
description: "Get the TOON-encoded org brain context package for this agent. Includes own session history, blockers, org decisions, and alerts.",
|
|
1684
1887
|
inputSchema: {
|
|
1685
1888
|
type: "object",
|
|
1686
|
-
properties: {
|
|
1889
|
+
properties: {
|
|
1890
|
+
include_code: {
|
|
1891
|
+
type: "boolean",
|
|
1892
|
+
description: "add-code-brain: when true, append a code_index section with top org-wide code/doc matches relevant to recent sessions. Default false — behaviour is byte-identical to pre-change when omitted.",
|
|
1893
|
+
},
|
|
1894
|
+
},
|
|
1895
|
+
},
|
|
1896
|
+
},
|
|
1897
|
+
{
|
|
1898
|
+
name: "brain_code_recall",
|
|
1899
|
+
description: "add-code-brain: search the org-wide indexed code + docs (Markdown / RST / OpenAPI / HTML / Jupyter / PDF + tree-sitter symbols). Use when org_search returns nothing for a code or architecture question, or when you need to discover files/symbols that no agent has written down. Read-only.",
|
|
1900
|
+
inputSchema: {
|
|
1901
|
+
type: "object",
|
|
1902
|
+
properties: {
|
|
1903
|
+
query: { type: "string", description: "Free-text query — symbol name, doc title fragment, or natural-language phrase" },
|
|
1904
|
+
k: { type: "number", description: "Max hits per kind (default 5, max 50)" },
|
|
1905
|
+
kinds: {
|
|
1906
|
+
type: "array",
|
|
1907
|
+
items: { type: "string", enum: ["docs", "symbols"] },
|
|
1908
|
+
description: "Restrict to docs and/or symbols. Default both.",
|
|
1909
|
+
},
|
|
1910
|
+
},
|
|
1911
|
+
required: ["query"],
|
|
1687
1912
|
},
|
|
1688
1913
|
},
|
|
1689
1914
|
{
|
|
@@ -2222,24 +2447,32 @@ export async function handleToolCall(name, args, client, context) {
|
|
|
2222
2447
|
try {
|
|
2223
2448
|
// Use provided name, or fall back to stored name
|
|
2224
2449
|
const reconnectName = args.name || existingState.name;
|
|
2225
|
-
|
|
2450
|
+
// fix-agent-register-project-binding §3 — pass the tool's `project`
|
|
2451
|
+
// arg so reconnect binds project_id. The reconnect path previously
|
|
2452
|
+
// ignored it, leaving local state.project_id empty and
|
|
2453
|
+
// create_epic/create_task unusable despite a healthy session.
|
|
2454
|
+
const reconnectResult = await client.reconnectAgent(existingState.agent_id, existingState.token, owner, args.model, reconnectName, projectKey);
|
|
2226
2455
|
context.setCurrentAgentId(existingState.agent_id);
|
|
2227
2456
|
// Set connect token on client for org operations
|
|
2228
2457
|
client.setConnectToken(existingState.token);
|
|
2229
2458
|
// Store working_dir in context for other tools to use
|
|
2230
2459
|
context.setWorkingDir(workingDir);
|
|
2231
|
-
//
|
|
2460
|
+
// Persist the server-returned project binding, falling back to the
|
|
2461
|
+
// existing values when the server did not resolve a project.
|
|
2232
2462
|
state.saveState(workingDir, {
|
|
2233
2463
|
...existingState,
|
|
2234
2464
|
name: reconnectResult.name,
|
|
2465
|
+
project_id: reconnectResult.project_id || existingState.project_id,
|
|
2466
|
+
project_key: reconnectResult.project_key || existingState.project_key,
|
|
2235
2467
|
last_task: undefined, // Cleared on reconnect
|
|
2236
2468
|
});
|
|
2469
|
+
const unresolvedNote = projectKey && !reconnectResult.project_resolved
|
|
2470
|
+
? ` (note: project "${projectKey}" could not be resolved; project not bound)`
|
|
2471
|
+
: "";
|
|
2237
2472
|
return {
|
|
2238
2473
|
...reconnectResult,
|
|
2239
2474
|
mode: "reconnected",
|
|
2240
|
-
message: reconnectResult.
|
|
2241
|
-
? `Reconnected as ${reconnectResult.name || existingState.agent_id}. You have ${reconnectResult.pending_tasks_count} pending tasks and ${reconnectResult.unread_messages_count} unread messages.`
|
|
2242
|
-
: `Reconnected as ${reconnectResult.name || existingState.agent_id}. You have ${reconnectResult.pending_tasks_count} pending tasks and ${reconnectResult.unread_messages_count} unread messages.`,
|
|
2475
|
+
message: `Reconnected as ${reconnectResult.name || existingState.agent_id}. You have ${reconnectResult.pending_tasks_count} pending tasks and ${reconnectResult.unread_messages_count} unread messages.${unresolvedNote}`,
|
|
2243
2476
|
};
|
|
2244
2477
|
}
|
|
2245
2478
|
catch (error) {
|
|
@@ -2595,6 +2828,19 @@ export async function handleToolCall(name, args, client, context) {
|
|
|
2595
2828
|
} : null,
|
|
2596
2829
|
};
|
|
2597
2830
|
}
|
|
2831
|
+
case "brain_flush_session": {
|
|
2832
|
+
// Flush the current session to the local .mv2 + server WITHOUT
|
|
2833
|
+
// disconnecting. Mirrors gracefulShutdown's agent-id resolution so it
|
|
2834
|
+
// works even if agent_register was never called this process. The agent
|
|
2835
|
+
// stays registered for the next reconnect.
|
|
2836
|
+
const workingDir = context.getWorkingDir();
|
|
2837
|
+
const flushAgentId = agentId || (workingDir ? state.readHookAgentId(workingDir) : null);
|
|
2838
|
+
if (!flushAgentId) {
|
|
2839
|
+
return { flushed: false, reason: "no agent id resolved" };
|
|
2840
|
+
}
|
|
2841
|
+
const { mv2Written } = await brainCapture.flushSessionToBrain(client, flushAgentId);
|
|
2842
|
+
return { flushed: true, agent_id: flushAgentId, mv2_written: mv2Written };
|
|
2843
|
+
}
|
|
2598
2844
|
case "now": {
|
|
2599
2845
|
// No wrapping — server_time would otherwise overshadow the explicit
|
|
2600
2846
|
// tool result. Force-refresh past the cache so the agent gets a fresh
|
|
@@ -2818,6 +3064,32 @@ export async function handleToolCall(name, args, client, context) {
|
|
|
2818
3064
|
const result = await client.getAgent(args.id);
|
|
2819
3065
|
return wrapWithPendingItems(client, agentId, result, context);
|
|
2820
3066
|
}
|
|
3067
|
+
case "calendar_schedule_meeting": {
|
|
3068
|
+
const result = await client.scheduleMeeting({
|
|
3069
|
+
title: args.title,
|
|
3070
|
+
start: args.start,
|
|
3071
|
+
end: args.end,
|
|
3072
|
+
attendee_emails: args.attendee_emails,
|
|
3073
|
+
description: args.description,
|
|
3074
|
+
timezone: args.timezone,
|
|
3075
|
+
recurrence: args.recurrence,
|
|
3076
|
+
dedupe_key: args.dedupe_key,
|
|
3077
|
+
source: args.source,
|
|
3078
|
+
});
|
|
3079
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3080
|
+
}
|
|
3081
|
+
case "email_send": {
|
|
3082
|
+
const result = await client.sendEmail({
|
|
3083
|
+
to: args.to,
|
|
3084
|
+
subject: args.subject,
|
|
3085
|
+
body_text: args.body_text,
|
|
3086
|
+
body_html: args.body_html,
|
|
3087
|
+
template: args.template,
|
|
3088
|
+
template_vars: args.template_vars,
|
|
3089
|
+
dedupe_key: args.dedupe_key,
|
|
3090
|
+
});
|
|
3091
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3092
|
+
}
|
|
2821
3093
|
case "list_channels": {
|
|
2822
3094
|
const result = await client.listChannels();
|
|
2823
3095
|
return wrapWithPendingItems(client, agentId, result, context);
|
|
@@ -2828,6 +3100,71 @@ export async function handleToolCall(name, args, client, context) {
|
|
|
2828
3100
|
const result = await client.listProjects(args.include_archived);
|
|
2829
3101
|
return wrapWithPendingItems(client, agentId, result, context);
|
|
2830
3102
|
}
|
|
3103
|
+
// add-api-knowledge-graph §3 — org-wide read of the API graph.
|
|
3104
|
+
case "api_search": {
|
|
3105
|
+
const result = await client.apiSearch({
|
|
3106
|
+
query: args.query,
|
|
3107
|
+
project_id: args.project_id,
|
|
3108
|
+
surface: args.surface,
|
|
3109
|
+
});
|
|
3110
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3111
|
+
}
|
|
3112
|
+
case "api_get_endpoint": {
|
|
3113
|
+
const result = await client.apiGetEndpoint(args.identity, args.project_id);
|
|
3114
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3115
|
+
}
|
|
3116
|
+
case "api_endpoint_impact": {
|
|
3117
|
+
const result = await client.apiEndpointImpact(args.identity, args.project_id);
|
|
3118
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3119
|
+
}
|
|
3120
|
+
case "api_graph": {
|
|
3121
|
+
const result = await client.apiGraph({
|
|
3122
|
+
project_id: args.project_id,
|
|
3123
|
+
surface: args.surface,
|
|
3124
|
+
});
|
|
3125
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3126
|
+
}
|
|
3127
|
+
// add-action-items Phase 1 — org-scoped (auth via the agent's connect
|
|
3128
|
+
// token); no agentId guard, mirroring list_projects.
|
|
3129
|
+
case "action_item_create": {
|
|
3130
|
+
const result = await client.createActionItem({
|
|
3131
|
+
title: args.title,
|
|
3132
|
+
assignee_user_id: args.assignee_user_id,
|
|
3133
|
+
details: args.details,
|
|
3134
|
+
due_at: args.due_at,
|
|
3135
|
+
source_kind: args.source_kind,
|
|
3136
|
+
source_ref: args.source_ref,
|
|
3137
|
+
});
|
|
3138
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3139
|
+
}
|
|
3140
|
+
case "action_item_list": {
|
|
3141
|
+
const result = await client.listActionItems({
|
|
3142
|
+
assignee: args.assignee,
|
|
3143
|
+
status: args.status,
|
|
3144
|
+
});
|
|
3145
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3146
|
+
}
|
|
3147
|
+
case "action_item_complete": {
|
|
3148
|
+
const result = await client.completeActionItem(args.id);
|
|
3149
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3150
|
+
}
|
|
3151
|
+
case "action_item_snooze": {
|
|
3152
|
+
const result = await client.snoozeActionItem(args.id, args.until);
|
|
3153
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3154
|
+
}
|
|
3155
|
+
case "action_item_promote": {
|
|
3156
|
+
const result = await client.promoteActionItem(args.id, args.story_id);
|
|
3157
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3158
|
+
}
|
|
3159
|
+
case "notify_prefs_set": {
|
|
3160
|
+
const result = await client.setNotifyPrefs({
|
|
3161
|
+
digest_channel: args.digest_channel,
|
|
3162
|
+
digest_hour_local: args.digest_hour_local,
|
|
3163
|
+
tz: args.tz,
|
|
3164
|
+
reminders_enabled: args.reminders_enabled,
|
|
3165
|
+
});
|
|
3166
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3167
|
+
}
|
|
2831
3168
|
case "join_channel": {
|
|
2832
3169
|
if (!agentId)
|
|
2833
3170
|
throw new Error("Not registered. Call agent_register first.");
|
|
@@ -3708,7 +4045,35 @@ export async function handleToolCall(name, args, client, context) {
|
|
|
3708
4045
|
case "brain_get_context": {
|
|
3709
4046
|
if (!agentId)
|
|
3710
4047
|
throw new Error("Not registered. Call agent_register first.");
|
|
3711
|
-
|
|
4048
|
+
const includeCode = Boolean(args.include_code);
|
|
4049
|
+
const qs = includeCode ? "?include_code=true" : "";
|
|
4050
|
+
// Use raw GET so the include_code flag round-trips without a typed
|
|
4051
|
+
// client helper. Server-side decides whether to append code_index.
|
|
4052
|
+
return client.get(`/brain/context/${encodeURIComponent(agentId)}${qs}`);
|
|
4053
|
+
}
|
|
4054
|
+
case "brain_code_recall": {
|
|
4055
|
+
const query = args.query || "";
|
|
4056
|
+
if (!query)
|
|
4057
|
+
throw new Error("query is required");
|
|
4058
|
+
const k = args.k || 5;
|
|
4059
|
+
const kindsArr = args.kinds || ["docs", "symbols"];
|
|
4060
|
+
const params = new URLSearchParams();
|
|
4061
|
+
params.set("q", query);
|
|
4062
|
+
params.set("k", String(k));
|
|
4063
|
+
params.set("kinds", kindsArr.join(","));
|
|
4064
|
+
try {
|
|
4065
|
+
return await client.get(`/brain/code/search?${params.toString()}`);
|
|
4066
|
+
}
|
|
4067
|
+
catch (e) {
|
|
4068
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
4069
|
+
if (msg.includes("code_brain_disabled")) {
|
|
4070
|
+
return {
|
|
4071
|
+
error: "code_brain_disabled",
|
|
4072
|
+
message: "The code brain is disabled on this server. It defaults to enabled — ask the operator to unset AGENTHUB_CODE_BRAIN_ENABLED (or set it back to true) and restart.",
|
|
4073
|
+
};
|
|
4074
|
+
}
|
|
4075
|
+
throw e;
|
|
4076
|
+
}
|
|
3712
4077
|
}
|
|
3713
4078
|
case "brain_list_sessions": {
|
|
3714
4079
|
const targetAgent = args.agent_id || agentId;
|