@pellux/goodvibes-agent 0.1.117 → 1.0.1

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 (255) hide show
  1. package/CHANGELOG.md +20 -4
  2. package/README.md +35 -14
  3. package/bin/goodvibes-agent.ts +16 -2
  4. package/dist/package/main.js +176093 -171065
  5. package/docs/README.md +11 -5
  6. package/docs/channels-remote-and-api.md +50 -0
  7. package/docs/connected-host.md +3 -3
  8. package/docs/getting-started.md +29 -15
  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 +44 -9
  13. package/docs/tools-and-commands.md +123 -0
  14. package/docs/voice-and-live-tts.md +51 -0
  15. package/package.json +2 -5
  16. package/src/agent/channel-delivery.ts +201 -0
  17. package/src/agent/media-generation.ts +159 -0
  18. package/src/agent/memory-prompt.ts +0 -1
  19. package/src/agent/note-registry.ts +329 -0
  20. package/src/agent/operator-actions.ts +343 -0
  21. package/src/agent/persona-registry.ts +15 -14
  22. package/src/agent/record-labels.ts +107 -0
  23. package/src/agent/reminder-schedule-format.ts +33 -24
  24. package/src/agent/reminder-schedule.ts +26 -25
  25. package/src/agent/routine-registry.ts +13 -12
  26. package/src/agent/routine-schedule-args.ts +2 -1
  27. package/src/agent/routine-schedule-format.ts +77 -53
  28. package/src/agent/routine-schedule-promotion.ts +34 -32
  29. package/src/agent/routine-schedule-receipts.ts +28 -26
  30. package/src/agent/runtime-profile-starters.ts +2 -2
  31. package/src/agent/runtime-profile.ts +18 -17
  32. package/src/agent/skill-registry.ts +25 -24
  33. package/src/cli/agent-knowledge-args.ts +5 -1
  34. package/src/cli/agent-knowledge-command.ts +39 -33
  35. package/src/cli/agent-knowledge-format.ts +80 -67
  36. package/src/cli/agent-knowledge-methods.ts +1 -1
  37. package/src/cli/agent-knowledge-runtime.ts +32 -26
  38. package/src/cli/bundle-command.ts +13 -8
  39. package/src/cli/config-overrides.ts +37 -36
  40. package/src/cli/external-runtime.ts +10 -4
  41. package/src/cli/help.ts +29 -11
  42. package/src/cli/local-library-command.ts +159 -73
  43. package/src/cli/management-commands.ts +98 -62
  44. package/src/cli/management.ts +52 -26
  45. package/src/cli/memory-command.ts +66 -32
  46. package/src/cli/parser.ts +37 -24
  47. package/src/cli/profiles-command.ts +52 -35
  48. package/src/cli/provider-auth-routes.ts +2 -1
  49. package/src/cli/routines-command.ts +59 -39
  50. package/src/cli/service-posture.ts +6 -6
  51. package/src/cli/status.ts +46 -121
  52. package/src/core/conversation-message-snapshot.ts +131 -0
  53. package/src/input/agent-workspace-activation.ts +33 -7
  54. package/src/input/agent-workspace-basic-command-editor-submission.ts +7 -3
  55. package/src/input/agent-workspace-basic-command-editors.ts +30 -10
  56. package/src/input/agent-workspace-categories.ts +276 -64
  57. package/src/input/agent-workspace-channel-command-editor-submission.ts +34 -0
  58. package/src/input/agent-workspace-channel-command-editors.ts +23 -5
  59. package/src/input/agent-workspace-channels.ts +35 -2
  60. package/src/input/agent-workspace-command-editor.ts +18 -2
  61. package/src/input/agent-workspace-config-reader.ts +16 -0
  62. package/src/input/agent-workspace-delegation-editor-submission.ts +1 -1
  63. package/src/input/agent-workspace-editors.ts +140 -2
  64. package/src/input/agent-workspace-knowledge-query-editor.ts +1 -1
  65. package/src/input/agent-workspace-knowledge-url-editor.ts +4 -11
  66. package/src/input/agent-workspace-learned-behavior.ts +2 -2
  67. package/src/input/agent-workspace-library-command-editor-submission.ts +1 -1
  68. package/src/input/agent-workspace-library-command-editors.ts +2 -2
  69. package/src/input/agent-workspace-local-operations.ts +218 -0
  70. package/src/input/agent-workspace-local-selection.ts +75 -0
  71. package/src/input/agent-workspace-media-command-editor-submission.ts +62 -0
  72. package/src/input/agent-workspace-media-command-editors.ts +27 -0
  73. package/src/input/agent-workspace-memory-command-editors.ts +1 -1
  74. package/src/input/agent-workspace-memory-editor.ts +2 -2
  75. package/src/input/agent-workspace-navigation.ts +38 -2
  76. package/src/input/agent-workspace-notify-editor-submission.ts +16 -2
  77. package/src/input/agent-workspace-operations-command-editor-submission.ts +63 -1
  78. package/src/input/agent-workspace-operations-command-editors.ts +80 -3
  79. package/src/input/agent-workspace-panel-route.ts +43 -0
  80. package/src/input/agent-workspace-reminder-schedule-editor.ts +1 -1
  81. package/src/input/agent-workspace-routine-schedule-editor.ts +2 -2
  82. package/src/input/agent-workspace-search.ts +169 -0
  83. package/src/input/agent-workspace-setup.ts +22 -11
  84. package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +6 -6
  85. package/src/input/agent-workspace-skill-bundle-command-editors.ts +10 -10
  86. package/src/input/agent-workspace-snapshot.ts +112 -13
  87. package/src/input/agent-workspace-task-command-editors.ts +4 -4
  88. package/src/input/agent-workspace-token.ts +18 -2
  89. package/src/input/agent-workspace-types.ts +92 -4
  90. package/src/input/agent-workspace-voice-media.ts +3 -6
  91. package/src/input/agent-workspace-web-research-editor.ts +104 -0
  92. package/src/input/agent-workspace.ts +136 -208
  93. package/src/input/command-registry.ts +4 -1
  94. package/src/input/commands/agent-local-library-args.ts +52 -0
  95. package/src/input/commands/agent-runtime-profile-runtime.ts +45 -41
  96. package/src/input/commands/agent-skills-runtime.ts +87 -99
  97. package/src/input/commands/agent-workspace-runtime.ts +23 -7
  98. package/src/input/commands/brief-runtime.ts +25 -24
  99. package/src/input/commands/channels-runtime.ts +145 -31
  100. package/src/input/commands/delegation-runtime.ts +29 -23
  101. package/src/input/commands/experience-runtime.ts +6 -4
  102. package/src/input/commands/guidance-runtime.ts +4 -4
  103. package/src/input/commands/health-runtime.ts +140 -115
  104. package/src/input/commands/knowledge.ts +57 -56
  105. package/src/input/commands/local-provider-runtime.ts +36 -18
  106. package/src/input/commands/local-runtime.ts +138 -16
  107. package/src/input/commands/local-setup-review.ts +7 -7
  108. package/src/input/commands/mcp-runtime.ts +118 -50
  109. package/src/input/commands/notify-runtime.ts +38 -9
  110. package/src/input/commands/operator-actions-runtime.ts +138 -0
  111. package/src/input/commands/operator-runtime.ts +6 -17
  112. package/src/input/commands/personas-runtime.ts +49 -59
  113. package/src/input/commands/planning-runtime.ts +1 -1
  114. package/src/input/commands/platform-access-runtime.ts +29 -29
  115. package/src/input/commands/provider-accounts-runtime.ts +60 -39
  116. package/src/input/commands/qrcode-runtime.ts +45 -6
  117. package/src/input/commands/recall-bundle.ts +11 -11
  118. package/src/input/commands/recall-capture.ts +13 -11
  119. package/src/input/commands/recall-query.ts +29 -21
  120. package/src/input/commands/recall-review.ts +2 -1
  121. package/src/input/commands/routines-runtime.ts +63 -72
  122. package/src/input/commands/schedule-runtime.ts +33 -20
  123. package/src/input/commands/security-runtime.ts +4 -4
  124. package/src/input/commands/session-content.ts +80 -78
  125. package/src/input/commands/session-workflow.ts +132 -93
  126. package/src/input/commands/session.ts +3 -174
  127. package/src/input/commands/shell-core.ts +32 -17
  128. package/src/input/commands/subscription-runtime.ts +53 -179
  129. package/src/input/commands/tasks-runtime.ts +20 -20
  130. package/src/input/commands/work-plan-runtime.ts +33 -8
  131. package/src/input/commands.ts +2 -2
  132. package/src/input/feed-context-factory.ts +2 -1
  133. package/src/input/file-picker.ts +3 -2
  134. package/src/input/handler-command-route.ts +4 -7
  135. package/src/input/handler-content-actions.ts +89 -1
  136. package/src/input/handler-feed-routes.ts +19 -12
  137. package/src/input/handler-feed.ts +6 -3
  138. package/src/input/handler-interactions.ts +7 -5
  139. package/src/input/handler-modal-stack.ts +3 -3
  140. package/src/input/handler-onboarding.ts +24 -80
  141. package/src/input/handler-shortcuts.ts +15 -4
  142. package/src/input/handler.ts +47 -17
  143. package/src/input/input-history.ts +5 -6
  144. package/src/input/keybindings.ts +22 -11
  145. package/src/input/onboarding/onboarding-wizard-apply.ts +13 -0
  146. package/src/input/onboarding/onboarding-wizard-constants.ts +1 -0
  147. package/src/input/onboarding/onboarding-wizard-operator-steps.ts +147 -57
  148. package/src/input/onboarding/onboarding-wizard-steps.ts +46 -29
  149. package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
  150. package/src/input/onboarding/onboarding-wizard.ts +68 -0
  151. package/src/input/profile-picker-modal.ts +31 -12
  152. package/src/input/session-picker-modal.ts +22 -4
  153. package/src/input/settings-modal-behavior.ts +0 -3
  154. package/src/input/settings-modal-subscriptions.ts +3 -3
  155. package/src/input/settings-modal-types.ts +2 -13
  156. package/src/input/settings-modal.ts +6 -52
  157. package/src/input/submission-intent.ts +0 -1
  158. package/src/input/submission-router.ts +3 -3
  159. package/src/main.ts +18 -8
  160. package/src/panels/approval-panel.ts +8 -8
  161. package/src/panels/automation-control-panel.ts +2 -2
  162. package/src/panels/base-panel.ts +1 -1
  163. package/src/panels/builtin/agent.ts +1 -1
  164. package/src/panels/builtin/operations.ts +1 -10
  165. package/src/panels/builtin/session.ts +9 -9
  166. package/src/panels/builtin/shared.ts +2 -2
  167. package/src/panels/cost-tracker-panel.ts +1 -1
  168. package/src/panels/docs-panel.ts +5 -3
  169. package/src/panels/index.ts +0 -1
  170. package/src/panels/knowledge-panel.ts +6 -5
  171. package/src/panels/memory-panel.ts +7 -6
  172. package/src/panels/panel-list-panel.ts +36 -80
  173. package/src/panels/project-planning-panel.ts +18 -11
  174. package/src/panels/provider-account-snapshot.ts +51 -25
  175. package/src/panels/provider-accounts-panel.ts +33 -18
  176. package/src/panels/provider-health-domains.ts +45 -4
  177. package/src/panels/provider-health-panel.ts +5 -4
  178. package/src/panels/qr-panel.ts +1 -1
  179. package/src/panels/schedule-panel.ts +9 -17
  180. package/src/panels/security-panel.ts +8 -8
  181. package/src/panels/session-browser-panel.ts +10 -10
  182. package/src/panels/subscription-panel.ts +3 -3
  183. package/src/panels/system-messages-panel.ts +1 -1
  184. package/src/panels/tasks-panel.ts +20 -13
  185. package/src/panels/tool-inspector-panel.ts +19 -12
  186. package/src/panels/work-plan-panel.ts +5 -5
  187. package/src/planning/project-planning-coordinator.ts +1 -1
  188. package/src/provider-auth-route-display.ts +9 -0
  189. package/src/renderer/agent-workspace-style.ts +34 -0
  190. package/src/renderer/agent-workspace.ts +228 -52
  191. package/src/renderer/autocomplete-overlay.ts +25 -6
  192. package/src/renderer/bookmark-modal.ts +19 -4
  193. package/src/renderer/buffer.ts +4 -2
  194. package/src/renderer/context-inspector.ts +50 -13
  195. package/src/renderer/diff.ts +1 -1
  196. package/src/renderer/file-picker-overlay.ts +19 -6
  197. package/src/renderer/help-overlay.ts +106 -33
  198. package/src/renderer/history-search-overlay.ts +19 -4
  199. package/src/renderer/live-tail-modal.ts +27 -5
  200. package/src/renderer/mcp-workspace.ts +164 -50
  201. package/src/renderer/model-picker-overlay.ts +58 -2
  202. package/src/renderer/model-workspace.ts +104 -20
  203. package/src/renderer/process-modal.ts +27 -6
  204. package/src/renderer/profile-picker-modal.ts +20 -4
  205. package/src/renderer/search-overlay.ts +25 -5
  206. package/src/renderer/selection-modal-overlay.ts +46 -14
  207. package/src/renderer/session-picker-modal.ts +18 -1
  208. package/src/renderer/settings-modal-helpers.ts +2 -40
  209. package/src/renderer/settings-modal.ts +83 -50
  210. package/src/renderer/tool-call.ts +20 -11
  211. package/src/runtime/agent-runtime-events.ts +6 -6
  212. package/src/runtime/bootstrap-command-context.ts +7 -1
  213. package/src/runtime/bootstrap-command-parts.ts +9 -7
  214. package/src/runtime/bootstrap-core.ts +19 -1
  215. package/src/runtime/bootstrap-hook-bridge.ts +7 -18
  216. package/src/runtime/bootstrap-shell.ts +4 -4
  217. package/src/runtime/bootstrap.ts +31 -22
  218. package/src/runtime/connected-host-auth.ts +7 -2
  219. package/src/runtime/diagnostics/panels/index.ts +2 -2
  220. package/src/runtime/diagnostics/panels/policy.ts +7 -7
  221. package/src/runtime/index.ts +2 -1
  222. package/src/runtime/onboarding/apply-file-helpers.ts +66 -0
  223. package/src/runtime/onboarding/apply.ts +80 -79
  224. package/src/runtime/onboarding/derivation.ts +5 -8
  225. package/src/runtime/onboarding/snapshot.ts +0 -15
  226. package/src/runtime/onboarding/types.ts +8 -19
  227. package/src/runtime/onboarding/verify.ts +32 -10
  228. package/src/runtime/operator-token-cleanup.ts +1 -1
  229. package/src/runtime/services.ts +91 -22
  230. package/src/runtime/store/selectors/index.ts +3 -3
  231. package/src/runtime/store/state.ts +1 -4
  232. package/src/runtime/surface-feature-flags.ts +41 -6
  233. package/src/runtime/ui-read-models.ts +3 -4
  234. package/src/runtime/ui-services.ts +6 -6
  235. package/src/shell/blocking-input.ts +2 -1
  236. package/src/shell/ui-openers.ts +3 -13
  237. package/src/tools/agent-analysis-registry-policy.ts +0 -1
  238. package/src/tools/agent-channel-send-tool.ts +133 -0
  239. package/src/tools/agent-context-policy.ts +1 -1
  240. package/src/tools/agent-knowledge-ingest-tool.ts +405 -0
  241. package/src/tools/agent-knowledge-tool.ts +170 -0
  242. package/src/tools/agent-local-registry-tool.ts +269 -69
  243. package/src/tools/agent-media-generate-tool.ts +133 -0
  244. package/src/tools/agent-notify-tool.ts +143 -0
  245. package/src/tools/agent-operator-action-tool.ts +137 -0
  246. package/src/tools/agent-operator-briefing-tool.ts +217 -0
  247. package/src/tools/agent-reminder-schedule-tool.ts +237 -0
  248. package/src/tools/agent-tool-policy-guard.ts +8 -8
  249. package/src/tools/agent-work-plan-tool.ts +256 -0
  250. package/src/version.ts +1 -1
  251. package/src/input/commands/policy-dispatch.ts +0 -339
  252. package/src/input/commands/policy.ts +0 -13
  253. package/src/panels/panel-picker.ts +0 -105
  254. package/src/panels/policy-panel.ts +0 -308
  255. package/src/renderer/panel-picker-overlay.ts +0 -202
@@ -1,5 +1,7 @@
1
1
  import { join } from 'node:path';
2
2
  import { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
3
+ import { shell as runtimeShell } from '@pellux/goodvibes-sdk/platform/runtime';
4
+ import type { shell as RuntimeShell } from '@pellux/goodvibes-sdk/platform/runtime';
3
5
  import { SecretsManager } from '../config/secrets.ts';
4
6
  import { ServiceRegistry } from '@pellux/goodvibes-sdk/platform/config';
5
7
  import { SubscriptionManager } from '@pellux/goodvibes-sdk/platform/config';
@@ -11,6 +13,8 @@ import { WatcherRegistry } from '@pellux/goodvibes-sdk/platform/watchers';
11
13
  import { ArtifactStore } from '@pellux/goodvibes-sdk/platform/artifacts';
12
14
  import {
13
15
  GOODVIBES_AGENT_KNOWLEDGE_DB_FILE,
16
+ HOME_GRAPH_KNOWLEDGE_DB_FILE,
17
+ HOME_GRAPH_KNOWLEDGE_EXTENSION,
14
18
  KnowledgeService,
15
19
  KnowledgeSemanticService,
16
20
  KnowledgeStore,
@@ -19,11 +23,12 @@ import {
19
23
  createWebKnowledgeGapRepairer,
20
24
  projectPlanningProjectIdFromPath,
21
25
  } from '@pellux/goodvibes-sdk/platform/knowledge';
26
+ import * as KnowledgePlatform from '@pellux/goodvibes-sdk/platform/knowledge';
22
27
  import { MediaProviderRegistry, ensureBuiltinMediaProviders } from '@pellux/goodvibes-sdk/platform/media';
23
28
  import { MultimodalService } from '@pellux/goodvibes-sdk/platform/multimodal';
24
29
  import { AgentManager } from '@pellux/goodvibes-sdk/platform/tools';
25
30
  import { AgentMessageBus } from '@pellux/goodvibes-sdk/platform/agents';
26
- import type { WrfcController } from '@pellux/goodvibes-sdk/platform/agents';
31
+ import { WrfcController } from '@pellux/goodvibes-sdk/platform/agents';
27
32
  import { AgentOrchestrator } from '@pellux/goodvibes-sdk/platform/agents';
28
33
  import { ArchetypeLoader } from '@pellux/goodvibes-sdk/platform/agents';
29
34
  import { ProcessManager } from '@pellux/goodvibes-sdk/platform/tools';
@@ -73,11 +78,10 @@ import { IdempotencyStore } from '@/runtime/index.ts';
73
78
  import { OverflowHandler } from '@pellux/goodvibes-sdk/platform/tools';
74
79
  import { ToolLLM } from '@pellux/goodvibes-sdk/platform/config';
75
80
  import { ComponentHealthMonitor } from '@/runtime/index.ts';
76
- import type { WorktreeRegistry } from '@/runtime/index.ts';
77
81
  import { SandboxSessionRegistry } from '@/runtime/index.ts';
78
82
  import { createShellPathService, type ShellPathService } from '@/runtime/index.ts';
79
83
  import { GOODVIBES_AGENT_SURFACE_ROOT } from '../config/surface.ts';
80
- import type { FeatureFlagManager } from '@/runtime/index.ts';
84
+ import type { FeatureFlagManager, RuntimeFoundationClientsOptions } from '@/runtime/index.ts';
81
85
  import { createFeatureFlagManager } from '@/runtime/index.ts';
82
86
  import { PolicyRuntimeState } from '@/runtime/index.ts';
83
87
  import {
@@ -86,6 +90,19 @@ import {
86
90
  } from '@pellux/goodvibes-sdk/platform/tools';
87
91
  import { WorkPlanStore } from '../work-plans/work-plan-store.ts';
88
92
 
93
+ type WorktreeRegistry = RuntimeShell.WorktreeRegistry;
94
+ type SdkRuntimeServices = RuntimeFoundationClientsOptions['runtimeServices'];
95
+ type SdkCompanionGraphService = NonNullable<SdkRuntimeServices>['homeGraphService'];
96
+ type KnowledgeServiceConstructor = new (
97
+ store: KnowledgeStore,
98
+ artifactStore: ArtifactStore,
99
+ options?: unknown,
100
+ ) => SdkCompanionGraphService;
101
+
102
+ const companionGraphServiceConstructor = KnowledgePlatform[
103
+ ['Home', 'GraphService'].join('') as keyof typeof KnowledgePlatform
104
+ ] as unknown as KnowledgeServiceConstructor;
105
+
89
106
  function buildFallbackModelDefinition(provider: string, modelId: string): ModelDefinition {
90
107
  const providerLower = provider.toLowerCase();
91
108
  const isReasoningProvider = providerLower.includes('openai')
@@ -113,22 +130,52 @@ function buildFallbackModelDefinition(provider: string, modelId: string): ModelD
113
130
  };
114
131
  }
115
132
 
116
- function createDisabledAgentWorktreeRegistry(): WorktreeRegistry {
117
- const registry: Pick<WorktreeRegistry, 'list' | 'attach' | 'setState' | 'cleanup'> = {
118
- async list() {
119
- return [];
133
+ class DisabledAgentWorktreeRegistry extends runtimeShell.WorktreeRegistry {
134
+ public override async list(): Promise<Awaited<ReturnType<WorktreeRegistry['list']>>> {
135
+ return [] as Awaited<ReturnType<WorktreeRegistry['list']>>;
136
+ }
137
+
138
+ public override attach(_path: string, _target: { sessionId?: string; taskId?: string }): void {
139
+ throw new Error('GoodVibes Agent does not own local worktree attachment. Delegate build, fix, and review work to GoodVibes TUI.');
140
+ }
141
+
142
+ public override setState(_path: string, _state: Parameters<WorktreeRegistry['setState']>[1]): void {
143
+ throw new Error('GoodVibes Agent does not own local worktree state. Delegate build, fix, and review work to GoodVibes TUI.');
144
+ }
145
+
146
+ public override async cleanup(_path: string): Promise<void> {
147
+ throw new Error('GoodVibes Agent does not own local worktree cleanup. Delegate build, fix, and review work to GoodVibes TUI.');
148
+ }
149
+ }
150
+
151
+ function createDisabledAgentWorktreeRegistry(workingDirectory: string): WorktreeRegistry {
152
+ return new DisabledAgentWorktreeRegistry(workingDirectory, {
153
+ surfaceRoot: GOODVIBES_AGENT_SURFACE_ROOT,
154
+ });
155
+ }
156
+
157
+ type AgentWrfcWorktreeFactory = NonNullable<ConstructorParameters<typeof WrfcController>[2]['createWorktree']>;
158
+ type AgentWrfcWorktreeOps = ReturnType<AgentWrfcWorktreeFactory>;
159
+
160
+ function agentWrfcWorktreeError(operation: string): Error {
161
+ return new Error(`GoodVibes Agent does not own local review worktree ${operation}. Delegate build, fix, and review work to GoodVibes TUI.`);
162
+ }
163
+
164
+ function createDisabledAgentWrfcWorktreeOps(): AgentWrfcWorktreeOps {
165
+ return {
166
+ async merge(_agentId: string): Promise<boolean> {
167
+ throw agentWrfcWorktreeError('merge');
120
168
  },
121
- attach(_path, _target) {
122
- throw new Error('GoodVibes Agent does not own local worktree attachment. Delegate build, fix, and review work to GoodVibes TUI.');
169
+ async cleanup(_agentId: string): Promise<void> {
170
+ throw agentWrfcWorktreeError('cleanup');
123
171
  },
124
- setState(_path, _state) {
125
- throw new Error('GoodVibes Agent does not own local worktree state. Delegate build, fix, and review work to GoodVibes TUI.');
172
+ async commitWorkingTree(_message: string): Promise<string | null> {
173
+ throw agentWrfcWorktreeError('commit');
126
174
  },
127
- async cleanup(_path) {
128
- throw new Error('GoodVibes Agent does not own local worktree cleanup. Delegate build, fix, and review work to GoodVibes TUI.');
175
+ async currentHead(): Promise<string | null> {
176
+ return null;
129
177
  },
130
178
  };
131
- return registry as unknown as WorktreeRegistry;
132
179
  }
133
180
 
134
181
  function ensureConfiguredModelIsRoutable(providerRegistry: ProviderRegistry, configManager: ConfigManager): void {
@@ -272,7 +319,7 @@ export interface RuntimeServicesOptions {
272
319
  readonly homeDirectory: string;
273
320
  }
274
321
 
275
- export interface RuntimeServices {
322
+ export interface RuntimeServices extends SdkRuntimeServices {
276
323
  readonly workingDirectory: string;
277
324
  readonly homeDirectory: string;
278
325
  readonly shellPaths: ShellPathService;
@@ -294,9 +341,10 @@ export interface RuntimeServices {
294
341
  readonly automationManager: AutomationManager;
295
342
  readonly gatewayMethods: GatewayMethodCatalog;
296
343
  readonly artifactStore: ArtifactStore;
297
- /** Compatibility alias that intentionally points at the isolated Agent Knowledge service, not default Knowledge/Wiki. */
344
+ /** Compatibility alias that intentionally points at the isolated Agent Knowledge service, not default knowledge. */
298
345
  readonly knowledgeService: KnowledgeService;
299
346
  readonly agentKnowledgeService: KnowledgeService;
347
+ readonly homeGraphService: SdkCompanionGraphService;
300
348
  readonly projectPlanningService: ProjectPlanningService;
301
349
  readonly projectPlanningProjectId: string;
302
350
  readonly workPlanStore: WorkPlanStore;
@@ -353,7 +401,7 @@ export interface RuntimeServices {
353
401
  readonly agentManager: AgentManager;
354
402
  readonly agentMessageBus: AgentMessageBus;
355
403
  readonly agentOrchestrator: AgentOrchestrator;
356
- readonly wrfcController: Pick<WrfcController, 'listChains'>;
404
+ readonly wrfcController: WrfcController;
357
405
  readonly processManager: ProcessManager;
358
406
  readonly modeManager: ModeManager;
359
407
  readonly fileUndoManager: FileUndoManager;
@@ -461,9 +509,13 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
461
509
  configManager,
462
510
  });
463
511
  agentManager.setRuntimeBus(options.runtimeBus);
464
- const wrfcController: Pick<WrfcController, 'listChains'> = {
465
- listChains: () => [],
466
- };
512
+ const wrfcController = Reflect.construct(WrfcController, [options.runtimeBus, agentMessageBus, {
513
+ agentManager,
514
+ configManager,
515
+ projectRoot: workingDirectory,
516
+ surfaceRoot: GOODVIBES_AGENT_SURFACE_ROOT,
517
+ createWorktree: createDisabledAgentWrfcWorktreeOps,
518
+ }]) as WrfcController;
467
519
  const hookDispatcher = new HookDispatcher({ toolLLM, projectRoot: workingDirectory }, hookActivityTracker);
468
520
  configManager.attachHookDispatcher(hookDispatcher);
469
521
  const hookWorkbench = createHookWorkbench({
@@ -511,7 +563,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
511
563
  deliveryManager,
512
564
  spawnTask: (input) => {
513
565
  throw new Error([
514
- 'GoodVibes Agent does not spawn local automation agents.',
566
+ 'GoodVibes Agent does not create local automation jobs and does not spawn local automation agents.',
515
567
  `Received automation prompt: ${input.prompt}`,
516
568
  'Use read-only automation observability here; explicit build/fix/review execution belongs to GoodVibes TUI delegation.',
517
569
  ].join(' '));
@@ -521,6 +573,10 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
521
573
  configManager,
522
574
  dbFileName: GOODVIBES_AGENT_KNOWLEDGE_DB_FILE,
523
575
  });
576
+ const homeGraphKnowledgeStore = new KnowledgeStore({
577
+ configManager,
578
+ dbFileName: HOME_GRAPH_KNOWLEDGE_DB_FILE,
579
+ });
524
580
  const knowledgeSemanticLlm = createProviderBackedKnowledgeSemanticLlm(providerRegistry, {
525
581
  timeoutMs: 20_000,
526
582
  maxConcurrent: 1,
@@ -529,12 +585,20 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
529
585
  llm: knowledgeSemanticLlm,
530
586
  maxLlmSourcesPerReindex: 3,
531
587
  });
588
+ const homeGraphSemanticService = new KnowledgeSemanticService(homeGraphKnowledgeStore, {
589
+ llm: knowledgeSemanticLlm,
590
+ maxLlmSourcesPerReindex: 3,
591
+ objectProfiles: HOME_GRAPH_KNOWLEDGE_EXTENSION.objectProfiles,
592
+ });
532
593
  const agentKnowledgeService = new KnowledgeService(agentKnowledgeStore, artifactStore, undefined, {
533
594
  memoryRegistry,
534
595
  runtimeBus: options.runtimeBus,
535
596
  semanticService: agentKnowledgeSemanticService,
536
597
  });
537
598
  agentKnowledgeService.attachRuntimeBus(options.runtimeBus);
599
+ const homeGraphService = new companionGraphServiceConstructor(homeGraphKnowledgeStore, artifactStore, {
600
+ semanticService: homeGraphSemanticService,
601
+ });
538
602
  const projectPlanningProjectId = projectPlanningProjectIdFromPath(workingDirectory);
539
603
  const projectPlanningService = new ProjectPlanningService(agentKnowledgeStore, {
540
604
  defaultProjectId: projectPlanningProjectId,
@@ -559,6 +623,10 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
559
623
  searchService: webSearchService,
560
624
  ingestService: agentKnowledgeService,
561
625
  }));
626
+ homeGraphSemanticService.setGapRepairer(createWebKnowledgeGapRepairer({
627
+ searchService: webSearchService,
628
+ ingestService: homeGraphService,
629
+ }));
562
630
  const mediaProviders = new MediaProviderRegistry();
563
631
  ensureBuiltinMediaProviders(mediaProviders, artifactStore, providerRegistry);
564
632
  const multimodalService = new MultimodalService(artifactStore, mediaProviders, voiceService, agentKnowledgeService);
@@ -593,7 +661,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
593
661
  mcpRegistry.setSandboxRuntime(configManager, sandboxSessionRegistry);
594
662
  const tokenAuditor = new ApiTokenAuditor({ managed: false });
595
663
  const componentHealthMonitor = new ComponentHealthMonitor();
596
- const worktreeRegistry = createDisabledAgentWorktreeRegistry();
664
+ const worktreeRegistry = createDisabledAgentWorktreeRegistry(workingDirectory);
597
665
  const webhookNotifier = new WebhookNotifier();
598
666
  const replayEngine = new DeterministicReplayEngine(workingDirectory);
599
667
  const providerOptimizer = new ProviderOptimizer(providerRegistry, providerCapabilityRegistry, false);
@@ -687,6 +755,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
687
755
  artifactStore,
688
756
  knowledgeService: agentKnowledgeService,
689
757
  agentKnowledgeService,
758
+ homeGraphService,
690
759
  projectPlanningService,
691
760
  projectPlanningProjectId,
692
761
  workPlanStore,
@@ -53,7 +53,7 @@ export function selectOverlays(state: RuntimeState): OverlayDomainState {
53
53
  return state.overlays;
54
54
  }
55
55
 
56
- /** Select the full panels domain slice. Casts from Record<string,unknown> to PanelDomainState. */
56
+ /** Select the full panels domain slice. */
57
57
  export function selectPanels(state: RuntimeState): PanelDomainState {
58
58
  return state.panels as unknown as PanelDomainState;
59
59
  }
@@ -274,7 +274,7 @@ export function selectPermissionMode(state: RuntimeState): PermissionMode {
274
274
  */
275
275
  export function selectActivePanels(state: RuntimeState): PanelState[] {
276
276
  const result: PanelState[] = [];
277
- const panelDomain = state.panels as unknown as PanelDomainState;
277
+ const panelDomain = selectPanels(state);
278
278
  for (const panel of panelDomain.panels.values()) {
279
279
  if (panel.open) {
280
280
  result.push(panel);
@@ -287,7 +287,7 @@ export function selectActivePanels(state: RuntimeState): PanelState[] {
287
287
  * Returns the panel currently holding focus.
288
288
  */
289
289
  export function selectFocusedPanel(state: RuntimeState): PanelState | undefined {
290
- const panelDomain = state.panels as unknown as PanelDomainState;
290
+ const panelDomain = selectPanels(state);
291
291
  return panelDomain.panels.get(panelDomain.focusedPanelId);
292
292
  }
293
293
 
@@ -76,10 +76,7 @@ export interface RuntimeState {
76
76
  model: ModelDomainState;
77
77
  conversation: ConversationDomainState;
78
78
  overlays: OverlayDomainState;
79
- /**
80
- * TUI panel state. Typed as Record<string,unknown> for SDK RuntimeState
81
- * compatibility. Use selectPanels() which casts to PanelDomainState.
82
- */
79
+ /** TUI panel state, exposed through the SDK-compatible runtime store shape. */
83
80
  panels: Record<string, unknown>;
84
81
  permissions: PermissionDomainState;
85
82
  tasks: TaskDomainState;
@@ -1,4 +1,4 @@
1
- import type { ConfigKey, ConfigManager, PersistedFlagState } from '../config/index.ts';
1
+ import type { ConfigManager, PersistedFlagState } from '../config/index.ts';
2
2
  import { surfaceFeatureGateId } from '@/runtime/index.ts';
3
3
 
4
4
  export const CONTROL_PLANE_FEATURE_FLAG = 'control-plane-gateway';
@@ -14,6 +14,42 @@ const CORE_CHANNEL_FEATURE_FLAGS = [
14
14
 
15
15
  export type FeatureFlagConfigKey = 'featureFlags' | `featureFlags.${string}`;
16
16
 
17
+ function isRecord(value: unknown): value is Record<string, unknown> {
18
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
19
+ }
20
+
21
+ export function isFeatureFlagConfigKey(key: string): key is FeatureFlagConfigKey {
22
+ return key === 'featureFlags' || key.startsWith('featureFlags.');
23
+ }
24
+
25
+ function isPersistedFlagState(value: unknown): value is PersistedFlagState {
26
+ return value === 'enabled' || value === 'disabled';
27
+ }
28
+
29
+ export function readFeatureFlagConfigValue(config: Pick<ConfigManager, 'getCategory'>, key: FeatureFlagConfigKey): unknown {
30
+ const flags = config.getCategory('featureFlags');
31
+ if (key === 'featureFlags') return flags;
32
+ return flags[key.slice('featureFlags.'.length)];
33
+ }
34
+
35
+ export function mergeFeatureFlagConfigValue(config: Pick<ConfigManager, 'mergeCategory'>, key: FeatureFlagConfigKey, value: unknown): void {
36
+ if (key === 'featureFlags') {
37
+ if (!isRecord(value)) throw new Error('featureFlags expects an object value.');
38
+ const patch: Partial<Record<string, PersistedFlagState>> = {};
39
+ for (const [flagId, state] of Object.entries(value)) {
40
+ if (!isPersistedFlagState(state)) throw new Error(`featureFlags.${flagId} expects enabled or disabled.`);
41
+ patch[flagId] = state;
42
+ }
43
+ config.mergeCategory('featureFlags', patch);
44
+ return;
45
+ }
46
+
47
+ if (!isPersistedFlagState(value)) throw new Error(`${key} expects enabled or disabled.`);
48
+ config.mergeCategory('featureFlags', {
49
+ [key.slice('featureFlags.'.length)]: value,
50
+ });
51
+ }
52
+
17
53
  export function getSurfaceFeatureFlag(surfaceId: string): string | null {
18
54
  return surfaceFeatureGateId(surfaceId);
19
55
  }
@@ -47,7 +83,7 @@ export function getServerSurfaceFeatureFlags(options: {
47
83
  }
48
84
 
49
85
  export function isFeatureFlagEnabled(config: Pick<ConfigManager, 'getCategory'>, flagId: string): boolean {
50
- const flags = (config.getCategory('featureFlags') as Record<string, PersistedFlagState | undefined>) ?? {};
86
+ const flags = config.getCategory('featureFlags');
51
87
  return flags[flagId] === 'enabled';
52
88
  }
53
89
 
@@ -58,8 +94,7 @@ export function getMissingSurfaceFeatureFlags(config: Pick<ConfigManager, 'getCa
58
94
  return required.filter((flagId) => !isFeatureFlagEnabled(config, flagId));
59
95
  }
60
96
 
61
- export function enableFeatureFlags(config: Pick<ConfigManager, 'setDynamic'>, flagIds: readonly string[]): void {
62
- for (const flagId of flagIds) {
63
- config.setDynamic(`featureFlags.${flagId}` as ConfigKey, 'enabled');
64
- }
97
+ export function enableFeatureFlags(config: Pick<ConfigManager, 'mergeCategory'>, flagIds: readonly string[]): void {
98
+ const patch = Object.fromEntries(flagIds.map((flagId) => [flagId, 'enabled'] as const));
99
+ config.mergeCategory('featureFlags', patch);
65
100
  }
@@ -53,10 +53,9 @@ export function createUiReadModels(
53
53
  runtimeServices: RuntimeServices,
54
54
  options: UiReadModelOptions = {},
55
55
  ): UiReadModels {
56
- const sdkRuntimeServices = runtimeServices as unknown as Parameters<typeof createCoreReadModels>[0];
57
56
  return {
58
- ...createCoreReadModels(sdkRuntimeServices),
59
- ...createOperationsReadModels(sdkRuntimeServices, options),
60
- ...createObservabilityReadModels(sdkRuntimeServices, options),
57
+ ...createCoreReadModels(runtimeServices),
58
+ ...createOperationsReadModels(runtimeServices, options),
59
+ ...createObservabilityReadModels(runtimeServices, options),
61
60
  };
62
61
  }
@@ -58,19 +58,19 @@ export interface UiPlatformServices {
58
58
  readonly policyRuntimeState: RuntimeServices['policyRuntimeState'];
59
59
  readonly externalServices?: {
60
60
  inspect(): {
61
- readonly daemonRunning: boolean;
62
- readonly daemonPortInUse?: boolean;
61
+ readonly connectedHostRunning: boolean;
62
+ readonly connectedHostPortInUse?: boolean;
63
63
  readonly httpListenerRunning: boolean;
64
64
  readonly httpListenerPortInUse?: boolean;
65
- readonly daemonStatus?: HostServiceStatus;
65
+ readonly connectedHostStatus?: HostServiceStatus;
66
66
  readonly httpListenerStatus?: HostServiceStatus;
67
67
  };
68
68
  restart(): Promise<{
69
- readonly daemonRunning: boolean;
70
- readonly daemonPortInUse?: boolean;
69
+ readonly connectedHostRunning: boolean;
70
+ readonly connectedHostPortInUse?: boolean;
71
71
  readonly httpListenerRunning: boolean;
72
72
  readonly httpListenerPortInUse?: boolean;
73
- readonly daemonStatus?: HostServiceStatus;
73
+ readonly connectedHostStatus?: HostServiceStatus;
74
74
  readonly httpListenerStatus?: HostServiceStatus;
75
75
  }>;
76
76
  };
@@ -2,6 +2,7 @@ import type { ConversationManager } from '../core/conversation';
2
2
  import type { PermissionRequest } from '@pellux/goodvibes-sdk/platform/permissions';
3
3
  import type { SessionSnapshot } from '@/runtime/index.ts';
4
4
  import type { SystemMessageRouter } from '../core/system-message-router.ts';
5
+ import { readConversationMessageSnapshots } from '../core/conversation-message-snapshot.ts';
5
6
 
6
7
  export type PendingPermissionState = PermissionRequest & {
7
8
  resolve: (approved: boolean, remember?: boolean) => void;
@@ -71,7 +72,7 @@ export function handleBlockingShellInput(
71
72
  if (data === '\x12') {
72
73
  const recovery = loadRecoveryConversation();
73
74
  if (recovery) {
74
- conversation.fromJSON({ messages: recovery.messages as Parameters<typeof conversation.fromJSON>[0]['messages'] });
75
+ conversation.fromJSON({ messages: readConversationMessageSnapshots(recovery.messages) });
75
76
  systemMessageRouter.high('[Recovery] Session restored.');
76
77
  } else {
77
78
  systemMessageRouter.high('[Recovery] Failed to restore saved data.');
@@ -13,6 +13,7 @@ import type { SecretsManager } from '@pellux/goodvibes-sdk/platform/config';
13
13
  import type { ServiceInspectionQuery } from '../runtime/ui-service-queries.ts';
14
14
  import type { ModelPickerTargetInfo } from '../input/model-picker.ts';
15
15
  import { syncServiceSettingToPlatform } from './service-settings-sync.ts';
16
+ import { agentWorkspaceCategoryForPanel } from '../input/agent-workspace-panel-route.ts';
16
17
 
17
18
  type WireShellUiOpenersOptions = {
18
19
  commandContext: CommandContext;
@@ -78,17 +79,6 @@ function buildConfiguredViaMap(
78
79
  return map;
79
80
  }
80
81
 
81
- function agentWorkspaceCategoryForPanel(panelId: string): string {
82
- if (panelId === 'knowledge') return 'knowledge';
83
- if (panelId === 'memory') return 'memory';
84
- if (panelId === 'work-plan' || panelId === 'tasks' || panelId === 'approval') return 'work';
85
- if (panelId === 'automation' || panelId === 'schedule') return 'automation';
86
- if (panelId === 'provider-health' || panelId === 'providers' || panelId === 'accounts' || panelId === 'subscription') return 'setup';
87
- if (panelId === 'security' || panelId === 'policy') return 'tools';
88
- if (panelId === 'qr-code') return 'channels';
89
- return 'home';
90
- }
91
-
92
82
  export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
93
83
  const {
94
84
  commandContext,
@@ -310,7 +300,7 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
310
300
  panelManager.hide();
311
301
  input.panelFocused = false;
312
302
  conversation.setSplashSuppressed(false);
313
- conversation.log('Panel picker is deferred in GoodVibes Agent. Opening the Agent operator workspace instead.', { fg: '214' });
303
+ conversation.log('Panel picker is handled through Agent Workspace. Use /agent for current operator controls.', { fg: '214' });
314
304
  input.openAgentWorkspace(commandContext, 'home');
315
305
  conversation.rebuildHistory();
316
306
  render();
@@ -333,7 +323,7 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
333
323
  panelManager.hide();
334
324
  input.panelFocused = false;
335
325
  conversation.setSplashSuppressed(false);
336
- conversation.log(`Panel "${panelId}" is not part of the Agent front door yet. Opening the matching Agent workspace.`, { fg: '214' });
326
+ conversation.log(`Panel route "${panelId}" is handled through Agent Workspace. Opening the matching operator area.`, { fg: '214' });
337
327
  input.openAgentWorkspace(commandContext, agentWorkspaceCategoryForPanel(panelId));
338
328
  conversation.rebuildHistory();
339
329
  render();
@@ -23,7 +23,6 @@ const READ_ONLY_ANALYZE_TOOL_MODES = [
23
23
  'surface',
24
24
  'breaking',
25
25
  'permissions',
26
- 'env_audit',
27
26
  'test_find',
28
27
  ] as const;
29
28
 
@@ -0,0 +1,133 @@
1
+ import type { ChannelDeliveryRouter } from '@pellux/goodvibes-sdk/platform/channels';
2
+ import type { Tool } from '@pellux/goodvibes-sdk/platform/types';
3
+ import type { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
4
+ import {
5
+ buildAgentChannelDeliveryPreview,
6
+ deliverAgentChannelMessage,
7
+ formatAgentChannelDeliveryPreview,
8
+ formatAgentChannelDeliveryResult,
9
+ } from '../agent/channel-delivery.ts';
10
+
11
+ export interface AgentChannelSendToolArgs {
12
+ readonly message?: unknown;
13
+ readonly title?: unknown;
14
+ readonly channel?: unknown;
15
+ readonly route?: unknown;
16
+ readonly webhook?: unknown;
17
+ readonly link?: unknown;
18
+ readonly confirm?: unknown;
19
+ readonly explicitUserRequest?: unknown;
20
+ }
21
+
22
+ function readString(value: unknown): string | undefined {
23
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
24
+ }
25
+
26
+ function readBoolean(value: unknown): boolean {
27
+ return value === true || value === 'true' || value === 'yes';
28
+ }
29
+
30
+ function failure(error: string): { readonly success: false; readonly error: string } {
31
+ return { success: false, error };
32
+ }
33
+
34
+ function output(text: string): { readonly success: true; readonly output: string } {
35
+ return { success: true, output: text };
36
+ }
37
+
38
+ export function createAgentChannelSendTool(
39
+ channelDeliveryRouter: Pick<ChannelDeliveryRouter, 'deliver' | 'listStrategies'>,
40
+ ): Tool {
41
+ return {
42
+ definition: {
43
+ name: 'agent_channel_send',
44
+ description: [
45
+ 'Send one explicitly confirmed message through a configured GoodVibes Agent delivery target from the main conversation.',
46
+ 'Use only when the user explicitly asks Agent to send, message, alert, or notify a channel target.',
47
+ 'Exactly one target must be supplied: channel, route, webhook, or link.',
48
+ 'This tool does not create channel routes, authorize accounts, manage connected-host hosting, use default knowledge, use non-Agent knowledge segments, create separate Agent jobs, run delegated review, or invoke arbitrary routes.',
49
+ 'Set confirm:true only for an explicit user request. Otherwise return the preview/confirmation error.',
50
+ ].join(' '),
51
+ parameters: {
52
+ type: 'object',
53
+ properties: {
54
+ message: {
55
+ type: 'string',
56
+ description: 'Plain-text message to deliver.',
57
+ },
58
+ title: {
59
+ type: 'string',
60
+ description: 'Optional delivery title.',
61
+ },
62
+ channel: {
63
+ type: 'string',
64
+ description: 'Optional channel target surface[:route[:label]], such as slack:ops:Ops or telephony:+15551234567. Use exactly one target field.',
65
+ },
66
+ route: {
67
+ type: 'string',
68
+ description: 'Optional route id or route:label. Use exactly one target field.',
69
+ },
70
+ webhook: {
71
+ type: 'string',
72
+ description: 'Optional http(s) webhook URL. Use exactly one target field.',
73
+ },
74
+ link: {
75
+ type: 'string',
76
+ description: 'Optional link delivery target. Use exactly one target field.',
77
+ },
78
+ confirm: {
79
+ type: 'boolean',
80
+ description: 'Required true only when the user explicitly asked for this exact channel delivery.',
81
+ },
82
+ explicitUserRequest: {
83
+ type: 'string',
84
+ description: 'Short quote or summary of the user request that authorized this delivery.',
85
+ },
86
+ },
87
+ required: ['message', 'confirm', 'explicitUserRequest'],
88
+ additionalProperties: false,
89
+ },
90
+ sideEffects: ['network'],
91
+ },
92
+ execute: async (rawArgs: Record<string, unknown>) => {
93
+ const args = rawArgs as AgentChannelSendToolArgs;
94
+ const explicitUserRequest = readString(args.explicitUserRequest);
95
+ if (!explicitUserRequest) {
96
+ return failure('explicitUserRequest is required so channel delivery stays tied to a direct user request.');
97
+ }
98
+ const input = {
99
+ message: readString(args.message) ?? '',
100
+ ...(readString(args.title) ? { title: readString(args.title) } : {}),
101
+ ...(readString(args.channel) ? { channel: readString(args.channel) } : {}),
102
+ ...(readString(args.route) ? { route: readString(args.route) } : {}),
103
+ ...(readString(args.webhook) ? { webhook: readString(args.webhook) } : {}),
104
+ ...(readString(args.link) ? { link: readString(args.link) } : {}),
105
+ };
106
+ let preview: ReturnType<typeof buildAgentChannelDeliveryPreview>;
107
+ try {
108
+ preview = buildAgentChannelDeliveryPreview(input);
109
+ } catch (error) {
110
+ return failure(error instanceof Error ? error.message : String(error));
111
+ }
112
+ if (!readBoolean(args.confirm)) {
113
+ return failure([
114
+ formatAgentChannelDeliveryPreview(preview, channelDeliveryRouter.listStrategies().length),
115
+ '',
116
+ 'Model tool confirmation required. Call this tool with confirm:true only when the user explicitly asked GoodVibes Agent to send this channel message.',
117
+ ].join('\n'));
118
+ }
119
+ try {
120
+ return output(formatAgentChannelDeliveryResult(await deliverAgentChannelMessage(channelDeliveryRouter, input)));
121
+ } catch (error) {
122
+ return failure(error instanceof Error ? error.message : String(error));
123
+ }
124
+ },
125
+ };
126
+ }
127
+
128
+ export function registerAgentChannelSendTool(
129
+ registry: ToolRegistry,
130
+ channelDeliveryRouter: Pick<ChannelDeliveryRouter, 'deliver' | 'listStrategies'>,
131
+ ): void {
132
+ registry.register(createAgentChannelSendTool(channelDeliveryRouter));
133
+ }
@@ -10,7 +10,7 @@ export function wrapBlockedContextToolForAgentPolicy(tool: Tool): void {
10
10
  tool.definition.description = [
11
11
  'Blocked in GoodVibes Agent main conversation: non-Agent runtime context.',
12
12
  'Use explicit Agent CLI/slash status, compat, setup, and Agent Knowledge commands for product-scoped context.',
13
- 'Default Knowledge/Wiki, non-Agent knowledge segments, and non-Agent runtime assumptions are not Agent fallbacks.',
13
+ 'Default knowledge, non-Agent knowledge segments, and non-Agent runtime assumptions are not Agent fallbacks.',
14
14
  ].join(' ');
15
15
  tool.definition.sideEffects = [];
16
16
  tool.definition.parameters = {