@swarmclawai/swarmclaw 1.2.6 → 1.2.9

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 (269) hide show
  1. package/README.md +54 -23
  2. package/next.config.ts +1 -0
  3. package/package.json +4 -3
  4. package/scripts/easy-setup.mjs +1 -1
  5. package/scripts/postinstall.mjs +1 -1
  6. package/skills/swarmclaw.md +115 -0
  7. package/skills/tools/browser.md +131 -0
  8. package/skills/tools/execute.md +98 -0
  9. package/skills/tools/files.md +98 -0
  10. package/skills/tools/memory.md +104 -0
  11. package/skills/tools/platform.md +144 -0
  12. package/skills/tools/skills.md +83 -0
  13. package/src/app/agents/[id]/page.tsx +1 -18
  14. package/src/app/api/agents/thread-route.test.ts +0 -1
  15. package/src/app/api/approvals/route.test.ts +6 -22
  16. package/src/app/api/chats/[id]/messages/route.ts +23 -19
  17. package/src/app/api/chats/messages-route.test.ts +105 -51
  18. package/src/app/api/connectors/route.ts +2 -2
  19. package/src/app/api/mcp-servers/[id]/test/route.ts +3 -2
  20. package/src/app/api/openclaw/deploy/route.ts +2 -0
  21. package/src/app/api/portability/export/route.ts +8 -0
  22. package/src/app/api/portability/import/route.test.ts +80 -0
  23. package/src/app/api/portability/import/route.ts +28 -0
  24. package/src/app/api/settings/route.ts +0 -2
  25. package/src/app/api/setup/doctor/route.ts +4 -4
  26. package/src/app/api/wallets/[id]/route.ts +15 -157
  27. package/src/app/api/wallets/generate/route.ts +22 -0
  28. package/src/app/api/wallets/route.test.ts +147 -0
  29. package/src/app/api/wallets/route.ts +13 -95
  30. package/src/app/autonomy/page.tsx +2 -57
  31. package/src/app/protocols/page.tsx +2 -21
  32. package/src/app/settings/page.tsx +0 -9
  33. package/src/app/wallets/page.tsx +105 -5
  34. package/src/cli/index.js +21 -33
  35. package/src/cli/spec.js +19 -30
  36. package/src/components/agents/agent-chat-list.tsx +23 -1
  37. package/src/components/agents/agent-sheet.tsx +2 -40
  38. package/src/components/agents/inspector-panel.tsx +165 -131
  39. package/src/components/chat/chat-area.tsx +38 -9
  40. package/src/components/chat/chat-card.tsx +0 -31
  41. package/src/components/chat/message-bubble.tsx +1 -108
  42. package/src/components/chat/message-list.tsx +33 -19
  43. package/src/components/connectors/connector-sheet.tsx +25 -1
  44. package/src/components/gateways/gateway-sheet.tsx +5 -2
  45. package/src/components/layout/sidebar-rail.tsx +6 -10
  46. package/src/components/projects/project-detail.tsx +3 -35
  47. package/src/components/projects/tabs/overview-tab.tsx +3 -59
  48. package/src/components/projects/tabs/work-tab.tsx +7 -77
  49. package/src/components/protocols/structured-session-launcher.tsx +1 -22
  50. package/src/components/shared/connector-platform-icon.tsx +1 -0
  51. package/src/components/tasks/task-card.tsx +4 -34
  52. package/src/components/tasks/task-sheet.tsx +6 -36
  53. package/src/components/wallets/wallet-list.tsx +150 -0
  54. package/src/lib/agent-execute-defaults.test.ts +24 -0
  55. package/src/lib/agent-execute-defaults.ts +62 -0
  56. package/src/lib/app/navigation.test.ts +0 -13
  57. package/src/lib/app/navigation.ts +2 -7
  58. package/src/lib/app/view-constants.ts +14 -19
  59. package/src/lib/chat/queued-message-queue.test.ts +134 -1
  60. package/src/lib/chat/queued-message-queue.ts +77 -2
  61. package/src/lib/server/agents/agent-service.ts +5 -0
  62. package/src/lib/server/agents/agent-thread-session.ts +0 -1
  63. package/src/lib/server/agents/delegation-advisory.test.ts +0 -1
  64. package/src/lib/server/agents/delegation-jobs.test.ts +0 -69
  65. package/src/lib/server/agents/delegation-jobs.ts +0 -25
  66. package/src/lib/server/agents/main-agent-loop.ts +1 -49
  67. package/src/lib/server/agents/subagent-runtime.ts +0 -1
  68. package/src/lib/server/approval-match.ts +0 -85
  69. package/src/lib/server/approvals.test.ts +6 -6
  70. package/src/lib/server/approvals.ts +0 -6
  71. package/src/lib/server/autonomy/supervisor-reflection.test.ts +0 -1
  72. package/src/lib/server/builtin-extensions.ts +1 -2
  73. package/src/lib/server/capability-router.test.ts +0 -2
  74. package/src/lib/server/chat-execution/chat-execution-advanced.test.ts +1 -1
  75. package/src/lib/server/chat-execution/chat-execution-tool-events.test.ts +15 -14
  76. package/src/lib/server/chat-execution/chat-execution-types.ts +0 -2
  77. package/src/lib/server/chat-execution/chat-execution-utils.ts +2 -4
  78. package/src/lib/server/chat-execution/chat-streaming-utils.ts +2 -30
  79. package/src/lib/server/chat-execution/chat-turn-finalization.ts +1 -36
  80. package/src/lib/server/chat-execution/chat-turn-preparation.ts +81 -64
  81. package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +4 -0
  82. package/src/lib/server/chat-execution/continuation-evaluator.ts +8 -0
  83. package/src/lib/server/chat-execution/iteration-event-handler.ts +0 -24
  84. package/src/lib/server/chat-execution/memory-mutation-tools.ts +1 -1
  85. package/src/lib/server/chat-execution/message-classifier.test.ts +0 -45
  86. package/src/lib/server/chat-execution/message-classifier.ts +11 -16
  87. package/src/lib/server/chat-execution/prompt-builder.test.ts +27 -0
  88. package/src/lib/server/chat-execution/prompt-builder.ts +14 -31
  89. package/src/lib/server/chat-execution/prompt-mode.test.ts +24 -0
  90. package/src/lib/server/chat-execution/prompt-mode.ts +5 -1
  91. package/src/lib/server/chat-execution/prompt-sections.ts +0 -1
  92. package/src/lib/server/chat-execution/situational-awareness.test.ts +2 -73
  93. package/src/lib/server/chat-execution/situational-awareness.ts +4 -38
  94. package/src/lib/server/chat-execution/stream-agent-chat.test.ts +13 -126
  95. package/src/lib/server/chat-execution/stream-agent-chat.ts +46 -21
  96. package/src/lib/server/chat-execution/stream-continuation.test.ts +4 -52
  97. package/src/lib/server/chat-execution/stream-continuation.ts +6 -48
  98. package/src/lib/server/chatrooms/chatroom-routing.test.ts +4 -0
  99. package/src/lib/server/chatrooms/session-mailbox.ts +0 -10
  100. package/src/lib/server/chats/chat-session-service.ts +3 -5
  101. package/src/lib/server/connectors/connector-inbound.ts +0 -1
  102. package/src/lib/server/connectors/connector-lifecycle.ts +19 -3
  103. package/src/lib/server/connectors/connector-service.ts +39 -9
  104. package/src/lib/server/connectors/discord.ts +2 -2
  105. package/src/lib/server/connectors/matrix.ts +3 -2
  106. package/src/lib/server/connectors/signal.ts +5 -4
  107. package/src/lib/server/connectors/slack.ts +10 -9
  108. package/src/lib/server/connectors/swarmdock-bidding.ts +74 -0
  109. package/src/lib/server/connectors/swarmdock-payloads.test.ts +85 -0
  110. package/src/lib/server/connectors/swarmdock-secret.test.ts +128 -0
  111. package/src/lib/server/connectors/swarmdock-secret.ts +152 -0
  112. package/src/lib/server/connectors/swarmdock-tasks.ts +119 -0
  113. package/src/lib/server/connectors/swarmdock.ts +255 -0
  114. package/src/lib/server/connectors/teams.ts +3 -2
  115. package/src/lib/server/connectors/telegram.ts +4 -4
  116. package/src/lib/server/connectors/whatsapp.ts +2 -2
  117. package/src/lib/server/daemon/controller.ts +7 -0
  118. package/src/lib/server/execution-brief.test.ts +2 -25
  119. package/src/lib/server/execution-brief.ts +12 -35
  120. package/src/lib/server/execution-engine/task-attempt.ts +0 -1
  121. package/src/lib/server/gateways/gateway-profile-service.ts +19 -1
  122. package/src/lib/server/messages/message-repository.test.ts +70 -0
  123. package/src/lib/server/messages/message-repository.ts +11 -6
  124. package/src/lib/server/openclaw/deploy.ts +32 -2
  125. package/src/lib/server/persistence/storage-context.ts +0 -5
  126. package/src/lib/server/plugins-advanced.test.ts +1 -2
  127. package/src/lib/server/portability/export.ts +109 -0
  128. package/src/lib/server/portability/import.ts +159 -0
  129. package/src/lib/server/protocols/protocol-normalization.ts +0 -4
  130. package/src/lib/server/protocols/protocol-queries.ts +0 -6
  131. package/src/lib/server/protocols/protocol-run-lifecycle.ts +4 -32
  132. package/src/lib/server/protocols/protocol-service.ts +0 -1
  133. package/src/lib/server/protocols/protocol-step-helpers.ts +0 -4
  134. package/src/lib/server/protocols/protocol-step-processors.ts +0 -6
  135. package/src/lib/server/protocols/protocol-swarm.ts +0 -2
  136. package/src/lib/server/protocols/protocol-types.ts +0 -2
  137. package/src/lib/server/provider-health.ts +1 -10
  138. package/src/lib/server/runtime/daemon-state/core.ts +0 -9
  139. package/src/lib/server/runtime/daemon-state.test.ts +0 -35
  140. package/src/lib/server/runtime/heartbeat-service.ts +3 -23
  141. package/src/lib/server/runtime/process-manager.ts +13 -9
  142. package/src/lib/server/runtime/queue/core.ts +11 -33
  143. package/src/lib/server/runtime/runtime-storage-write-paths.test.ts +6 -6
  144. package/src/lib/server/runtime/scheduler.ts +0 -13
  145. package/src/lib/server/runtime/session-run-manager/drain.ts +0 -24
  146. package/src/lib/server/runtime/session-run-manager/enqueue.ts +0 -1
  147. package/src/lib/server/runtime/session-run-manager/queries.ts +15 -1
  148. package/src/lib/server/runtime/session-run-manager/recovery.ts +0 -1
  149. package/src/lib/server/runtime/session-run-manager.test.ts +58 -28
  150. package/src/lib/server/sandbox/session-runtime.test.ts +18 -1
  151. package/src/lib/server/sandbox/session-runtime.ts +40 -28
  152. package/src/lib/server/session-tools/autonomy-tools.test.ts +7 -9
  153. package/src/lib/server/session-tools/context.ts +1 -1
  154. package/src/lib/server/session-tools/credential-env.ts +109 -0
  155. package/src/lib/server/session-tools/crud.ts +3 -17
  156. package/src/lib/server/session-tools/delegate.ts +0 -4
  157. package/src/lib/server/session-tools/edit_file.ts +3 -2
  158. package/src/lib/server/session-tools/execute.test.ts +58 -0
  159. package/src/lib/server/session-tools/execute.ts +334 -0
  160. package/src/lib/server/session-tools/files-tool.ts +635 -0
  161. package/src/lib/server/session-tools/index.ts +14 -8
  162. package/src/lib/server/session-tools/memory-tool.ts +242 -0
  163. package/src/lib/server/session-tools/memory.ts +1 -1
  164. package/src/lib/server/session-tools/openclaw-nodes.ts +3 -2
  165. package/src/lib/server/session-tools/openclaw-workspace.ts +3 -2
  166. package/src/lib/server/session-tools/platform-tool.ts +617 -0
  167. package/src/lib/server/session-tools/session-info.ts +3 -2
  168. package/src/lib/server/session-tools/session-tools-wiring.test.ts +3 -4
  169. package/src/lib/server/session-tools/shell.ts +7 -122
  170. package/src/lib/server/session-tools/skills-tool.ts +396 -0
  171. package/src/lib/server/session-tools/team-context.ts +0 -3
  172. package/src/lib/server/session-tools/web.ts +2 -2
  173. package/src/lib/server/storage-normalization.ts +10 -0
  174. package/src/lib/server/storage.ts +18 -45
  175. package/src/lib/server/tasks/task-checkout.ts +59 -0
  176. package/src/lib/server/tasks/task-lifecycle.ts +2 -0
  177. package/src/lib/server/tasks/task-route-service.ts +4 -26
  178. package/src/lib/server/tasks/task-service.ts +0 -7
  179. package/src/lib/server/tool-aliases.ts +2 -2
  180. package/src/lib/server/tool-capability-policy-advanced.test.ts +13 -6
  181. package/src/lib/server/tool-capability-policy.test.ts +2 -1
  182. package/src/lib/server/tool-capability-policy.ts +60 -35
  183. package/src/lib/server/tool-planning.ts +11 -12
  184. package/src/lib/server/universal-tool-access.ts +0 -1
  185. package/src/lib/server/wallets/wallet-crypto.ts +33 -0
  186. package/src/lib/server/wallets/wallet-repository.ts +24 -0
  187. package/src/lib/server/wallets/wallet-service.ts +119 -0
  188. package/src/lib/server/working-state/extraction.ts +8 -42
  189. package/src/lib/server/working-state/normalization.ts +10 -103
  190. package/src/lib/server/working-state/service.ts +12 -21
  191. package/src/lib/setup-defaults.ts +5 -0
  192. package/src/lib/strip-internal-metadata.test.ts +1 -1
  193. package/src/lib/strip-internal-metadata.ts +1 -1
  194. package/src/lib/tool-definitions.ts +1 -1
  195. package/src/lib/validation/schemas.test.ts +16 -0
  196. package/src/lib/validation/schemas.ts +49 -2
  197. package/src/stores/slices/data-slice.ts +5 -1
  198. package/src/stores/slices/ui-slice.ts +0 -4
  199. package/src/stores/use-chat-store.test.ts +231 -0
  200. package/src/stores/use-chat-store.ts +62 -13
  201. package/src/types/agent.ts +264 -0
  202. package/src/types/app-settings.ts +173 -0
  203. package/src/types/approval.ts +25 -0
  204. package/src/types/connector.ts +188 -0
  205. package/src/types/extension.ts +386 -0
  206. package/src/types/index.ts +16 -3555
  207. package/src/types/message.ts +56 -0
  208. package/src/types/misc.ts +737 -0
  209. package/src/types/protocol.ts +420 -0
  210. package/src/types/provider.ts +52 -0
  211. package/src/types/run.ts +180 -0
  212. package/src/types/schedule.ts +59 -0
  213. package/src/types/session.ts +215 -0
  214. package/src/types/skill.ts +157 -0
  215. package/src/types/swarmdock.ts +29 -0
  216. package/src/types/task.ts +144 -0
  217. package/src/types/working-state.ts +204 -0
  218. package/src/views/settings/section-heartbeat.tsx +2 -2
  219. package/src/views/settings/section-runtime-loop.tsx +0 -14
  220. package/src/app/api/canvas/[sessionId]/route.ts +0 -35
  221. package/src/app/api/missions/[id]/actions/route.ts +0 -31
  222. package/src/app/api/missions/[id]/events/route.ts +0 -14
  223. package/src/app/api/missions/[id]/route.ts +0 -10
  224. package/src/app/api/missions/route.test.ts +0 -244
  225. package/src/app/api/missions/route.ts +0 -57
  226. package/src/app/api/wallets/[id]/approve/route.ts +0 -79
  227. package/src/app/api/wallets/[id]/balance-history/route.ts +0 -18
  228. package/src/app/api/wallets/[id]/send/route.ts +0 -113
  229. package/src/app/api/wallets/[id]/transactions/route.ts +0 -18
  230. package/src/app/missions/[id]/page.tsx +0 -3
  231. package/src/app/missions/page.tsx +0 -685
  232. package/src/components/canvas/canvas-panel.tsx +0 -267
  233. package/src/components/wallets/wallet-approval-dialog.tsx +0 -107
  234. package/src/components/wallets/wallet-panel.tsx +0 -1010
  235. package/src/components/wallets/wallet-section.tsx +0 -260
  236. package/src/features/missions/queries.ts +0 -23
  237. package/src/lib/canvas-content.test.ts +0 -360
  238. package/src/lib/canvas-content.ts +0 -198
  239. package/src/lib/server/canvas-content.test.ts +0 -32
  240. package/src/lib/server/canvas-content.ts +0 -6
  241. package/src/lib/server/ethereum.ts +0 -591
  242. package/src/lib/server/evm-swap.ts +0 -476
  243. package/src/lib/server/missions/mission-intent.test.ts +0 -63
  244. package/src/lib/server/missions/mission-intent.ts +0 -569
  245. package/src/lib/server/missions/mission-repository.ts +0 -74
  246. package/src/lib/server/missions/mission-service/actions.ts +0 -6
  247. package/src/lib/server/missions/mission-service/bindings.ts +0 -9
  248. package/src/lib/server/missions/mission-service/context.ts +0 -4
  249. package/src/lib/server/missions/mission-service/core.ts +0 -2271
  250. package/src/lib/server/missions/mission-service/queries.ts +0 -12
  251. package/src/lib/server/missions/mission-service/recovery.ts +0 -5
  252. package/src/lib/server/missions/mission-service/ticks.ts +0 -9
  253. package/src/lib/server/missions/mission-service.test.ts +0 -888
  254. package/src/lib/server/missions/mission-service.ts +0 -6
  255. package/src/lib/server/session-tools/canvas.ts +0 -105
  256. package/src/lib/server/session-tools/sandbox.ts +0 -281
  257. package/src/lib/server/session-tools/wallet-tool.test.ts +0 -150
  258. package/src/lib/server/session-tools/wallet.ts +0 -1287
  259. package/src/lib/server/solana.ts +0 -327
  260. package/src/lib/server/wallet/wallet-execution.test.ts +0 -198
  261. package/src/lib/server/wallet/wallet-portfolio.test.ts +0 -98
  262. package/src/lib/server/wallet/wallet-portfolio.ts +0 -772
  263. package/src/lib/server/wallet/wallet-service.test.ts +0 -81
  264. package/src/lib/server/wallet/wallet-service.ts +0 -225
  265. package/src/lib/wallet/wallet-transactions.test.ts +0 -75
  266. package/src/lib/wallet/wallet-transactions.ts +0 -43
  267. package/src/lib/wallet/wallet.test.ts +0 -333
  268. package/src/lib/wallet/wallet.ts +0 -183
  269. package/src/views/settings/section-wallets.tsx +0 -35
@@ -0,0 +1,617 @@
1
+ /**
2
+ * platform-tool — Consolidated platform tool (v2).
3
+ *
4
+ * Single `platform` tool that dispatches dotted actions to existing service
5
+ * functions. Replaces the need to expose separate tools for tasks, chatrooms,
6
+ * connectors, subagents, human-loop, and platform CRUD.
7
+ *
8
+ * Action namespace:
9
+ * tasks.* — Task CRUD and lifecycle
10
+ * communicate.* — Human-loop, connector messaging, delegation, subagent spawn
11
+ * projects.* — Project listing and retrieval
12
+ * chatrooms.* — Chatroom messaging and listing
13
+ * agents.* — Agent listing and retrieval
14
+ */
15
+
16
+ import { z } from 'zod'
17
+ import { tool, type StructuredToolInterface } from '@langchain/core/tools'
18
+ import type { Extension, ExtensionHooks, BoardTask, BoardTaskStatus, Project, Chatroom } from '@/types'
19
+ import type { ToolBuildContext } from './context'
20
+ import { truncate, MAX_OUTPUT } from './context'
21
+ import { registerNativeCapability } from '../native-capabilities'
22
+ import { normalizeToolInputArgs } from './normalize-tool-args'
23
+ import { errorMessage } from '@/lib/shared-utils'
24
+ import { genId } from '@/lib/id'
25
+ import {
26
+ loadAgents,
27
+ loadTasks,
28
+ loadTask,
29
+ upsertTask,
30
+ loadProjects,
31
+ loadChatrooms,
32
+ loadChatroom,
33
+ saveChatrooms,
34
+ } from '../storage'
35
+ import { notify } from '../ws-hub'
36
+ import { logExecution } from '../execution-log'
37
+ import { logActivity } from '../storage'
38
+
39
+ // ---------------------------------------------------------------------------
40
+ // Types
41
+ // ---------------------------------------------------------------------------
42
+
43
+ interface PlatformActionContext {
44
+ agentId?: string | null
45
+ sessionId?: string | null
46
+ cwd: string
47
+ delegationEnabled?: boolean
48
+ delegationTargetMode?: 'all' | 'selected'
49
+ delegationTargetAgentIds?: string[]
50
+ bctx?: ToolBuildContext
51
+ }
52
+
53
+ type ActionHandler = (
54
+ params: Record<string, unknown>,
55
+ ctx: PlatformActionContext,
56
+ ) => Promise<string> | string
57
+
58
+ // ---------------------------------------------------------------------------
59
+ // Helpers
60
+ // ---------------------------------------------------------------------------
61
+
62
+ function requireString(params: Record<string, unknown>, key: string): string {
63
+ const val = typeof params[key] === 'string' ? (params[key] as string).trim() : ''
64
+ if (!val) throw new Error(`${key} is required.`)
65
+ return val
66
+ }
67
+
68
+ function optionalString(params: Record<string, unknown>, key: string): string | undefined {
69
+ const val = params[key]
70
+ return typeof val === 'string' && val.trim() ? val.trim() : undefined
71
+ }
72
+
73
+ const VALID_TASK_STATUSES: BoardTaskStatus[] = [
74
+ 'backlog', 'queued', 'running', 'completed', 'failed', 'cancelled', 'archived', 'deferred',
75
+ ]
76
+
77
+ function isValidTaskStatus(value: unknown): value is BoardTaskStatus {
78
+ return typeof value === 'string' && VALID_TASK_STATUSES.includes(value as BoardTaskStatus)
79
+ }
80
+
81
+ // ---------------------------------------------------------------------------
82
+ // tasks.* handlers
83
+ // ---------------------------------------------------------------------------
84
+
85
+ function handleTasksCreate(params: Record<string, unknown>, ctx: PlatformActionContext): string {
86
+ const title = requireString(params, 'title')
87
+ const description = optionalString(params, 'description') || ''
88
+ const assignee = optionalString(params, 'assignee') || optionalString(params, 'agentId') || ctx.agentId || ''
89
+ const status: BoardTaskStatus = isValidTaskStatus(params.status) ? params.status : 'backlog'
90
+ const projectId = optionalString(params, 'projectId')
91
+
92
+ const now = Date.now()
93
+ const task: BoardTask = {
94
+ id: genId(),
95
+ title,
96
+ description,
97
+ status,
98
+ agentId: assignee,
99
+ projectId,
100
+ createdByAgentId: ctx.agentId || null,
101
+ createdInSessionId: ctx.sessionId || null,
102
+ createdAt: now,
103
+ updatedAt: now,
104
+ }
105
+
106
+ upsertTask(task.id, task)
107
+ notify('tasks')
108
+
109
+ logActivity({
110
+ entityType: 'task',
111
+ entityId: task.id,
112
+ action: 'created',
113
+ actor: 'agent',
114
+ actorId: ctx.agentId || undefined,
115
+ summary: `Task created: ${title}`,
116
+ })
117
+
118
+ return JSON.stringify({ ok: true, task: { id: task.id, title, status, agentId: assignee, projectId } })
119
+ }
120
+
121
+ function handleTasksUpdate(params: Record<string, unknown>, ctx: PlatformActionContext): string {
122
+ const taskId = requireString(params, 'taskId')
123
+ const existing = loadTask(taskId)
124
+ if (!existing) return `Error: task "${taskId}" not found.`
125
+
126
+ const updates: Partial<BoardTask> = { updatedAt: Date.now() }
127
+ if (typeof params.title === 'string' && params.title.trim()) updates.title = params.title.trim()
128
+ if (typeof params.description === 'string') updates.description = params.description.trim()
129
+ if (isValidTaskStatus(params.status)) updates.status = params.status
130
+ if (typeof params.result === 'string') updates.result = params.result.trim()
131
+ if (typeof params.agentId === 'string') updates.agentId = params.agentId.trim()
132
+ if (typeof params.projectId === 'string') updates.projectId = params.projectId.trim()
133
+
134
+ if (updates.status === 'completed' && !existing.completedAt) {
135
+ updates.completedAt = Date.now()
136
+ }
137
+
138
+ const merged = { ...existing, ...updates }
139
+ upsertTask(taskId, merged)
140
+ notify('tasks')
141
+
142
+ logActivity({
143
+ entityType: 'task',
144
+ entityId: taskId,
145
+ action: 'updated',
146
+ actor: 'agent',
147
+ actorId: ctx.agentId || undefined,
148
+ summary: `Task updated: ${merged.title}`,
149
+ })
150
+
151
+ return JSON.stringify({ ok: true, task: { id: taskId, title: merged.title, status: merged.status } })
152
+ }
153
+
154
+ function handleTasksList(params: Record<string, unknown>, _ctx: PlatformActionContext): string {
155
+ const all = loadTasks()
156
+ let tasks = Object.values(all) as BoardTask[]
157
+
158
+ // Optional filters
159
+ const filterStatus = optionalString(params, 'status')
160
+ const filterAgentId = optionalString(params, 'agentId') || optionalString(params, 'assignee')
161
+ const filterProjectId = optionalString(params, 'projectId')
162
+
163
+ if (filterStatus && isValidTaskStatus(filterStatus)) {
164
+ tasks = tasks.filter((t) => t.status === filterStatus)
165
+ }
166
+ if (filterAgentId) {
167
+ tasks = tasks.filter((t) => t.agentId === filterAgentId)
168
+ }
169
+ if (filterProjectId) {
170
+ tasks = tasks.filter((t) => t.projectId === filterProjectId)
171
+ }
172
+
173
+ // Default: exclude archived
174
+ if (!filterStatus) {
175
+ tasks = tasks.filter((t) => t.status !== 'archived')
176
+ }
177
+
178
+ tasks.sort((a, b) => b.updatedAt - a.updatedAt)
179
+
180
+ const limit = typeof params.limit === 'number' ? Math.min(params.limit, 100) : 50
181
+ const summary = tasks.slice(0, limit).map((t) => ({
182
+ id: t.id,
183
+ title: t.title,
184
+ status: t.status,
185
+ agentId: t.agentId,
186
+ projectId: t.projectId || null,
187
+ updatedAt: t.updatedAt,
188
+ }))
189
+ return JSON.stringify(summary)
190
+ }
191
+
192
+ function handleTasksGet(params: Record<string, unknown>, _ctx: PlatformActionContext): string {
193
+ const taskId = requireString(params, 'taskId')
194
+ const task = loadTask(taskId)
195
+ if (!task) return `Error: task "${taskId}" not found.`
196
+ return JSON.stringify(task)
197
+ }
198
+
199
+ function handleTasksComplete(params: Record<string, unknown>, ctx: PlatformActionContext): string {
200
+ const taskId = requireString(params, 'taskId')
201
+ const existing = loadTask(taskId)
202
+ if (!existing) return `Error: task "${taskId}" not found.`
203
+
204
+ const result = optionalString(params, 'result') || 'Completed'
205
+ const now = Date.now()
206
+ const merged: BoardTask = {
207
+ ...existing,
208
+ status: 'completed',
209
+ result,
210
+ completedAt: now,
211
+ updatedAt: now,
212
+ }
213
+ upsertTask(taskId, merged)
214
+ notify('tasks')
215
+
216
+ logActivity({
217
+ entityType: 'task',
218
+ entityId: taskId,
219
+ action: 'completed',
220
+ actor: 'agent',
221
+ actorId: ctx.agentId || undefined,
222
+ summary: `Task completed: ${merged.title}`,
223
+ })
224
+
225
+ return JSON.stringify({ ok: true, task: { id: taskId, title: merged.title, status: 'completed', result } })
226
+ }
227
+
228
+ // ---------------------------------------------------------------------------
229
+ // communicate.* handlers
230
+ // ---------------------------------------------------------------------------
231
+
232
+ async function handleCommunicateAskHuman(
233
+ params: Record<string, unknown>,
234
+ ctx: PlatformActionContext,
235
+ ): Promise<string> {
236
+ // Delegate to the existing human-loop executeHumanLoopAction logic
237
+ // by dynamically importing to avoid circular deps
238
+ const { buildHumanLoopTools } = await import('./human-loop')
239
+ const bctx = ctx.bctx
240
+ if (!bctx) return 'Error: build context not available for ask_human.'
241
+
242
+ // Determine the sub-action: map communicate.ask_human params to human-loop actions
243
+ const subAction = optionalString(params, 'subAction') || 'request_input'
244
+ const humanArgs: Record<string, unknown> = {
245
+ ...params,
246
+ action: subAction,
247
+ }
248
+ // Remove our routing keys
249
+ delete humanArgs.subAction
250
+
251
+ // Build the human-loop tool and invoke it
252
+ const tools = buildHumanLoopTools(bctx)
253
+ if (tools.length === 0) return 'Error: ask_human extension is not enabled.'
254
+ const result = await tools[0].invoke(humanArgs)
255
+ const resultStr = typeof result === 'string' ? result : JSON.stringify(result)
256
+
257
+ // Tag durable_wait outputs so the terminal boundary resolver can detect them
258
+ if (
259
+ (subAction === 'wait_for_reply' || subAction === 'wait_for_approval')
260
+ && resultStr.includes('"status":"active"')
261
+ ) {
262
+ // The output already has the right shape for resolveSuccessfulTerminalToolBoundary
263
+ // to detect it via the ask_human canonical name check. But since our tool is named
264
+ // "platform", we embed a marker field so the boundary resolver can be extended.
265
+ try {
266
+ const parsed = JSON.parse(resultStr) as Record<string, unknown>
267
+ return JSON.stringify({
268
+ ...parsed,
269
+ __terminal_boundary: 'durable_wait',
270
+ })
271
+ } catch {
272
+ return resultStr
273
+ }
274
+ }
275
+
276
+ return resultStr
277
+ }
278
+
279
+ async function handleCommunicateSendMessage(
280
+ params: Record<string, unknown>,
281
+ ctx: PlatformActionContext,
282
+ ): Promise<string> {
283
+ // Delegate to the existing connector action
284
+ const bctx = ctx.bctx
285
+ if (!bctx) return 'Error: build context not available for send_message.'
286
+
287
+ const { buildConnectorTools } = await import('./connector')
288
+ const tools = buildConnectorTools(bctx)
289
+ if (tools.length === 0) return 'Error: connector_message_tool extension is not enabled.'
290
+
291
+ const connectorArgs: Record<string, unknown> = {
292
+ action: 'send',
293
+ ...params,
294
+ }
295
+ const result = await tools[0].invoke(connectorArgs)
296
+ return typeof result === 'string' ? result : JSON.stringify(result)
297
+ }
298
+
299
+ async function handleCommunicateDelegate(
300
+ params: Record<string, unknown>,
301
+ ctx: PlatformActionContext,
302
+ ): Promise<string> {
303
+ const bctx = ctx.bctx
304
+ if (!bctx) return 'Error: build context not available for delegate.'
305
+
306
+ const { buildDelegateTools } = await import('./delegate')
307
+ const tools = buildDelegateTools(bctx)
308
+ if (tools.length === 0) return 'Error: delegate extension is not enabled or delegation is disabled.'
309
+
310
+ const delegateArgs: Record<string, unknown> = {
311
+ action: 'start',
312
+ ...params,
313
+ }
314
+ const result = await tools[0].invoke(delegateArgs)
315
+ return typeof result === 'string' ? result : JSON.stringify(result)
316
+ }
317
+
318
+ async function handleCommunicateSpawn(
319
+ params: Record<string, unknown>,
320
+ ctx: PlatformActionContext,
321
+ ): Promise<string> {
322
+ const bctx = ctx.bctx
323
+ if (!bctx) return 'Error: build context not available for spawn.'
324
+
325
+ const { buildSubagentTools } = await import('./subagent')
326
+ const tools = buildSubagentTools(bctx)
327
+ if (tools.length === 0) return 'Error: spawn_subagent extension is not enabled or delegation is disabled.'
328
+
329
+ const subagentArgs: Record<string, unknown> = {
330
+ action: 'start',
331
+ ...params,
332
+ }
333
+ const result = await tools[0].invoke(subagentArgs)
334
+ return typeof result === 'string' ? result : JSON.stringify(result)
335
+ }
336
+
337
+ // ---------------------------------------------------------------------------
338
+ // projects.* handlers
339
+ // ---------------------------------------------------------------------------
340
+
341
+ function handleProjectsList(_params: Record<string, unknown>, _ctx: PlatformActionContext): string {
342
+ const all = loadProjects()
343
+ const projects = Object.values(all) as Project[]
344
+ const summary = projects.map((p) => ({
345
+ id: p.id,
346
+ name: p.name,
347
+ description: p.description,
348
+ updatedAt: p.updatedAt,
349
+ }))
350
+ return JSON.stringify(summary)
351
+ }
352
+
353
+ function handleProjectsGet(params: Record<string, unknown>, _ctx: PlatformActionContext): string {
354
+ const projectId = requireString(params, 'projectId')
355
+ const all = loadProjects()
356
+ const project = (all as Record<string, Project>)[projectId]
357
+ if (!project) return `Error: project "${projectId}" not found.`
358
+ return JSON.stringify(project)
359
+ }
360
+
361
+ // ---------------------------------------------------------------------------
362
+ // chatrooms.* handlers
363
+ // ---------------------------------------------------------------------------
364
+
365
+ function handleChatroomsSend(params: Record<string, unknown>, ctx: PlatformActionContext): string {
366
+ const chatroomId = requireString(params, 'chatroomId')
367
+ const message = requireString(params, 'message')
368
+ const chatrooms = loadChatrooms() as Record<string, Chatroom>
369
+ const chatroom = chatrooms[chatroomId]
370
+ if (!chatroom) return `Error: chatroom "${chatroomId}" not found.`
371
+
372
+ const agents = loadAgents()
373
+ const senderName = ctx.agentId ? (agents[ctx.agentId]?.name || 'Agent') : 'Agent'
374
+ const msgId = genId()
375
+ const targetAgentId = optionalString(params, 'targetAgentId')
376
+
377
+ chatroom.messages.push({
378
+ id: msgId,
379
+ senderId: ctx.agentId || 'agent',
380
+ senderName,
381
+ role: 'assistant' as const,
382
+ text: message,
383
+ mentions: [],
384
+ reactions: [],
385
+ time: Date.now(),
386
+ ...(targetAgentId ? { targetAgentId } : {}),
387
+ })
388
+ chatroom.updatedAt = Date.now()
389
+ saveChatrooms(chatrooms)
390
+ notify(`chatroom:${chatroomId}`)
391
+
392
+ logExecution(ctx.sessionId || '', 'chatroom_message', `Message sent in ${chatroom.name}`, {
393
+ agentId: ctx.agentId,
394
+ detail: { chatroomId, senderId: ctx.agentId, messageLen: message.length },
395
+ })
396
+
397
+ return JSON.stringify({ ok: true, messageId: msgId })
398
+ }
399
+
400
+ function handleChatroomsList(_params: Record<string, unknown>, _ctx: PlatformActionContext): string {
401
+ const chatrooms = loadChatrooms() as Record<string, Chatroom>
402
+ const list = Object.values(chatrooms).map((cr) => ({
403
+ id: cr.id,
404
+ name: cr.name,
405
+ description: cr.description,
406
+ memberCount: cr.agentIds.length,
407
+ messageCount: cr.messages.length,
408
+ }))
409
+ return JSON.stringify(list)
410
+ }
411
+
412
+ function handleChatroomsHistory(params: Record<string, unknown>, _ctx: PlatformActionContext): string {
413
+ const chatroomId = requireString(params, 'chatroomId')
414
+ const chatroom = loadChatroom(chatroomId)
415
+ if (!chatroom) return `Error: chatroom "${chatroomId}" not found.`
416
+
417
+ const limit = typeof params.limit === 'number' ? Math.min(params.limit, 50) : 20
418
+ const messages = chatroom.messages.slice(-limit).map((msg) => ({
419
+ id: msg.id,
420
+ sender: msg.senderName,
421
+ senderId: msg.senderId,
422
+ text: msg.text.slice(0, 300),
423
+ time: msg.time,
424
+ ...(msg.targetAgentId ? { targetAgentId: msg.targetAgentId } : {}),
425
+ ...(msg.replyToId ? { replyToId: msg.replyToId } : {}),
426
+ }))
427
+ return JSON.stringify(messages)
428
+ }
429
+
430
+ // ---------------------------------------------------------------------------
431
+ // agents.* handlers
432
+ // ---------------------------------------------------------------------------
433
+
434
+ function handleAgentsList(_params: Record<string, unknown>, _ctx: PlatformActionContext): string {
435
+ const all = loadAgents()
436
+ const agents = Object.values(all).map((a) => ({
437
+ id: a.id,
438
+ name: a.name,
439
+ description: a.description || '',
440
+ provider: a.provider,
441
+ model: a.model,
442
+ }))
443
+ return JSON.stringify(agents)
444
+ }
445
+
446
+ function handleAgentsGet(params: Record<string, unknown>, _ctx: PlatformActionContext): string {
447
+ const agentId = requireString(params, 'agentId')
448
+ const all = loadAgents()
449
+ const agent = all[agentId]
450
+ if (!agent) return `Error: agent "${agentId}" not found.`
451
+ // Return a safe subset — omit API keys and sensitive config
452
+ return JSON.stringify({
453
+ id: agent.id,
454
+ name: agent.name,
455
+ description: agent.description || '',
456
+ provider: agent.provider,
457
+ model: agent.model,
458
+ soul: agent.soul || '',
459
+ capabilities: agent.capabilities || [],
460
+ })
461
+ }
462
+
463
+ // ---------------------------------------------------------------------------
464
+ // Action dispatch
465
+ // ---------------------------------------------------------------------------
466
+
467
+ const ACTION_MAP: Record<string, ActionHandler> = {
468
+ // Tasks
469
+ 'tasks.create': handleTasksCreate,
470
+ 'tasks.update': handleTasksUpdate,
471
+ 'tasks.list': handleTasksList,
472
+ 'tasks.get': handleTasksGet,
473
+ 'tasks.complete': handleTasksComplete,
474
+
475
+ // Communication
476
+ 'communicate.ask_human': handleCommunicateAskHuman,
477
+ 'communicate.send_message': handleCommunicateSendMessage,
478
+ 'communicate.delegate': handleCommunicateDelegate,
479
+ 'communicate.spawn': handleCommunicateSpawn,
480
+
481
+ // Projects
482
+ 'projects.list': handleProjectsList,
483
+ 'projects.get': handleProjectsGet,
484
+
485
+ // Chatrooms
486
+ 'chatrooms.send': handleChatroomsSend,
487
+ 'chatrooms.list': handleChatroomsList,
488
+ 'chatrooms.history': handleChatroomsHistory,
489
+
490
+ // Agents
491
+ 'agents.list': handleAgentsList,
492
+ 'agents.get': handleAgentsGet,
493
+ }
494
+
495
+ const VALID_ACTIONS = Object.keys(ACTION_MAP)
496
+
497
+ async function executePlatformV2Action(
498
+ args: Record<string, unknown>,
499
+ ctx: PlatformActionContext,
500
+ ): Promise<string> {
501
+ const normalized = normalizeToolInputArgs(args)
502
+ const action = typeof normalized.action === 'string'
503
+ ? normalized.action.trim().toLowerCase()
504
+ : ''
505
+
506
+ if (!action) {
507
+ return `Error: action is required. Valid actions: ${VALID_ACTIONS.join(', ')}.`
508
+ }
509
+
510
+ const handler = ACTION_MAP[action]
511
+ if (!handler) {
512
+ // Try fuzzy matching — help LLMs that drop the dot or use underscores
513
+ const fuzzyAction = action.replace(/_/g, '.')
514
+ const fuzzyHandler = ACTION_MAP[fuzzyAction]
515
+ if (fuzzyHandler) {
516
+ try {
517
+ const result = await fuzzyHandler(normalized, ctx)
518
+ return truncate(result, MAX_OUTPUT)
519
+ } catch (err: unknown) {
520
+ return `Error: ${errorMessage(err)}`
521
+ }
522
+ }
523
+
524
+ return `Error: Unknown action "${action}". Valid actions: ${VALID_ACTIONS.join(', ')}.`
525
+ }
526
+
527
+ try {
528
+ const result = await handler(normalized, ctx)
529
+ return truncate(result, MAX_OUTPUT)
530
+ } catch (err: unknown) {
531
+ return `Error: ${errorMessage(err)}`
532
+ }
533
+ }
534
+
535
+ // ---------------------------------------------------------------------------
536
+ // Extension registration
537
+ // ---------------------------------------------------------------------------
538
+
539
+ const PlatformV2Extension: Extension = {
540
+ name: 'Platform V2',
541
+ enabledByDefault: false,
542
+ description: 'Consolidated platform tool for tasks, communication, projects, chatrooms, and agent management.',
543
+ hooks: {
544
+ getCapabilityDescription: () =>
545
+ 'I can manage tasks, communicate with humans and other agents, browse projects and chatrooms, and list agents — all via the unified `platform` tool with dotted actions like `tasks.create`, `communicate.ask_human`, `chatrooms.send`.',
546
+ getOperatingGuidance: () => [
547
+ 'Use `platform` with action "tasks.create" to create tasks, "tasks.update" to update, "tasks.complete" to mark done.',
548
+ 'Use "communicate.ask_human" with subAction "request_input" to ask a human, then "wait_for_reply" to pause. Do not repeat pending questions.',
549
+ 'Use "communicate.send_message" to send connector messages, "communicate.delegate" to delegate to CLI agents, "communicate.spawn" to spawn subagents.',
550
+ 'Use "projects.list" and "projects.get" for project info, "chatrooms.list" and "chatrooms.send" for chatrooms.',
551
+ 'Use "agents.list" and "agents.get" to discover available agents.',
552
+ ],
553
+ } as ExtensionHooks,
554
+ tools: [
555
+ {
556
+ name: 'platform',
557
+ description:
558
+ 'Consolidated platform tool. Use dotted action names: '
559
+ + 'tasks.create, tasks.update, tasks.list, tasks.get, tasks.complete, '
560
+ + 'communicate.ask_human (subAction: request_input|wait_for_reply|wait_for_approval|list_mailbox|ack_mailbox|status), '
561
+ + 'communicate.send_message, communicate.delegate, communicate.spawn, '
562
+ + 'projects.list, projects.get, '
563
+ + 'chatrooms.send, chatrooms.list, chatrooms.history, '
564
+ + 'agents.list, agents.get.',
565
+ parameters: {
566
+ type: 'object',
567
+ properties: {
568
+ action: {
569
+ type: 'string',
570
+ description: 'Dotted action name, e.g. "tasks.create", "communicate.ask_human"',
571
+ },
572
+ },
573
+ required: ['action'],
574
+ },
575
+ execute: async (args, context) => {
576
+ const sessionAgentId = context.session.agentId || undefined
577
+ return executePlatformV2Action(args as Record<string, unknown>, {
578
+ agentId: sessionAgentId,
579
+ sessionId: context.session.id,
580
+ cwd: context.session.cwd || process.cwd(),
581
+ })
582
+ },
583
+ },
584
+ ],
585
+ }
586
+
587
+ registerNativeCapability('platform_v2', PlatformV2Extension)
588
+
589
+ // ---------------------------------------------------------------------------
590
+ // Tool builder (called from session-tools/index.ts)
591
+ // ---------------------------------------------------------------------------
592
+
593
+ export function buildPlatformV2Tools(bctx: ToolBuildContext): StructuredToolInterface[] {
594
+ if (!bctx.hasExtension('platform_v2')) return []
595
+
596
+ const sessionAgentId = bctx.ctx?.agentId || undefined
597
+
598
+ return [
599
+ tool(
600
+ async (args) =>
601
+ executePlatformV2Action(args, {
602
+ agentId: sessionAgentId,
603
+ sessionId: bctx.ctx?.sessionId,
604
+ cwd: bctx.cwd,
605
+ delegationEnabled: bctx.ctx?.delegationEnabled,
606
+ delegationTargetMode: bctx.ctx?.delegationTargetMode,
607
+ delegationTargetAgentIds: bctx.ctx?.delegationTargetAgentIds,
608
+ bctx,
609
+ }),
610
+ {
611
+ name: 'platform',
612
+ description: PlatformV2Extension.tools![0].description,
613
+ schema: z.object({}).passthrough(),
614
+ },
615
+ ),
616
+ ]
617
+ }
@@ -1,3 +1,4 @@
1
+ import { errorMessage } from '@/lib/shared-utils'
1
2
  import { z } from 'zod'
2
3
  import { tool, type StructuredToolInterface } from '@langchain/core/tools'
3
4
  import { genId } from '@/lib/id'
@@ -38,7 +39,7 @@ async function executeWhoAmI(context: { sessionId?: string; agentId?: string })
38
39
  toolPolicy,
39
40
  rootSessionId,
40
41
  }))
41
- } catch (err: any) { return `Error: ${err.message}` }
42
+ } catch (err: unknown) { return `Error: ${errorMessage(err)}` }
42
43
  }
43
44
 
44
45
  function normalizeRuntimeExtensionId(extensionId: string): string {
@@ -226,7 +227,7 @@ async function executeSessionsAction(args: any, context: { sessionId?: string; a
226
227
  return JSON.stringify({ sessionId: targetId, updated: Object.keys(patch).filter((key) => allowedKeys.has(key)) })
227
228
  }
228
229
  return `Unknown action "${action}".`
229
- } catch (err: any) { return `Error: ${err.message}` }
230
+ } catch (err: unknown) { return `Error: ${errorMessage(err)}` }
230
231
  }
231
232
 
232
233
  /**
@@ -115,10 +115,9 @@ describe('buildSessionTools signature', () => {
115
115
  await built.cleanup()
116
116
  })
117
117
 
118
- it('sandbox execution functions are still importable for shell integration', async () => {
119
- const sandbox = await import('./sandbox')
120
- assert.equal(typeof sandbox.executeSandboxExec, 'function')
121
- assert.equal(typeof sandbox.executeListRuntimes, 'function')
118
+ it('execute tool builder is importable', async () => {
119
+ const execute = await import('./execute')
120
+ assert.equal(typeof execute.buildExecuteTools, 'function')
122
121
  })
123
122
  })
124
123