agenthub-multiagent-mcp 1.49.0 → 1.51.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.
Files changed (47) hide show
  1. package/README.md +192 -192
  2. package/dist/brain/backend.d.ts.map +1 -1
  3. package/dist/brain/backend.js +11 -2
  4. package/dist/brain/backend.js.map +1 -1
  5. package/dist/brain/memvidBrain.d.ts +24 -0
  6. package/dist/brain/memvidBrain.d.ts.map +1 -1
  7. package/dist/brain/memvidBrain.js +350 -93
  8. package/dist/brain/memvidBrain.js.map +1 -1
  9. package/dist/brain/memvidCompaction.test.d.ts +10 -0
  10. package/dist/brain/memvidCompaction.test.d.ts.map +1 -0
  11. package/dist/brain/memvidCompaction.test.js +71 -0
  12. package/dist/brain/memvidCompaction.test.js.map +1 -0
  13. package/dist/channel.js +3 -3
  14. package/dist/client.d.ts +43 -0
  15. package/dist/client.d.ts.map +1 -1
  16. package/dist/client.js +53 -2
  17. package/dist/client.js.map +1 -1
  18. package/dist/commands/setup-shell.js +11 -11
  19. package/dist/employee.d.ts +0 -5
  20. package/dist/employee.d.ts.map +1 -1
  21. package/dist/employee.js +14 -1
  22. package/dist/employee.js.map +1 -1
  23. package/dist/hooks/periodicReminder.js +9 -9
  24. package/dist/setup.js +56 -56
  25. package/dist/tools/index.d.ts.map +1 -1
  26. package/dist/tools/index.js +167 -11
  27. package/dist/tools/index.js.map +1 -1
  28. package/dist/tools/openspec-viz/diagram-generator.js +54 -54
  29. package/dist/tools/openspec-viz/html-generator.js +503 -503
  30. package/dist/tools/tools.test.js +4 -2
  31. package/dist/tools/tools.test.js.map +1 -1
  32. package/dist/worker.js +10 -10
  33. package/native/agenthub-memvid/agenthub-memvid.linux-x64-gnu.node +0 -0
  34. package/native/agenthub-memvid/agenthub-memvid.win32-x64-msvc.node +0 -0
  35. package/native/agenthub-memvid/index.d.ts +58 -0
  36. package/native/agenthub-memvid/index.js +319 -0
  37. package/package.json +77 -75
  38. package/skills/catalog.json +76 -76
  39. package/skills/commands/close-session.md +144 -144
  40. package/skills/commands/start-session.md +77 -77
  41. package/skills/manifest.json +50 -50
  42. package/skills/skills/code-brain-hook/SKILL.md +61 -61
  43. package/skills/skills/deploy-staging/SKILL.md +164 -164
  44. package/skills/skills/deploy-vps-openclaw/SKILL.md +97 -97
  45. package/skills/skills/file-bug/SKILL.md +75 -75
  46. package/skills/skills/karpathy-guidelines/SKILL.md +67 -67
  47. package/skills/skills/multiagent-brainstorm/SKILL.md +142 -142
package/dist/setup.js CHANGED
@@ -181,28 +181,28 @@ function installWindowsStartup(config) {
181
181
  const thisDir = path.dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Z]:)/, "$1"));
182
182
  const workerJs = path.join(thisDir, "worker.js").replace(/\\/g, "\\\\");
183
183
  // Smart bat script: check before start, no zombie loop
184
- const batContent = `@echo off
185
- :: AgentHub Worker — auto-starts on login, checks before spawning
186
- set AGENTHUB_API_KEY=${config.api_key}
187
- set AGENTHUB_URL=${config.server}
188
-
189
- :check
190
- :: Check if worker is already running
191
- set PID_FILE=%TEMP%\\agenthub-workers\\worker.pid
192
- if exist "%PID_FILE%" (
193
- for /f %%i in (%PID_FILE%) do (
194
- tasklist /fi "PID eq %%i" 2>nul | find "node" >nul 2>&1
195
- if not errorlevel 1 (
196
- timeout /t 60 /nobreak >nul
197
- goto check
198
- )
199
- )
200
- )
201
- :: Start worker
202
- node "${workerJs}"
203
- :: Worker exited, wait and retry
204
- timeout /t 10 /nobreak >nul
205
- goto check
184
+ const batContent = `@echo off
185
+ :: AgentHub Worker — auto-starts on login, checks before spawning
186
+ set AGENTHUB_API_KEY=${config.api_key}
187
+ set AGENTHUB_URL=${config.server}
188
+
189
+ :check
190
+ :: Check if worker is already running
191
+ set PID_FILE=%TEMP%\\agenthub-workers\\worker.pid
192
+ if exist "%PID_FILE%" (
193
+ for /f %%i in (%PID_FILE%) do (
194
+ tasklist /fi "PID eq %%i" 2>nul | find "node" >nul 2>&1
195
+ if not errorlevel 1 (
196
+ timeout /t 60 /nobreak >nul
197
+ goto check
198
+ )
199
+ )
200
+ )
201
+ :: Start worker
202
+ node "${workerJs}"
203
+ :: Worker exited, wait and retry
204
+ timeout /t 10 /nobreak >nul
205
+ goto check
206
206
  `;
207
207
  // Write the .bat OUTSIDE the Startup folder. Windows auto-runs every file in
208
208
  // Startup at logon, so a .bat placed there opens its OWN visible console
@@ -214,9 +214,9 @@ goto check
214
214
  const batPath = path.join(CONFIG_DIR, "agenthub-worker.bat");
215
215
  fs.writeFileSync(batPath, batContent);
216
216
  // VBS to run bat hidden
217
- const vbsContent = `' Start AgentHub Worker hidden (no console window)
218
- Set WshShell = CreateObject("WScript.Shell")
219
- WshShell.Run """${batPath.replace(/\\/g, "\\\\")}""", 0, False
217
+ const vbsContent = `' Start AgentHub Worker hidden (no console window)
218
+ Set WshShell = CreateObject("WScript.Shell")
219
+ WshShell.Run """${batPath.replace(/\\/g, "\\\\")}""", 0, False
220
220
  `;
221
221
  const vbsPath = path.join(startupDir, "AgentHub-Worker.vbs");
222
222
  fs.writeFileSync(vbsPath, vbsContent);
@@ -241,26 +241,26 @@ function installMacStartup() {
241
241
  const plistDir = path.join(os.homedir(), "Library", "LaunchAgents");
242
242
  const thisDir = path.dirname(new URL(import.meta.url).pathname);
243
243
  const workerJs = path.join(thisDir, "worker.js");
244
- const plist = `<?xml version="1.0" encoding="UTF-8"?>
245
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
246
- <plist version="1.0">
247
- <dict>
248
- <key>Label</key>
249
- <string>com.agenthub.worker</string>
250
- <key>ProgramArguments</key>
251
- <array>
252
- <string>node</string>
253
- <string>${workerJs}</string>
254
- </array>
255
- <key>RunAtLoad</key>
256
- <true/>
257
- <key>KeepAlive</key>
258
- <true/>
259
- <key>StandardOutPath</key>
260
- <string>${path.join(CONFIG_DIR, "worker.log")}</string>
261
- <key>StandardErrorPath</key>
262
- <string>${path.join(CONFIG_DIR, "worker.log")}</string>
263
- </dict>
244
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
245
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
246
+ <plist version="1.0">
247
+ <dict>
248
+ <key>Label</key>
249
+ <string>com.agenthub.worker</string>
250
+ <key>ProgramArguments</key>
251
+ <array>
252
+ <string>node</string>
253
+ <string>${workerJs}</string>
254
+ </array>
255
+ <key>RunAtLoad</key>
256
+ <true/>
257
+ <key>KeepAlive</key>
258
+ <true/>
259
+ <key>StandardOutPath</key>
260
+ <string>${path.join(CONFIG_DIR, "worker.log")}</string>
261
+ <key>StandardErrorPath</key>
262
+ <string>${path.join(CONFIG_DIR, "worker.log")}</string>
263
+ </dict>
264
264
  </plist>`;
265
265
  try {
266
266
  fs.mkdirSync(plistDir, { recursive: true });
@@ -273,17 +273,17 @@ function installMacStartup() {
273
273
  function installLinuxStartup() {
274
274
  const thisDir = path.dirname(new URL(import.meta.url).pathname);
275
275
  const workerJs = path.join(thisDir, "worker.js");
276
- const serviceContent = `[Unit]
277
- Description=AgentHub Worker
278
- After=network.target
279
-
280
- [Service]
281
- ExecStart=/usr/bin/node ${workerJs}
282
- Restart=always
283
- RestartSec=10
284
-
285
- [Install]
286
- WantedBy=default.target
276
+ const serviceContent = `[Unit]
277
+ Description=AgentHub Worker
278
+ After=network.target
279
+
280
+ [Service]
281
+ ExecStart=/usr/bin/node ${workerJs}
282
+ Restart=always
283
+ RestartSec=10
284
+
285
+ [Install]
286
+ WantedBy=default.target
287
287
  `;
288
288
  const serviceDir = path.join(os.homedir(), ".config", "systemd", "user");
289
289
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,OAAO,EAAqD,MAAM,cAAc,CAAC;AACrG,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AA+B/D,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,iBAAiB,EAAE,MAAM,MAAM,CAAC;IAChC,iBAAiB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,aAAa,EAAE,MAAM,MAAM,CAAC;IAC5B,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,cAAc,EAAE,MAAM,WAAW,CAAC;CACnC;AAED,UAAU,WAAW;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAwJD,wBAAgB,aAAa,IAAI,IAAI,EAAE,CAuqFtC;AA2BD,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,OAAO,CAAC,CA+gFlB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,OAAO,EAAqD,MAAM,cAAc,CAAC;AACrG,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AA+B/D,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,iBAAiB,EAAE,MAAM,MAAM,CAAC;IAChC,iBAAiB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,aAAa,EAAE,MAAM,MAAM,CAAC;IAC5B,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,cAAc,EAAE,MAAM,WAAW,CAAC;CACnC;AAED,UAAU,WAAW;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAwJD,wBAAgB,aAAa,IAAI,IAAI,EAAE,CAwwFtC;AA2BD,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,OAAO,CAAC,CA0lFlB"}
@@ -130,12 +130,12 @@ function isUrgentMessage(msg) {
130
130
  * Build instruction text for urgent messages
131
131
  */
132
132
  function buildUrgentInstruction(msg) {
133
- return `URGENT: Agent [${msg.from_agent}] needs immediate assistance.
134
-
135
- Query: ${msg.subject}
136
-
137
- Details: ${msg.body}
138
-
133
+ return `URGENT: Agent [${msg.from_agent}] needs immediate assistance.
134
+
135
+ Query: ${msg.subject}
136
+
137
+ Details: ${msg.body}
138
+
139
139
  Spawn a subagent with Task tool to research and respond to this query. Use send_message to reply when done.`;
140
140
  }
141
141
  /**
@@ -143,10 +143,10 @@ Spawn a subagent with Task tool to research and respond to this query. Use send_
143
143
  */
144
144
  function buildTaskInstruction(task) {
145
145
  const assignerName = task.assigned_by_name || task.assigned_by;
146
- return `ACTION REQUIRED: You have a task assigned by ${assignerName}.
147
-
148
- Task: ${task.task}
149
-
146
+ return `ACTION REQUIRED: You have a task assigned by ${assignerName}.
147
+
148
+ Task: ${task.task}
149
+
150
150
  Use accept_task with task_id="${task.id}" to accept and start working, or decline_task to decline with a reason.`;
151
151
  }
152
152
  export function registerTools() {
@@ -738,7 +738,7 @@ export function registerTools() {
738
738
  },
739
739
  {
740
740
  name: "slack_upload_file",
741
- description: "Upload a file you produced (PDF, CSV, docx, deck, log, etc.) to a Slack channel or a person's DM (employees only, governed). Generate the file locally first, then call this with the target and the file's local path. target is a Slack channel id (C0123ABC), a #channel-name (the bot must be a member), or a user id (U0123ABC) to deliver it as a DM. For delivering into your current task's thread, use deliver_file instead.",
741
+ description: "Upload a file you produced (PDF, CSV, docx, deck, log, etc.) to a Slack channel or a person's DM. Employees use a trusted path; other agents (e.g. a Claude Code session) use a governed path — the target must be on the deployment's file-delivery allowlist and the kill switch enabled, else the call is rejected (target_not_allowed / file_delivery_disabled). Generate the file locally first, then call this with the target and the file's local path. target is a Slack channel id (C0123ABC), a #channel-name (the bot must be a member), or a user id (U0123ABC) to deliver it as a DM. For delivering into your current task's thread, use deliver_file instead.",
742
742
  inputSchema: {
743
743
  type: "object",
744
744
  properties: {
@@ -2783,6 +2783,100 @@ export function registerTools() {
2783
2783
  required: ["capability_id"],
2784
2784
  },
2785
2785
  },
2786
+ // add-zoho-mail-connector Phase 5 — per-owner Zoho Mail connector. Every tool
2787
+ // acts only on mailboxes you own (server-enforced); you never see anyone
2788
+ // else's mail. Requires AGENTHUB_ZOHO_CONNECTOR_ENABLED on the server.
2789
+ {
2790
+ name: "zoho_mailbox_register",
2791
+ description: "Register one of YOUR OWN Zoho mailboxes (self-authorized). Provide the mailbox's OAuth Self-Client credentials (client_id/secret + refresh_token with ZohoMail.messages.READ + .CREATE) and account_id. send_mode 'direct' dispatches through the governed send path; 'draft_only' (default) only saves drafts — you send them from Zoho web. Credentials are encrypted server-side and never returned. (requires agent_register first)",
2792
+ inputSchema: {
2793
+ type: "object",
2794
+ properties: {
2795
+ email: { type: "string", description: "The mailbox address, e.g. sumit@contetial.com" },
2796
+ account_id: { type: "string", description: "Zoho Mail account id for this mailbox" },
2797
+ client_id: { type: "string", description: "Self-Client OAuth client id" },
2798
+ client_secret: { type: "string", description: "Self-Client OAuth client secret" },
2799
+ refresh_token: { type: "string", description: "OAuth refresh token (read + create scope)" },
2800
+ dc: { type: "string", description: "Data center: com | in | eu (default com)" },
2801
+ send_mode: { type: "string", enum: ["direct", "draft_only"], description: "direct dispatches; draft_only only drafts (default)" },
2802
+ },
2803
+ required: ["email", "account_id", "client_id", "client_secret", "refresh_token"],
2804
+ },
2805
+ },
2806
+ {
2807
+ name: "zoho_mailbox_list",
2808
+ description: "List the Zoho mailboxes YOU have registered (metadata only, no credentials). (requires agent_register first)",
2809
+ inputSchema: { type: "object", properties: {} },
2810
+ },
2811
+ {
2812
+ name: "zoho_list_messages",
2813
+ description: "List messages in one of your registered mailboxes. (requires agent_register first)",
2814
+ inputSchema: {
2815
+ type: "object",
2816
+ properties: {
2817
+ mailbox: { type: "string", description: "Your mailbox address (must be registered by you)" },
2818
+ folder: { type: "string", description: "Optional Zoho folder id (default inbox)" },
2819
+ limit: { type: "number", description: "Max messages (default 20)" },
2820
+ start: { type: "number", description: "Paging offset" },
2821
+ },
2822
+ required: ["mailbox"],
2823
+ },
2824
+ },
2825
+ {
2826
+ name: "zoho_search",
2827
+ description: "Search messages in one of your registered mailboxes by a Zoho search key. (requires agent_register first)",
2828
+ inputSchema: {
2829
+ type: "object",
2830
+ properties: {
2831
+ mailbox: { type: "string", description: "Your mailbox address" },
2832
+ query: { type: "string", description: "Zoho search key" },
2833
+ limit: { type: "number", description: "Max results (default 20)" },
2834
+ },
2835
+ required: ["mailbox", "query"],
2836
+ },
2837
+ },
2838
+ {
2839
+ name: "zoho_read_message",
2840
+ description: "Read one message (headers + body) from one of your registered mailboxes. (requires agent_register first)",
2841
+ inputSchema: {
2842
+ type: "object",
2843
+ properties: {
2844
+ mailbox: { type: "string", description: "Your mailbox address" },
2845
+ message_id: { type: "string", description: "Zoho message id" },
2846
+ folder: { type: "string", description: "Zoho folder id the message is in" },
2847
+ },
2848
+ required: ["mailbox", "message_id"],
2849
+ },
2850
+ },
2851
+ {
2852
+ name: "zoho_send_email",
2853
+ description: "Send an email from one of your registered mailboxes. Behaviour depends on the mailbox's send_mode: 'direct' dispatches (governed by the recipient allowlist + daily cap); 'draft_only' saves a draft to your Zoho Drafts and does NOT send — you send it yourself from Zoho web. (requires agent_register first)",
2854
+ inputSchema: {
2855
+ type: "object",
2856
+ properties: {
2857
+ mailbox: { type: "string", description: "Your mailbox address" },
2858
+ to: { type: "array", items: { type: "string" }, description: "Recipient email addresses" },
2859
+ subject: { type: "string", description: "Subject line" },
2860
+ body_text: { type: "string", description: "Plain-text body" },
2861
+ body_html: { type: "string", description: "HTML body (overrides body_text format)" },
2862
+ dedupe_key: { type: "string", description: "Optional idempotency key (direct sends)" },
2863
+ },
2864
+ required: ["mailbox", "to", "subject"],
2865
+ },
2866
+ },
2867
+ {
2868
+ name: "zoho_mark_read",
2869
+ description: "Mark a message read (or unread) in one of your registered mailboxes. (requires agent_register first)",
2870
+ inputSchema: {
2871
+ type: "object",
2872
+ properties: {
2873
+ mailbox: { type: "string", description: "Your mailbox address" },
2874
+ message_id: { type: "string", description: "Zoho message id" },
2875
+ read: { type: "boolean", description: "true = mark read (default), false = mark unread" },
2876
+ },
2877
+ required: ["mailbox", "message_id"],
2878
+ },
2879
+ },
2786
2880
  ];
2787
2881
  }
2788
2882
  // requireBrainstormProject resolves the bound project id or throws the standard
@@ -5122,6 +5216,68 @@ export async function handleToolCall(name, args, client, context) {
5122
5216
  url += "?" + params.join("&");
5123
5217
  return client.get(url);
5124
5218
  }
5219
+ // add-zoho-mail-connector Phase 5 — per-owner Zoho Mail connector tools.
5220
+ case "zoho_mailbox_register": {
5221
+ if (!agentId)
5222
+ throw new Error("Not registered. Call agent_register first.");
5223
+ const res = await client.zohoMailboxRegister({
5224
+ email: args.email,
5225
+ account_id: args.account_id,
5226
+ client_id: args.client_id,
5227
+ client_secret: args.client_secret,
5228
+ refresh_token: args.refresh_token,
5229
+ dc: args.dc,
5230
+ send_mode: args.send_mode,
5231
+ });
5232
+ return { content: [{ type: "text", text: `Registered mailbox ${res.email} (send_mode: ${res.send_mode}).` }] };
5233
+ }
5234
+ case "zoho_mailbox_list": {
5235
+ if (!agentId)
5236
+ throw new Error("Not registered. Call agent_register first.");
5237
+ const res = await client.zohoMailboxList();
5238
+ return { content: [{ type: "text", text: JSON.stringify(res, null, 2) }] };
5239
+ }
5240
+ case "zoho_list_messages": {
5241
+ if (!agentId)
5242
+ throw new Error("Not registered. Call agent_register first.");
5243
+ const res = await client.zohoListMessages(args.mailbox, args.folder, args.limit, args.start);
5244
+ return { content: [{ type: "text", text: JSON.stringify(res, null, 2) }] };
5245
+ }
5246
+ case "zoho_search": {
5247
+ if (!agentId)
5248
+ throw new Error("Not registered. Call agent_register first.");
5249
+ const res = await client.zohoSearchMessages(args.mailbox, args.query, args.limit);
5250
+ return { content: [{ type: "text", text: JSON.stringify(res, null, 2) }] };
5251
+ }
5252
+ case "zoho_read_message": {
5253
+ if (!agentId)
5254
+ throw new Error("Not registered. Call agent_register first.");
5255
+ const res = await client.zohoReadMessage(args.mailbox, args.message_id, args.folder);
5256
+ return { content: [{ type: "text", text: JSON.stringify(res, null, 2) }] };
5257
+ }
5258
+ case "zoho_send_email": {
5259
+ if (!agentId)
5260
+ throw new Error("Not registered. Call agent_register first.");
5261
+ const res = await client.zohoSend({
5262
+ mailbox: args.mailbox,
5263
+ to: args.to,
5264
+ subject: args.subject,
5265
+ body_text: args.body_text,
5266
+ body_html: args.body_html,
5267
+ dedupe_key: args.dedupe_key,
5268
+ });
5269
+ const msg = res.dispatched
5270
+ ? `Sent from ${args.mailbox} (message id ${res.message_id}).`
5271
+ : `Draft saved in ${args.mailbox} (id ${res.message_id}). ${res.note ?? "Send it yourself from Zoho web."}`;
5272
+ return { content: [{ type: "text", text: msg }] };
5273
+ }
5274
+ case "zoho_mark_read": {
5275
+ if (!agentId)
5276
+ throw new Error("Not registered. Call agent_register first.");
5277
+ const read = args.read === undefined ? true : args.read;
5278
+ await client.zohoMarkRead(args.mailbox, args.message_id, read);
5279
+ return { content: [{ type: "text", text: `Marked ${args.message_id} ${read ? "read" : "unread"}.` }] };
5280
+ }
5125
5281
  default:
5126
5282
  throw new Error(`Unknown tool: ${name}`);
5127
5283
  }