@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,684 @@
1
+ import type { ArtifactDescriptor, ArtifactStore } from '@pellux/goodvibes-sdk/platform/artifacts';
2
+ import type { Tool } from '@pellux/goodvibes-sdk/platform/types';
3
+ import type { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
4
+ import type { ShellPathService } from '@/runtime/index.ts';
5
+ import {
6
+ AgentDocumentRegistry,
7
+ renderAgentDocumentMarkdown,
8
+ type AgentDocumentRecord,
9
+ type AgentDocumentStatus,
10
+ } from '../agent/document-registry.ts';
11
+
12
+ export type AgentDocumentsToolMode = 'list' | 'show' | 'create' | 'update' | 'review' | 'comment' | 'resolveComment' | 'suggest' | 'acceptSuggestion' | 'rejectSuggestion' | 'export' | 'insertArtifact' | 'attachArtifact';
13
+
14
+ export interface AgentDocumentsToolArgs {
15
+ readonly mode?: unknown;
16
+ readonly documentId?: unknown;
17
+ readonly query?: unknown;
18
+ readonly title?: unknown;
19
+ readonly body?: unknown;
20
+ readonly tags?: unknown;
21
+ readonly status?: unknown;
22
+ readonly artifactId?: unknown;
23
+ readonly attachmentLabel?: unknown;
24
+ readonly attachmentNote?: unknown;
25
+ readonly commentId?: unknown;
26
+ readonly comment?: unknown;
27
+ readonly suggestionId?: unknown;
28
+ readonly suggestionRationale?: unknown;
29
+ readonly placement?: unknown;
30
+ readonly sectionTitle?: unknown;
31
+ readonly includeContent?: unknown;
32
+ readonly changeSummary?: unknown;
33
+ readonly includeVersions?: unknown;
34
+ readonly limit?: unknown;
35
+ readonly confirm?: unknown;
36
+ readonly explicitUserRequest?: unknown;
37
+ }
38
+
39
+ type AgentDocumentArtifactStore = Partial<Pick<ArtifactStore, 'create' | 'get' | 'readContent'>>;
40
+
41
+ const MODES: readonly AgentDocumentsToolMode[] = ['list', 'show', 'create', 'update', 'review', 'comment', 'resolveComment', 'suggest', 'acceptSuggestion', 'rejectSuggestion', 'export', 'insertArtifact', 'attachArtifact'];
42
+ const MAX_INSERT_TEXT_BYTES = 40_000;
43
+
44
+ function isMode(value: unknown): value is AgentDocumentsToolMode {
45
+ return typeof value === 'string' && MODES.includes(value as AgentDocumentsToolMode);
46
+ }
47
+
48
+ function readString(value: unknown): string {
49
+ return typeof value === 'string' ? value.trim() : '';
50
+ }
51
+
52
+ function readStringList(value: unknown): readonly string[] {
53
+ if (typeof value === 'string') return value.split(/[,\n]/).map((entry) => entry.trim()).filter(Boolean);
54
+ if (!Array.isArray(value)) return [];
55
+ return value.filter((entry): entry is string => typeof entry === 'string').map((entry) => entry.trim()).filter(Boolean);
56
+ }
57
+
58
+ function readBoolean(value: unknown, fallback = false): boolean {
59
+ if (value === undefined || value === null || readString(value) === '') return fallback;
60
+ return value === true || value === 'true' || value === 'yes';
61
+ }
62
+
63
+ function readLimit(value: unknown, fallback: number): number {
64
+ const parsed = typeof value === 'string' && value.trim() ? Number(value) : value;
65
+ if (typeof parsed !== 'number' || !Number.isFinite(parsed)) return fallback;
66
+ return Math.max(1, Math.min(100, Math.trunc(parsed)));
67
+ }
68
+
69
+ function readStatus(value: unknown): AgentDocumentStatus | undefined {
70
+ if (value === 'draft' || value === 'reviewed' || value === 'archived') return value;
71
+ if (value === undefined || value === null || readString(value) === '') return undefined;
72
+ throw new Error('status must be draft, reviewed, or archived.');
73
+ }
74
+
75
+ function readPlacement(value: unknown): 'append' | 'prepend' | 'replace' {
76
+ const placement = readString(value).toLowerCase();
77
+ if (!placement || placement === 'append') return 'append';
78
+ if (placement === 'prepend' || placement === 'replace') return placement;
79
+ throw new Error('placement must be append, prepend, or replace.');
80
+ }
81
+
82
+ function output(text: string): { readonly success: true; readonly output: string } {
83
+ return { success: true, output: text };
84
+ }
85
+
86
+ function failure(error: string): { readonly success: false; readonly error: string } {
87
+ return { success: false, error };
88
+ }
89
+
90
+ function requireDocumentId(args: AgentDocumentsToolArgs): string {
91
+ const id = readString(args.documentId);
92
+ if (!id) throw new Error('documentId is required.');
93
+ return id;
94
+ }
95
+
96
+ function requireArtifactId(args: AgentDocumentsToolArgs): string {
97
+ const id = readString(args.artifactId);
98
+ if (!id) throw new Error('artifactId is required.');
99
+ return id;
100
+ }
101
+
102
+ function requireCommentId(args: AgentDocumentsToolArgs): string {
103
+ const id = readString(args.commentId);
104
+ if (!id) throw new Error('commentId is required.');
105
+ return id;
106
+ }
107
+
108
+ function requireSuggestionId(args: AgentDocumentsToolArgs): string {
109
+ const id = readString(args.suggestionId);
110
+ if (!id) throw new Error('suggestionId is required.');
111
+ return id;
112
+ }
113
+
114
+ function requireConfirmed(args: AgentDocumentsToolArgs, action: string): void {
115
+ const explicitUserRequest = readString(args.explicitUserRequest);
116
+ if (!explicitUserRequest) throw new Error(`${action} requires explicitUserRequest with the user's exact request or a short faithful summary.`);
117
+ if (args.confirm !== true) throw new Error(`${action} requires confirm:true after an explicit user request.`);
118
+ }
119
+
120
+ function formatDocumentSummary(document: AgentDocumentRecord): string {
121
+ const tags = document.tags.length > 0 ? ` tags ${document.tags.join(', ')}` : '';
122
+ const artifact = document.lastArtifactId ? ` artifact ${document.lastArtifactId}` : '';
123
+ const openComments = document.comments.filter((comment) => comment.status === 'open').length;
124
+ const comments = document.comments.length > 0 ? ` comments ${openComments}/${document.comments.length}` : '';
125
+ const proposedSuggestions = document.suggestions.filter((suggestion) => suggestion.status === 'proposed').length;
126
+ const suggestions = document.suggestions.length > 0 ? ` suggestions ${proposedSuggestions}/${document.suggestions.length}` : '';
127
+ const attachments = document.attachments.length > 0 ? ` attachments ${document.attachments.length}` : '';
128
+ return `${document.id} ${document.status} versions ${document.versions.length}${comments}${suggestions}${attachments} updated ${document.updatedAt}${tags}${artifact} ${document.title}`;
129
+ }
130
+
131
+ function formatList(documents: readonly AgentDocumentRecord[], total: number, query: string): string {
132
+ if (documents.length === 0) {
133
+ return [
134
+ 'Agent documents',
135
+ ` query ${query || '(all)'}`,
136
+ 'No Agent document drafts matched.',
137
+ ].join('\n');
138
+ }
139
+ return [
140
+ 'Agent documents',
141
+ ` returned ${documents.length}/${total}`,
142
+ ` query ${query || '(all)'}`,
143
+ ...documents.map((document) => ` - ${formatDocumentSummary(document)}`),
144
+ ].join('\n');
145
+ }
146
+
147
+ function formatShow(document: AgentDocumentRecord, includeVersions: boolean): string {
148
+ const openComments = document.comments.filter((comment) => comment.status === 'open').length;
149
+ const proposedSuggestions = document.suggestions.filter((suggestion) => suggestion.status === 'proposed').length;
150
+ const lines = [
151
+ 'Agent document',
152
+ ` id ${document.id}`,
153
+ ` title ${document.title}`,
154
+ ` status ${document.status}`,
155
+ ` tags ${document.tags.join(', ') || '(none)'}`,
156
+ ` versions ${document.versions.length}`,
157
+ ` comments ${openComments}/${document.comments.length}`,
158
+ ` suggestions ${proposedSuggestions}/${document.suggestions.length}`,
159
+ ` attachments ${document.attachments.length}`,
160
+ ` created ${document.createdAt}`,
161
+ ` updated ${document.updatedAt}`,
162
+ ` lastArtifact ${document.lastArtifactId ?? '(none)'}`,
163
+ '',
164
+ document.body,
165
+ ];
166
+ if (includeVersions) {
167
+ lines.push(
168
+ '',
169
+ 'Versions',
170
+ ...document.versions.map((version) => ` - ${version.id} ${version.createdAt} ${version.summary}`),
171
+ );
172
+ }
173
+ if (document.comments.length > 0) {
174
+ lines.push(
175
+ '',
176
+ 'Comments',
177
+ ...document.comments.map((comment) => ` - ${comment.id} ${comment.status} ${comment.createdAt} ${comment.body}`),
178
+ );
179
+ }
180
+ if (document.suggestions.length > 0) {
181
+ lines.push(
182
+ '',
183
+ 'Suggestions',
184
+ ...document.suggestions.map((suggestion) => ` - ${suggestion.id} ${suggestion.status} ${suggestion.createdAt} ${suggestion.summary} ${suggestion.rationale}`),
185
+ );
186
+ }
187
+ if (document.attachments.length > 0) {
188
+ lines.push(
189
+ '',
190
+ 'Attachments',
191
+ ...document.attachments.map((attachment) => {
192
+ const details = [
193
+ attachment.filename ?? '',
194
+ attachment.mimeType ?? '',
195
+ attachment.kind ?? '',
196
+ ].filter(Boolean).join(' / ');
197
+ const note = attachment.note ? ` ${attachment.note}` : '';
198
+ return ` - ${attachment.id} artifact ${attachment.artifactId} ${attachment.label}${details ? ` ${details}` : ''}${note}`;
199
+ }),
200
+ );
201
+ }
202
+ return lines.join('\n');
203
+ }
204
+
205
+ function exportedVersionId(document: AgentDocumentRecord): string {
206
+ return document.versions.at(-1)?.id ?? 'v1';
207
+ }
208
+
209
+ function formatBytes(value: number): string {
210
+ if (!Number.isFinite(value) || value < 0) return '(unknown)';
211
+ if (value < 1024) return `${value} B`;
212
+ if (value < 1024 * 1024) return `${(value / 1024).toFixed(1)} KB`;
213
+ return `${(value / (1024 * 1024)).toFixed(1)} MB`;
214
+ }
215
+
216
+ function isTextLike(mimeType: string): boolean {
217
+ const normalized = mimeType.toLowerCase();
218
+ return normalized.startsWith('text/')
219
+ || normalized.includes('json')
220
+ || normalized.includes('xml')
221
+ || normalized.includes('yaml')
222
+ || normalized.includes('csv')
223
+ || normalized.includes('javascript')
224
+ || normalized.includes('typescript');
225
+ }
226
+
227
+ function artifactSectionTitle(artifact: ArtifactDescriptor, explicitTitle: string): string {
228
+ return explicitTitle || artifact.filename || artifact.id;
229
+ }
230
+
231
+ async function loadArtifactForInsert(
232
+ artifactStore: AgentDocumentArtifactStore | undefined,
233
+ artifactId: string,
234
+ includeContent: boolean,
235
+ ): Promise<{ readonly descriptor: ArtifactDescriptor; readonly buffer?: Buffer }> {
236
+ if (!artifactStore?.get && !artifactStore?.readContent) {
237
+ throw new Error('Artifact insertion requires an artifact store with get or readContent support.');
238
+ }
239
+ if (includeContent && artifactStore.readContent) {
240
+ const { record, buffer } = await artifactStore.readContent(artifactId);
241
+ return { descriptor: record, buffer };
242
+ }
243
+ const descriptor = artifactStore.get?.(artifactId) ?? null;
244
+ if (descriptor) return { descriptor };
245
+ if (artifactStore.readContent) {
246
+ const { record, buffer } = await artifactStore.readContent(artifactId);
247
+ return { descriptor: record, buffer: includeContent ? buffer : undefined };
248
+ }
249
+ throw new Error(`Unknown artifact ${artifactId}. Use agent_artifacts mode:"list" to inspect available artifacts.`);
250
+ }
251
+
252
+ function renderArtifactDocumentBlock(
253
+ artifact: ArtifactDescriptor,
254
+ buffer: Buffer | undefined,
255
+ options: {
256
+ readonly sectionTitle: string;
257
+ readonly includeContent: boolean;
258
+ },
259
+ ): string {
260
+ const heading = artifactSectionTitle(artifact, options.sectionTitle);
261
+ const lines = [
262
+ `## ${heading}`,
263
+ '',
264
+ `Artifact ID: ${artifact.id}`,
265
+ `Filename: ${artifact.filename ?? '(none)'}`,
266
+ `MIME: ${artifact.mimeType}`,
267
+ `Size: ${formatBytes(artifact.sizeBytes)}`,
268
+ ];
269
+
270
+ if (!options.includeContent) {
271
+ lines.push('', '_Content omitted by request; use the artifact id above to inspect or attach it._');
272
+ return lines.join('\n');
273
+ }
274
+
275
+ if (!isTextLike(artifact.mimeType)) {
276
+ lines.push('', `_Content omitted for non-text artifact ${artifact.mimeType}; binary/base64 bytes were not inserted._`);
277
+ return lines.join('\n');
278
+ }
279
+
280
+ if (!buffer) {
281
+ lines.push('', '_Text content could not be read in this runtime._');
282
+ return lines.join('\n');
283
+ }
284
+
285
+ const sliced = buffer.subarray(0, Math.min(buffer.byteLength, MAX_INSERT_TEXT_BYTES));
286
+ const text = sliced.toString('utf-8').replace(/\0/g, '').trim();
287
+ lines.push('', text || '_Empty text artifact._');
288
+ if (buffer.byteLength > sliced.byteLength) {
289
+ lines.push('', `_Truncated ${formatBytes(buffer.byteLength - sliced.byteLength)} from the inserted artifact preview._`);
290
+ }
291
+ return lines.join('\n');
292
+ }
293
+
294
+ function insertBlock(body: string, block: string, placement: 'append' | 'prepend' | 'replace'): string {
295
+ if (placement === 'replace') return block;
296
+ if (placement === 'prepend') return `${block}\n\n${body}`;
297
+ return `${body}\n\n${block}`;
298
+ }
299
+
300
+ function formatExport(document: AgentDocumentRecord, artifact: ArtifactDescriptor): string {
301
+ const openComments = document.comments.filter((comment) => comment.status === 'open').length;
302
+ const proposedSuggestions = document.suggestions.filter((suggestion) => suggestion.status === 'proposed').length;
303
+ return [
304
+ 'Exported Agent document',
305
+ ` document ${document.id}`,
306
+ ` version ${exportedVersionId(document)}`,
307
+ ` attachments ${document.attachments.length}`,
308
+ ` comments ${openComments}/${document.comments.length}`,
309
+ ` suggestions ${proposedSuggestions}/${document.suggestions.length}`,
310
+ ` artifact ${artifact.id}`,
311
+ ` filename ${artifact.filename ?? '(none)'}`,
312
+ ` route agent_artifacts mode:"show" artifactId:"${artifact.id}" includeContent:true`,
313
+ ' policy markdown body plus reviewer appendix exported; artifact content not printed',
314
+ ].join('\n');
315
+ }
316
+
317
+ async function exportDocument(
318
+ registry: AgentDocumentRegistry,
319
+ artifactStore: AgentDocumentArtifactStore | undefined,
320
+ document: AgentDocumentRecord,
321
+ ): Promise<string> {
322
+ if (!artifactStore?.create) throw new Error('Agent document export requires an artifact store with create support.');
323
+ const versionId = exportedVersionId(document);
324
+ const openComments = document.comments.filter((comment) => comment.status === 'open').length;
325
+ const proposedSuggestions = document.suggestions.filter((suggestion) => suggestion.status === 'proposed').length;
326
+ const acceptedSuggestions = document.suggestions.filter((suggestion) => suggestion.status === 'accepted').length;
327
+ const rejectedSuggestions = document.suggestions.filter((suggestion) => suggestion.status === 'rejected').length;
328
+ const artifact = await artifactStore.create({
329
+ kind: 'document',
330
+ mimeType: 'text/markdown',
331
+ filename: `${document.id}-${versionId}.md`,
332
+ text: renderAgentDocumentMarkdown(document),
333
+ metadata: {
334
+ purpose: 'agent-document-export',
335
+ source: 'agent-documents',
336
+ documentId: document.id,
337
+ versionId,
338
+ status: document.status,
339
+ attachmentIds: document.attachments.map((attachment) => attachment.artifactId),
340
+ commentCounts: {
341
+ open: openComments,
342
+ resolved: document.comments.length - openComments,
343
+ total: document.comments.length,
344
+ },
345
+ suggestionCounts: {
346
+ proposed: proposedSuggestions,
347
+ accepted: acceptedSuggestions,
348
+ rejected: rejectedSuggestions,
349
+ total: document.suggestions.length,
350
+ },
351
+ reviewSummary: {
352
+ hasOpenComments: openComments > 0,
353
+ hasProposedSuggestions: proposedSuggestions > 0,
354
+ exportedReviewAppendix: document.comments.length > 0 || document.suggestions.length > 0,
355
+ },
356
+ },
357
+ });
358
+ registry.updateArtifactId(document.id, artifact.id);
359
+ return formatExport({ ...document, lastArtifactId: artifact.id }, artifact);
360
+ }
361
+
362
+ async function attachArtifactToDocument(
363
+ registry: AgentDocumentRegistry,
364
+ artifactStore: AgentDocumentArtifactStore | undefined,
365
+ args: AgentDocumentsToolArgs,
366
+ ): Promise<string> {
367
+ const documentId = requireDocumentId(args);
368
+ const artifactId = requireArtifactId(args);
369
+ const document = registry.get(documentId);
370
+ if (!document) throw new Error(`Unknown Agent document ${documentId}.`);
371
+ const loaded = await loadArtifactForInsert(artifactStore, artifactId, false);
372
+ const updated = registry.attachArtifact(document.id, {
373
+ artifactId: loaded.descriptor.id,
374
+ label: readString(args.attachmentLabel) || loaded.descriptor.filename || loaded.descriptor.id,
375
+ note: readString(args.attachmentNote),
376
+ filename: loaded.descriptor.filename,
377
+ mimeType: loaded.descriptor.mimeType,
378
+ kind: loaded.descriptor.kind,
379
+ sizeBytes: loaded.descriptor.sizeBytes,
380
+ });
381
+ const attachment = updated.attachments.find((entry) => entry.artifactId === loaded.descriptor.id);
382
+ return [
383
+ 'Attached artifact to Agent document',
384
+ ` document ${updated.id}`,
385
+ ` attachment ${attachment?.id ?? '(unknown)'}`,
386
+ ` artifact ${loaded.descriptor.id}`,
387
+ ` attachments ${updated.attachments.length}`,
388
+ ` versions ${updated.versions.length}`,
389
+ ' body unchanged',
390
+ ].join('\n');
391
+ }
392
+
393
+ async function insertArtifactIntoDocument(
394
+ registry: AgentDocumentRegistry,
395
+ artifactStore: AgentDocumentArtifactStore | undefined,
396
+ args: AgentDocumentsToolArgs,
397
+ ): Promise<string> {
398
+ const documentId = requireDocumentId(args);
399
+ const artifactId = requireArtifactId(args);
400
+ const document = registry.get(documentId);
401
+ if (!document) throw new Error(`Unknown Agent document ${documentId}.`);
402
+ const includeContent = readBoolean(args.includeContent, true);
403
+ const placement = readPlacement(args.placement);
404
+ const loaded = await loadArtifactForInsert(artifactStore, artifactId, includeContent);
405
+ const block = renderArtifactDocumentBlock(loaded.descriptor, loaded.buffer, {
406
+ sectionTitle: readString(args.sectionTitle),
407
+ includeContent,
408
+ });
409
+ const updated = registry.update(document.id, {
410
+ body: insertBlock(document.body, block, placement),
411
+ summary: readString(args.changeSummary) || `Inserted artifact ${artifactId}.`,
412
+ });
413
+ return [
414
+ 'Inserted artifact into Agent document',
415
+ ` document ${updated.id}`,
416
+ ` artifact ${loaded.descriptor.id}`,
417
+ ` placement ${placement}`,
418
+ ` versions ${updated.versions.length}`,
419
+ ` inserted ${isTextLike(loaded.descriptor.mimeType) && includeContent ? 'bounded text content' : 'safe artifact reference'}`,
420
+ ].join('\n');
421
+ }
422
+
423
+ export function createAgentDocumentsTool(
424
+ shellPaths: ShellPathService,
425
+ artifactStore?: AgentDocumentArtifactStore,
426
+ ): Tool {
427
+ return {
428
+ definition: {
429
+ name: 'agent_documents',
430
+ description: 'Create drafts, review suggestions, attach artifacts, and export.',
431
+ parameters: {
432
+ type: 'object',
433
+ properties: {
434
+ mode: {
435
+ type: 'string',
436
+ enum: [...MODES],
437
+ description: 'Document action. Defaults to list.',
438
+ },
439
+ documentId: {
440
+ type: 'string',
441
+ description: 'Document id or exact title for show, update, review, or export.',
442
+ },
443
+ query: {
444
+ type: 'string',
445
+ description: 'Search query for list mode.',
446
+ },
447
+ title: {
448
+ type: 'string',
449
+ description: 'Document title for create or update.',
450
+ },
451
+ body: {
452
+ type: 'string',
453
+ description: 'Markdown body for create or update.',
454
+ },
455
+ tags: {
456
+ type: 'array',
457
+ items: { type: 'string' },
458
+ description: 'Optional document tags.',
459
+ },
460
+ status: {
461
+ type: 'string',
462
+ enum: ['draft', 'reviewed', 'archived'],
463
+ description: 'Optional document status for update.',
464
+ },
465
+ commentId: {
466
+ type: 'string',
467
+ description: 'Comment id for mode:"resolveComment".',
468
+ },
469
+ comment: {
470
+ type: 'string',
471
+ description: 'Review comment body for mode:"comment".',
472
+ },
473
+ suggestionId: {
474
+ type: 'string',
475
+ description: 'Suggestion id for acceptSuggestion or rejectSuggestion.',
476
+ },
477
+ suggestionRationale: {
478
+ type: 'string',
479
+ description: 'Why the suggested replacement helps the user.',
480
+ },
481
+ artifactId: {
482
+ type: 'string',
483
+ description: 'Saved artifact id for insertArtifact or attachArtifact.',
484
+ },
485
+ attachmentLabel: {
486
+ type: 'string',
487
+ description: 'Optional user-facing label for mode:"attachArtifact".',
488
+ },
489
+ attachmentNote: {
490
+ type: 'string',
491
+ description: 'Optional attachment note for mode:"attachArtifact".',
492
+ },
493
+ placement: {
494
+ type: 'string',
495
+ enum: ['append', 'prepend', 'replace'],
496
+ description: 'Where to place inserted artifact content in the document body.',
497
+ },
498
+ sectionTitle: {
499
+ type: 'string',
500
+ description: 'Optional markdown heading for inserted artifact content.',
501
+ },
502
+ includeContent: {
503
+ type: 'boolean',
504
+ description: 'Insert bounded text for text artifacts; reference non-text artifacts.',
505
+ },
506
+ changeSummary: {
507
+ type: 'string',
508
+ description: 'Short version note for create or update.',
509
+ },
510
+ includeVersions: {
511
+ type: 'boolean',
512
+ description: 'Include version history in show mode.',
513
+ },
514
+ limit: {
515
+ type: 'number',
516
+ description: 'Maximum list rows.',
517
+ },
518
+ confirm: {
519
+ type: 'boolean',
520
+ description: 'Required true for document writes and export.',
521
+ },
522
+ explicitUserRequest: {
523
+ type: 'string',
524
+ description: 'User request authorizing the document mutation.',
525
+ },
526
+ },
527
+ additionalProperties: false,
528
+ },
529
+ sideEffects: ['state'],
530
+ },
531
+ execute: async (rawArgs: unknown) => {
532
+ const args = rawArgs as AgentDocumentsToolArgs;
533
+ const mode = args.mode === undefined || readString(args.mode) === ''
534
+ ? 'list'
535
+ : isMode(args.mode)
536
+ ? args.mode
537
+ : null;
538
+ if (mode === null) return failure(`mode must be one of ${MODES.join(', ')}.`);
539
+
540
+ const registry = AgentDocumentRegistry.fromShellPaths(shellPaths);
541
+ try {
542
+ if (mode === 'list') {
543
+ const query = readString(args.query);
544
+ const matches = query ? registry.search(query) : registry.list();
545
+ const limit = readLimit(args.limit, 20);
546
+ return output(formatList(matches.slice(0, limit), matches.length, query));
547
+ }
548
+ if (mode === 'show') {
549
+ const document = registry.get(requireDocumentId(args));
550
+ if (!document) return failure(`Unknown Agent document ${readString(args.documentId)}.`);
551
+ return output(formatShow(document, readBoolean(args.includeVersions, true)));
552
+ }
553
+ if (mode === 'create') {
554
+ requireConfirmed(args, 'Agent document creation');
555
+ const title = readString(args.title);
556
+ const body = readString(args.body);
557
+ const document = registry.create({
558
+ title,
559
+ body,
560
+ tags: readStringList(args.tags),
561
+ summary: readString(args.changeSummary) || 'Initial draft.',
562
+ });
563
+ return output([
564
+ 'Created Agent document',
565
+ ` id ${document.id}`,
566
+ ` title ${document.title}`,
567
+ ` versions ${document.versions.length}`,
568
+ ].join('\n'));
569
+ }
570
+ if (mode === 'update') {
571
+ requireConfirmed(args, 'Agent document update');
572
+ const id = requireDocumentId(args);
573
+ const document = registry.update(id, {
574
+ title: args.title === undefined ? undefined : readString(args.title),
575
+ body: args.body === undefined ? undefined : readString(args.body),
576
+ tags: args.tags === undefined ? undefined : readStringList(args.tags),
577
+ status: readStatus(args.status),
578
+ summary: readString(args.changeSummary) || 'Updated draft.',
579
+ });
580
+ return output([
581
+ 'Updated Agent document',
582
+ ` id ${document.id}`,
583
+ ` title ${document.title}`,
584
+ ` versions ${document.versions.length}`,
585
+ ].join('\n'));
586
+ }
587
+ if (mode === 'review') {
588
+ requireConfirmed(args, 'Agent document review');
589
+ const document = registry.markReviewed(requireDocumentId(args));
590
+ return output([
591
+ 'Reviewed Agent document',
592
+ ` id ${document.id}`,
593
+ ` status ${document.status}`,
594
+ ` versions ${document.versions.length}`,
595
+ ].join('\n'));
596
+ }
597
+ if (mode === 'comment') {
598
+ requireConfirmed(args, 'Agent document comment');
599
+ const document = registry.addComment(requireDocumentId(args), { body: readString(args.comment) });
600
+ const latest = document.comments.at(-1);
601
+ return output([
602
+ 'Added Agent document comment',
603
+ ` document ${document.id}`,
604
+ ` comment ${latest?.id ?? '(unknown)'}`,
605
+ ` open ${document.comments.filter((comment) => comment.status === 'open').length}/${document.comments.length}`,
606
+ ].join('\n'));
607
+ }
608
+ if (mode === 'resolveComment') {
609
+ requireConfirmed(args, 'Agent document comment resolution');
610
+ const document = registry.resolveComment(requireDocumentId(args), requireCommentId(args));
611
+ return output([
612
+ 'Resolved Agent document comment',
613
+ ` document ${document.id}`,
614
+ ` comment ${readString(args.commentId)}`,
615
+ ` open ${document.comments.filter((comment) => comment.status === 'open').length}/${document.comments.length}`,
616
+ ].join('\n'));
617
+ }
618
+ if (mode === 'suggest') {
619
+ requireConfirmed(args, 'Agent document suggestion');
620
+ const document = registry.suggestUpdate(requireDocumentId(args), {
621
+ title: args.title === undefined ? undefined : readString(args.title),
622
+ body: readString(args.body),
623
+ tags: args.tags === undefined ? undefined : readStringList(args.tags),
624
+ status: readStatus(args.status),
625
+ summary: readString(args.changeSummary) || 'AI suggested revision.',
626
+ rationale: readString(args.suggestionRationale) || 'No rationale provided.',
627
+ });
628
+ const latest = document.suggestions.at(-1);
629
+ return output([
630
+ 'Added Agent document suggestion',
631
+ ` document ${document.id}`,
632
+ ` suggestion ${latest?.id ?? '(unknown)'}`,
633
+ ` proposed ${document.suggestions.filter((suggestion) => suggestion.status === 'proposed').length}/${document.suggestions.length}`,
634
+ ` versions ${document.versions.length}`,
635
+ ].join('\n'));
636
+ }
637
+ if (mode === 'acceptSuggestion') {
638
+ requireConfirmed(args, 'Agent document suggestion acceptance');
639
+ const document = registry.acceptSuggestion(requireDocumentId(args), requireSuggestionId(args));
640
+ return output([
641
+ 'Accepted Agent document suggestion',
642
+ ` document ${document.id}`,
643
+ ` suggestion ${readString(args.suggestionId)}`,
644
+ ` versions ${document.versions.length}`,
645
+ ` proposed ${document.suggestions.filter((suggestion) => suggestion.status === 'proposed').length}/${document.suggestions.length}`,
646
+ ].join('\n'));
647
+ }
648
+ if (mode === 'rejectSuggestion') {
649
+ requireConfirmed(args, 'Agent document suggestion rejection');
650
+ const document = registry.rejectSuggestion(requireDocumentId(args), requireSuggestionId(args));
651
+ return output([
652
+ 'Rejected Agent document suggestion',
653
+ ` document ${document.id}`,
654
+ ` suggestion ${readString(args.suggestionId)}`,
655
+ ` versions ${document.versions.length}`,
656
+ ` proposed ${document.suggestions.filter((suggestion) => suggestion.status === 'proposed').length}/${document.suggestions.length}`,
657
+ ].join('\n'));
658
+ }
659
+ if (mode === 'insertArtifact') {
660
+ requireConfirmed(args, 'Agent document artifact insertion');
661
+ return output(await insertArtifactIntoDocument(registry, artifactStore, args));
662
+ }
663
+ if (mode === 'attachArtifact') {
664
+ requireConfirmed(args, 'Agent document artifact attachment');
665
+ return output(await attachArtifactToDocument(registry, artifactStore, args));
666
+ }
667
+ requireConfirmed(args, 'Agent document export');
668
+ const document = registry.get(requireDocumentId(args));
669
+ if (!document) return failure(`Unknown Agent document ${readString(args.documentId)}.`);
670
+ return output(await exportDocument(registry, artifactStore, document));
671
+ } catch (error) {
672
+ return failure(error instanceof Error ? error.message : String(error));
673
+ }
674
+ },
675
+ };
676
+ }
677
+
678
+ export function registerAgentDocumentsTool(
679
+ registry: ToolRegistry,
680
+ shellPaths: ShellPathService,
681
+ artifactStore?: AgentDocumentArtifactStore,
682
+ ): void {
683
+ registry.register(createAgentDocumentsTool(shellPaths, artifactStore));
684
+ }