@pellux/goodvibes-agent 0.1.116 → 1.0.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 (285) hide show
  1. package/CHANGELOG.md +48 -661
  2. package/README.md +64 -57
  3. package/bin/goodvibes-agent.ts +16 -2
  4. package/dist/package/main.js +199180 -192185
  5. package/docs/README.md +15 -8
  6. package/docs/channels-remote-and-api.md +50 -0
  7. package/docs/connected-host.md +59 -0
  8. package/docs/getting-started.md +61 -50
  9. package/docs/knowledge-artifacts-and-multimodal.md +91 -0
  10. package/docs/project-planning.md +79 -0
  11. package/docs/providers-and-routing.md +46 -0
  12. package/docs/release-and-publishing.md +48 -11
  13. package/docs/tools-and-commands.md +123 -0
  14. package/docs/voice-and-live-tts.md +51 -0
  15. package/package.json +3 -6
  16. package/src/agent/behavior-discovery-summary.ts +1 -3
  17. package/src/agent/channel-delivery.ts +201 -0
  18. package/src/agent/media-generation.ts +159 -0
  19. package/src/agent/memory-prompt.ts +0 -1
  20. package/src/agent/note-registry.ts +329 -0
  21. package/src/agent/operator-actions.ts +343 -0
  22. package/src/agent/persona-discovery.ts +1 -3
  23. package/src/agent/persona-registry.ts +19 -16
  24. package/src/agent/record-labels.ts +107 -0
  25. package/src/agent/reminder-schedule-format.ts +33 -24
  26. package/src/agent/reminder-schedule.ts +27 -26
  27. package/src/agent/routine-registry.ts +54 -14
  28. package/src/agent/routine-schedule-args.ts +2 -1
  29. package/src/agent/routine-schedule-format.ts +77 -53
  30. package/src/agent/routine-schedule-promotion.ts +35 -33
  31. package/src/agent/routine-schedule-receipts.ts +29 -27
  32. package/src/agent/runtime-profile-starters.ts +5 -5
  33. package/src/agent/runtime-profile.ts +91 -12
  34. package/src/agent/skill-registry.ts +210 -32
  35. package/src/cli/agent-knowledge-args.ts +5 -1
  36. package/src/cli/agent-knowledge-command.ts +40 -34
  37. package/src/cli/agent-knowledge-format.ts +80 -67
  38. package/src/cli/agent-knowledge-methods.ts +1 -1
  39. package/src/cli/agent-knowledge-runtime.ts +32 -26
  40. package/src/cli/bundle-command.ts +14 -9
  41. package/src/cli/config-overrides.ts +37 -36
  42. package/src/cli/entrypoint.ts +16 -7
  43. package/src/cli/external-runtime.ts +10 -4
  44. package/src/cli/help.ts +54 -29
  45. package/src/cli/local-library-command.ts +203 -83
  46. package/src/cli/management-commands.ts +105 -68
  47. package/src/cli/management.ts +55 -74
  48. package/src/cli/memory-command.ts +66 -32
  49. package/src/cli/parser.ts +37 -3
  50. package/src/cli/profiles-command.ts +166 -31
  51. package/src/cli/provider-auth-routes.ts +2 -1
  52. package/src/cli/routines-command.ts +176 -40
  53. package/src/cli/service-posture.ts +16 -16
  54. package/src/cli/status.ts +56 -135
  55. package/src/core/conversation-message-snapshot.ts +131 -0
  56. package/src/input/agent-workspace-access-command-editor-submission.ts +149 -0
  57. package/src/input/agent-workspace-access-command-editors.ts +170 -0
  58. package/src/input/agent-workspace-activation.ts +35 -8
  59. package/src/input/agent-workspace-basic-command-editor-submission.ts +351 -94
  60. package/src/input/agent-workspace-basic-command-editors.ts +502 -9
  61. package/src/input/agent-workspace-categories.ts +423 -51
  62. package/src/input/agent-workspace-channel-command-editor-submission.ts +83 -0
  63. package/src/input/agent-workspace-channel-command-editors.ts +65 -0
  64. package/src/input/agent-workspace-channels.ts +35 -2
  65. package/src/input/agent-workspace-command-editor.ts +41 -3
  66. package/src/input/agent-workspace-config-reader.ts +16 -0
  67. package/src/input/agent-workspace-delegation-editor-submission.ts +55 -0
  68. package/src/input/agent-workspace-editors.ts +166 -1
  69. package/src/input/agent-workspace-knowledge-command-editor-submission.ts +132 -0
  70. package/src/input/agent-workspace-knowledge-command-editors.ts +110 -0
  71. package/src/input/agent-workspace-knowledge-query-editor.ts +1 -1
  72. package/src/input/agent-workspace-learned-behavior.ts +67 -0
  73. package/src/input/agent-workspace-library-command-editor-submission.ts +50 -0
  74. package/src/input/agent-workspace-library-command-editors.ts +35 -0
  75. package/src/input/agent-workspace-local-operations.ts +218 -0
  76. package/src/input/agent-workspace-local-selection.ts +75 -0
  77. package/src/input/agent-workspace-mcp-command-editor-submission.ts +95 -0
  78. package/src/input/agent-workspace-media-command-editor-submission.ts +62 -0
  79. package/src/input/agent-workspace-media-command-editors.ts +27 -0
  80. package/src/input/agent-workspace-memory-command-editor-submission.ts +232 -0
  81. package/src/input/agent-workspace-memory-command-editors.ts +180 -0
  82. package/src/input/agent-workspace-memory-editor.ts +2 -2
  83. package/src/input/agent-workspace-navigation.ts +81 -0
  84. package/src/input/agent-workspace-notify-editor-submission.ts +121 -0
  85. package/src/input/agent-workspace-operations-command-editor-submission.ts +208 -0
  86. package/src/input/agent-workspace-operations-command-editors.ts +196 -0
  87. package/src/input/agent-workspace-panel-route.ts +43 -0
  88. package/src/input/agent-workspace-provider-command-editor-submission.ts +155 -0
  89. package/src/input/agent-workspace-provider-command-editors.ts +93 -0
  90. package/src/input/agent-workspace-reminder-schedule-editor.ts +1 -1
  91. package/src/input/agent-workspace-requirements.ts +11 -0
  92. package/src/input/agent-workspace-routine-schedule-editor.ts +2 -2
  93. package/src/input/agent-workspace-search.ts +169 -0
  94. package/src/input/agent-workspace-secret-editor-submission.ts +153 -0
  95. package/src/input/agent-workspace-session-command-editor-submission.ts +199 -0
  96. package/src/input/agent-workspace-session-command-editors.ts +249 -0
  97. package/src/input/agent-workspace-setup.ts +37 -16
  98. package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +101 -0
  99. package/src/input/agent-workspace-skill-bundle-command-editors.ts +110 -0
  100. package/src/input/agent-workspace-snapshot.ts +162 -21
  101. package/src/input/agent-workspace-task-command-editor-submission.ts +61 -0
  102. package/src/input/agent-workspace-task-command-editors.ts +47 -0
  103. package/src/input/agent-workspace-token.ts +18 -2
  104. package/src/input/agent-workspace-types.ts +212 -4
  105. package/src/input/agent-workspace-voice-media.ts +4 -7
  106. package/src/input/agent-workspace-web-research-editor.ts +104 -0
  107. package/src/input/agent-workspace-workplan-editor-submission.ts +153 -0
  108. package/src/input/agent-workspace.ts +178 -240
  109. package/src/input/command-registry.ts +5 -2
  110. package/src/input/commands/agent-runtime-profile-runtime.ts +126 -37
  111. package/src/input/commands/agent-skills-runtime.ts +149 -72
  112. package/src/input/commands/agent-workspace-runtime.ts +23 -6
  113. package/src/input/commands/brief-runtime.ts +55 -25
  114. package/src/input/commands/channels-runtime.ts +147 -33
  115. package/src/input/commands/compat-runtime.ts +32 -0
  116. package/src/input/commands/delegation-runtime.ts +29 -23
  117. package/src/input/commands/experience-runtime.ts +6 -4
  118. package/src/input/commands/guidance-runtime.ts +4 -4
  119. package/src/input/commands/health-runtime.ts +149 -126
  120. package/src/input/commands/knowledge.ts +82 -60
  121. package/src/input/commands/local-provider-runtime.ts +43 -21
  122. package/src/input/commands/local-runtime.ts +138 -16
  123. package/src/input/commands/local-setup-review.ts +10 -20
  124. package/src/input/commands/mcp-runtime.ts +63 -46
  125. package/src/input/commands/notify-runtime.ts +38 -9
  126. package/src/input/commands/operator-actions-runtime.ts +138 -0
  127. package/src/input/commands/operator-runtime.ts +6 -76
  128. package/src/input/commands/personas-runtime.ts +46 -31
  129. package/src/input/commands/planning-runtime.ts +1 -1
  130. package/src/input/commands/platform-access-runtime.ts +35 -98
  131. package/src/input/commands/product-runtime.ts +2 -2
  132. package/src/input/commands/provider-accounts-runtime.ts +60 -39
  133. package/src/input/commands/qrcode-runtime.ts +50 -9
  134. package/src/input/commands/recall-bundle.ts +11 -11
  135. package/src/input/commands/recall-capture.ts +13 -11
  136. package/src/input/commands/recall-query.ts +29 -21
  137. package/src/input/commands/recall-review.ts +2 -1
  138. package/src/input/commands/routines-runtime.ts +99 -45
  139. package/src/input/commands/runtime-services.ts +1 -14
  140. package/src/input/commands/schedule-runtime.ts +33 -20
  141. package/src/input/commands/security-runtime.ts +6 -11
  142. package/src/input/commands/session-content.ts +80 -78
  143. package/src/input/commands/session-workflow.ts +134 -104
  144. package/src/input/commands/session.ts +3 -174
  145. package/src/input/commands/shell-core.ts +38 -17
  146. package/src/input/commands/subscription-runtime.ts +54 -185
  147. package/src/input/commands/support-bundle-runtime.ts +53 -0
  148. package/src/input/commands/tasks-runtime.ts +21 -21
  149. package/src/input/commands/work-plan-runtime.ts +33 -8
  150. package/src/input/commands.ts +6 -2
  151. package/src/input/feed-context-factory.ts +2 -2
  152. package/src/input/file-picker.ts +3 -2
  153. package/src/input/handler-command-route.ts +4 -7
  154. package/src/input/handler-content-actions.ts +89 -26
  155. package/src/input/handler-feed-routes.ts +19 -12
  156. package/src/input/handler-feed.ts +6 -5
  157. package/src/input/handler-interactions.ts +9 -36
  158. package/src/input/handler-modal-stack.ts +3 -3
  159. package/src/input/handler-onboarding.ts +52 -91
  160. package/src/input/handler-shortcuts.ts +16 -7
  161. package/src/input/handler.ts +50 -23
  162. package/src/input/input-history.ts +5 -6
  163. package/src/input/keybindings.ts +27 -16
  164. package/src/input/mcp-workspace.ts +90 -26
  165. package/src/input/onboarding/handler-onboarding-routes.ts +3 -1
  166. package/src/input/onboarding/onboarding-wizard-apply.ts +62 -0
  167. package/src/input/onboarding/onboarding-wizard-constants.ts +1 -0
  168. package/src/input/onboarding/onboarding-wizard-helpers.ts +1 -1
  169. package/src/input/onboarding/onboarding-wizard-operator-steps.ts +223 -55
  170. package/src/input/onboarding/onboarding-wizard-steps.ts +59 -35
  171. package/src/input/onboarding/onboarding-wizard-types.ts +4 -1
  172. package/src/input/onboarding/onboarding-wizard.ts +87 -2
  173. package/src/input/profile-picker-modal.ts +31 -12
  174. package/src/input/session-picker-modal.ts +21 -4
  175. package/src/input/settings-modal-agent-policy.ts +6 -6
  176. package/src/input/settings-modal-behavior.ts +0 -3
  177. package/src/input/settings-modal-subscriptions.ts +3 -3
  178. package/src/input/settings-modal-types.ts +2 -13
  179. package/src/input/settings-modal.ts +20 -66
  180. package/src/input/submission-intent.ts +0 -1
  181. package/src/input/submission-router.ts +3 -3
  182. package/src/main.ts +18 -10
  183. package/src/panels/approval-panel.ts +8 -8
  184. package/src/panels/automation-control-panel.ts +4 -4
  185. package/src/panels/base-panel.ts +1 -1
  186. package/src/panels/builtin/agent.ts +1 -1
  187. package/src/panels/builtin/operations.ts +3 -12
  188. package/src/panels/builtin/session.ts +32 -24
  189. package/src/panels/builtin/shared.ts +7 -7
  190. package/src/panels/cost-tracker-panel.ts +1 -1
  191. package/src/panels/docs-panel.ts +5 -3
  192. package/src/panels/index.ts +0 -1
  193. package/src/panels/knowledge-panel.ts +6 -5
  194. package/src/panels/memory-panel.ts +7 -6
  195. package/src/panels/panel-list-panel.ts +36 -80
  196. package/src/panels/project-planning-panel.ts +19 -12
  197. package/src/panels/provider-account-snapshot.ts +51 -25
  198. package/src/panels/provider-accounts-panel.ts +33 -18
  199. package/src/panels/provider-health-domains.ts +48 -7
  200. package/src/panels/provider-health-panel.ts +5 -4
  201. package/src/panels/qr-panel.ts +44 -20
  202. package/src/panels/schedule-panel.ts +9 -17
  203. package/src/panels/security-panel.ts +8 -8
  204. package/src/panels/session-browser-panel.ts +10 -10
  205. package/src/panels/subscription-panel.ts +6 -6
  206. package/src/panels/system-messages-panel.ts +3 -3
  207. package/src/panels/tasks-panel.ts +21 -14
  208. package/src/panels/tool-inspector-panel.ts +19 -12
  209. package/src/panels/work-plan-panel.ts +5 -5
  210. package/src/planning/project-planning-coordinator.ts +3 -3
  211. package/src/provider-auth-route-display.ts +9 -0
  212. package/src/renderer/agent-workspace-style.ts +34 -0
  213. package/src/renderer/agent-workspace.ts +254 -57
  214. package/src/renderer/autocomplete-overlay.ts +25 -6
  215. package/src/renderer/block-actions.ts +1 -3
  216. package/src/renderer/bookmark-modal.ts +19 -4
  217. package/src/renderer/buffer.ts +4 -2
  218. package/src/renderer/context-inspector.ts +50 -13
  219. package/src/renderer/diff.ts +1 -1
  220. package/src/renderer/file-picker-overlay.ts +19 -6
  221. package/src/renderer/help-overlay.ts +112 -34
  222. package/src/renderer/history-search-overlay.ts +19 -4
  223. package/src/renderer/live-tail-modal.ts +27 -5
  224. package/src/renderer/mcp-workspace.ts +176 -43
  225. package/src/renderer/model-picker-overlay.ts +58 -3
  226. package/src/renderer/model-workspace.ts +104 -22
  227. package/src/renderer/onboarding/onboarding-wizard.ts +20 -4
  228. package/src/renderer/process-modal.ts +27 -6
  229. package/src/renderer/profile-picker-modal.ts +21 -5
  230. package/src/renderer/search-overlay.ts +25 -5
  231. package/src/renderer/selection-modal-overlay.ts +46 -14
  232. package/src/renderer/session-picker-modal.ts +18 -1
  233. package/src/renderer/settings-modal-helpers.ts +2 -40
  234. package/src/renderer/settings-modal.ts +88 -55
  235. package/src/renderer/system-message.ts +1 -1
  236. package/src/renderer/tool-call.ts +20 -11
  237. package/src/runtime/agent-runtime-events.ts +129 -0
  238. package/src/runtime/bootstrap-command-context.ts +7 -1
  239. package/src/runtime/bootstrap-command-parts.ts +11 -8
  240. package/src/runtime/bootstrap-core.ts +23 -65
  241. package/src/runtime/bootstrap-hook-bridge.ts +7 -18
  242. package/src/runtime/bootstrap-shell.ts +46 -8
  243. package/src/runtime/bootstrap.ts +36 -46
  244. package/src/runtime/connected-host-auth.ts +47 -0
  245. package/src/runtime/diagnostics/panels/index.ts +2 -2
  246. package/src/runtime/diagnostics/panels/panel-resources.ts +1 -1
  247. package/src/runtime/diagnostics/panels/policy.ts +7 -7
  248. package/src/runtime/index.ts +2 -5
  249. package/src/runtime/onboarding/apply-file-helpers.ts +66 -0
  250. package/src/runtime/onboarding/apply.ts +288 -74
  251. package/src/runtime/onboarding/derivation.ts +10 -13
  252. package/src/runtime/onboarding/snapshot.ts +2 -17
  253. package/src/runtime/onboarding/types.ts +34 -20
  254. package/src/runtime/onboarding/verify.ts +105 -4
  255. package/src/runtime/operator-token-cleanup.ts +3 -3
  256. package/src/runtime/services.ts +95 -11
  257. package/src/runtime/store/selectors/index.ts +3 -3
  258. package/src/runtime/store/state.ts +1 -4
  259. package/src/runtime/surface-feature-flags.ts +41 -6
  260. package/src/runtime/ui-read-models.ts +3 -4
  261. package/src/runtime/ui-services.ts +6 -6
  262. package/src/shell/blocking-input.ts +2 -1
  263. package/src/shell/service-settings-sync.ts +7 -7
  264. package/src/shell/ui-openers.ts +17 -38
  265. package/src/tools/agent-analysis-registry-policy.ts +0 -1
  266. package/src/tools/agent-channel-send-tool.ts +133 -0
  267. package/src/tools/agent-context-policy.ts +1 -1
  268. package/src/tools/agent-knowledge-ingest-tool.ts +405 -0
  269. package/src/tools/agent-knowledge-tool.ts +170 -0
  270. package/src/tools/agent-local-registry-tool.ts +279 -73
  271. package/src/tools/agent-media-generate-tool.ts +133 -0
  272. package/src/tools/agent-notify-tool.ts +143 -0
  273. package/src/tools/agent-operator-action-tool.ts +137 -0
  274. package/src/tools/agent-operator-briefing-tool.ts +217 -0
  275. package/src/tools/agent-reminder-schedule-tool.ts +237 -0
  276. package/src/tools/agent-tool-policy-guard.ts +13 -15
  277. package/src/tools/agent-work-plan-tool.ts +256 -0
  278. package/src/version.ts +1 -1
  279. package/docs/connected-services.md +0 -51
  280. package/src/cli/package-verification.ts +0 -274
  281. package/src/input/commands/policy-dispatch.ts +0 -339
  282. package/src/input/commands/policy.ts +0 -17
  283. package/src/panels/panel-picker.ts +0 -105
  284. package/src/panels/policy-panel.ts +0 -308
  285. package/src/renderer/panel-picker-overlay.ts +0 -202
@@ -0,0 +1,133 @@
1
+ import type { ArtifactStore } from '@pellux/goodvibes-sdk/platform/artifacts';
2
+ import type { MediaProviderRegistry } from '@pellux/goodvibes-sdk/platform/media';
3
+ import type { Tool } from '@pellux/goodvibes-sdk/platform/types';
4
+ import type { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
5
+ import {
6
+ formatAgentMediaGenerationResult,
7
+ generateAgentMedia,
8
+ } from '../agent/media-generation.ts';
9
+
10
+ export interface AgentMediaGenerateToolArgs {
11
+ readonly prompt?: unknown;
12
+ readonly providerId?: unknown;
13
+ readonly modelId?: unknown;
14
+ readonly outputMimeType?: unknown;
15
+ readonly confirm?: unknown;
16
+ readonly explicitUserRequest?: unknown;
17
+ }
18
+
19
+ function readString(value: unknown): string {
20
+ return typeof value === 'string' ? value.trim() : '';
21
+ }
22
+
23
+ function readBoolean(value: unknown): boolean {
24
+ return value === true || value === 'true' || value === 'yes';
25
+ }
26
+
27
+ function failure(error: string): { readonly success: false; readonly error: string } {
28
+ return { success: false, error };
29
+ }
30
+
31
+ function output(text: string): { readonly success: true; readonly output: string } {
32
+ return { success: true, output: text };
33
+ }
34
+
35
+ function preview(args: AgentMediaGenerateToolArgs, providerCount: number): string {
36
+ const prompt = readString(args.prompt);
37
+ return [
38
+ 'Agent media generation preview',
39
+ ` prompt ${prompt || '(missing)'}`,
40
+ ` provider ${readString(args.providerId) || '(auto)'}`,
41
+ ` model ${readString(args.modelId) || '(provider default)'}`,
42
+ ` output mime ${readString(args.outputMimeType) || '(provider default)'}`,
43
+ ` generation providers ${providerCount}`,
44
+ ' policy media generation requires an explicit user request and confirm:true',
45
+ ].join('\n');
46
+ }
47
+
48
+ export function createAgentMediaGenerateTool(
49
+ mediaProviderRegistry: MediaProviderRegistry,
50
+ artifactStore: Pick<ArtifactStore, 'create'>,
51
+ ): Tool {
52
+ return {
53
+ definition: {
54
+ name: 'agent_media_generate',
55
+ description: [
56
+ 'Generate image or video artifacts through configured GoodVibes Agent media providers from the main conversation.',
57
+ 'Use only when the user explicitly asks Agent to generate media.',
58
+ 'Generated bytes are stored as GoodVibes artifacts and the response returns artifact ids; do not print inline base64.',
59
+ 'This tool does not use default knowledge, non-Agent knowledge segments, connected-host lifecycle routes, separate Agent jobs, delegated review, channel sends, or arbitrary route invocation.',
60
+ 'Set confirm:true only for an explicit user request. Otherwise return the preview/confirmation error.',
61
+ ].join(' '),
62
+ parameters: {
63
+ type: 'object',
64
+ properties: {
65
+ prompt: {
66
+ type: 'string',
67
+ description: 'Media generation prompt.',
68
+ },
69
+ providerId: {
70
+ type: 'string',
71
+ description: 'Optional configured media provider id. If omitted, the first generation-capable provider is used.',
72
+ },
73
+ modelId: {
74
+ type: 'string',
75
+ description: 'Optional provider-specific media model id.',
76
+ },
77
+ outputMimeType: {
78
+ type: 'string',
79
+ description: 'Optional requested output MIME type, such as image/png or video/mp4.',
80
+ },
81
+ confirm: {
82
+ type: 'boolean',
83
+ description: 'Required true only when the user explicitly asked for this media generation.',
84
+ },
85
+ explicitUserRequest: {
86
+ type: 'string',
87
+ description: 'Short quote or summary of the user request that authorized this generation.',
88
+ },
89
+ },
90
+ required: ['prompt', 'confirm', 'explicitUserRequest'],
91
+ additionalProperties: false,
92
+ },
93
+ sideEffects: ['network', 'state'],
94
+ },
95
+ execute: async (rawArgs: Record<string, unknown>) => {
96
+ const args = rawArgs as AgentMediaGenerateToolArgs;
97
+ const prompt = readString(args.prompt);
98
+ const explicitUserRequest = readString(args.explicitUserRequest);
99
+ const generationProviderCount = mediaProviderRegistry.list()
100
+ .filter((provider) => provider.capabilities.includes('generate')).length;
101
+ if (!prompt) return failure('prompt is required.');
102
+ if (!explicitUserRequest) {
103
+ return failure('explicitUserRequest is required so media generation stays tied to a direct user request.');
104
+ }
105
+ if (!readBoolean(args.confirm)) {
106
+ return failure([
107
+ preview(args, generationProviderCount),
108
+ '',
109
+ 'Model tool confirmation required. Call this tool with confirm:true only when the user explicitly asked GoodVibes Agent to generate this media.',
110
+ ].join('\n'));
111
+ }
112
+ try {
113
+ const result = await generateAgentMedia(mediaProviderRegistry, artifactStore, {
114
+ prompt,
115
+ ...(readString(args.providerId) ? { providerId: readString(args.providerId) } : {}),
116
+ ...(readString(args.modelId) ? { modelId: readString(args.modelId) } : {}),
117
+ ...(readString(args.outputMimeType) ? { outputMimeType: readString(args.outputMimeType) } : {}),
118
+ });
119
+ return output(formatAgentMediaGenerationResult(result));
120
+ } catch (error) {
121
+ return failure(error instanceof Error ? error.message : String(error));
122
+ }
123
+ },
124
+ };
125
+ }
126
+
127
+ export function registerAgentMediaGenerateTool(
128
+ registry: ToolRegistry,
129
+ mediaProviderRegistry: MediaProviderRegistry,
130
+ artifactStore: Pick<ArtifactStore, 'create'>,
131
+ ): void {
132
+ registry.register(createAgentMediaGenerateTool(mediaProviderRegistry, artifactStore));
133
+ }
@@ -0,0 +1,143 @@
1
+ import type { WebhookNotifierSendResult } from '@pellux/goodvibes-sdk/platform/integrations';
2
+ import type { Tool } from '@pellux/goodvibes-sdk/platform/types';
3
+ import type { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
4
+
5
+ interface AgentNotificationConfigReader {
6
+ getCategory(category: string): unknown;
7
+ }
8
+
9
+ interface AgentNotificationSender {
10
+ setUrls(urls: string[]): void;
11
+ send(text: string): Promise<WebhookNotifierSendResult>;
12
+ }
13
+
14
+ export interface AgentNotifyToolArgs {
15
+ readonly message?: unknown;
16
+ readonly confirm?: unknown;
17
+ readonly explicitUserRequest?: unknown;
18
+ }
19
+
20
+ type JsonRecord = Record<string, unknown>;
21
+
22
+ function isRecord(value: unknown): value is JsonRecord {
23
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
24
+ }
25
+
26
+ function readString(value: unknown): string {
27
+ return typeof value === 'string' ? value.trim() : '';
28
+ }
29
+
30
+ function readBoolean(value: unknown): boolean {
31
+ return value === true || value === 'true' || value === 'yes';
32
+ }
33
+
34
+ function configuredWebhookUrls(configManager: AgentNotificationConfigReader): readonly string[] {
35
+ const category = configManager.getCategory('notifications');
36
+ if (!isRecord(category) || !Array.isArray(category.webhookUrls)) return [];
37
+ return category.webhookUrls
38
+ .filter((url): url is string => typeof url === 'string')
39
+ .map((url) => url.trim())
40
+ .filter((url) => url.length > 0);
41
+ }
42
+
43
+ function preview(message: string, targetCount: number): string {
44
+ return [
45
+ 'Agent notification preview',
46
+ ` message ${message}`,
47
+ ` configured targets: ${targetCount}`,
48
+ ' policy external notification delivery requires an explicit user request and confirm:true',
49
+ ].join('\n');
50
+ }
51
+
52
+ function formatSendResult(result: WebhookNotifierSendResult): string {
53
+ const lines = [
54
+ 'Agent notification sent',
55
+ ` attempted: ${result.attempted}`,
56
+ ` delivered: ${result.delivered}`,
57
+ ` failed: ${result.failed}`,
58
+ ];
59
+ result.results.slice(0, 20).forEach((delivery, index) => {
60
+ lines.push(` target ${index + 1} ${delivery.ok ? 'ok' : `failed${delivery.error ? ` (${delivery.error})` : ''}`}`);
61
+ });
62
+ if (result.results.length > 20) lines.push(` ${result.results.length - 20} more target(s) omitted.`);
63
+ return lines.join('\n');
64
+ }
65
+
66
+ function failure(error: string): { readonly success: false; readonly error: string } {
67
+ return { success: false, error };
68
+ }
69
+
70
+ function output(text: string): { readonly success: true; readonly output: string } {
71
+ return { success: true, output: text };
72
+ }
73
+
74
+ export function createAgentNotifyTool(
75
+ configManager: AgentNotificationConfigReader,
76
+ notifier: AgentNotificationSender,
77
+ ): Tool {
78
+ return {
79
+ definition: {
80
+ name: 'agent_notify',
81
+ description: [
82
+ 'Send one plain-text notification to configured GoodVibes Agent webhook notification targets from the main conversation.',
83
+ 'Use only when the user explicitly asks Agent to notify, message, alert, or send a configured notification.',
84
+ 'This uses Agent-local notification webhook targets; it does not create channel routes, authorize accounts, manage connected-host hosting, create separate Agent jobs, run delegated review, or write to default knowledge or non-Agent knowledge segments.',
85
+ 'Set confirm:true only for an explicit user request. Otherwise return the preview/confirmation error.',
86
+ ].join(' '),
87
+ parameters: {
88
+ type: 'object',
89
+ properties: {
90
+ message: {
91
+ type: 'string',
92
+ description: 'Plain-text notification body to send to configured Agent notification webhook targets.',
93
+ },
94
+ confirm: {
95
+ type: 'boolean',
96
+ description: 'Required true only when the user explicitly asked to send this notification.',
97
+ },
98
+ explicitUserRequest: {
99
+ type: 'string',
100
+ description: 'Short quote or summary of the user request that authorized this external notification.',
101
+ },
102
+ },
103
+ required: ['message', 'confirm', 'explicitUserRequest'],
104
+ additionalProperties: false,
105
+ },
106
+ sideEffects: ['network'],
107
+ },
108
+ execute: async (rawArgs: unknown) => {
109
+ try {
110
+ const args = rawArgs as AgentNotifyToolArgs;
111
+ const message = readString(args.message);
112
+ const explicitUserRequest = readString(args.explicitUserRequest);
113
+ if (!message) return failure('message is required.');
114
+ if (!explicitUserRequest) {
115
+ return failure('explicitUserRequest is required so notification delivery stays tied to a direct user request.');
116
+ }
117
+ const urls = configuredWebhookUrls(configManager);
118
+ if (!readBoolean(args.confirm)) {
119
+ return failure([
120
+ preview(message, urls.length),
121
+ '',
122
+ 'Model tool confirmation required. Call this tool with confirm:true only when the user explicitly asked GoodVibes Agent to send this notification.',
123
+ ].join('\n'));
124
+ }
125
+ if (urls.length === 0) {
126
+ return failure('No Agent notification webhook targets are configured. Add one from the Channels workspace before sending notifications.');
127
+ }
128
+ notifier.setUrls([...urls]);
129
+ return output(formatSendResult(await notifier.send(message)));
130
+ } catch (error) {
131
+ return failure(error instanceof Error ? error.message : String(error));
132
+ }
133
+ },
134
+ };
135
+ }
136
+
137
+ export function registerAgentNotifyTool(
138
+ registry: ToolRegistry,
139
+ configManager: AgentNotificationConfigReader,
140
+ notifier: AgentNotificationSender,
141
+ ): void {
142
+ registry.register(createAgentNotifyTool(configManager, notifier));
143
+ }
@@ -0,0 +1,137 @@
1
+ import type { Tool } from '@pellux/goodvibes-sdk/platform/types';
2
+ import type { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
3
+ import type { ShellPathService } from '@/runtime/index.ts';
4
+ import type { AgentConnectedHostConfigReader } from '../agent/routine-schedule-promotion.ts';
5
+ import { resolveAgentConnectedHostConnection } from '../agent/routine-schedule-promotion.ts';
6
+ import {
7
+ buildOperatorActionRequest,
8
+ formatOperatorActionFailure,
9
+ formatOperatorActionSuccess,
10
+ formatOperatorActionToolPreview,
11
+ isRecord,
12
+ OPERATOR_ACTIONS,
13
+ postOperatorAction,
14
+ readOperatorActionBoolean,
15
+ readOperatorActionString,
16
+ } from '../agent/operator-actions.ts';
17
+
18
+ interface OperatorActionToolArgs {
19
+ readonly action?: unknown;
20
+ readonly targetId?: unknown;
21
+ readonly approvalId?: unknown;
22
+ readonly jobId?: unknown;
23
+ readonly runId?: unknown;
24
+ readonly scheduleId?: unknown;
25
+ readonly note?: unknown;
26
+ readonly remember?: unknown;
27
+ readonly confirm?: unknown;
28
+ readonly explicitUserRequest?: unknown;
29
+ }
30
+
31
+ export function createAgentOperatorActionTool(
32
+ shellPaths: ShellPathService,
33
+ configManager: AgentConnectedHostConfigReader,
34
+ ): Tool {
35
+ return {
36
+ definition: {
37
+ name: 'agent_operator_action',
38
+ description: [
39
+ 'Perform one explicit, confirmed connected-host operator action from the main conversation.',
40
+ 'Allowed actions are approvals.approve, approvals.deny, approvals.cancel, automation.jobs.run, automation.jobs.pause, automation.jobs.resume, automation.runs.cancel, automation.runs.retry, and schedules.run.',
41
+ 'Use only when the user explicitly asks for that exact approval, automation job, automation run, or schedule action.',
42
+ 'This tool never creates, edits, deletes, or discovers automation definitions; never manages connected-host hosting; never uses default knowledge, non-Agent knowledge segments, separate Agent jobs, delegated review, or arbitrary route invocation.',
43
+ 'Set confirm:true only for an explicit user request. Otherwise return the preview/confirmation error.',
44
+ ].join(' '),
45
+ parameters: {
46
+ type: 'object',
47
+ properties: {
48
+ action: {
49
+ type: 'string',
50
+ enum: Object.keys(OPERATOR_ACTIONS),
51
+ description: 'Exact allowlisted public operator method id to execute.',
52
+ },
53
+ targetId: {
54
+ type: 'string',
55
+ description: 'Generic target id. Prefer the specific approvalId, jobId, runId, or scheduleId field when known.',
56
+ },
57
+ approvalId: {
58
+ type: 'string',
59
+ description: 'Approval id for approvals.approve, approvals.deny, or approvals.cancel.',
60
+ },
61
+ jobId: {
62
+ type: 'string',
63
+ description: 'Automation job id for automation.jobs.run, automation.jobs.pause, or automation.jobs.resume.',
64
+ },
65
+ runId: {
66
+ type: 'string',
67
+ description: 'Automation run id for automation.runs.cancel or automation.runs.retry.',
68
+ },
69
+ scheduleId: {
70
+ type: 'string',
71
+ description: 'Schedule id for schedules.run.',
72
+ },
73
+ note: {
74
+ type: 'string',
75
+ description: 'Optional approval note for approval actions.',
76
+ },
77
+ remember: {
78
+ type: 'boolean',
79
+ description: 'Optional approval remember flag for approval actions only.',
80
+ },
81
+ confirm: {
82
+ type: 'boolean',
83
+ description: 'Required true only when the user explicitly asked for this exact operator action.',
84
+ },
85
+ explicitUserRequest: {
86
+ type: 'string',
87
+ description: 'Short quote or summary of the user request that authorized this action.',
88
+ },
89
+ },
90
+ required: ['action', 'confirm', 'explicitUserRequest'],
91
+ additionalProperties: false,
92
+ },
93
+ sideEffects: ['network', 'state'],
94
+ },
95
+ execute: async (rawArgs: unknown) => {
96
+ const request = buildOperatorActionRequest(rawArgs);
97
+ if (!request.ok) {
98
+ return { success: false, error: request.error };
99
+ }
100
+ const args = isRecord(rawArgs) ? rawArgs as OperatorActionToolArgs : {};
101
+ const explicitUserRequest = readOperatorActionString(args.explicitUserRequest);
102
+ if (!explicitUserRequest) {
103
+ return {
104
+ success: false,
105
+ error: formatOperatorActionToolPreview(request, ''),
106
+ };
107
+ }
108
+ if (!readOperatorActionBoolean(args.confirm)) {
109
+ return {
110
+ success: false,
111
+ error: formatOperatorActionToolPreview(request, explicitUserRequest),
112
+ };
113
+ }
114
+ const connection = resolveAgentConnectedHostConnection(configManager, shellPaths.homeDirectory);
115
+ if (!connection.token) {
116
+ return {
117
+ success: false,
118
+ error: `auth_required: no connected-host operator token found at ${connection.tokenPath}`,
119
+ };
120
+ }
121
+ const result = await postOperatorAction(connection, request);
122
+ if (!result.ok) return { success: false, error: formatOperatorActionFailure(result) };
123
+ return {
124
+ success: true,
125
+ output: formatOperatorActionSuccess(connection.baseUrl, result),
126
+ };
127
+ },
128
+ };
129
+ }
130
+
131
+ export function registerAgentOperatorActionTool(
132
+ registry: ToolRegistry,
133
+ shellPaths: ShellPathService,
134
+ configManager: AgentConnectedHostConfigReader,
135
+ ): void {
136
+ registry.register(createAgentOperatorActionTool(shellPaths, configManager));
137
+ }
@@ -0,0 +1,217 @@
1
+ import type { Tool } from '@pellux/goodvibes-sdk/platform/types';
2
+ import type { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
3
+ import type { ShellPathService } from '@/runtime/index.ts';
4
+ import type { AgentConnectedHostConfigReader } from '../agent/routine-schedule-promotion.ts';
5
+ import { resolveAgentConnectedHostConnection } from '../agent/routine-schedule-promotion.ts';
6
+
7
+ type JsonRecord = Record<string, unknown>;
8
+
9
+ interface OperatorRouteDescriptor {
10
+ readonly id: string;
11
+ readonly path: string;
12
+ }
13
+
14
+ interface OperatorRouteSuccess {
15
+ readonly ok: true;
16
+ readonly route: OperatorRouteDescriptor;
17
+ readonly body: unknown;
18
+ }
19
+
20
+ interface OperatorRouteFailure {
21
+ readonly ok: false;
22
+ readonly route: OperatorRouteDescriptor;
23
+ readonly kind: 'auth_required' | 'connected_host_unavailable' | 'connected_host_route_unavailable' | 'connected_host_error';
24
+ readonly error: string;
25
+ }
26
+
27
+ type OperatorRouteResult = OperatorRouteSuccess | OperatorRouteFailure;
28
+
29
+ const OPERATOR_BRIEFING_ROUTES: readonly OperatorRouteDescriptor[] = [
30
+ { id: 'projectPlanning.workPlan.snapshot', path: '/api/projects/planning/work-plan' },
31
+ { id: 'approvals.list', path: '/api/approvals' },
32
+ { id: 'automation.integration.snapshot', path: '/api/automation' },
33
+ { id: 'schedules.list', path: '/api/automation/schedules' },
34
+ { id: 'scheduler.capacity', path: '/api/runtime/scheduler' },
35
+ ] as const;
36
+
37
+ function isRecord(value: unknown): value is JsonRecord {
38
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
39
+ }
40
+
41
+ function readRecord(value: unknown, key: string): JsonRecord {
42
+ return isRecord(value) && isRecord(value[key]) ? value[key] as JsonRecord : {};
43
+ }
44
+
45
+ function readArray(value: unknown, key: string): readonly unknown[] {
46
+ return isRecord(value) && Array.isArray(value[key]) ? value[key] : [];
47
+ }
48
+
49
+ function readNumber(value: unknown, key: string): number | null {
50
+ if (!isRecord(value)) return null;
51
+ const candidate = value[key];
52
+ return typeof candidate === 'number' && Number.isFinite(candidate) ? candidate : null;
53
+ }
54
+
55
+ function readString(value: unknown, key: string): string | null {
56
+ if (!isRecord(value)) return null;
57
+ const candidate = value[key];
58
+ return typeof candidate === 'string' ? candidate : null;
59
+ }
60
+
61
+ function summarizeFetchError(error: unknown): string {
62
+ return error instanceof Error ? error.message : String(error);
63
+ }
64
+
65
+ async function readResponseBody(response: Response): Promise<unknown> {
66
+ const text = await response.text();
67
+ if (!text.trim()) return {};
68
+ try {
69
+ return JSON.parse(text) as unknown;
70
+ } catch {
71
+ return text;
72
+ }
73
+ }
74
+
75
+ function classifyHttpFailure(route: OperatorRouteDescriptor, status: number, body: unknown): OperatorRouteFailure {
76
+ const detail = isRecord(body) && typeof body.error === 'string' ? body.error : '';
77
+ return {
78
+ ok: false,
79
+ route,
80
+ kind: status === 401 || status === 403
81
+ ? 'auth_required'
82
+ : status === 404
83
+ ? 'connected_host_route_unavailable'
84
+ : 'connected_host_error',
85
+ error: `HTTP ${status}${detail ? `: ${detail}` : ''}`,
86
+ };
87
+ }
88
+
89
+ async function fetchOperatorRoute(
90
+ baseUrl: string,
91
+ token: string,
92
+ route: OperatorRouteDescriptor,
93
+ ): Promise<OperatorRouteResult> {
94
+ try {
95
+ const response = await fetch(`${baseUrl}${route.path}`, {
96
+ headers: { authorization: `Bearer ${token}` },
97
+ });
98
+ const body = await readResponseBody(response);
99
+ if (!response.ok) return classifyHttpFailure(route, response.status, body);
100
+ return { ok: true, route, body };
101
+ } catch (error) {
102
+ return {
103
+ ok: false,
104
+ route,
105
+ kind: 'connected_host_unavailable',
106
+ error: summarizeFetchError(error),
107
+ };
108
+ }
109
+ }
110
+
111
+ function formatWorkPlan(body: unknown): string {
112
+ const counts = readRecord(body, 'counts');
113
+ const total = readNumber(counts, 'total') ?? readArray(body, 'tasks').length;
114
+ return ` work plan: total ${total}; pending ${readNumber(counts, 'pending') ?? 0}; active ${readNumber(counts, 'in_progress') ?? 0}; blocked ${readNumber(counts, 'blocked') ?? 0}; done ${readNumber(counts, 'done') ?? 0}`;
115
+ }
116
+
117
+ function formatApprovals(body: unknown): string {
118
+ const approvals = readArray(body, 'approvals');
119
+ const pending = approvals.filter((entry) => readString(entry, 'status') === 'pending').length;
120
+ return ` approvals: pending ${pending}; total ${approvals.length}; mode ${readString(body, 'mode') ?? 'unknown'}; awaiting decision ${readString(body, 'awaitingDecision') ?? String(Boolean(isRecord(body) && body.awaitingDecision))}`;
121
+ }
122
+
123
+ function formatAutomation(body: unknown): string {
124
+ const totals = readRecord(body, 'totals');
125
+ const jobs = readNumber(totals, 'jobs') ?? readArray(body, 'jobs').length;
126
+ return ` automation: jobs ${jobs}; enabled ${readNumber(totals, 'enabled') ?? 0}; paused ${readNumber(totals, 'paused') ?? 0}; recent runs ${readNumber(totals, 'runs') ?? readArray(body, 'recentRuns').length}`;
127
+ }
128
+
129
+ function formatSchedules(body: unknown): string {
130
+ const jobs = readArray(body, 'jobs');
131
+ const runs = readArray(body, 'runs');
132
+ const enabled = jobs.filter((entry) => isRecord(entry) && entry.enabled === true).length;
133
+ return ` schedules: jobs ${jobs.length}; enabled ${enabled}; runs ${runs.length}`;
134
+ }
135
+
136
+ function formatCapacity(body: unknown): string {
137
+ return ` scheduler: slots ${readNumber(body, 'slotsInUse') ?? 0}/${readNumber(body, 'slotsTotal') ?? 0}; queue ${readNumber(body, 'queueDepth') ?? 0}; oldest queued ms ${readNumber(body, 'oldestQueuedAgeMs') ?? 0}`;
138
+ }
139
+
140
+ function formatRouteFailureKind(kind: OperatorRouteFailure['kind']): string {
141
+ if (kind === 'auth_required') return 'authorization required';
142
+ if (kind === 'connected_host_unavailable') return 'connected host unavailable';
143
+ if (kind === 'connected_host_route_unavailable') return 'connected host route unavailable';
144
+ return 'connected host error';
145
+ }
146
+
147
+ function formatRoute(result: OperatorRouteResult): string {
148
+ if (!result.ok) return ` ${result.route.id}: unavailable (${result.kind}; ${result.error})`;
149
+ if (result.route.id === 'projectPlanning.workPlan.snapshot') return formatWorkPlan(result.body);
150
+ if (result.route.id === 'approvals.list') return formatApprovals(result.body);
151
+ if (result.route.id === 'automation.integration.snapshot') return formatAutomation(result.body);
152
+ if (result.route.id === 'schedules.list') return formatSchedules(result.body);
153
+ return formatCapacity(result.body);
154
+ }
155
+
156
+ function formatBriefing(baseUrl: string, results: readonly OperatorRouteResult[]): string {
157
+ const failures = results.filter((result): result is OperatorRouteFailure => !result.ok);
158
+ return [
159
+ 'Agent operator briefing',
160
+ ` connected host ${baseUrl}`,
161
+ ' policy read-only public operator routes; no connected-host lifecycle, mutation routes, separate Agent jobs, delegated review, default knowledge, or non-Agent knowledge segments',
162
+ '',
163
+ ...results.map(formatRoute),
164
+ '',
165
+ failures.length === 0
166
+ ? ' warnings: none'
167
+ : ` warnings: ${failures.length} route(s) unavailable; retry after host/auth/version repair`,
168
+ ].join('\n');
169
+ }
170
+
171
+ export function createAgentOperatorBriefingTool(
172
+ shellPaths: ShellPathService,
173
+ configManager: AgentConnectedHostConfigReader,
174
+ ): Tool {
175
+ return {
176
+ definition: {
177
+ name: 'agent_operator_briefing',
178
+ description: [
179
+ 'Read connected GoodVibes operator state for a concise main-conversation briefing.',
180
+ 'Use when the user asks what needs attention, what is pending, what is scheduled, or what the operator status is.',
181
+ 'This is read-only and calls only public work-plan, approvals, automation, schedules, and scheduler routes.',
182
+ 'It never uses default knowledge, non-Agent knowledge segments, channel send routes, mutation routes, connected-host lifecycle, separate Agent jobs, or delegated review.',
183
+ ].join(' '),
184
+ parameters: {
185
+ type: 'object',
186
+ properties: {},
187
+ additionalProperties: false,
188
+ },
189
+ sideEffects: ['network'],
190
+ },
191
+ execute: async () => {
192
+ const connection = resolveAgentConnectedHostConnection(configManager, shellPaths.homeDirectory);
193
+ if (!connection.token) {
194
+ return {
195
+ success: false,
196
+ error: `auth_required: no connected-host operator token found at ${connection.tokenPath}`,
197
+ };
198
+ }
199
+ const results: OperatorRouteResult[] = [];
200
+ for (const route of OPERATOR_BRIEFING_ROUTES) {
201
+ results.push(await fetchOperatorRoute(connection.baseUrl, connection.token, route));
202
+ }
203
+ return {
204
+ success: true,
205
+ output: formatBriefing(connection.baseUrl, results),
206
+ };
207
+ },
208
+ };
209
+ }
210
+
211
+ export function registerAgentOperatorBriefingTool(
212
+ registry: ToolRegistry,
213
+ shellPaths: ShellPathService,
214
+ configManager: AgentConnectedHostConfigReader,
215
+ ): void {
216
+ registry.register(createAgentOperatorBriefingTool(shellPaths, configManager));
217
+ }