@shykaruu/jarvis-brain 0.4.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 (330) hide show
  1. package/LICENSE +153 -0
  2. package/README.md +428 -0
  3. package/bin/jarvis.ts +449 -0
  4. package/package.json +79 -0
  5. package/roles/activity-observer.yaml +60 -0
  6. package/roles/ceo-founder.yaml +144 -0
  7. package/roles/chief-of-staff.yaml +158 -0
  8. package/roles/dev-lead.yaml +182 -0
  9. package/roles/executive-assistant.yaml +77 -0
  10. package/roles/marketing-director.yaml +168 -0
  11. package/roles/personal-assistant.yaml +266 -0
  12. package/roles/research-specialist.yaml +60 -0
  13. package/roles/specialists/content-writer.yaml +53 -0
  14. package/roles/specialists/customer-support.yaml +57 -0
  15. package/roles/specialists/data-analyst.yaml +57 -0
  16. package/roles/specialists/financial-analyst.yaml +56 -0
  17. package/roles/specialists/hr-specialist.yaml +55 -0
  18. package/roles/specialists/legal-advisor.yaml +58 -0
  19. package/roles/specialists/marketing-strategist.yaml +56 -0
  20. package/roles/specialists/project-coordinator.yaml +55 -0
  21. package/roles/specialists/research-analyst.yaml +58 -0
  22. package/roles/specialists/software-engineer.yaml +57 -0
  23. package/roles/specialists/system-administrator.yaml +57 -0
  24. package/roles/system-admin.yaml +76 -0
  25. package/scripts/ensure-bun.cjs +16 -0
  26. package/src/actions/README.md +421 -0
  27. package/src/actions/app-control/desktop-controller.test.ts +26 -0
  28. package/src/actions/app-control/desktop-controller.ts +438 -0
  29. package/src/actions/app-control/interface.ts +64 -0
  30. package/src/actions/app-control/linux.ts +273 -0
  31. package/src/actions/app-control/macos.ts +54 -0
  32. package/src/actions/app-control/sidecar-launcher.test.ts +23 -0
  33. package/src/actions/app-control/sidecar-launcher.ts +286 -0
  34. package/src/actions/app-control/windows.ts +44 -0
  35. package/src/actions/browser/cdp.ts +138 -0
  36. package/src/actions/browser/chrome-launcher.ts +261 -0
  37. package/src/actions/browser/session.ts +506 -0
  38. package/src/actions/browser/stealth.ts +49 -0
  39. package/src/actions/index.ts +20 -0
  40. package/src/actions/terminal/executor.ts +157 -0
  41. package/src/actions/terminal/wsl-bridge.ts +126 -0
  42. package/src/actions/test.ts +93 -0
  43. package/src/actions/tools/agents.ts +363 -0
  44. package/src/actions/tools/builtin.ts +950 -0
  45. package/src/actions/tools/commitments.ts +192 -0
  46. package/src/actions/tools/content.ts +217 -0
  47. package/src/actions/tools/delegate.ts +147 -0
  48. package/src/actions/tools/desktop.test.ts +55 -0
  49. package/src/actions/tools/desktop.ts +305 -0
  50. package/src/actions/tools/documents.ts +169 -0
  51. package/src/actions/tools/goals.ts +376 -0
  52. package/src/actions/tools/local-tools-guard.ts +31 -0
  53. package/src/actions/tools/registry.ts +173 -0
  54. package/src/actions/tools/research.ts +111 -0
  55. package/src/actions/tools/sidecar-list.ts +57 -0
  56. package/src/actions/tools/sidecar-route.ts +105 -0
  57. package/src/actions/tools/workflows.ts +216 -0
  58. package/src/agents/agent.ts +132 -0
  59. package/src/agents/delegation.ts +107 -0
  60. package/src/agents/hierarchy.ts +113 -0
  61. package/src/agents/index.ts +19 -0
  62. package/src/agents/messaging.ts +125 -0
  63. package/src/agents/orchestrator.ts +592 -0
  64. package/src/agents/role-discovery.ts +61 -0
  65. package/src/agents/sub-agent-runner.ts +309 -0
  66. package/src/agents/task-manager.ts +151 -0
  67. package/src/authority/approval-delivery.ts +59 -0
  68. package/src/authority/approval.ts +196 -0
  69. package/src/authority/audit.ts +158 -0
  70. package/src/authority/authority.test.ts +519 -0
  71. package/src/authority/deferred-executor.ts +103 -0
  72. package/src/authority/emergency.ts +66 -0
  73. package/src/authority/engine.ts +301 -0
  74. package/src/authority/index.ts +12 -0
  75. package/src/authority/learning.ts +111 -0
  76. package/src/authority/tool-action-map.ts +74 -0
  77. package/src/awareness/analytics.ts +466 -0
  78. package/src/awareness/awareness.test.ts +332 -0
  79. package/src/awareness/capture-engine.ts +305 -0
  80. package/src/awareness/context-graph.ts +130 -0
  81. package/src/awareness/context-tracker.ts +349 -0
  82. package/src/awareness/index.ts +25 -0
  83. package/src/awareness/intelligence.ts +321 -0
  84. package/src/awareness/ocr-engine.ts +88 -0
  85. package/src/awareness/service.ts +528 -0
  86. package/src/awareness/struggle-detector.ts +342 -0
  87. package/src/awareness/suggestion-engine.ts +476 -0
  88. package/src/awareness/types.ts +201 -0
  89. package/src/cli/autostart.ts +417 -0
  90. package/src/cli/deps.ts +449 -0
  91. package/src/cli/doctor.ts +238 -0
  92. package/src/cli/helpers.ts +401 -0
  93. package/src/cli/onboard.ts +827 -0
  94. package/src/cli/uninstall.test.ts +37 -0
  95. package/src/cli/uninstall.ts +202 -0
  96. package/src/comms/README.md +329 -0
  97. package/src/comms/auth-error.html +48 -0
  98. package/src/comms/channels/discord.ts +228 -0
  99. package/src/comms/channels/signal.ts +56 -0
  100. package/src/comms/channels/telegram.ts +316 -0
  101. package/src/comms/channels/whatsapp.ts +60 -0
  102. package/src/comms/channels.test.ts +173 -0
  103. package/src/comms/dashboard-auth.ts +75 -0
  104. package/src/comms/desktop-notify.ts +114 -0
  105. package/src/comms/example.ts +129 -0
  106. package/src/comms/index.ts +129 -0
  107. package/src/comms/streaming.ts +149 -0
  108. package/src/comms/voice.test.ts +504 -0
  109. package/src/comms/voice.ts +341 -0
  110. package/src/comms/websocket.test.ts +409 -0
  111. package/src/comms/websocket.ts +669 -0
  112. package/src/config/README.md +389 -0
  113. package/src/config/index.ts +6 -0
  114. package/src/config/loader.test.ts +183 -0
  115. package/src/config/loader.ts +148 -0
  116. package/src/config/types.ts +293 -0
  117. package/src/daemon/README.md +232 -0
  118. package/src/daemon/agent-service-interface.ts +9 -0
  119. package/src/daemon/agent-service.ts +667 -0
  120. package/src/daemon/api-routes.ts +3067 -0
  121. package/src/daemon/background-agent-service.ts +396 -0
  122. package/src/daemon/background-agent.test.ts +78 -0
  123. package/src/daemon/channel-service.ts +201 -0
  124. package/src/daemon/commitment-executor.ts +297 -0
  125. package/src/daemon/dashboard-auth.test.ts +170 -0
  126. package/src/daemon/event-classifier.ts +239 -0
  127. package/src/daemon/event-coalescer.ts +123 -0
  128. package/src/daemon/event-reactor.ts +214 -0
  129. package/src/daemon/flock.c +7 -0
  130. package/src/daemon/health.ts +220 -0
  131. package/src/daemon/index.ts +1070 -0
  132. package/src/daemon/llm-settings.test.ts +78 -0
  133. package/src/daemon/llm-settings.ts +450 -0
  134. package/src/daemon/observer-service.ts +150 -0
  135. package/src/daemon/pid.test.ts +283 -0
  136. package/src/daemon/pid.ts +224 -0
  137. package/src/daemon/research-queue.ts +155 -0
  138. package/src/daemon/services.ts +175 -0
  139. package/src/daemon/ws-service.ts +926 -0
  140. package/src/global.d.ts +4 -0
  141. package/src/goals/accountability.ts +240 -0
  142. package/src/goals/awareness-bridge.ts +185 -0
  143. package/src/goals/estimator.ts +185 -0
  144. package/src/goals/events.ts +28 -0
  145. package/src/goals/goals.test.ts +400 -0
  146. package/src/goals/integration.test.ts +329 -0
  147. package/src/goals/nl-builder.test.ts +220 -0
  148. package/src/goals/nl-builder.ts +256 -0
  149. package/src/goals/rhythm.test.ts +177 -0
  150. package/src/goals/rhythm.ts +275 -0
  151. package/src/goals/service.test.ts +135 -0
  152. package/src/goals/service.ts +407 -0
  153. package/src/goals/types.ts +106 -0
  154. package/src/goals/workflow-bridge.ts +96 -0
  155. package/src/integrations/google-api.ts +134 -0
  156. package/src/integrations/google-auth.ts +175 -0
  157. package/src/llm/README.md +291 -0
  158. package/src/llm/anthropic.ts +400 -0
  159. package/src/llm/gemini.ts +380 -0
  160. package/src/llm/groq.ts +406 -0
  161. package/src/llm/history.ts +147 -0
  162. package/src/llm/index.ts +21 -0
  163. package/src/llm/manager.ts +226 -0
  164. package/src/llm/ollama.ts +316 -0
  165. package/src/llm/openai.ts +411 -0
  166. package/src/llm/openrouter.ts +390 -0
  167. package/src/llm/provider.test.ts +487 -0
  168. package/src/llm/provider.ts +61 -0
  169. package/src/llm/test.ts +88 -0
  170. package/src/observers/README.md +278 -0
  171. package/src/observers/calendar.ts +113 -0
  172. package/src/observers/clipboard.ts +136 -0
  173. package/src/observers/email.ts +109 -0
  174. package/src/observers/example.ts +58 -0
  175. package/src/observers/file-watcher.ts +124 -0
  176. package/src/observers/index.ts +159 -0
  177. package/src/observers/notifications.ts +197 -0
  178. package/src/observers/observers.test.ts +203 -0
  179. package/src/observers/processes.ts +225 -0
  180. package/src/personality/README.md +61 -0
  181. package/src/personality/adapter.ts +196 -0
  182. package/src/personality/index.ts +20 -0
  183. package/src/personality/learner.ts +209 -0
  184. package/src/personality/model.ts +132 -0
  185. package/src/personality/personality.test.ts +236 -0
  186. package/src/roles/README.md +252 -0
  187. package/src/roles/authority.ts +120 -0
  188. package/src/roles/example-usage.ts +198 -0
  189. package/src/roles/index.ts +42 -0
  190. package/src/roles/loader.ts +143 -0
  191. package/src/roles/prompt-builder.ts +218 -0
  192. package/src/roles/test-multi.ts +102 -0
  193. package/src/roles/test-role.yaml +77 -0
  194. package/src/roles/test-utils.ts +93 -0
  195. package/src/roles/test.ts +106 -0
  196. package/src/roles/tool-guide.ts +195 -0
  197. package/src/roles/types.ts +36 -0
  198. package/src/roles/utils.ts +200 -0
  199. package/src/scripts/google-setup.ts +168 -0
  200. package/src/sidecar/connection.ts +179 -0
  201. package/src/sidecar/index.ts +6 -0
  202. package/src/sidecar/manager.ts +542 -0
  203. package/src/sidecar/protocol.ts +85 -0
  204. package/src/sidecar/rpc.ts +161 -0
  205. package/src/sidecar/scheduler.ts +136 -0
  206. package/src/sidecar/types.ts +112 -0
  207. package/src/sidecar/validator.ts +144 -0
  208. package/src/sites/builder-tools.ts +215 -0
  209. package/src/sites/dev-server-manager.ts +286 -0
  210. package/src/sites/fixtures/security-test-site/.jarvis-project.json +6 -0
  211. package/src/sites/fixtures/security-test-site/Makefile +15 -0
  212. package/src/sites/fixtures/security-test-site/README.md +18 -0
  213. package/src/sites/fixtures/security-test-site/index.html +12 -0
  214. package/src/sites/fixtures/security-test-site/index.ts +16 -0
  215. package/src/sites/fixtures/security-test-site/package.json +13 -0
  216. package/src/sites/fixtures/security-test-site/src/app.tsx +780 -0
  217. package/src/sites/fixtures/security-test-site/tsconfig.json +10 -0
  218. package/src/sites/git-manager.ts +240 -0
  219. package/src/sites/github-manager.ts +355 -0
  220. package/src/sites/index.ts +25 -0
  221. package/src/sites/project-manager.ts +389 -0
  222. package/src/sites/proxy.ts +133 -0
  223. package/src/sites/service.ts +136 -0
  224. package/src/sites/templates.ts +169 -0
  225. package/src/sites/types.ts +89 -0
  226. package/src/user/profile-followup.test.ts +84 -0
  227. package/src/user/profile-followup.ts +185 -0
  228. package/src/user/profile.ts +224 -0
  229. package/src/vault/README.md +110 -0
  230. package/src/vault/awareness.ts +341 -0
  231. package/src/vault/commitments.ts +299 -0
  232. package/src/vault/content-pipeline.ts +270 -0
  233. package/src/vault/conversations.ts +173 -0
  234. package/src/vault/dashboard-sessions.ts +44 -0
  235. package/src/vault/documents.ts +130 -0
  236. package/src/vault/entities.ts +185 -0
  237. package/src/vault/extractor.test.ts +356 -0
  238. package/src/vault/extractor.ts +345 -0
  239. package/src/vault/facts.ts +190 -0
  240. package/src/vault/goals.ts +477 -0
  241. package/src/vault/index.ts +87 -0
  242. package/src/vault/keychain.ts +99 -0
  243. package/src/vault/observations.ts +115 -0
  244. package/src/vault/relationships.ts +178 -0
  245. package/src/vault/retrieval.test.ts +139 -0
  246. package/src/vault/retrieval.ts +258 -0
  247. package/src/vault/schema.ts +709 -0
  248. package/src/vault/settings.ts +38 -0
  249. package/src/vault/user-profile.test.ts +113 -0
  250. package/src/vault/user-profile.ts +176 -0
  251. package/src/vault/vectors.ts +92 -0
  252. package/src/vault/webapp-template-seeds.ts +116 -0
  253. package/src/vault/webapp-templates.ts +244 -0
  254. package/src/vault/workflows.ts +403 -0
  255. package/src/workflows/auto-suggest.ts +290 -0
  256. package/src/workflows/engine.ts +366 -0
  257. package/src/workflows/events.ts +24 -0
  258. package/src/workflows/executor.ts +207 -0
  259. package/src/workflows/nl-builder.ts +198 -0
  260. package/src/workflows/nodes/actions/agent-task.ts +73 -0
  261. package/src/workflows/nodes/actions/calendar-action.ts +85 -0
  262. package/src/workflows/nodes/actions/code-execution.ts +73 -0
  263. package/src/workflows/nodes/actions/discord.ts +77 -0
  264. package/src/workflows/nodes/actions/file-write.ts +73 -0
  265. package/src/workflows/nodes/actions/gmail.ts +69 -0
  266. package/src/workflows/nodes/actions/http-request.ts +117 -0
  267. package/src/workflows/nodes/actions/notification.ts +85 -0
  268. package/src/workflows/nodes/actions/run-tool.ts +55 -0
  269. package/src/workflows/nodes/actions/send-message.ts +82 -0
  270. package/src/workflows/nodes/actions/shell-command.ts +76 -0
  271. package/src/workflows/nodes/actions/telegram.ts +60 -0
  272. package/src/workflows/nodes/builtin.ts +119 -0
  273. package/src/workflows/nodes/error/error-handler.ts +37 -0
  274. package/src/workflows/nodes/error/fallback.ts +47 -0
  275. package/src/workflows/nodes/error/retry.ts +82 -0
  276. package/src/workflows/nodes/logic/delay.ts +42 -0
  277. package/src/workflows/nodes/logic/if-else.ts +41 -0
  278. package/src/workflows/nodes/logic/loop.ts +90 -0
  279. package/src/workflows/nodes/logic/merge.ts +38 -0
  280. package/src/workflows/nodes/logic/race.ts +40 -0
  281. package/src/workflows/nodes/logic/switch.ts +59 -0
  282. package/src/workflows/nodes/logic/template-render.ts +53 -0
  283. package/src/workflows/nodes/logic/variable-get.ts +37 -0
  284. package/src/workflows/nodes/logic/variable-set.ts +59 -0
  285. package/src/workflows/nodes/registry.ts +99 -0
  286. package/src/workflows/nodes/transform/aggregate.ts +99 -0
  287. package/src/workflows/nodes/transform/csv-parse.ts +70 -0
  288. package/src/workflows/nodes/transform/json-parse.ts +63 -0
  289. package/src/workflows/nodes/transform/map-filter.ts +84 -0
  290. package/src/workflows/nodes/transform/regex-match.ts +89 -0
  291. package/src/workflows/nodes/triggers/calendar.ts +33 -0
  292. package/src/workflows/nodes/triggers/clipboard.ts +32 -0
  293. package/src/workflows/nodes/triggers/cron.ts +40 -0
  294. package/src/workflows/nodes/triggers/email.ts +40 -0
  295. package/src/workflows/nodes/triggers/file-change.ts +45 -0
  296. package/src/workflows/nodes/triggers/git.ts +46 -0
  297. package/src/workflows/nodes/triggers/manual.ts +23 -0
  298. package/src/workflows/nodes/triggers/poll.ts +81 -0
  299. package/src/workflows/nodes/triggers/process.ts +44 -0
  300. package/src/workflows/nodes/triggers/screen-event.ts +37 -0
  301. package/src/workflows/nodes/triggers/webhook.ts +39 -0
  302. package/src/workflows/safe-eval.ts +139 -0
  303. package/src/workflows/template.ts +118 -0
  304. package/src/workflows/triggers/cron.ts +311 -0
  305. package/src/workflows/triggers/manager.ts +285 -0
  306. package/src/workflows/triggers/observer-bridge.ts +172 -0
  307. package/src/workflows/triggers/poller.ts +201 -0
  308. package/src/workflows/triggers/screen-condition.ts +218 -0
  309. package/src/workflows/triggers/triggers.test.ts +740 -0
  310. package/src/workflows/triggers/webhook.ts +191 -0
  311. package/src/workflows/types.ts +133 -0
  312. package/src/workflows/variables.ts +72 -0
  313. package/src/workflows/workflows.test.ts +383 -0
  314. package/src/workflows/yaml.ts +104 -0
  315. package/ui/dist/index-3gr23jt9.js +112614 -0
  316. package/ui/dist/index-9vmj8127.css +14239 -0
  317. package/ui/dist/index-hy9pc1gm.js +112873 -0
  318. package/ui/dist/index-j2ep5d1w.js +112374 -0
  319. package/ui/dist/index-jt00vjqs.js +112858 -0
  320. package/ui/dist/index-k9ymx5qb.js +112374 -0
  321. package/ui/dist/index.html +16 -0
  322. package/ui/public/audio/pcm-capture-processor.js +11 -0
  323. package/ui/public/openwakeword/models/embedding_model.onnx +0 -0
  324. package/ui/public/openwakeword/models/hey_jarvis_v0.1.onnx +0 -0
  325. package/ui/public/openwakeword/models/melspectrogram.onnx +0 -0
  326. package/ui/public/openwakeword/models/silero_vad.onnx +0 -0
  327. package/ui/public/ort/ort-wasm-simd-threaded.jsep.mjs +106 -0
  328. package/ui/public/ort/ort-wasm-simd-threaded.jsep.wasm +0 -0
  329. package/ui/public/ort/ort-wasm-simd-threaded.mjs +59 -0
  330. package/ui/public/ort/ort-wasm-simd-threaded.wasm +0 -0
@@ -0,0 +1,363 @@
1
+ /**
2
+ * Manage Agents Tool — Multi-Agent Hierarchy
3
+ *
4
+ * Allows the primary agent to spawn persistent sub-agents, assign them tasks
5
+ * (async), check status, collect results, and terminate them.
6
+ *
7
+ * For quick sync delegation (spawn → run → return → terminate in one call),
8
+ * use delegate_task instead.
9
+ */
10
+
11
+ import type { AgentOrchestrator } from '../../agents/orchestrator.ts';
12
+ import type { LLMManager } from '../../llm/manager.ts';
13
+ import type { RoleDefinition } from '../../roles/types.ts';
14
+ import type { ToolDefinition } from './registry.ts';
15
+ import type { AgentTaskManager } from '../../agents/task-manager.ts';
16
+ import { createScopedToolRegistry, type ProgressCallback } from '../../agents/sub-agent-runner.ts';
17
+
18
+ export type AgentToolDeps = {
19
+ orchestrator: AgentOrchestrator;
20
+ llmManager: LLMManager;
21
+ specialists: Map<string, RoleDefinition>;
22
+ taskManager: AgentTaskManager;
23
+ onProgress?: ProgressCallback;
24
+ };
25
+
26
+ // Track scoped registries for persistent agents so they can be reused across tasks
27
+ const agentRegistries = new Map<string, ReturnType<typeof createScopedToolRegistry>>();
28
+
29
+ export type PersistentAgentSummary = {
30
+ agent_id: string;
31
+ name: string;
32
+ specialist: string;
33
+ status: string;
34
+ current_task: string | null;
35
+ busy: boolean;
36
+ };
37
+
38
+ export function spawnPersistentAgent(deps: AgentToolDeps, specialistId: string) {
39
+ if (!specialistId) {
40
+ throw new Error('A specialist role is required to spawn an agent.');
41
+ }
42
+
43
+ const role = deps.specialists.get(specialistId);
44
+ if (!role) {
45
+ throw new Error(`Unknown specialist "${specialistId}". Available: ${Array.from(deps.specialists.keys()).join(', ')}`);
46
+ }
47
+
48
+ const primary = deps.orchestrator.getPrimary();
49
+ if (!primary) {
50
+ throw new Error('No primary agent exists');
51
+ }
52
+
53
+ const agent = deps.orchestrator.spawnSubAgent(primary.id, role);
54
+ const scopedRegistry = createScopedToolRegistry(agent.agent.authority.allowed_tools);
55
+ agentRegistries.set(agent.id, scopedRegistry);
56
+
57
+ // A persistent agent should start idle until it has a task.
58
+ agent.idle();
59
+
60
+ console.log(`[ManageAgents] Spawned ${role.name} (${agent.id}) with ${scopedRegistry.count()} tools`);
61
+
62
+ return {
63
+ agent,
64
+ summary: {
65
+ agent_id: agent.id,
66
+ name: role.name,
67
+ specialist: specialistId,
68
+ status: agent.status,
69
+ tools_available: scopedRegistry.count(),
70
+ tool_categories: agent.agent.authority.allowed_tools,
71
+ },
72
+ };
73
+ }
74
+
75
+ export async function assignPersistentAgentTask(
76
+ deps: AgentToolDeps,
77
+ params: { agentId: string; task: string; context?: string }
78
+ ) {
79
+ const { agentId, task, context = '' } = params;
80
+ if (!agentId) throw new Error('"agentId" is required');
81
+ if (!task) throw new Error('"task" is required');
82
+
83
+ const agent = deps.orchestrator.getAgent(agentId);
84
+ if (!agent) throw new Error(`Agent "${agentId}" not found. Use list to see active agents.`);
85
+
86
+ if (deps.taskManager.isAgentBusy(agentId)) {
87
+ throw new Error(`Agent "${agent.agent.role.name}" is already running a task.`);
88
+ }
89
+
90
+ const scopedRegistry = agentRegistries.get(agentId);
91
+ if (!scopedRegistry) {
92
+ throw new Error(`No tool registry for agent "${agentId}". Was it spawned via manage_agents?`);
93
+ }
94
+
95
+ deps.onProgress?.({
96
+ type: 'text',
97
+ agentName: agent.agent.role.name,
98
+ agentId,
99
+ data: `[Assigning task to ${agent.agent.role.name}...]`,
100
+ });
101
+
102
+ const taskId = deps.taskManager.launch({
103
+ agent,
104
+ task,
105
+ context,
106
+ llmManager: deps.llmManager,
107
+ toolRegistry: scopedRegistry,
108
+ onProgress: deps.onProgress,
109
+ });
110
+
111
+ console.log(`[ManageAgents] Assigned task ${taskId} to ${agent.agent.role.name}`);
112
+
113
+ return {
114
+ task_id: taskId,
115
+ agent_id: agentId,
116
+ agent_name: agent.agent.role.name,
117
+ status: 'running',
118
+ message: `Task assigned to ${agent.agent.role.name}. Use status or collect to check progress.`,
119
+ };
120
+ }
121
+
122
+ export function listPersistentAgents(deps: AgentToolDeps) {
123
+ const allAgents = deps.orchestrator.getAllAgents();
124
+ const primary = deps.orchestrator.getPrimary();
125
+ const subAgents = allAgents.filter(a => a.id !== primary?.id);
126
+
127
+ const agents: PersistentAgentSummary[] = subAgents.map(a => ({
128
+ agent_id: a.id,
129
+ name: a.agent.role.name,
130
+ specialist: a.agent.role.id,
131
+ status: a.agent.status,
132
+ current_task: a.agent.current_task,
133
+ busy: deps.taskManager.isAgentBusy(a.id),
134
+ }));
135
+
136
+ const tasks = deps.taskManager.listTasks().map(t => ({
137
+ task_id: t.id,
138
+ agent_name: t.agentName,
139
+ status: t.status,
140
+ task: t.task.slice(0, 100),
141
+ elapsed_seconds: Math.round(((t.completedAt ?? Date.now()) - t.startedAt) / 1000),
142
+ }));
143
+
144
+ return {
145
+ active_agents: agents.length,
146
+ agents,
147
+ tasks_total: tasks.length,
148
+ tasks_running: tasks.filter(t => t.status === 'running').length,
149
+ tasks,
150
+ };
151
+ }
152
+
153
+ export function terminatePersistentAgent(deps: AgentToolDeps, agentId: string) {
154
+ if (!agentId) throw new Error('"agentId" is required');
155
+
156
+ const agent = deps.orchestrator.getAgent(agentId);
157
+ if (!agent) throw new Error(`Agent "${agentId}" not found`);
158
+ if (!agentRegistries.has(agentId)) {
159
+ throw new Error(`Agent "${agentId}" is not a persistent managed agent.`);
160
+ }
161
+
162
+ const name = agent.agent.role.name;
163
+ agentRegistries.delete(agentId);
164
+ deps.orchestrator.terminateAgent(agentId);
165
+
166
+ console.log(`[ManageAgents] Terminated ${name} (${agentId})`);
167
+
168
+ return {
169
+ terminated: agentId,
170
+ name,
171
+ message: `${name} terminated.`,
172
+ };
173
+ }
174
+
175
+ export function createManageAgentsTool(deps: AgentToolDeps): ToolDefinition {
176
+ return {
177
+ name: 'manage_agents',
178
+ description: [
179
+ 'Manage persistent sub-agents for complex or parallel work.',
180
+ 'Use this when you need agents that stay alive across multiple tasks or run in parallel.',
181
+ 'For quick one-shot tasks, use delegate_task instead.',
182
+ '',
183
+ 'Actions:',
184
+ ' spawn — Create a persistent specialist agent (returns agent_id)',
185
+ ' assign — Send a task to an existing agent (async, returns task_id)',
186
+ ' status — Check agent or task status',
187
+ ' collect — Get full result of a completed task',
188
+ ' list — Show all active agents and tasks',
189
+ ' terminate — Shut down an agent',
190
+ '',
191
+ 'Available specialists: ' + Array.from(deps.specialists.keys()).join(', '),
192
+ '',
193
+ 'Workflow: spawn → assign → status/collect → terminate',
194
+ ].join('\n'),
195
+ category: 'delegation',
196
+ parameters: {
197
+ action: {
198
+ type: 'string',
199
+ description: 'The action: spawn, assign, status, collect, list, terminate',
200
+ required: true,
201
+ },
202
+ specialist: {
203
+ type: 'string',
204
+ description: 'Specialist role ID (required for spawn)',
205
+ required: false,
206
+ },
207
+ agent_id: {
208
+ type: 'string',
209
+ description: 'Agent ID (required for assign, terminate; optional for status)',
210
+ required: false,
211
+ },
212
+ task_id: {
213
+ type: 'string',
214
+ description: 'Task ID (for status, collect)',
215
+ required: false,
216
+ },
217
+ task: {
218
+ type: 'string',
219
+ description: 'Task description (required for assign)',
220
+ required: false,
221
+ },
222
+ context: {
223
+ type: 'string',
224
+ description: 'Background context for the task (optional for assign)',
225
+ required: false,
226
+ },
227
+ },
228
+ execute: async (params) => {
229
+ const action = params.action as string;
230
+
231
+ switch (action) {
232
+ case 'spawn':
233
+ return handleSpawn(deps, params);
234
+ case 'assign':
235
+ return handleAssign(deps, params);
236
+ case 'status':
237
+ return handleStatus(deps, params);
238
+ case 'collect':
239
+ return handleCollect(deps, params);
240
+ case 'list':
241
+ return handleList(deps);
242
+ case 'terminate':
243
+ return handleTerminate(deps, params);
244
+ default:
245
+ return `Error: Unknown action "${action}". Use: spawn, assign, status, collect, list, terminate`;
246
+ }
247
+ },
248
+ };
249
+ }
250
+
251
+ function handleSpawn(deps: AgentToolDeps, params: Record<string, unknown>): string {
252
+ try {
253
+ const specialistId = params.specialist as string;
254
+ return JSON.stringify(spawnPersistentAgent(deps, specialistId).summary);
255
+ } catch (err) {
256
+ return `Error: ${err instanceof Error ? err.message : String(err)}`;
257
+ }
258
+ }
259
+
260
+ async function handleAssign(deps: AgentToolDeps, params: Record<string, unknown>): Promise<string> {
261
+ try {
262
+ return JSON.stringify(await assignPersistentAgentTask(deps, {
263
+ agentId: params.agent_id as string,
264
+ task: params.task as string,
265
+ context: params.context as string | undefined,
266
+ }));
267
+ } catch (err) {
268
+ return `Error: ${err instanceof Error ? err.message : String(err)}`;
269
+ }
270
+ }
271
+
272
+ function handleStatus(deps: AgentToolDeps, params: Record<string, unknown>): string {
273
+ const taskId = params.task_id as string;
274
+ const agentId = params.agent_id as string;
275
+
276
+ if (taskId) {
277
+ const task = deps.taskManager.getTask(taskId);
278
+ if (!task) return `Error: Task "${taskId}" not found`;
279
+
280
+ const elapsed = task.completedAt
281
+ ? task.completedAt - task.startedAt
282
+ : Date.now() - task.startedAt;
283
+
284
+ return JSON.stringify({
285
+ task_id: task.id,
286
+ agent_name: task.agentName,
287
+ status: task.status,
288
+ task: task.task,
289
+ elapsed_ms: elapsed,
290
+ elapsed_seconds: Math.round(elapsed / 1000),
291
+ has_result: task.result !== null,
292
+ });
293
+ }
294
+
295
+ if (agentId) {
296
+ const agent = deps.orchestrator.getAgent(agentId);
297
+ if (!agent) return `Error: Agent "${agentId}" not found`;
298
+
299
+ const agentTask = deps.taskManager.getAgentTask(agentId);
300
+ return JSON.stringify({
301
+ agent_id: agentId,
302
+ name: agent.agent.role.name,
303
+ status: agent.agent.status,
304
+ current_task: agent.agent.current_task,
305
+ busy: deps.taskManager.isAgentBusy(agentId),
306
+ latest_task: agentTask ? {
307
+ task_id: agentTask.id,
308
+ status: agentTask.status,
309
+ task: agentTask.task,
310
+ } : null,
311
+ });
312
+ }
313
+
314
+ // No ID given — return summary
315
+ return handleList(deps);
316
+ }
317
+
318
+ function handleCollect(deps: AgentToolDeps, params: Record<string, unknown>): string {
319
+ const taskId = params.task_id as string;
320
+ if (!taskId) return 'Error: "task_id" is required for collect';
321
+
322
+ const task = deps.taskManager.getTask(taskId);
323
+ if (!task) return `Error: Task "${taskId}" not found`;
324
+
325
+ if (task.status === 'running') {
326
+ const elapsed = Math.round((Date.now() - task.startedAt) / 1000);
327
+ return JSON.stringify({
328
+ task_id: task.id,
329
+ status: 'running',
330
+ agent_name: task.agentName,
331
+ elapsed_seconds: elapsed,
332
+ message: 'Task is still running. Check back later.',
333
+ });
334
+ }
335
+
336
+ const result = task.result!;
337
+ const toolsList = result.toolsUsed.length > 0
338
+ ? result.toolsUsed.join(', ')
339
+ : 'none';
340
+
341
+ return JSON.stringify({
342
+ task_id: task.id,
343
+ status: task.status,
344
+ agent_name: task.agentName,
345
+ success: result.success,
346
+ response: result.response,
347
+ tools_used: toolsList,
348
+ tokens_used: result.tokensUsed.input + result.tokensUsed.output,
349
+ elapsed_seconds: Math.round(((task.completedAt ?? Date.now()) - task.startedAt) / 1000),
350
+ });
351
+ }
352
+
353
+ function handleList(deps: AgentToolDeps): string {
354
+ return JSON.stringify(listPersistentAgents(deps));
355
+ }
356
+
357
+ function handleTerminate(deps: AgentToolDeps, params: Record<string, unknown>): string {
358
+ try {
359
+ return JSON.stringify(terminatePersistentAgent(deps, params.agent_id as string));
360
+ } catch (err) {
361
+ return `Error: ${err instanceof Error ? err.message : String(err)}`;
362
+ }
363
+ }