@pellux/goodvibes-agent 1.1.7 → 1.3.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 (274) hide show
  1. package/CHANGELOG.md +329 -0
  2. package/README.md +46 -30
  3. package/dist/package/{ast-grep-napi.linux-x64-gnu-mkk8xwww.node → ast-grep-napi.linux-x64-gnu-swtppvy9.node} +0 -0
  4. package/dist/package/{ast-grep-napi.linux-x64-musl-ryqtgdv6.node → ast-grep-napi.linux-x64-musl-ttfcdtap.node} +0 -0
  5. package/dist/package/main.js +138903 -91477
  6. package/docs/README.md +14 -7
  7. package/docs/channels-remote-and-api.md +8 -5
  8. package/docs/connected-host.md +14 -12
  9. package/docs/getting-started.md +58 -30
  10. package/docs/knowledge-artifacts-and-multimodal.md +7 -5
  11. package/docs/project-planning.md +2 -2
  12. package/docs/providers-and-routing.md +11 -3
  13. package/docs/tools-and-commands.md +116 -45
  14. package/docs/voice-and-live-tts.md +3 -1
  15. package/package.json +3 -2
  16. package/release/release-notes.md +142 -5
  17. package/release/release-readiness.json +124 -94
  18. package/src/agent/assistant-cockpit.ts +247 -0
  19. package/src/agent/autonomy-schedule-format.ts +96 -0
  20. package/src/agent/autonomy-schedule.ts +514 -0
  21. package/src/agent/channel-delivery-receipts.ts +292 -0
  22. package/src/agent/competitive-feature-inventory.ts +294 -0
  23. package/src/agent/document-registry-types.ts +106 -0
  24. package/src/agent/document-registry.ts +734 -0
  25. package/src/agent/harness-control.ts +2 -2
  26. package/src/agent/memory-prompt.ts +2 -2
  27. package/src/agent/operator-actions.ts +50 -6
  28. package/src/agent/persona-registry.ts +8 -0
  29. package/src/agent/project-context-files.ts +273 -0
  30. package/src/agent/prompt-context-receipts.ts +502 -0
  31. package/src/agent/reminder-schedule-format.ts +16 -2
  32. package/src/agent/research-run-registry.ts +582 -0
  33. package/src/agent/research-source-registry.ts +441 -0
  34. package/src/agent/routine-registry.ts +19 -3
  35. package/src/agent/routine-schedule-format.ts +27 -2
  36. package/src/agent/runtime-profile-starters.ts +7 -0
  37. package/src/agent/runtime-profile.ts +106 -4
  38. package/src/agent/schedule-edit-format.ts +129 -0
  39. package/src/agent/schedule-edit.ts +496 -0
  40. package/src/agent/schedule-next-routes.ts +42 -0
  41. package/src/agent/setup-wizard-artifact-receipts.ts +366 -0
  42. package/src/agent/setup-wizard-checkpoint.ts +140 -0
  43. package/src/agent/setup-wizard.ts +683 -0
  44. package/src/agent/skill-registry-types.ts +102 -0
  45. package/src/agent/skill-registry.ts +81 -107
  46. package/src/agent/vibe-confirmation-routes.ts +62 -0
  47. package/src/agent/vibe-file.ts +285 -0
  48. package/src/cli/agent-knowledge-command.ts +45 -2
  49. package/src/cli/help.ts +3 -0
  50. package/src/cli/profiles-command.ts +25 -11
  51. package/src/config/agent-settings-policy.ts +1 -18
  52. package/src/input/agent-workspace-activation.ts +53 -0
  53. package/src/input/agent-workspace-artifact-browser-editor.ts +494 -0
  54. package/src/input/agent-workspace-artifact-metadata.ts +17 -0
  55. package/src/input/agent-workspace-basic-command-editors.ts +5 -1
  56. package/src/input/agent-workspace-categories.ts +153 -155
  57. package/src/input/agent-workspace-channel-triage.ts +481 -0
  58. package/src/input/agent-workspace-channels.ts +263 -0
  59. package/src/input/agent-workspace-command-editor.ts +152 -0
  60. package/src/input/agent-workspace-context-snapshot.ts +349 -0
  61. package/src/input/agent-workspace-delegation-editor-submission.ts +8 -0
  62. package/src/input/agent-workspace-document-editor.ts +502 -0
  63. package/src/input/agent-workspace-document-ops-editor.ts +523 -0
  64. package/src/input/agent-workspace-host-category.ts +1 -1
  65. package/src/input/agent-workspace-local-library-snapshot.ts +167 -0
  66. package/src/input/agent-workspace-model-compare-editor.ts +376 -0
  67. package/src/input/agent-workspace-model-compare-prompt-submission.ts +552 -0
  68. package/src/input/agent-workspace-model-compare-types.ts +82 -0
  69. package/src/input/agent-workspace-model-compare-utils.ts +35 -0
  70. package/src/input/agent-workspace-onboarding-categories.ts +141 -0
  71. package/src/input/agent-workspace-operations-command-editor-submission.ts +53 -0
  72. package/src/input/agent-workspace-operations-command-editors.ts +22 -0
  73. package/src/input/agent-workspace-research-report-editor.ts +212 -0
  74. package/src/input/agent-workspace-research-run-editor.ts +144 -0
  75. package/src/input/agent-workspace-research-source-editor.ts +167 -0
  76. package/src/input/agent-workspace-review-packet-snapshot.ts +593 -0
  77. package/src/input/agent-workspace-review-packet-utils.ts +265 -0
  78. package/src/input/agent-workspace-settings.ts +313 -30
  79. package/src/input/agent-workspace-setup-checkpoint-action.ts +127 -0
  80. package/src/input/agent-workspace-setup-snapshot.ts +217 -0
  81. package/src/input/agent-workspace-setup.ts +61 -1
  82. package/src/input/agent-workspace-snapshot.ts +184 -149
  83. package/src/input/agent-workspace-types.ts +287 -2
  84. package/src/input/agent-workspace.ts +23 -1
  85. package/src/input/command-registry.ts +9 -2
  86. package/src/input/commands/agent-runtime-profile-runtime.ts +28 -13
  87. package/src/input/commands/channels-runtime.ts +102 -114
  88. package/src/input/commands/delegation-runtime.ts +87 -24
  89. package/src/input/commands/knowledge-browser-flags.ts +12 -0
  90. package/src/input/commands/knowledge.ts +28 -14
  91. package/src/input/commands/operator-actions-runtime.ts +6 -3
  92. package/src/input/commands/schedule-runtime.ts +71 -14
  93. package/src/input/commands/session-content.ts +2 -2
  94. package/src/input/commands/shell-core.ts +2 -0
  95. package/src/input/commands/vibe-runtime.ts +140 -0
  96. package/src/input/commands.ts +2 -0
  97. package/src/input/connected-host-routes.ts +123 -0
  98. package/src/input/settings-modal-types.ts +14 -0
  99. package/src/input/setup-wizard-live-receipts.ts +76 -0
  100. package/src/main.ts +15 -15
  101. package/src/panels/qr-panel.ts +2 -2
  102. package/src/renderer/agent-workspace-context-lines.ts +701 -0
  103. package/src/renderer/agent-workspace.ts +68 -421
  104. package/src/renderer/settings-modal-helpers.ts +12 -0
  105. package/src/renderer/settings-modal.ts +12 -0
  106. package/src/runtime/bootstrap-command-context.ts +11 -1
  107. package/src/runtime/bootstrap-command-parts.ts +14 -3
  108. package/src/runtime/bootstrap-core.ts +39 -1
  109. package/src/runtime/bootstrap-shell.ts +6 -0
  110. package/src/runtime/bootstrap.ts +113 -30
  111. package/src/runtime/connected-host-auth.ts +3 -2
  112. package/src/runtime/execution-ledger.ts +231 -0
  113. package/src/runtime/services.ts +88 -10
  114. package/src/runtime/tool-permission-safety.ts +199 -5
  115. package/src/shell/session-continuity-hints.ts +39 -0
  116. package/src/tools/agent-artifacts-tool.ts +606 -0
  117. package/src/tools/agent-audit-tool.ts +155 -0
  118. package/src/tools/agent-autonomy-schedule-tool.ts +238 -0
  119. package/src/tools/agent-autonomy-tool.ts +140 -0
  120. package/src/tools/agent-channel-send-tool.ts +24 -2
  121. package/src/tools/agent-channels-tool.ts +140 -0
  122. package/src/tools/agent-computer-tool.ts +168 -0
  123. package/src/tools/agent-context-policy.ts +137 -7
  124. package/src/tools/agent-context-tool.ts +143 -0
  125. package/src/tools/agent-delegation-tool.ts +128 -0
  126. package/src/tools/agent-device-tool.ts +240 -0
  127. package/src/tools/agent-documents-tool.ts +684 -0
  128. package/src/tools/agent-execution-tool.ts +230 -0
  129. package/src/tools/agent-harness-agent-orchestration-policy.ts +75 -0
  130. package/src/tools/agent-harness-agent-orchestration.ts +791 -0
  131. package/src/tools/agent-harness-autonomy-intake.ts +785 -0
  132. package/src/tools/agent-harness-autonomy-live-records.ts +742 -0
  133. package/src/tools/agent-harness-autonomy-queue-types.ts +88 -0
  134. package/src/tools/agent-harness-autonomy-queue.ts +571 -0
  135. package/src/tools/agent-harness-autonomy-watcher-read-models.ts +509 -0
  136. package/src/tools/agent-harness-background-processes-types.ts +35 -0
  137. package/src/tools/agent-harness-background-processes.ts +798 -0
  138. package/src/tools/agent-harness-browser-cockpit-route.ts +373 -0
  139. package/src/tools/agent-harness-browser-control.ts +346 -0
  140. package/src/tools/agent-harness-browser-pwa-read-models.ts +622 -0
  141. package/src/tools/agent-harness-channel-metadata.ts +168 -10
  142. package/src/tools/agent-harness-cli-command-policy.ts +110 -0
  143. package/src/tools/agent-harness-command-catalog.ts +4 -2
  144. package/src/tools/agent-harness-command-runner.ts +42 -0
  145. package/src/tools/agent-harness-connected-host-status.ts +7 -4
  146. package/src/tools/agent-harness-delegation-posture.ts +234 -19
  147. package/src/tools/agent-harness-device-live-read-models.ts +366 -0
  148. package/src/tools/agent-harness-document-ops-reviewer-readiness.ts +234 -0
  149. package/src/tools/agent-harness-document-ops-types.ts +72 -0
  150. package/src/tools/agent-harness-document-ops.ts +671 -0
  151. package/src/tools/agent-harness-execution-history.ts +489 -0
  152. package/src/tools/agent-harness-execution-posture.ts +385 -0
  153. package/src/tools/agent-harness-file-recovery.ts +135 -0
  154. package/src/tools/agent-harness-interactive-runtime-records.ts +421 -0
  155. package/src/tools/agent-harness-keybinding-metadata.ts +16 -12
  156. package/src/tools/agent-harness-learning-curator-common.ts +102 -0
  157. package/src/tools/agent-harness-learning-curator-consolidation.ts +295 -0
  158. package/src/tools/agent-harness-learning-curator-proposals.ts +606 -0
  159. package/src/tools/agent-harness-learning-curator-types.ts +151 -0
  160. package/src/tools/agent-harness-learning-curator.ts +417 -0
  161. package/src/tools/agent-harness-local-model-benchmarks.ts +269 -0
  162. package/src/tools/agent-harness-local-model-cookbook.ts +238 -0
  163. package/src/tools/agent-harness-local-model-endpoints.ts +788 -0
  164. package/src/tools/agent-harness-local-model-smoke.ts +277 -0
  165. package/src/tools/agent-harness-local-model-url.ts +78 -0
  166. package/src/tools/agent-harness-media-posture.ts +223 -2
  167. package/src/tools/agent-harness-memory-external-providers.ts +796 -0
  168. package/src/tools/agent-harness-memory-posture.ts +672 -0
  169. package/src/tools/agent-harness-memory-provider-certification.ts +219 -0
  170. package/src/tools/agent-harness-memory-refinement.ts +340 -0
  171. package/src/tools/agent-harness-metadata.ts +100 -168
  172. package/src/tools/agent-harness-mode-catalog.ts +86 -33
  173. package/src/tools/agent-harness-model-catalog.ts +162 -0
  174. package/src/tools/agent-harness-model-provider-health.ts +283 -0
  175. package/src/tools/agent-harness-model-readiness.ts +432 -0
  176. package/src/tools/agent-harness-model-routing-types.ts +327 -0
  177. package/src/tools/agent-harness-model-routing-utils.ts +30 -0
  178. package/src/tools/agent-harness-model-routing.ts +165 -193
  179. package/src/tools/agent-harness-operator-methods.ts +115 -133
  180. package/src/tools/agent-harness-pairing-posture.ts +452 -18
  181. package/src/tools/agent-harness-personal-ops-certification.ts +116 -0
  182. package/src/tools/agent-harness-personal-ops-discovery.ts +533 -0
  183. package/src/tools/agent-harness-personal-ops-intake.ts +385 -0
  184. package/src/tools/agent-harness-personal-ops-lanes.ts +270 -0
  185. package/src/tools/agent-harness-personal-ops-operations.ts +225 -0
  186. package/src/tools/agent-harness-personal-ops-provider-records.ts +358 -0
  187. package/src/tools/agent-harness-personal-ops-provider-task-records.ts +321 -0
  188. package/src/tools/agent-harness-personal-ops-records.ts +684 -0
  189. package/src/tools/agent-harness-personal-ops-runner.ts +637 -0
  190. package/src/tools/agent-harness-personal-ops-types.ts +240 -0
  191. package/src/tools/agent-harness-personal-ops.ts +613 -0
  192. package/src/tools/agent-harness-project-context.ts +144 -0
  193. package/src/tools/agent-harness-prompt-context.ts +589 -0
  194. package/src/tools/agent-harness-provider-account-metadata.ts +2 -2
  195. package/src/tools/agent-harness-release-evidence.ts +9 -7
  196. package/src/tools/agent-harness-release-readiness.ts +9 -7
  197. package/src/tools/agent-harness-remote-read-models.ts +541 -0
  198. package/src/tools/agent-harness-research-briefing.ts +446 -0
  199. package/src/tools/agent-harness-research-live-read-models.ts +500 -0
  200. package/src/tools/agent-harness-research-queue.ts +250 -0
  201. package/src/tools/agent-harness-research-runs.ts +307 -0
  202. package/src/tools/agent-harness-research-workflow.ts +395 -0
  203. package/src/tools/agent-harness-security-posture.ts +2 -2
  204. package/src/tools/agent-harness-service-posture.ts +9 -9
  205. package/src/tools/agent-harness-session-metadata.ts +3 -3
  206. package/src/tools/agent-harness-setup-connected-host.ts +501 -0
  207. package/src/tools/agent-harness-setup-describe.ts +557 -0
  208. package/src/tools/agent-harness-setup-handoffs.ts +502 -0
  209. package/src/tools/agent-harness-setup-model-helpers.ts +102 -0
  210. package/src/tools/agent-harness-setup-plan.ts +253 -0
  211. package/src/tools/agent-harness-setup-posture-types.ts +218 -0
  212. package/src/tools/agent-harness-setup-posture-utils.ts +173 -0
  213. package/src/tools/agent-harness-setup-posture.ts +449 -221
  214. package/src/tools/agent-harness-setup-smoke.ts +533 -0
  215. package/src/tools/agent-harness-sudo-posture.ts +114 -0
  216. package/src/tools/agent-harness-tool-schema.ts +142 -7
  217. package/src/tools/agent-harness-tool-types.ts +83 -0
  218. package/src/tools/agent-harness-tool-utils.ts +43 -0
  219. package/src/tools/agent-harness-tool.ts +319 -370
  220. package/src/tools/agent-harness-ui-surface-metadata.ts +76 -69
  221. package/src/tools/agent-harness-ui-surface-types.ts +46 -0
  222. package/src/tools/agent-harness-vibe-health.ts +105 -0
  223. package/src/tools/agent-harness-workspace-action-runner.ts +149 -0
  224. package/src/tools/agent-harness-workspace-actions.ts +114 -8
  225. package/src/tools/agent-harness-workspace-editor-execution.ts +214 -0
  226. package/src/tools/agent-harness-workspace-editor-runner.ts +755 -0
  227. package/src/tools/agent-host-tool.ts +159 -0
  228. package/src/tools/agent-knowledge-ingest-tool.ts +34 -1
  229. package/src/tools/agent-learning-consolidation-core.ts +327 -0
  230. package/src/tools/agent-learning-consolidation-tool.ts +758 -0
  231. package/src/tools/agent-memory-tool.ts +253 -0
  232. package/src/tools/agent-model-compare-export.ts +315 -0
  233. package/src/tools/agent-model-compare-handoff.ts +592 -0
  234. package/src/tools/agent-model-compare-judgment.ts +633 -0
  235. package/src/tools/agent-model-compare-run.ts +735 -0
  236. package/src/tools/agent-model-compare-tool.ts +698 -0
  237. package/src/tools/agent-model-compare-types.ts +191 -0
  238. package/src/tools/agent-model-compare-utils.ts +93 -0
  239. package/src/tools/agent-models-tool.ts +208 -0
  240. package/src/tools/agent-operator-action-tool.ts +1 -1
  241. package/src/tools/agent-operator-method-tool.ts +643 -0
  242. package/src/tools/agent-personal-ops-tool.ts +197 -0
  243. package/src/tools/agent-policy-explanation.ts +415 -0
  244. package/src/tools/agent-research-report-tool.ts +608 -0
  245. package/src/tools/agent-research-runner.ts +367 -0
  246. package/src/tools/agent-research-runs-tool.ts +434 -0
  247. package/src/tools/agent-research-sources-tool.ts +443 -0
  248. package/src/tools/agent-research-tool.ts +515 -0
  249. package/src/tools/agent-review-packet-presets-core.ts +757 -0
  250. package/src/tools/agent-review-packet-presets-tool.ts +466 -0
  251. package/src/tools/agent-review-packet-share-tool.ts +305 -0
  252. package/src/tools/agent-route-planner-candidates-setup.ts +411 -0
  253. package/src/tools/agent-route-planner-candidates-surfaces.ts +432 -0
  254. package/src/tools/agent-route-planner-candidates-work.ts +251 -0
  255. package/src/tools/agent-route-planner-helpers.ts +667 -0
  256. package/src/tools/agent-route-planner.ts +185 -0
  257. package/src/tools/agent-route-tool.ts +105 -0
  258. package/src/tools/agent-schedule-edit-tool.ts +210 -0
  259. package/src/tools/agent-schedule-tool.ts +282 -0
  260. package/src/tools/agent-security-tool.ts +145 -0
  261. package/src/tools/agent-sessions-tool.ts +122 -0
  262. package/src/tools/agent-settings-import-tool.ts +104 -0
  263. package/src/tools/agent-settings-tool.ts +176 -0
  264. package/src/tools/agent-setup-tool.ts +226 -0
  265. package/src/tools/agent-support-tool.ts +122 -0
  266. package/src/tools/agent-terminal-process-tools.ts +167 -0
  267. package/src/tools/agent-tool-policy-guard-types.ts +77 -0
  268. package/src/tools/agent-tool-policy-guard.ts +106 -85
  269. package/src/tools/agent-vibe-tool.ts +297 -0
  270. package/src/tools/agent-work-plan-tool.ts +265 -6
  271. package/src/tools/agent-workspace-tool.ts +305 -0
  272. package/src/tools/artifact-archive.ts +169 -0
  273. package/src/tools/tool-definition-compaction.ts +36 -0
  274. package/src/version.ts +1 -1
@@ -0,0 +1,297 @@
1
+ import type { Tool } from '@pellux/goodvibes-sdk/platform/types';
2
+ import type { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
3
+ import { AgentPersonaRegistry } from '../agent/persona-registry.ts';
4
+ import {
5
+ discoverVibeFiles,
6
+ initVibeFile,
7
+ loadVibeImportSource,
8
+ resolveVibePathReference,
9
+ type AgentVibeFile,
10
+ } from '../agent/vibe-file.ts';
11
+ import {
12
+ vibeImportPersonaConfirmationRoutes,
13
+ vibeInitConfirmationRoutes,
14
+ } from '../agent/vibe-confirmation-routes.ts';
15
+ import type { CommandContext } from '../input/command-registry.ts';
16
+ import { requireShellPaths } from '../input/commands/runtime-services.ts';
17
+ import { previewHarnessText } from './agent-harness-text.ts';
18
+
19
+ type AgentVibeAction = 'status' | 'show' | 'init' | 'import_persona';
20
+
21
+ interface AgentVibeToolArgs {
22
+ readonly action?: unknown;
23
+ readonly mode?: unknown;
24
+ readonly scope?: unknown;
25
+ readonly reference?: unknown;
26
+ readonly path?: unknown;
27
+ readonly target?: unknown;
28
+ readonly query?: unknown;
29
+ readonly name?: unknown;
30
+ readonly description?: unknown;
31
+ readonly force?: unknown;
32
+ readonly review?: unknown;
33
+ readonly use?: unknown;
34
+ readonly includeParameters?: unknown;
35
+ readonly confirm?: unknown;
36
+ readonly explicitUserRequest?: unknown;
37
+ }
38
+
39
+ function output(value: unknown): { readonly success: true; readonly output: string } {
40
+ return {
41
+ success: true,
42
+ output: typeof value === 'string' ? value : JSON.stringify(value, null, 2),
43
+ };
44
+ }
45
+
46
+ function error(message: string): { readonly success: false; readonly error: string } {
47
+ return { success: false, error: message };
48
+ }
49
+
50
+ function readString(value: unknown): string {
51
+ return typeof value === 'string' ? value.trim() : '';
52
+ }
53
+
54
+ function readBoolean(value: unknown): boolean {
55
+ return value === true || (typeof value === 'string' && ['true', 'yes', 'use', 'review', 'apply', 'run'].includes(value.trim().toLowerCase()));
56
+ }
57
+
58
+ function normalizeVibeAction(value: unknown): AgentVibeAction | null {
59
+ const action = readString(value).toLowerCase().replace(/-/g, '_');
60
+ if (!action) return null;
61
+ if (action === 'status' || action === 'summary' || action === 'list' || action === 'inspect') return 'status';
62
+ if (action === 'show' || action === 'file' || action === 'source' || action === 'read') return 'show';
63
+ if (action === 'init' || action === 'create' || action === 'new') return 'init';
64
+ if (action === 'import' || action === 'import_persona' || action === 'persona') return 'import_persona';
65
+ return null;
66
+ }
67
+
68
+ function readAction(args: AgentVibeToolArgs): AgentVibeAction {
69
+ return normalizeVibeAction(args.action) ?? normalizeVibeAction(args.mode) ?? 'status';
70
+ }
71
+
72
+ function readReference(args: AgentVibeToolArgs): string {
73
+ return readString(args.path)
74
+ || readString(args.reference)
75
+ || readString(args.target)
76
+ || readString(args.query)
77
+ || readString(args.scope)
78
+ || 'project';
79
+ }
80
+
81
+ function readScope(args: AgentVibeToolArgs): 'project' | 'global' {
82
+ return readString(args.scope).toLowerCase() === 'global' ? 'global' : 'project';
83
+ }
84
+
85
+ function requiresConfirmedUserIntent(args: AgentVibeToolArgs, action: AgentVibeAction): string | null {
86
+ if (!readBoolean(args.confirm)) return `vibe action:"${action}" requires confirm:true after the user asks for this personality change.`;
87
+ const explicitUserRequest = readString(args.explicitUserRequest);
88
+ if (!explicitUserRequest) return `vibe action:"${action}" requires explicitUserRequest when confirm is true.`;
89
+ return null;
90
+ }
91
+
92
+ function describeVibeFile(file: AgentVibeFile): Record<string, unknown> {
93
+ return {
94
+ scope: file.scope,
95
+ path: file.path,
96
+ truncated: file.truncated,
97
+ name: file.frontmatter.name?.trim() || undefined,
98
+ description: file.frontmatter.description?.trim() || file.frontmatter.summary?.trim() || undefined,
99
+ bodyCharacters: file.body.length,
100
+ };
101
+ }
102
+
103
+ export function createAgentVibeTool(commandContext: CommandContext): Tool {
104
+ return {
105
+ definition: {
106
+ name: 'vibe',
107
+ description: 'Inspect and manage VIBE.md personality files.',
108
+ parameters: {
109
+ type: 'object',
110
+ properties: {
111
+ action: {
112
+ type: 'string',
113
+ enum: ['status', 'show', 'init', 'import_persona'],
114
+ description: 'Read status/show; confirm init or import_persona.',
115
+ },
116
+ mode: { type: 'string', description: 'Alias for action.' },
117
+ scope: { type: 'string', enum: ['project', 'global'], description: 'VIBE.md scope for init/show/import.' },
118
+ reference: { type: 'string', description: 'project, global, or path reference.' },
119
+ path: { type: 'string', description: 'Path reference for show/import.' },
120
+ target: { type: 'string', description: 'Alias for reference.' },
121
+ query: { type: 'string', description: 'Alias for reference.' },
122
+ name: { type: 'string', description: 'Persona name when importing.' },
123
+ description: { type: 'string', description: 'Persona description when importing.' },
124
+ force: { type: 'boolean', description: 'Replace existing VIBE.md on confirmed init.' },
125
+ review: { type: 'boolean', description: 'Mark imported persona reviewed.' },
126
+ use: { type: 'boolean', description: 'Set imported persona active.' },
127
+ includeParameters: { type: 'boolean', description: 'Include searched paths and route details.' },
128
+ confirm: { type: 'boolean', description: 'Required true for init/import_persona.' },
129
+ explicitUserRequest: { type: 'string', description: 'User request authorizing confirmed personality effects.' },
130
+ },
131
+ additionalProperties: false,
132
+ },
133
+ sideEffects: ['state'],
134
+ concurrency: 'serial',
135
+ },
136
+ execute: async (rawArgs: unknown) => {
137
+ const args = (rawArgs && typeof rawArgs === 'object' && !Array.isArray(rawArgs) ? rawArgs : {}) as AgentVibeToolArgs;
138
+ const shellPaths = requireShellPaths(commandContext);
139
+ const action = readAction(args);
140
+
141
+ try {
142
+ if (action === 'status') {
143
+ const snapshot = discoverVibeFiles(shellPaths);
144
+ return output({
145
+ status: snapshot.blocked.length > 0 || snapshot.files.some((file) => file.truncated)
146
+ ? 'check'
147
+ : snapshot.files.length > 0
148
+ ? 'ready'
149
+ : 'recommended',
150
+ action: 'vibe_status',
151
+ applied: snapshot.files.length,
152
+ blocked: snapshot.blocked.length,
153
+ truncated: snapshot.files.filter((file) => file.truncated).length,
154
+ projectInitPath: snapshot.projectInitPath,
155
+ globalInitPath: snapshot.globalInitPath,
156
+ files: snapshot.files.map(describeVibeFile),
157
+ blockedFiles: snapshot.blocked.map((file) => ({
158
+ scope: file.scope,
159
+ path: file.path,
160
+ reason: previewHarnessText(file.reason, 220),
161
+ })),
162
+ routes: {
163
+ showProject: 'vibe action:"show" scope:"project"',
164
+ showGlobal: 'vibe action:"show" scope:"global"',
165
+ initProject: 'vibe action:"init" scope:"project" confirm:true explicitUserRequest:"..."',
166
+ importProjectPersona: 'vibe action:"import_persona" scope:"project" review:true use:true confirm:true explicitUserRequest:"..."',
167
+ promptContext: 'context action:"prompt" includeParameters:true',
168
+ },
169
+ ...(readBoolean(args.includeParameters) ? { searchedPaths: snapshot.searchedPaths } : {}),
170
+ });
171
+ }
172
+
173
+ if (action === 'show') {
174
+ const reference = readReference(args);
175
+ const source = loadVibeImportSource(shellPaths, reference);
176
+ return output({
177
+ status: 'ready',
178
+ action: 'vibe_show',
179
+ source: source.scope,
180
+ path: source.path,
181
+ name: source.name,
182
+ description: source.description,
183
+ bodyCharacters: source.body.length,
184
+ body: source.body,
185
+ policy: {
186
+ boundary: 'Loaded only after VIBE.md secret-looking content scan; blocked files are not returned.',
187
+ },
188
+ });
189
+ }
190
+
191
+ if (action === 'init') {
192
+ const scope = readScope(args);
193
+ const path = resolveVibePathReference(shellPaths, scope);
194
+ if (!readBoolean(args.confirm)) {
195
+ return output({
196
+ status: 'confirmation_required',
197
+ action: 'vibe_init',
198
+ scope,
199
+ path,
200
+ force: readBoolean(args.force),
201
+ next: 'Run with confirm:true and explicitUserRequest after the user asks to create or replace this VIBE.md file.',
202
+ confirmationRoutes: vibeInitConfirmationRoutes(scope, readBoolean(args.force)),
203
+ });
204
+ }
205
+ const intentError = requiresConfirmedUserIntent(args, 'init');
206
+ if (intentError) return error(intentError);
207
+ const result = initVibeFile(shellPaths, { scope, force: readBoolean(args.force) });
208
+ return output({
209
+ status: result.created ? 'created' : 'already_exists',
210
+ action: 'vibe_init',
211
+ scope,
212
+ path: result.path,
213
+ created: result.created,
214
+ next: result.created ? 'Edit this VIBE.md with the personality instructions the user wants.' : 'Use force:true only when the user asks to replace the existing file.',
215
+ policy: {
216
+ confirmation: 'confirmed',
217
+ explicitUserRequest: readString(args.explicitUserRequest),
218
+ boundary: 'Wrote only the scoped VIBE.md starter file.',
219
+ },
220
+ });
221
+ }
222
+
223
+ if (action === 'import_persona') {
224
+ const reference = readReference(args);
225
+ const source = loadVibeImportSource(shellPaths, reference);
226
+ const name = readString(args.name) || source.name;
227
+ const description = readString(args.description) || source.description;
228
+ if (!readBoolean(args.confirm)) {
229
+ return output({
230
+ status: 'confirmation_required',
231
+ action: 'vibe_import_persona',
232
+ source: source.scope,
233
+ path: source.path,
234
+ name,
235
+ description,
236
+ review: readBoolean(args.review),
237
+ use: readBoolean(args.use),
238
+ bodyCharacters: source.body.length,
239
+ next: 'Run with confirm:true and explicitUserRequest after the user asks to import VIBE.md into Agent-local personas.',
240
+ confirmationRoutes: vibeImportPersonaConfirmationRoutes({
241
+ reference,
242
+ name,
243
+ description,
244
+ review: readBoolean(args.review),
245
+ use: readBoolean(args.use),
246
+ }),
247
+ });
248
+ }
249
+ const intentError = requiresConfirmedUserIntent(args, 'import_persona');
250
+ if (intentError) return error(intentError);
251
+ const personaRegistry = AgentPersonaRegistry.fromShellPaths(shellPaths);
252
+ const created = personaRegistry.create({
253
+ name,
254
+ description,
255
+ body: source.body,
256
+ tags: ['vibe', source.scope],
257
+ triggers: ['vibe'],
258
+ source: 'imported',
259
+ provenance: `Imported VIBE.md (${source.scope}): ${source.path}`,
260
+ });
261
+ const reviewed = readBoolean(args.review) ? personaRegistry.markReviewed(created.id) : created;
262
+ const active = readBoolean(args.use) ? personaRegistry.setActive(created.id) : null;
263
+ return output({
264
+ status: 'imported',
265
+ action: 'vibe_import_persona',
266
+ source: source.scope,
267
+ path: source.path,
268
+ persona: {
269
+ id: reviewed.id,
270
+ name: reviewed.name,
271
+ description: reviewed.description,
272
+ reviewState: reviewed.reviewState,
273
+ active: active?.id === reviewed.id,
274
+ },
275
+ routes: {
276
+ inspectPersona: `agent_local_registry domain:"persona" action:"get" id:"${reviewed.id}"`,
277
+ promptContext: 'context action:"prompt" includeParameters:true',
278
+ },
279
+ policy: {
280
+ confirmation: 'confirmed',
281
+ explicitUserRequest: readString(args.explicitUserRequest),
282
+ boundary: 'Created only one Agent-local persona from secret-scanned VIBE.md content; no default Knowledge write.',
283
+ },
284
+ });
285
+ }
286
+
287
+ return error('Unknown VIBE.md action. Use action:"status" to inspect personality files.');
288
+ } catch (caught) {
289
+ return error(caught instanceof Error ? caught.message : String(caught));
290
+ }
291
+ },
292
+ };
293
+ }
294
+
295
+ export function registerAgentVibeTool(registry: ToolRegistry, commandContext: CommandContext): void {
296
+ if (!registry.has('vibe')) registry.register(createAgentVibeTool(commandContext));
297
+ }
@@ -13,27 +13,47 @@ export type AgentWorkPlanAction =
13
13
  | 'create'
14
14
  | 'update'
15
15
  | 'set_status'
16
+ | 'dispatch_agents'
16
17
  | 'remove'
17
18
  | 'clear_completed';
18
19
 
19
20
  export interface AgentWorkPlanToolArgs {
20
21
  readonly action?: unknown;
21
22
  readonly id?: unknown;
23
+ readonly ids?: unknown;
22
24
  readonly title?: unknown;
23
25
  readonly status?: unknown;
24
26
  readonly owner?: unknown;
25
27
  readonly source?: unknown;
26
28
  readonly notes?: unknown;
29
+ readonly template?: unknown;
30
+ readonly model?: unknown;
31
+ readonly provider?: unknown;
32
+ readonly reasoningEffort?: unknown;
33
+ readonly tools?: unknown;
34
+ readonly successCriteria?: unknown;
35
+ readonly requiredEvidence?: unknown;
36
+ readonly writeScope?: unknown;
37
+ readonly executionProtocol?: unknown;
38
+ readonly reviewMode?: unknown;
39
+ readonly communicationLane?: unknown;
40
+ readonly cohort?: unknown;
41
+ readonly agentContext?: unknown;
27
42
  readonly confirm?: unknown;
28
43
  readonly explicitUserRequest?: unknown;
29
44
  }
30
45
 
46
+ interface AgentWorkPlanToolOptions {
47
+ readonly toolRegistry?: Pick<ToolRegistry, 'has' | 'execute'>;
48
+ }
49
+
31
50
  const ACTIONS: readonly AgentWorkPlanAction[] = [
32
51
  'list',
33
52
  'get',
34
53
  'create',
35
54
  'update',
36
55
  'set_status',
56
+ 'dispatch_agents',
37
57
  'remove',
38
58
  'clear_completed',
39
59
  ];
@@ -54,6 +74,12 @@ function readBoolean(value: unknown): boolean {
54
74
  return value === true || value === 'true' || value === 'yes';
55
75
  }
56
76
 
77
+ function readStringList(value: unknown): readonly string[] {
78
+ if (Array.isArray(value)) return value.map(readString).filter((entry) => entry.length > 0);
79
+ const text = readString(value);
80
+ return text ? text.split(/[\n,]/).map((entry) => entry.trim()).filter((entry) => entry.length > 0) : [];
81
+ }
82
+
57
83
  function failure(error: string): { readonly success: false; readonly error: string } {
58
84
  return { success: false, error };
59
85
  }
@@ -74,6 +100,11 @@ function requireTitle(args: AgentWorkPlanToolArgs): string {
74
100
  return title;
75
101
  }
76
102
 
103
+ function dispatchIds(args: AgentWorkPlanToolArgs): readonly string[] {
104
+ const selected = [...readStringList(args.ids), readString(args.id)].filter((entry) => entry.length > 0);
105
+ return [...new Set(selected)];
106
+ }
107
+
77
108
  function readOptionalStatus(value: unknown): WorkPlanItemStatus | undefined {
78
109
  if (value === undefined || value === null || readString(value) === '') return undefined;
79
110
  if (!isStatus(value)) throw new Error(`Invalid status. Valid values ${WORK_PLAN_STATUSES.join(', ')}.`);
@@ -91,6 +122,51 @@ function formatItem(item: WorkPlanItem): string {
91
122
  return `${item.id} ${formatStatus(item.status)}${owner}${source}${completed} ${item.title}`;
92
123
  }
93
124
 
125
+ function routeArg(value: string): string {
126
+ return JSON.stringify(value);
127
+ }
128
+
129
+ function agentRoute(mode: string, agentId: string): string {
130
+ return `agent { mode: ${routeArg(mode)}, agentId: ${routeArg(agentId)} }`;
131
+ }
132
+
133
+ function workPlanRoute(action: string, id?: string, status?: WorkPlanItemStatus): string {
134
+ return [
135
+ `agent_work_plan action:${routeArg(action)}`,
136
+ id ? `id:${routeArg(id)}` : '',
137
+ status ? `status:${routeArg(status)}` : '',
138
+ ].filter(Boolean).join(' ');
139
+ }
140
+
141
+ function linkedAgentId(item: WorkPlanItem): string {
142
+ const value = item.linked?.agentId;
143
+ return typeof value === 'string' && value.trim() ? value.trim() : '';
144
+ }
145
+
146
+ function nextRouteLine(id: string, route: string): string {
147
+ return ` ${id} ${route}`;
148
+ }
149
+
150
+ function workPlanNextRouteLines(item: WorkPlanItem): readonly string[] {
151
+ const agentId = linkedAgentId(item);
152
+ const lines = [
153
+ ' nextRoutes',
154
+ nextRouteLine('inspectWorkItem', workPlanRoute('get', item.id)),
155
+ nextRouteLine('markDone', workPlanRoute('set_status', item.id, 'done')),
156
+ nextRouteLine('markBlocked', workPlanRoute('set_status', item.id, 'blocked')),
157
+ ];
158
+ if (agentId) {
159
+ lines.push(
160
+ nextRouteLine('inspectAgent', agentRoute('get', agentId)),
161
+ nextRouteLine('waitAgent', agentRoute('wait', agentId)),
162
+ nextRouteLine('messageAgent', agentRoute('message', agentId)),
163
+ nextRouteLine('cancelAgent', agentRoute('cancel', agentId)),
164
+ nextRouteLine('orchestrationDetail', `agent_harness mode:"agent_orchestration_agent" agentId:${routeArg(agentId)} includeParameters:true`),
165
+ );
166
+ }
167
+ return lines;
168
+ }
169
+
94
170
  function formatItemDetail(item: WorkPlanItem): string {
95
171
  return [
96
172
  formatItem(item),
@@ -101,9 +177,16 @@ function formatItemDetail(item: WorkPlanItem): string {
101
177
  : 'linked (none)',
102
178
  '',
103
179
  item.notes || '(no notes)',
180
+ '',
181
+ ...workPlanNextRouteLines(item),
104
182
  ].join('\n');
105
183
  }
106
184
 
185
+ function previewText(value: string, maxLength: number): string {
186
+ const normalized = value.replace(/\s+/g, ' ').trim();
187
+ return normalized.length > maxLength ? `${normalized.slice(0, Math.max(0, maxLength - 1))}...` : normalized;
188
+ }
189
+
107
190
  function resolveItem(store: WorkPlanStore, idOrPrefix: string): WorkPlanItem {
108
191
  const needle = idOrPrefix.trim();
109
192
  if (!needle) throw new Error('id is required.');
@@ -118,6 +201,12 @@ function resolveItem(store: WorkPlanStore, idOrPrefix: string): WorkPlanItem {
118
201
  throw new Error(`Work plan item not found ${needle}`);
119
202
  }
120
203
 
204
+ function resolveDispatchItems(store: WorkPlanStore, args: AgentWorkPlanToolArgs): readonly WorkPlanItem[] {
205
+ const ids = dispatchIds(args);
206
+ if (ids.length === 0) throw new Error('id or ids is required for dispatch_agents.');
207
+ return ids.map((id) => resolveItem(store, id));
208
+ }
209
+
121
210
  function listOutput(store: WorkPlanStore): string {
122
211
  const plan = store.getActivePlan();
123
212
  const counts = new Map<WorkPlanItemStatus, number>(WORK_PLAN_STATUSES.map((status) => [status, 0]));
@@ -134,6 +223,7 @@ function listOutput(store: WorkPlanStore): string {
134
223
  }
135
224
  lines.push('', ...plan.items.slice(0, 20).map(formatItem));
136
225
  if (plan.items.length > 20) lines.push(`${plan.items.length - 20} more item(s) omitted.`);
226
+ lines.push('', 'Visible dispatch route: agent_work_plan action:"dispatch_agents" ids:["..."] confirm:true explicitUserRequest:"..."');
137
227
  return lines.join('\n');
138
228
  }
139
229
 
@@ -150,6 +240,21 @@ function requireDestructiveConfirmation(args: AgentWorkPlanToolArgs, action: str
150
240
  return null;
151
241
  }
152
242
 
243
+ function requireConfirmedDispatch(args: AgentWorkPlanToolArgs, items: readonly WorkPlanItem[], route: string): string | null {
244
+ const explicitUserRequest = readString(args.explicitUserRequest);
245
+ if (!explicitUserRequest) return 'explicitUserRequest is required before dispatching work plan items to visible agents.';
246
+ if (readBoolean(args.confirm)) return null;
247
+ return [
248
+ 'Agent work plan dispatch preview',
249
+ ' policy visible agent dispatch requires confirm:true and an explicit user request',
250
+ ` request ${previewText(explicitUserRequest, 160)}`,
251
+ ` route ${route}`,
252
+ ` selected ${items.length}`,
253
+ ...items.map((item) => ` - ${item.id} ${formatStatus(item.status)} ${previewText(item.title, 120)}`),
254
+ ' no agents were spawned and no work-plan receipts were written',
255
+ ].join('\n');
256
+ }
257
+
153
258
  function updatePatch(args: AgentWorkPlanToolArgs): Parameters<WorkPlanStore['updateItem']>[1] {
154
259
  const status = readOptionalStatus(args.status);
155
260
  return {
@@ -161,28 +266,181 @@ function updatePatch(args: AgentWorkPlanToolArgs): Parameters<WorkPlanStore['upd
161
266
  };
162
267
  }
163
268
 
164
- export function createAgentWorkPlanTool(store: WorkPlanStore): Tool {
269
+ function optionalStringProperty(value: unknown): string | undefined {
270
+ return readString(value) || undefined;
271
+ }
272
+
273
+ function optionalStringListProperty(value: unknown): readonly string[] | undefined {
274
+ const entries = readStringList(value);
275
+ return entries.length > 0 ? entries : undefined;
276
+ }
277
+
278
+ function workPlanAgentContext(item: WorkPlanItem, args: AgentWorkPlanToolArgs): string {
279
+ return [
280
+ readString(args.agentContext),
281
+ `Work plan item id: ${item.id}`,
282
+ `Work plan item title: ${item.title}`,
283
+ item.owner ? `Work plan owner: ${item.owner}` : '',
284
+ item.source ? `Work plan source: ${item.source}` : '',
285
+ item.notes ? `Work plan notes:\n${item.notes}` : '',
286
+ 'Keep progress visible, return evidence, and make the final status easy for the user to review.',
287
+ ].filter((part) => part.trim().length > 0).join('\n\n');
288
+ }
289
+
290
+ function agentTaskForItem(item: WorkPlanItem, args: AgentWorkPlanToolArgs): Record<string, unknown> {
291
+ return {
292
+ task: item.title,
293
+ ...(optionalStringProperty(args.template) ? { template: optionalStringProperty(args.template) } : {}),
294
+ ...(optionalStringProperty(args.model) ? { model: optionalStringProperty(args.model) } : {}),
295
+ ...(optionalStringProperty(args.provider) ? { provider: optionalStringProperty(args.provider) } : {}),
296
+ ...(optionalStringProperty(args.reasoningEffort) ? { reasoningEffort: optionalStringProperty(args.reasoningEffort) } : {}),
297
+ ...(optionalStringListProperty(args.tools) ? { tools: optionalStringListProperty(args.tools), restrictTools: true } : {}),
298
+ ...(optionalStringListProperty(args.successCriteria) ? { successCriteria: optionalStringListProperty(args.successCriteria) } : {}),
299
+ requiredEvidence: optionalStringListProperty(args.requiredEvidence) ?? ['result summary', 'verification or blocker', 'changed files or artifacts when applicable'],
300
+ ...(optionalStringListProperty(args.writeScope) ? { writeScope: optionalStringListProperty(args.writeScope) } : {}),
301
+ ...(optionalStringProperty(args.executionProtocol) ? { executionProtocol: optionalStringProperty(args.executionProtocol) } : {}),
302
+ ...(optionalStringProperty(args.reviewMode) ? { reviewMode: optionalStringProperty(args.reviewMode) } : {}),
303
+ ...(optionalStringProperty(args.communicationLane) ? { communicationLane: optionalStringProperty(args.communicationLane) } : {}),
304
+ context: workPlanAgentContext(item, args),
305
+ orchestrationNodeId: item.id,
306
+ };
307
+ }
308
+
309
+ function parseAgentToolOutput(outputText: string): Record<string, unknown> {
310
+ const parsed = JSON.parse(outputText) as unknown;
311
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error('agent tool returned non-object output.');
312
+ return parsed as Record<string, unknown>;
313
+ }
314
+
315
+ function agentIdsFromDispatchPayload(payload: Record<string, unknown>): readonly string[] {
316
+ const direct = readString(payload.agentId);
317
+ if (direct) return [direct];
318
+ if (!Array.isArray(payload.agents)) return [];
319
+ return payload.agents
320
+ .map((entry) => entry && typeof entry === 'object' ? readString((entry as Record<string, unknown>).id) || readString((entry as Record<string, unknown>).agentId) : '')
321
+ .filter((entry) => entry.length > 0);
322
+ }
323
+
324
+ function appendDispatchReceipt(notes: string | undefined, agentId: string, route: string, cohort: string, explicitUserRequest: string): string {
325
+ const receipt = [
326
+ `Agent dispatch receipt ${new Date().toISOString()}`,
327
+ `agent ${agentId}`,
328
+ `route ${route}`,
329
+ `cohort ${cohort}`,
330
+ `request ${previewText(explicitUserRequest, 160)}`,
331
+ ].join('; ');
332
+ return [notes?.trim() ?? '', receipt].filter((part) => part.length > 0).join('\n\n');
333
+ }
334
+
335
+ function dispatchNextRouteLines(receipts: readonly { readonly item: WorkPlanItem; readonly agentId: string }[]): readonly string[] {
336
+ return [
337
+ ' nextRoutes',
338
+ nextRouteLine('orchestration', 'agent_harness mode:"agent_orchestration" includeParameters:true'),
339
+ nextRouteLine('workPlan', 'agent_work_plan action:"list"'),
340
+ ...receipts.flatMap((receipt, index) => {
341
+ const prefix = receipts.length > 1 ? `agent${index + 1}` : 'agent';
342
+ return [
343
+ nextRouteLine(`${prefix}.inspect`, agentRoute('get', receipt.agentId)),
344
+ nextRouteLine(`${prefix}.wait`, agentRoute('wait', receipt.agentId)),
345
+ nextRouteLine(`${prefix}.message`, agentRoute('message', receipt.agentId)),
346
+ nextRouteLine(`${prefix}.cancel`, agentRoute('cancel', receipt.agentId)),
347
+ nextRouteLine(`${prefix}.workItem`, workPlanRoute('get', receipt.item.id)),
348
+ ];
349
+ }),
350
+ ];
351
+ }
352
+
353
+ async function dispatchAgentsFromWorkPlan(
354
+ store: WorkPlanStore,
355
+ args: AgentWorkPlanToolArgs,
356
+ options: AgentWorkPlanToolOptions,
357
+ ): Promise<{ readonly success: true; readonly output: string } | { readonly success: false; readonly error: string }> {
358
+ if (!options.toolRegistry || !options.toolRegistry.has('agent')) {
359
+ return failure('agent_work_plan dispatch_agents requires the first-class agent tool to be registered.');
360
+ }
361
+ const items = resolveDispatchItems(store, args);
362
+ const plan = store.getActivePlan();
363
+ const cohort = readString(args.cohort) || `workplan-${plan.id}`;
364
+ const route = items.length === 1 ? 'agent { mode: "spawn" }' : 'agent { mode: "batch-spawn" }';
365
+ const denied = requireConfirmedDispatch(args, items, route);
366
+ if (denied) return failure(denied);
367
+ const explicitUserRequest = readString(args.explicitUserRequest);
368
+ const commonArgs = {
369
+ authoritativeTask: explicitUserRequest,
370
+ cohort,
371
+ orchestrationGraphId: `workplan:${plan.id}`,
372
+ };
373
+ const agentArgs = items.length === 1
374
+ ? {
375
+ mode: 'spawn',
376
+ ...commonArgs,
377
+ ...agentTaskForItem(items[0]!, args),
378
+ }
379
+ : {
380
+ mode: 'batch-spawn',
381
+ ...commonArgs,
382
+ tasks: items.map((item) => agentTaskForItem(item, args)),
383
+ };
384
+ const result = await options.toolRegistry.execute(`agent-work-plan-dispatch-${Date.now()}`, 'agent', agentArgs);
385
+ if (!result.success) return failure(result.error ?? 'Agent dispatch failed.');
386
+ const payload = parseAgentToolOutput(result.output ?? '{}');
387
+ const agentIds = agentIdsFromDispatchPayload(payload);
388
+ if (agentIds.length === 0) return failure('Agent dispatch succeeded but returned no agent ids to attach to the work plan.');
389
+ const receipts = items.map((item, index) => {
390
+ const agentId = agentIds[Math.min(index, agentIds.length - 1)]!;
391
+ const updated = store.updateItem(item.id, {
392
+ status: item.status === 'done' || item.status === 'cancelled' ? item.status : 'in_progress',
393
+ linked: { ...(item.linked ?? {}), agentId },
394
+ notes: appendDispatchReceipt(item.notes, agentId, route, cohort, explicitUserRequest),
395
+ });
396
+ return { item: updated, agentId };
397
+ });
398
+ return output([
399
+ 'Dispatched Agent work plan items',
400
+ ` route ${route}`,
401
+ ` cohort ${cohort}`,
402
+ ` saved receipts ${receipts.length}`,
403
+ ...receipts.map((receipt) => ` - ${receipt.item.id} -> ${receipt.agentId} ${previewText(receipt.item.title, 100)}`),
404
+ ...dispatchNextRouteLines(receipts),
405
+ ].join('\n'));
406
+ }
407
+
408
+ export function createAgentWorkPlanTool(store: WorkPlanStore, options: AgentWorkPlanToolOptions = {}): Tool {
165
409
  return {
166
410
  definition: {
167
411
  name: 'agent_work_plan',
168
- description: 'Inspect or update the visible Agent-local work plan.',
412
+ description: 'Manage and dispatch approved visible Agent work plan items.',
169
413
  parameters: {
170
414
  type: 'object',
171
415
  properties: {
172
416
  action: { type: 'string', enum: [...ACTIONS] },
173
417
  id: { type: 'string', description: 'Work plan item id or unique id prefix for get/update/set_status/remove.' },
418
+ ids: { type: 'array', items: { type: 'string' }, description: 'Work plan item ids or unique prefixes for dispatch_agents.' },
174
419
  title: { type: 'string', description: 'Work plan item title for create/update.' },
175
420
  status: { type: 'string', enum: [...WORK_PLAN_STATUSES], description: 'Work plan item status.' },
176
421
  owner: { type: 'string', description: 'Optional owner label.' },
177
422
  source: { type: 'string', description: 'Optional source/provenance label.' },
178
423
  notes: { type: 'string', description: 'Optional work-plan notes.' },
179
- confirm: { type: 'boolean', description: 'Required true for remove and clear_completed.' },
180
- explicitUserRequest: { type: 'string', description: 'Required for destructive remove and clear_completed actions.' },
424
+ template: { type: 'string', description: 'Agent template for dispatch_agents.' },
425
+ model: { type: 'string', description: 'Provider-qualified model for dispatch_agents.' },
426
+ provider: { type: 'string', description: 'Provider id for dispatch_agents when model is provider-qualified.' },
427
+ reasoningEffort: { type: 'string', enum: ['instant', 'low', 'medium', 'high'], description: 'Reasoning effort for dispatch_agents.' },
428
+ tools: { type: 'array', items: { type: 'string' }, description: 'Exact tool subset for dispatched agents.' },
429
+ successCriteria: { type: 'array', items: { type: 'string' }, description: 'Success criteria for dispatched agents.' },
430
+ requiredEvidence: { type: 'array', items: { type: 'string' }, description: 'Required evidence for dispatched agents.' },
431
+ writeScope: { type: 'array', items: { type: 'string' }, description: 'Expected write scope for dispatched agents.' },
432
+ executionProtocol: { type: 'string', enum: ['direct', 'gather-plan-apply'], description: 'Execution protocol for dispatch_agents.' },
433
+ reviewMode: { type: 'string', enum: ['none', 'wrfc'], description: 'Review mode for dispatch_agents.' },
434
+ communicationLane: { type: 'string', enum: ['parent-only', 'parent-and-children', 'cohort', 'direct'], description: 'Communication lane for dispatch_agents.' },
435
+ cohort: { type: 'string', description: 'Agent cohort for dispatch_agents. Defaults to the work plan id.' },
436
+ agentContext: { type: 'string', description: 'Additional context prepended to each dispatched agent task.' },
437
+ confirm: { type: 'boolean', description: 'Required true for remove, clear_completed, and dispatch_agents.' },
438
+ explicitUserRequest: { type: 'string', description: 'Required for remove, clear_completed, and agent dispatch.' },
181
439
  },
182
440
  required: ['action'],
183
441
  additionalProperties: false,
184
442
  },
185
- sideEffects: ['state'],
443
+ sideEffects: ['state', 'agent', 'workflow'],
186
444
  },
187
445
  execute: async (rawArgs: unknown) => {
188
446
  try {
@@ -190,6 +448,7 @@ export function createAgentWorkPlanTool(store: WorkPlanStore): Tool {
190
448
  if (!isAction(args.action)) return failure(`Unknown Agent work plan action. Valid values ${ACTIONS.join(', ')}.`);
191
449
  if (args.action === 'list') return output(listOutput(store));
192
450
  if (args.action === 'get') return output(formatItemDetail(resolveItem(store, requireId(args))));
451
+ if (args.action === 'dispatch_agents') return dispatchAgentsFromWorkPlan(store, args, options);
193
452
  if (args.action === 'create') {
194
453
  const item = store.addItem(requireTitle(args), {
195
454
  status: readOptionalStatus(args.status) ?? 'pending',
@@ -247,5 +506,5 @@ export function createAgentWorkPlanTool(store: WorkPlanStore): Tool {
247
506
  }
248
507
 
249
508
  export function registerAgentWorkPlanTool(registry: ToolRegistry, store: WorkPlanStore): void {
250
- registry.register(createAgentWorkPlanTool(store));
509
+ registry.register(createAgentWorkPlanTool(store, { toolRegistry: registry }));
251
510
  }