@swarmclawai/swarmclaw 0.6.7 → 0.7.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 (203) hide show
  1. package/README.md +82 -39
  2. package/next.config.ts +31 -6
  3. package/package.json +3 -2
  4. package/src/app/api/agents/[id]/thread/route.ts +1 -0
  5. package/src/app/api/agents/route.ts +19 -5
  6. package/src/app/api/approvals/route.ts +22 -0
  7. package/src/app/api/chatrooms/[id]/chat/route.ts +4 -0
  8. package/src/app/api/clawhub/install/route.ts +2 -2
  9. package/src/app/api/eval/run/route.ts +37 -0
  10. package/src/app/api/eval/scenarios/route.ts +24 -0
  11. package/src/app/api/eval/suite/route.ts +29 -0
  12. package/src/app/api/mcp-servers/[id]/conformance/route.ts +26 -0
  13. package/src/app/api/mcp-servers/[id]/invoke/route.ts +81 -0
  14. package/src/app/api/memory/graph/route.ts +46 -0
  15. package/src/app/api/memory/route.ts +36 -5
  16. package/src/app/api/notifications/route.ts +3 -0
  17. package/src/app/api/plugins/install/route.ts +57 -5
  18. package/src/app/api/plugins/marketplace/route.ts +73 -22
  19. package/src/app/api/plugins/route.ts +61 -1
  20. package/src/app/api/plugins/ui/route.ts +34 -0
  21. package/src/app/api/sessions/[id]/checkpoints/route.ts +31 -0
  22. package/src/app/api/sessions/[id]/restore/route.ts +36 -0
  23. package/src/app/api/settings/route.ts +62 -0
  24. package/src/app/api/setup/doctor/route.ts +22 -5
  25. package/src/app/api/souls/[id]/route.ts +65 -0
  26. package/src/app/api/souls/route.ts +70 -0
  27. package/src/app/api/tasks/[id]/approve/route.ts +4 -3
  28. package/src/app/api/tasks/[id]/route.ts +16 -3
  29. package/src/app/api/tasks/route.ts +10 -2
  30. package/src/app/api/usage/route.ts +9 -2
  31. package/src/app/globals.css +27 -0
  32. package/src/app/page.tsx +10 -5
  33. package/src/cli/index.js +37 -0
  34. package/src/components/activity/activity-feed.tsx +9 -2
  35. package/src/components/agents/agent-avatar.tsx +5 -1
  36. package/src/components/agents/agent-card.tsx +55 -9
  37. package/src/components/agents/agent-sheet.tsx +112 -34
  38. package/src/components/agents/inspector-panel.tsx +1 -1
  39. package/src/components/agents/soul-library-picker.tsx +84 -13
  40. package/src/components/auth/access-key-gate.tsx +63 -54
  41. package/src/components/auth/user-picker.tsx +37 -32
  42. package/src/components/chat/activity-moment.tsx +2 -0
  43. package/src/components/chat/chat-area.tsx +11 -0
  44. package/src/components/chat/chat-header.tsx +69 -25
  45. package/src/components/chat/chat-tool-toggles.tsx +2 -2
  46. package/src/components/chat/checkpoint-timeline.tsx +112 -0
  47. package/src/components/chat/code-block.tsx +3 -1
  48. package/src/components/chat/exec-approval-card.tsx +8 -1
  49. package/src/components/chat/message-bubble.tsx +164 -4
  50. package/src/components/chat/message-list.tsx +46 -4
  51. package/src/components/chat/session-approval-card.tsx +80 -0
  52. package/src/components/chat/session-debug-panel.tsx +106 -84
  53. package/src/components/chat/streaming-bubble.tsx +6 -5
  54. package/src/components/chat/task-approval-card.tsx +78 -0
  55. package/src/components/chat/thinking-indicator.tsx +48 -12
  56. package/src/components/chat/tool-call-bubble.tsx +3 -0
  57. package/src/components/chat/tool-request-banner.tsx +39 -20
  58. package/src/components/chatrooms/chatroom-list.tsx +11 -4
  59. package/src/components/chatrooms/chatroom-sheet.tsx +7 -2
  60. package/src/components/connectors/connector-list.tsx +33 -11
  61. package/src/components/connectors/connector-sheet.tsx +37 -7
  62. package/src/components/home/home-view.tsx +54 -24
  63. package/src/components/input/chat-input.tsx +22 -1
  64. package/src/components/knowledge/knowledge-list.tsx +17 -18
  65. package/src/components/knowledge/knowledge-sheet.tsx +9 -5
  66. package/src/components/layout/app-layout.tsx +87 -19
  67. package/src/components/mcp-servers/mcp-server-list.tsx +352 -50
  68. package/src/components/mcp-servers/mcp-server-sheet.tsx +25 -9
  69. package/src/components/memory/memory-browser.tsx +73 -45
  70. package/src/components/memory/memory-graph-view.tsx +203 -0
  71. package/src/components/memory/memory-list.tsx +20 -13
  72. package/src/components/plugins/plugin-list.tsx +214 -60
  73. package/src/components/plugins/plugin-sheet.tsx +119 -24
  74. package/src/components/projects/project-list.tsx +17 -9
  75. package/src/components/providers/provider-list.tsx +21 -6
  76. package/src/components/providers/provider-sheet.tsx +42 -25
  77. package/src/components/runs/run-list.tsx +17 -13
  78. package/src/components/schedules/schedule-card.tsx +10 -3
  79. package/src/components/schedules/schedule-list.tsx +2 -2
  80. package/src/components/schedules/schedule-sheet.tsx +28 -9
  81. package/src/components/secrets/secret-sheet.tsx +7 -2
  82. package/src/components/secrets/secrets-list.tsx +18 -5
  83. package/src/components/sessions/new-session-sheet.tsx +183 -376
  84. package/src/components/sessions/session-card.tsx +10 -2
  85. package/src/components/settings/gateway-connection-panel.tsx +9 -8
  86. package/src/components/shared/command-palette.tsx +13 -5
  87. package/src/components/shared/empty-state.tsx +20 -8
  88. package/src/components/shared/hint-tip.tsx +31 -0
  89. package/src/components/shared/notification-center.tsx +134 -86
  90. package/src/components/shared/profile-sheet.tsx +4 -0
  91. package/src/components/shared/settings/plugin-manager.tsx +360 -135
  92. package/src/components/shared/settings/section-capability-policy.tsx +3 -3
  93. package/src/components/shared/settings/section-runtime-loop.tsx +149 -4
  94. package/src/components/skills/clawhub-browser.tsx +1 -0
  95. package/src/components/skills/skill-list.tsx +31 -12
  96. package/src/components/skills/skill-sheet.tsx +20 -7
  97. package/src/components/tasks/approvals-panel.tsx +224 -0
  98. package/src/components/tasks/task-board.tsx +20 -12
  99. package/src/components/tasks/task-card.tsx +21 -7
  100. package/src/components/tasks/task-column.tsx +4 -3
  101. package/src/components/tasks/task-list.tsx +1 -1
  102. package/src/components/tasks/task-sheet.tsx +130 -1
  103. package/src/components/ui/dialog.tsx +1 -0
  104. package/src/components/ui/sheet.tsx +1 -0
  105. package/src/components/usage/metrics-dashboard.tsx +72 -48
  106. package/src/components/wallets/wallet-panel.tsx +65 -41
  107. package/src/components/wallets/wallet-section.tsx +9 -3
  108. package/src/components/webhooks/webhook-list.tsx +21 -12
  109. package/src/components/webhooks/webhook-sheet.tsx +13 -3
  110. package/src/lib/approval-display.test.ts +45 -0
  111. package/src/lib/approval-display.ts +62 -0
  112. package/src/lib/clipboard.ts +38 -0
  113. package/src/lib/memory.ts +8 -0
  114. package/src/lib/providers/claude-cli.ts +5 -3
  115. package/src/lib/providers/index.ts +67 -21
  116. package/src/lib/runtime-loop.ts +3 -2
  117. package/src/lib/server/approvals.ts +150 -0
  118. package/src/lib/server/chat-execution.ts +319 -74
  119. package/src/lib/server/chatroom-helpers.ts +63 -5
  120. package/src/lib/server/chatroom-orchestration.ts +74 -0
  121. package/src/lib/server/clawhub-client.ts +82 -6
  122. package/src/lib/server/connectors/manager.ts +27 -1
  123. package/src/lib/server/context-manager.ts +132 -50
  124. package/src/lib/server/cost.test.ts +73 -0
  125. package/src/lib/server/cost.ts +165 -34
  126. package/src/lib/server/daemon-state.ts +112 -1
  127. package/src/lib/server/data-dir.ts +18 -1
  128. package/src/lib/server/eval/runner.ts +126 -0
  129. package/src/lib/server/eval/scenarios.ts +218 -0
  130. package/src/lib/server/eval/scorer.ts +96 -0
  131. package/src/lib/server/eval/store.ts +37 -0
  132. package/src/lib/server/eval/types.ts +48 -0
  133. package/src/lib/server/execution-log.ts +12 -8
  134. package/src/lib/server/guardian.ts +34 -0
  135. package/src/lib/server/heartbeat-service.ts +53 -1
  136. package/src/lib/server/integrity-monitor.ts +208 -0
  137. package/src/lib/server/langgraph-checkpoint.ts +10 -0
  138. package/src/lib/server/link-understanding.ts +55 -0
  139. package/src/lib/server/llm-response-cache.test.ts +102 -0
  140. package/src/lib/server/llm-response-cache.ts +227 -0
  141. package/src/lib/server/main-agent-loop.ts +115 -16
  142. package/src/lib/server/main-session.ts +6 -3
  143. package/src/lib/server/mcp-conformance.test.ts +18 -0
  144. package/src/lib/server/mcp-conformance.ts +233 -0
  145. package/src/lib/server/memory-db.ts +193 -19
  146. package/src/lib/server/memory-retrieval.test.ts +56 -0
  147. package/src/lib/server/mmr.ts +73 -0
  148. package/src/lib/server/orchestrator-lg.ts +7 -1
  149. package/src/lib/server/orchestrator.ts +4 -3
  150. package/src/lib/server/plugins.ts +662 -132
  151. package/src/lib/server/process-manager.ts +18 -0
  152. package/src/lib/server/query-expansion.ts +57 -0
  153. package/src/lib/server/queue.ts +280 -11
  154. package/src/lib/server/runtime-settings.ts +9 -0
  155. package/src/lib/server/session-run-manager.test.ts +23 -0
  156. package/src/lib/server/session-run-manager.ts +32 -2
  157. package/src/lib/server/session-tools/canvas.ts +85 -50
  158. package/src/lib/server/session-tools/chatroom.ts +130 -127
  159. package/src/lib/server/session-tools/connector.ts +233 -454
  160. package/src/lib/server/session-tools/context-mgmt.ts +87 -105
  161. package/src/lib/server/session-tools/crud.ts +84 -7
  162. package/src/lib/server/session-tools/delegate.ts +351 -752
  163. package/src/lib/server/session-tools/discovery.ts +198 -0
  164. package/src/lib/server/session-tools/edit_file.ts +82 -0
  165. package/src/lib/server/session-tools/file-send.test.ts +39 -0
  166. package/src/lib/server/session-tools/file.ts +257 -425
  167. package/src/lib/server/session-tools/git.ts +87 -47
  168. package/src/lib/server/session-tools/http.ts +95 -33
  169. package/src/lib/server/session-tools/index.ts +217 -138
  170. package/src/lib/server/session-tools/memory.ts +154 -239
  171. package/src/lib/server/session-tools/monitor.ts +126 -0
  172. package/src/lib/server/session-tools/normalize-tool-args.test.ts +61 -0
  173. package/src/lib/server/session-tools/normalize-tool-args.ts +48 -0
  174. package/src/lib/server/session-tools/openclaw-nodes.ts +82 -99
  175. package/src/lib/server/session-tools/openclaw-workspace.ts +103 -93
  176. package/src/lib/server/session-tools/platform.ts +86 -0
  177. package/src/lib/server/session-tools/plugin-creator.ts +239 -0
  178. package/src/lib/server/session-tools/sample-ui.ts +97 -0
  179. package/src/lib/server/session-tools/sandbox.ts +175 -148
  180. package/src/lib/server/session-tools/schedule.ts +78 -0
  181. package/src/lib/server/session-tools/session-info.ts +104 -410
  182. package/src/lib/server/session-tools/shell-normalize.test.ts +43 -0
  183. package/src/lib/server/session-tools/shell.ts +171 -143
  184. package/src/lib/server/session-tools/subagent.ts +77 -77
  185. package/src/lib/server/session-tools/wallet.ts +182 -106
  186. package/src/lib/server/session-tools/web.ts +181 -327
  187. package/src/lib/server/storage.ts +36 -0
  188. package/src/lib/server/stream-agent-chat.ts +348 -242
  189. package/src/lib/server/task-quality-gate.test.ts +44 -0
  190. package/src/lib/server/task-quality-gate.ts +67 -0
  191. package/src/lib/server/task-validation.test.ts +78 -0
  192. package/src/lib/server/task-validation.ts +67 -2
  193. package/src/lib/server/tool-aliases.ts +68 -0
  194. package/src/lib/server/tool-capability-policy.ts +24 -5
  195. package/src/lib/server/tool-retry.ts +62 -0
  196. package/src/lib/server/transcript-repair.ts +72 -0
  197. package/src/lib/setup-defaults.ts +1 -0
  198. package/src/lib/tasks.ts +7 -1
  199. package/src/lib/tool-definitions.ts +24 -23
  200. package/src/lib/validation/schemas.ts +13 -0
  201. package/src/lib/view-routes.ts +2 -23
  202. package/src/stores/use-app-store.ts +23 -1
  203. package/src/types/index.ts +155 -10
@@ -4,6 +4,9 @@ import { execFile } from 'child_process'
4
4
  import { promisify } from 'util'
5
5
  import type { ToolBuildContext } from './context'
6
6
  import { findBinaryOnPath, safePath, truncate, MAX_OUTPUT } from './context'
7
+ import type { Plugin, PluginHooks } from '@/types'
8
+ import { getPluginManager } from '../plugins'
9
+ import { normalizeToolInputArgs } from './normalize-tool-args'
7
10
 
8
11
  const execFileAsync = promisify(execFile)
9
12
 
@@ -13,59 +16,96 @@ const GIT_ACTIONS = [
13
16
  'tag', 'reset', 'show',
14
17
  ] as const
15
18
 
16
- export function buildGitTools(bctx: ToolBuildContext): StructuredToolInterface[] {
17
- if (!bctx.hasTool('git')) return []
18
-
19
+ /**
20
+ * Core Git Execution Logic
21
+ */
22
+ async function executeGitAction(args: Record<string, unknown>, bctx: { cwd: string }) {
23
+ const normalized = normalizeToolInputArgs(args)
24
+ const action = typeof normalized.action === 'string' ? normalized.action : ''
25
+ const cmdArgsRaw = (normalized.args ?? normalized.commandArgs ?? normalized.cmdArgs) as string[] | undefined
26
+ const repoPath = (normalized.repoPath ?? normalized.path) as string | undefined
27
+ const timeoutSec = (normalized.timeoutSec ?? normalized.timeout) as number | undefined
19
28
  const gitPath = findBinaryOnPath('git')
20
- if (!gitPath) return []
29
+ if (!gitPath) return JSON.stringify({ error: 'Git binary not found on system path' })
30
+ if (!action || !GIT_ACTIONS.includes(action as (typeof GIT_ACTIONS)[number])) {
31
+ return JSON.stringify({ error: `Invalid or missing git action. Allowed: ${GIT_ACTIONS.join(', ')}` })
32
+ }
21
33
 
22
- return [
23
- tool(
24
- async ({ action, args, repoPath, timeoutSec }) => {
25
- try {
26
- const cwd = repoPath ? safePath(bctx.cwd, repoPath) : bctx.cwd
27
- const timeout = Math.max(5, Math.min(timeoutSec ?? 60, 300)) * 1000
34
+ try {
35
+ const cwd = repoPath ? safePath(bctx.cwd, repoPath) : bctx.cwd
36
+ const timeout = Math.max(5, Math.min(timeoutSec ?? 60, 300)) * 1000
37
+
38
+ if (action !== 'clone') {
39
+ try {
40
+ await execFileAsync(gitPath, ['rev-parse', '--is-inside-work-tree'], { cwd, timeout: 5000 })
41
+ } catch {
42
+ return JSON.stringify({ error: `Not a git repository: ${cwd}` })
43
+ }
44
+ }
28
45
 
29
- // Verify we're in a git repo (except for clone)
30
- if (action !== 'clone') {
31
- try {
32
- await execFileAsync(gitPath, ['rev-parse', '--is-inside-work-tree'], { cwd, timeout: 5000 })
33
- } catch {
34
- return JSON.stringify({ error: `Not a git repository: ${cwd}` })
35
- }
36
- }
46
+ const cmdArgs = [action, ...(cmdArgsRaw ?? [])]
47
+ const result = await execFileAsync(gitPath, cmdArgs, {
48
+ cwd,
49
+ timeout,
50
+ maxBuffer: MAX_OUTPUT,
51
+ env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
52
+ })
53
+ return JSON.stringify({
54
+ exitCode: 0,
55
+ stdout: truncate(result.stdout ?? '', MAX_OUTPUT),
56
+ stderr: truncate(result.stderr ?? '', MAX_OUTPUT),
57
+ })
58
+ } catch (err: unknown) {
59
+ const execErr = err as { code?: number; stdout?: string; stderr?: string; message?: string }
60
+ return JSON.stringify({
61
+ exitCode: execErr.code ?? 1,
62
+ stdout: truncate(execErr.stdout ?? '', MAX_OUTPUT),
63
+ stderr: truncate(execErr.stderr ?? execErr.message ?? String(err), MAX_OUTPUT),
64
+ })
65
+ }
66
+ }
37
67
 
38
- const cmdArgs = [action, ...(args ?? [])]
39
- const result = await execFileAsync(gitPath, cmdArgs, {
40
- cwd,
41
- timeout,
42
- maxBuffer: MAX_OUTPUT,
43
- env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
44
- })
45
- return JSON.stringify({
46
- exitCode: 0,
47
- stdout: truncate(result.stdout ?? '', MAX_OUTPUT),
48
- stderr: truncate(result.stderr ?? '', MAX_OUTPUT),
49
- })
50
- } catch (err: unknown) {
51
- const execErr = err as { code?: number; stdout?: string; stderr?: string; message?: string }
52
- return JSON.stringify({
53
- exitCode: execErr.code ?? 1,
54
- stdout: truncate(execErr.stdout ?? '', MAX_OUTPUT),
55
- stderr: truncate(execErr.stderr ?? execErr.message ?? String(err), MAX_OUTPUT),
56
- })
57
- }
68
+ /**
69
+ * Register as a Built-in Plugin
70
+ */
71
+ const GitPlugin: Plugin = {
72
+ name: 'Core Git',
73
+ description: 'Structured git operations: status, commit, push, diff, and more.',
74
+ hooks: {} as PluginHooks,
75
+ tools: [
76
+ {
77
+ name: 'git',
78
+ description: 'Run git operations in the workspace.',
79
+ parameters: {
80
+ type: 'object',
81
+ properties: {
82
+ action: { type: 'string', enum: [...GIT_ACTIONS] },
83
+ args: { type: 'array', items: { type: 'string' } },
84
+ repoPath: { type: 'string' },
85
+ timeoutSec: { type: 'number' }
86
+ },
87
+ required: ['action']
58
88
  },
89
+ execute: async (args, context) => executeGitAction(args, { cwd: context.session.cwd || process.cwd() })
90
+ }
91
+ ]
92
+ }
93
+
94
+ getPluginManager().registerBuiltin('git', GitPlugin)
95
+
96
+ /**
97
+ * Legacy Bridge
98
+ */
99
+ export function buildGitTools(bctx: ToolBuildContext): StructuredToolInterface[] {
100
+ if (!bctx.hasTool('git')) return []
101
+ return [
102
+ tool(
103
+ async (args) => executeGitAction(args, { cwd: bctx.cwd }),
59
104
  {
60
105
  name: 'git',
61
- description: 'Run git operations. Verify the repo exists before committing or pushing. Use args for subcommand flags (e.g. args: ["-m", "message"] for commit).',
62
- schema: z.object({
63
- action: z.enum(GIT_ACTIONS).describe('Git subcommand to run'),
64
- args: z.array(z.string()).optional().describe('Additional arguments (e.g. ["-m", "fix: typo"], ["--oneline", "-n", "5"])'),
65
- repoPath: z.string().optional().describe('Relative path to git repo (defaults to working directory)'),
66
- timeoutSec: z.number().optional().describe('Timeout in seconds (default 60, max 300)'),
67
- }),
68
- },
69
- ),
106
+ description: GitPlugin.tools![0].description,
107
+ schema: z.object({}).passthrough()
108
+ }
109
+ )
70
110
  ]
71
111
  }
@@ -2,47 +2,109 @@ import { z } from 'zod'
2
2
  import { tool, type StructuredToolInterface } from '@langchain/core/tools'
3
3
  import type { ToolBuildContext } from './context'
4
4
  import { truncate, MAX_OUTPUT } from './context'
5
+ import { withRetry } from '../tool-retry'
6
+ import type { Plugin, PluginHooks } from '@/types'
7
+ import { getPluginManager } from '../plugins'
8
+ import { normalizeToolInputArgs } from './normalize-tool-args'
5
9
 
10
+ interface HttpRequestArgs {
11
+ method: string
12
+ url: string
13
+ headers?: Record<string, string>
14
+ body?: string
15
+ timeoutSec?: number
16
+ followRedirects?: boolean
17
+ }
18
+
19
+ /**
20
+ * Core HTTP Execution Logic
21
+ */
22
+ async function executeHttpAction(args: HttpRequestArgs) {
23
+ const normalized = normalizeToolInputArgs((args ?? {}) as unknown as Record<string, unknown>)
24
+ const requestArgs: HttpRequestArgs = {
25
+ method: String(normalized.method || '').toUpperCase(),
26
+ url: String(normalized.url || ''),
27
+ headers: normalized.headers as Record<string, string> | undefined,
28
+ body: typeof normalized.body === 'string' ? normalized.body : undefined,
29
+ timeoutSec: typeof normalized.timeoutSec === 'number' ? normalized.timeoutSec : undefined,
30
+ followRedirects: typeof normalized.followRedirects === 'boolean' ? normalized.followRedirects : undefined,
31
+ }
32
+ return withRetry(async (_a: HttpRequestArgs) => {
33
+ try {
34
+ const timeout = Math.max(1, Math.min(_a.timeoutSec ?? 30, 120)) * 1000
35
+ const init: RequestInit = {
36
+ method: _a.method,
37
+ headers: (_a.headers ?? undefined) as Record<string, string> | undefined,
38
+ signal: AbortSignal.timeout(timeout),
39
+ }
40
+ if (_a.body && _a.method !== 'GET' && _a.method !== 'HEAD') {
41
+ init.body = _a.body
42
+ }
43
+ if (_a.followRedirects === false) {
44
+ init.redirect = 'manual'
45
+ }
46
+ const res = await fetch(_a.url, init)
47
+ const resHeaders: Record<string, string> = {}
48
+ for (const key of ['content-type', 'location', 'x-request-id', 'retry-after', 'content-length']) {
49
+ const val = res.headers.get(key)
50
+ if (val) resHeaders[key] = val
51
+ }
52
+ let resBody: string
53
+ const ct = res.headers.get('content-type') ?? ''
54
+ if (ct.includes('image/') || ct.includes('audio/') || ct.includes('video/') || ct.includes('application/octet-stream')) {
55
+ resBody = `[binary content, ${res.headers.get('content-length') ?? 'unknown'} bytes]`
56
+ } else {
57
+ resBody = truncate(await res.text(), MAX_OUTPUT)
58
+ }
59
+ return JSON.stringify({ status: res.status, statusText: res.statusText, headers: resHeaders, body: resBody })
60
+ } catch (err: unknown) {
61
+ return JSON.stringify({ error: err instanceof Error ? err.message : String(err) })
62
+ }
63
+ }, requestArgs)
64
+ }
65
+
66
+ /**
67
+ * Register as a Built-in Plugin
68
+ */
69
+ const HttpPlugin: Plugin = {
70
+ name: 'Core HTTP',
71
+ description: 'Make direct HTTP API calls with custom methods, headers, and bodies.',
72
+ hooks: {} as PluginHooks,
73
+ tools: [
74
+ {
75
+ name: 'http_request',
76
+ description: 'Make an HTTP API request.',
77
+ parameters: {
78
+ type: 'object',
79
+ properties: {
80
+ method: { type: 'string', enum: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'] },
81
+ url: { type: 'string' },
82
+ headers: { type: 'object', additionalProperties: { type: 'string' } },
83
+ body: { type: 'string' },
84
+ timeoutSec: { type: 'number' },
85
+ followRedirects: { type: 'boolean' }
86
+ },
87
+ required: ['method', 'url']
88
+ },
89
+ execute: async (args) => executeHttpAction(args as unknown as HttpRequestArgs)
90
+ }
91
+ ]
92
+ }
93
+
94
+ getPluginManager().registerBuiltin('http', HttpPlugin)
95
+
96
+ /**
97
+ * Legacy Bridge
98
+ */
6
99
  export function buildHttpTools(bctx: ToolBuildContext): StructuredToolInterface[] {
7
100
  if (!bctx.hasTool('http_request')) return []
8
101
 
9
102
  return [
10
103
  tool(
11
- async ({ method, url, headers, body, timeoutSec, followRedirects }) => {
12
- try {
13
- const timeout = Math.max(1, Math.min(timeoutSec ?? 30, 120)) * 1000
14
- const init: RequestInit = {
15
- method,
16
- headers: (headers ?? undefined) as Record<string, string> | undefined,
17
- signal: AbortSignal.timeout(timeout),
18
- }
19
- if (body && method !== 'GET' && method !== 'HEAD') {
20
- init.body = body
21
- }
22
- if (followRedirects === false) {
23
- init.redirect = 'manual'
24
- }
25
- const res = await fetch(url, init)
26
- const resHeaders: Record<string, string> = {}
27
- for (const key of ['content-type', 'location', 'x-request-id', 'retry-after', 'content-length']) {
28
- const val = res.headers.get(key)
29
- if (val) resHeaders[key] = val
30
- }
31
- let resBody: string
32
- const ct = res.headers.get('content-type') ?? ''
33
- if (ct.includes('image/') || ct.includes('audio/') || ct.includes('video/') || ct.includes('application/octet-stream')) {
34
- resBody = `[binary content, ${res.headers.get('content-length') ?? 'unknown'} bytes]`
35
- } else {
36
- resBody = truncate(await res.text(), MAX_OUTPUT)
37
- }
38
- return JSON.stringify({ status: res.status, statusText: res.statusText, headers: resHeaders, body: resBody })
39
- } catch (err: unknown) {
40
- return JSON.stringify({ error: err instanceof Error ? err.message : String(err) })
41
- }
42
- },
104
+ (args: HttpRequestArgs) => executeHttpAction(args),
43
105
  {
44
106
  name: 'http_request',
45
- description: 'Make an HTTP API request. Supports all methods, custom headers, and request bodies. Returns status, headers, and body.',
107
+ description: HttpPlugin.tools![0].description,
46
108
  schema: z.object({
47
109
  method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']).describe('HTTP method'),
48
110
  url: z.string().describe('Full URL to request'),