@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
@@ -1,4 +1,4 @@
1
- import type { KnowledgeService } from '@pellux/goodvibes-sdk/platform/knowledge';
1
+ import type { KnowledgeMapResult, KnowledgeService } from '@pellux/goodvibes-sdk/platform/knowledge';
2
2
  import type { BrowserKnowledgeKind, BrowserKnowledgeSourceKind } from '@pellux/goodvibes-sdk/platform/knowledge';
3
3
  import type { CommandContext, SlashCommand } from '../command-registry.ts';
4
4
  import { requireYesFlag, stripYesFlag } from './confirmation.ts';
@@ -12,10 +12,14 @@ type KnowledgeAskInput = Parameters<KnowledgeService['ask']>[0];
12
12
  type KnowledgeAskResult = Awaited<ReturnType<KnowledgeService['ask']>>;
13
13
  type KnowledgeAskMode = NonNullable<KnowledgeAskInput['mode']>;
14
14
 
15
+ function isRecord(value: unknown): value is Record<string, unknown> {
16
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
17
+ }
18
+
15
19
  function requireAgentKnowledgeApi(context: CommandContext) {
16
20
  const knowledgeApi = context.clients?.agentKnowledgeApi;
17
21
  if (!knowledgeApi) {
18
- context.print('[knowledge] Agent Knowledge API is not available in this runtime. Refusing to use default Knowledge/Wiki or non-Agent knowledge fallback.');
22
+ context.print('[knowledge] Agent Knowledge API is not available in this runtime. Refusing to use default knowledge or non-Agent knowledge fallback.');
19
23
  return null;
20
24
  }
21
25
  return knowledgeApi;
@@ -48,7 +52,7 @@ function findDisallowedKnowledgeScopeFlag(args: readonly string[]): string | nul
48
52
  function printScopeFlagRejection(context: CommandContext, flag: string): void {
49
53
  context.print([
50
54
  `[knowledge] Agent Knowledge is isolated; ${flag} is not accepted.`,
51
- '[knowledge] GoodVibes Agent must not use default Knowledge/Wiki or non-Agent product spaces.',
55
+ '[knowledge] GoodVibes Agent must not use default knowledge or non-Agent product spaces.',
52
56
  '[knowledge] Use only /api/goodvibes-agent/knowledge/* Agent-owned routes.',
53
57
  ].join('\n'));
54
58
  }
@@ -110,10 +114,7 @@ function readJsonObjectFlag(args: string[], name: string): Record<string, unknow
110
114
  if (!value) return undefined;
111
115
  try {
112
116
  const parsed = JSON.parse(value) as unknown;
113
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
114
- return null;
115
- }
116
- return parsed as Record<string, unknown>;
117
+ return isRecord(parsed) ? parsed : null;
117
118
  } catch {
118
119
  return null;
119
120
  }
@@ -131,7 +132,7 @@ function requireAgentKnowledgeAsk(context: CommandContext): ((input: KnowledgeAs
131
132
  const serviceAsk = context.extensions.agentKnowledgeService?.ask?.bind(context.extensions.agentKnowledgeService);
132
133
  if (serviceAsk) return serviceAsk;
133
134
 
134
- context.print('[knowledge] Agent Knowledge ask is not available in this runtime. Refusing to use default Knowledge/Wiki or non-Agent knowledge fallback.');
135
+ context.print('[knowledge] Agent Knowledge ask is not available in this runtime. Refusing to use default knowledge or non-Agent knowledge fallback.');
135
136
  return null;
136
137
  }
137
138
 
@@ -139,11 +140,20 @@ function cleanInline(value: unknown): string {
139
140
  return typeof value === 'string' ? value.replace(/\s+/g, ' ').trim() : '';
140
141
  }
141
142
 
143
+ function formatKnowledgeMap(result: KnowledgeMapResult): string {
144
+ return [
145
+ 'Agent Knowledge map',
146
+ ` nodes: ${result.nodeCount}${result.totalNodeCount !== undefined && result.totalNodeCount !== result.nodeCount ? ` of ${result.totalNodeCount}` : ''}`,
147
+ ` edges: ${result.edgeCount}${result.totalEdgeCount !== undefined && result.totalEdgeCount !== result.edgeCount ? ` of ${result.totalEdgeCount}` : ''}`,
148
+ ' route /api/goodvibes-agent/knowledge/map',
149
+ ].join('\n');
150
+ }
151
+
142
152
  function nodeLabel(node: { readonly kind?: string; readonly title?: string; readonly summary?: string; readonly confidence?: number }): string {
143
153
  const kind = cleanInline(node.kind) || 'node';
144
154
  const title = cleanInline(node.title) || 'untitled';
145
155
  const summary = cleanInline(node.summary);
146
- const confidence = typeof node.confidence === 'number' ? ` confidence=${node.confidence}` : '';
156
+ const confidence = typeof node.confidence === 'number' ? ` confidence ${node.confidence}` : '';
147
157
  return summary ? `[${kind}] ${title}${confidence} - ${summary}` : `[${kind}] ${title}${confidence}`;
148
158
  }
149
159
 
@@ -170,7 +180,7 @@ function renderKnowledgeAskResult(result: KnowledgeAskResult): string {
170
180
  `[knowledge] ${result.query}`,
171
181
  answer.text,
172
182
  '',
173
- `mode: ${answer.mode} confidence: ${answer.confidence} synthesized: ${answer.synthesized ? 'yes' : 'no'}`,
183
+ `mode ${answer.mode} confidence ${answer.confidence} synthesized ${answer.synthesized ? 'yes' : 'no'}`,
174
184
  ];
175
185
 
176
186
  if (answer.sources.length > 0) {
@@ -199,16 +209,16 @@ function renderKnowledgeAskResult(result: KnowledgeAskResult): string {
199
209
  export const knowledgeCommand: SlashCommand = {
200
210
  name: 'knowledge',
201
211
  aliases: ['know', 'kb'],
202
- description: 'Agent Knowledge/Wiki: isolated Agent-owned sources, graph, review queue, and compact prompt packets.',
212
+ description: 'Agent Knowledge: isolated Agent-owned sources, graph, review queue, and compact prompt packets.',
203
213
  usage: '<subcommand> [args]',
204
214
  argsHint: 'status|ask|ingest-url --yes|ingest-file --yes|import-bookmarks --yes|list|search|get|queue|review-issue --yes',
205
215
  handler: async (args: string[], context: CommandContext): Promise<void> => {
206
- const knowledge = requireAgentKnowledgeApi(context);
207
- if (!knowledge) {
216
+ if (args.length === 0 && context.openAgentWorkspace) {
217
+ context.openAgentWorkspace('knowledge');
208
218
  return;
209
219
  }
210
- if (args.length === 0 && context.openKnowledgePanel) {
211
- context.openKnowledgePanel();
220
+ const knowledge = requireAgentKnowledgeApi(context);
221
+ if (!knowledge) {
212
222
  return;
213
223
  }
214
224
  const sub = (args[0] ?? 'status').toLowerCase();
@@ -321,9 +331,9 @@ export const knowledgeCommand: SlashCommand = {
321
331
  return;
322
332
  }
323
333
  const result = await knowledge.ingest.bookmarksFile({ path, sessionId: context.session.runtime.sessionId });
324
- context.print(`[knowledge] Imported bookmarks: ${result.imported} ok, ${result.failed} failed`);
334
+ context.print(`[knowledge] Imported bookmarks ${result.imported} ok, ${result.failed} failed`);
325
335
  if (result.errors.length > 0) {
326
- for (const error of result.errors.slice(0, 10)) context.print(` error: ${error}`);
336
+ for (const error of result.errors.slice(0, 10)) context.print(` error ${error}`);
327
337
  }
328
338
  break;
329
339
  }
@@ -339,9 +349,9 @@ export const knowledgeCommand: SlashCommand = {
339
349
  return;
340
350
  }
341
351
  const result = await knowledge.ingest.urlsFile({ path, sessionId: context.session.runtime.sessionId });
342
- context.print(`[knowledge] Imported URL list: ${result.imported} ok, ${result.failed} failed`);
352
+ context.print(`[knowledge] Imported URL list ${result.imported} ok, ${result.failed} failed`);
343
353
  if (result.errors.length > 0) {
344
- for (const error of result.errors.slice(0, 10)) context.print(` error: ${error}`);
354
+ for (const error of result.errors.slice(0, 10)) context.print(` error ${error}`);
345
355
  }
346
356
  break;
347
357
  }
@@ -362,9 +372,9 @@ export const knowledgeCommand: SlashCommand = {
362
372
  sessionId: context.session.runtime.sessionId,
363
373
  });
364
374
  context.print(`[knowledge] Imported browser knowledge: ${result.imported} ok, ${result.failed} failed`);
365
- if (result.profiles.length > 0) context.print(` profiles: ${result.profiles.length}`);
375
+ if (result.profiles.length > 0) context.print(` profiles ${result.profiles.length}`);
366
376
  if (result.errors.length > 0) {
367
- for (const error of result.errors.slice(0, 10)) context.print(` error: ${error}`);
377
+ for (const error of result.errors.slice(0, 10)) context.print(` error ${error}`);
368
378
  }
369
379
  break;
370
380
  }
@@ -396,7 +406,7 @@ export const knowledgeCommand: SlashCommand = {
396
406
  });
397
407
  context.print(`[knowledge] Imported connector input: ${result.imported} ok, ${result.failed} failed`);
398
408
  if (result.errors.length > 0) {
399
- for (const error of result.errors.slice(0, 10)) context.print(` error: ${error}`);
409
+ for (const error of result.errors.slice(0, 10)) context.print(` error ${error}`);
400
410
  }
401
411
  break;
402
412
  }
@@ -410,7 +420,7 @@ export const knowledgeCommand: SlashCommand = {
410
420
  context.print('[knowledge] No nodes.');
411
421
  return;
412
422
  }
413
- context.print(`[knowledge] ${nodes.length} node(s):`);
423
+ context.print(`[knowledge] ${nodes.length} node(s)`);
414
424
  for (const node of nodes) {
415
425
  context.print(` ${node.id} [${node.kind}] ${node.title}`);
416
426
  if (node.summary) context.print(` ${node.summary}`);
@@ -423,7 +433,7 @@ export const knowledgeCommand: SlashCommand = {
423
433
  context.print('[knowledge] No issues.');
424
434
  return;
425
435
  }
426
- context.print(`[knowledge] ${issues.length} issue(s):`);
436
+ context.print(`[knowledge] ${issues.length} issue(s)`);
427
437
  for (const issue of issues) {
428
438
  context.print(` ${issue.id} [${issue.severity}] ${issue.code}`);
429
439
  context.print(` ${issue.message}`);
@@ -435,7 +445,7 @@ export const knowledgeCommand: SlashCommand = {
435
445
  context.print('[knowledge] No sources.');
436
446
  return;
437
447
  }
438
- context.print(`[knowledge] ${sources.length} source(s):`);
448
+ context.print(`[knowledge] ${sources.length} source(s)`);
439
449
  for (const source of sources) {
440
450
  context.print(` ${source.id} [${source.sourceType}/${source.status}] ${source.title ?? source.canonicalUri ?? source.sourceUri ?? 'untitled'}`);
441
451
  if (source.summary) context.print(` ${source.summary}`);
@@ -456,10 +466,10 @@ export const knowledgeCommand: SlashCommand = {
456
466
  context.print('[knowledge] No results.');
457
467
  return;
458
468
  }
459
- context.print(`[knowledge] ${results.length} result(s):`);
469
+ context.print(`[knowledge] ${results.length} result(s)`);
460
470
  for (const result of results) {
461
471
  const title = result.source?.title ?? result.source?.canonicalUri ?? result.node?.title ?? result.id;
462
- context.print(` ${result.id} [${result.kind}] score=${result.score} ${title}`);
472
+ context.print(` ${result.id} [${result.kind}] score ${result.score} ${title}`);
463
473
  context.print(` ${result.reason}`);
464
474
  }
465
475
  break;
@@ -473,28 +483,28 @@ export const knowledgeCommand: SlashCommand = {
473
483
  }
474
484
  const item = knowledge.graph.items.get(id);
475
485
  if (!item) {
476
- context.print(`[knowledge] Unknown item: ${id}`);
486
+ context.print(`[knowledge] Unknown item ${id}`);
477
487
  return;
478
488
  }
479
489
  if (item.source) {
480
490
  context.print(`[knowledge] source ${item.source.id}`);
481
- context.print(` title: ${item.source.title ?? 'untitled'}`);
482
- context.print(` uri: ${item.source.canonicalUri ?? item.source.sourceUri ?? 'n/a'}`);
483
- context.print(` status: ${item.source.status}`);
484
- if (item.source.summary) context.print(` summary: ${item.source.summary}`);
491
+ context.print(` title ${item.source.title ?? 'untitled'}`);
492
+ context.print(` uri ${item.source.canonicalUri ?? item.source.sourceUri ?? 'n/a'}`);
493
+ context.print(` status ${item.source.status}`);
494
+ if (item.source.summary) context.print(` summary ${item.source.summary}`);
485
495
  } else if (item.node) {
486
496
  context.print(`[knowledge] node ${item.node.id}`);
487
- context.print(` kind: ${item.node.kind}`);
488
- context.print(` title: ${item.node.title}`);
489
- if (item.node.summary) context.print(` summary: ${item.node.summary}`);
497
+ context.print(` type ${item.node.kind}`);
498
+ context.print(` title ${item.node.title}`);
499
+ if (item.node.summary) context.print(` summary ${item.node.summary}`);
490
500
  } else if (item.issue) {
491
501
  context.print(`[knowledge] issue ${item.issue.id}`);
492
- context.print(` severity: ${item.issue.severity}`);
493
- context.print(` code: ${item.issue.code}`);
494
- context.print(` message: ${item.issue.message}`);
502
+ context.print(` severity ${item.issue.severity}`);
503
+ context.print(` code ${item.issue.code}`);
504
+ context.print(` message ${item.issue.message}`);
495
505
  }
496
506
  if (item.relatedEdges.length > 0) {
497
- context.print(' relations:');
507
+ context.print(' relations');
498
508
  for (const edge of item.relatedEdges.slice(0, 12)) {
499
509
  context.print(` ${edge.fromKind}:${edge.fromId} -[${edge.relation}]-> ${edge.toKind}:${edge.toId}`);
500
510
  }
@@ -502,13 +512,24 @@ export const knowledgeCommand: SlashCommand = {
502
512
  break;
503
513
  }
504
514
 
515
+ case 'map': {
516
+ const limit = readPositiveIntFlag(rest, '--limit', 50);
517
+ const query = positionalArgs(rest, ['--limit']).join(' ').trim();
518
+ const result = await knowledge.graph.map({
519
+ limit,
520
+ ...(query.length > 0 ? { query } : {}),
521
+ });
522
+ context.print(formatKnowledgeMap(result));
523
+ break;
524
+ }
525
+
505
526
  case 'lint': {
506
527
  const issues = await knowledge.status.lint();
507
528
  if (issues.length === 0) {
508
529
  context.print('[knowledge] No lint issues.');
509
530
  return;
510
531
  }
511
- context.print(`[knowledge] ${issues.length} lint issue(s):`);
532
+ context.print(`[knowledge] ${issues.length} lint issue(s)`);
512
533
  for (const issue of issues) {
513
534
  context.print(` ${issue.id} [${issue.severity}] ${issue.code}`);
514
535
  context.print(` ${issue.message}`);
@@ -524,7 +545,7 @@ export const knowledgeCommand: SlashCommand = {
524
545
  context.print('Knowledge review queue is empty.');
525
546
  return;
526
547
  }
527
- context.print(`[knowledge] Review queue (${issues.length}):`);
548
+ context.print(`[knowledge] Review queue (${issues.length})`);
528
549
  for (const issue of issues) {
529
550
  context.print(` ${issue.id} [${issue.severity}] ${issue.code}`);
530
551
  context.print(` ${issue.message}`);
@@ -558,11 +579,11 @@ export const knowledgeCommand: SlashCommand = {
558
579
  });
559
580
  context.print([
560
581
  `[knowledge] Reviewed issue ${result.issue.id}`,
561
- ` action: ${action}`,
562
- ` status: ${result.issue.status}`,
563
- ...(result.node ? [` node: ${result.node.id} ${result.node.title}`] : []),
564
- ...(result.source ? [` source: ${result.source.id} ${result.source.title ?? result.source.canonicalUri ?? result.source.sourceUri ?? 'untitled'}`] : []),
565
- ...(result.appliedFacts ? [` applied facts: ${Object.keys(result.appliedFacts).join(', ') || 'none'}`] : []),
582
+ ` action ${action}`,
583
+ ` status ${result.issue.status}`,
584
+ ...(result.node ? [` node ${result.node.id} ${result.node.title}`] : []),
585
+ ...(result.source ? [` knowledge source ${result.source.id} ${result.source.title ?? result.source.canonicalUri ?? result.source.sourceUri ?? 'untitled'}`] : []),
586
+ ...(result.appliedFacts ? [` applied facts ${Object.keys(result.appliedFacts).join(', ') || 'none'}`] : []),
566
587
  ].join('\n'));
567
588
  } catch (error) {
568
589
  context.print(`[knowledge] ${error instanceof Error ? error.message : String(error)}`);
@@ -578,9 +599,9 @@ export const knowledgeCommand: SlashCommand = {
578
599
  context.print('[knowledge] No consolidation candidates.');
579
600
  return;
580
601
  }
581
- context.print(`[knowledge] Consolidation candidates (${candidates.length}):`);
602
+ context.print(`[knowledge] Consolidation candidates (${candidates.length})`);
582
603
  for (const candidate of candidates) {
583
- context.print(` ${candidate.id} [${candidate.status}] score=${candidate.score} ${candidate.title}`);
604
+ context.print(` ${candidate.id} [${candidate.status}] score ${candidate.score} ${candidate.title}`);
584
605
  context.print(` ${candidate.candidateType}`);
585
606
  }
586
607
  break;
@@ -595,7 +616,7 @@ export const knowledgeCommand: SlashCommand = {
595
616
  }
596
617
  const report = await knowledge.connectors.doctor(second);
597
618
  if (!report) {
598
- context.print(`[knowledge] Connector doctor report unavailable: ${second}`);
619
+ context.print(`[knowledge] Connector doctor report unavailable ${second}`);
599
620
  return;
600
621
  }
601
622
  context.print([
@@ -610,14 +631,14 @@ export const knowledgeCommand: SlashCommand = {
610
631
  if (first) {
611
632
  const connector = knowledge.connectors.get(first);
612
633
  if (!connector) {
613
- context.print(`[knowledge] Unknown connector: ${first}`);
634
+ context.print(`[knowledge] Unknown connector ${first}`);
614
635
  return;
615
636
  }
616
637
  context.print([
617
638
  `[knowledge] Connector ${connector.id}`,
618
- ` name: ${connector.displayName ?? connector.id}`,
619
- ` sourceType: ${connector.sourceType}`,
620
- ` description: ${connector.description}`,
639
+ ` name ${connector.displayName ?? connector.id}`,
640
+ ` source type ${connector.sourceType}`,
641
+ ` description ${connector.description}`,
621
642
  ` capabilities: ${connector.capabilities?.join(', ') || 'none'}`,
622
643
  ].join('\n'));
623
644
  return;
@@ -627,7 +648,7 @@ export const knowledgeCommand: SlashCommand = {
627
648
  context.print('[knowledge] No connectors.');
628
649
  return;
629
650
  }
630
- context.print(`[knowledge] Connectors (${connectors.length}):`);
651
+ context.print(`[knowledge] Connectors (${connectors.length})`);
631
652
  for (const connector of connectors) {
632
653
  context.print(` ${connector.id} [${connector.sourceType}] ${connector.displayName ?? connector.id}`);
633
654
  context.print(` ${connector.description}`);
@@ -643,7 +664,7 @@ export const knowledgeCommand: SlashCommand = {
643
664
  context.print('[knowledge] No consolidation reports.');
644
665
  return;
645
666
  }
646
- context.print(`[knowledge] Consolidation reports (${reports.length}):`);
667
+ context.print(`[knowledge] Consolidation reports (${reports.length})`);
647
668
  for (const report of reports) {
648
669
  context.print(` ${report.id} [${report.kind}] ${report.title}`);
649
670
  context.print(` ${report.summary}`);
@@ -657,7 +678,7 @@ export const knowledgeCommand: SlashCommand = {
657
678
  context.print('[knowledge] No knowledge schedules.');
658
679
  return;
659
680
  }
660
- context.print(`[knowledge] Managed schedules (${schedules.length}):`);
681
+ context.print(`[knowledge] Managed schedules (${schedules.length})`);
661
682
  for (const schedule of schedules) {
662
683
  context.print(` ${schedule.id} [${schedule.enabled ? 'enabled' : 'disabled'}] ${schedule.jobId}`);
663
684
  context.print(` ${schedule.label}`);
@@ -709,10 +730,10 @@ export const knowledgeCommand: SlashCommand = {
709
730
  const result = await knowledge.status.reindex();
710
731
  context.print([
711
732
  '[knowledge] Reindex complete',
712
- ` sources: ${result.status.sourceCount}`,
713
- ` nodes: ${result.status.nodeCount}`,
714
- ` edges: ${result.status.edgeCount}`,
715
- ` issues: ${result.status.issueCount}`,
733
+ ` sources ${result.status.sourceCount}`,
734
+ ` nodes ${result.status.nodeCount}`,
735
+ ` edges ${result.status.edgeCount}`,
736
+ ` issues ${result.status.issueCount}`,
716
737
  ].join('\n'));
717
738
  break;
718
739
  }
@@ -743,6 +764,7 @@ export const knowledgeCommand: SlashCommand = {
743
764
  ' list [--kind <sources|nodes|issues>] [--limit <n>]',
744
765
  ' search <query> [--limit <n>]',
745
766
  ' get <id>',
767
+ ' map [query] [--limit <n>]',
746
768
  ' queue [limit]',
747
769
  ' review-issue <issueId> <accept|reject|resolve|reopen|edit|forget> [--reviewer <name>] [--value <json-object>] --yes',
748
770
  ' candidates [limit]',
@@ -13,6 +13,14 @@ function isValidProviderName(name: string): boolean {
13
13
  return /^[a-zA-Z0-9_-]+$/.test(name);
14
14
  }
15
15
 
16
+ function readDiscoveredModelIds(body: unknown): string[] {
17
+ if (!body || typeof body !== 'object' || !('data' in body) || !Array.isArray(body.data)) return [];
18
+ return body.data
19
+ .filter((model): model is { readonly id: unknown } => Boolean(model) && typeof model === 'object' && 'id' in model)
20
+ .map((model) => String(model.id))
21
+ .filter(Boolean);
22
+ }
23
+
16
24
  export function registerLocalProviderRuntimeCommands(registry: CommandRegistry): void {
17
25
  registry.register({
18
26
  name: 'provider',
@@ -36,20 +44,26 @@ export function registerLocalProviderRuntimeCommands(registry: CommandRegistry):
36
44
  return;
37
45
  }
38
46
  if (!isValidProviderName(name)) {
39
- ctx.print('Error: Provider name must contain only letters, numbers, hyphens, and underscores.');
47
+ ctx.print([
48
+ 'Error',
49
+ ' message Provider name must contain only letters, numbers, hyphens, and underscores.',
50
+ ].join('\n'));
40
51
  return;
41
52
  }
42
53
  let parsedUrl: URL;
43
54
  try {
44
55
  parsedUrl = new URL(baseURL);
45
56
  } catch {
46
- ctx.print(`Error: '${baseURL}' is not a valid URL. Example: http://192.168.0.85:8001/v1`);
57
+ ctx.print([
58
+ 'Error',
59
+ ` message ${baseURL} is not a valid URL. Example http://192.168.0.85:8001/v1`,
60
+ ].join('\n'));
47
61
  return;
48
62
  }
49
63
  const providersDir = shellPaths.resolveUserPath(GOODVIBES_AGENT_SURFACE_ROOT, 'providers');
50
64
  const providerFile = join(providersDir, `${name}.json`);
51
65
  if (existsSync(providerFile)) {
52
- ctx.print(`Error: Provider '${name}' already exists at ${providerFile}\nRemove it first with: /provider remove ${name} --yes`);
66
+ ctx.print(`Provider ${name} already exists at ${providerFile}\nRemove it first with /provider remove ${name} --yes`);
53
67
  return;
54
68
  }
55
69
 
@@ -64,12 +78,7 @@ export function registerLocalProviderRuntimeCommands(registry: CommandRegistry):
64
78
  clearTimeout(timeoutId);
65
79
  if (res.ok) {
66
80
  const body = await res.json() as unknown;
67
- if (body && typeof body === 'object' && 'data' in body && Array.isArray((body as Record<string, unknown>).data)) {
68
- discoveredModelIds = ((body as { data: unknown[] }).data)
69
- .filter((m): m is Record<string, unknown> => typeof m === 'object' && m !== null && 'id' in m)
70
- .map((m) => String(m.id))
71
- .filter(Boolean);
72
- }
81
+ discoveredModelIds = readDiscoveredModelIds(body);
73
82
  }
74
83
  } catch {
75
84
  ctx.print(`Could not reach ${baseURL}/models — creating provider with a minimal starter config.`);
@@ -91,13 +100,13 @@ export function registerLocalProviderRuntimeCommands(registry: CommandRegistry):
91
100
  id: defaultModel,
92
101
  displayName: defaultModel,
93
102
  contextWindow: 8192,
94
- capabilities: { toolCalling: true, codeEditing: true, reasoning: false, multimodal: false },
103
+ capabilities: { toolCalling: true, codeEditing: false, reasoning: false, multimodal: false },
95
104
  }]
96
105
  : discoveredModelIds.map((id) => ({
97
106
  id,
98
107
  displayName: id,
99
108
  contextWindow: contextWindows[id] ?? 8192,
100
- capabilities: { toolCalling: true, codeEditing: true, reasoning: false, multimodal: false },
109
+ capabilities: { toolCalling: true, codeEditing: false, reasoning: false, multimodal: false },
101
110
  }));
102
111
  const config: CustomProviderConfig = {
103
112
  name,
@@ -111,10 +120,10 @@ export function registerLocalProviderRuntimeCommands(registry: CommandRegistry):
111
120
  mkdirSync(providersDir, { recursive: true });
112
121
  await writeFile(providerFile, JSON.stringify(config, null, 2), 'utf-8');
113
122
  } catch (e) {
114
- ctx.print(`Error writing provider file: ${summarizeError(e)}`);
123
+ ctx.print(`Error writing provider file ${summarizeError(e)}`);
115
124
  return;
116
125
  }
117
- ctx.print(`Provider '${name}' added with ${models.length} model(s):\n${discoveredModelIds.length > 0 ? discoveredModelIds.map((id) => ` • ${id} (${(contextWindows[id] ?? 8192).toLocaleString()} ctx)`).join('\n') : ` • ${defaultModel} (starter entry)`}\nThe file watcher will auto-register it shortly.`);
126
+ ctx.print(`Provider ${name} added with ${models.length} model(s)\n${discoveredModelIds.length > 0 ? discoveredModelIds.map((id) => ` • ${id} (${(contextWindows[id] ?? 8192).toLocaleString()} ctx)`).join('\n') : ` • ${defaultModel} (starter entry)`}\nThe file watcher will auto-register it shortly.`);
118
127
  return;
119
128
  }
120
129
 
@@ -129,19 +138,22 @@ export function registerLocalProviderRuntimeCommands(registry: CommandRegistry):
129
138
  return;
130
139
  }
131
140
  if (!isValidProviderName(name)) {
132
- ctx.print('Error: Provider name must contain only letters, numbers, hyphens, and underscores.');
141
+ ctx.print([
142
+ 'Error',
143
+ ' message Provider name must contain only letters, numbers, hyphens, and underscores.',
144
+ ].join('\n'));
133
145
  return;
134
146
  }
135
147
  const providerFile = shellPaths.resolveUserPath(GOODVIBES_AGENT_SURFACE_ROOT, 'providers', `${name}.json`);
136
148
  if (!existsSync(providerFile)) {
137
- ctx.print(`Error: No custom provider '${name}' found at ${providerFile}`);
149
+ ctx.print(`No custom provider ${name} found at ${providerFile}`);
138
150
  return;
139
151
  }
140
152
  try {
141
153
  await unlink(providerFile);
142
- ctx.print(`Provider '${name}' removed. The file watcher will deregister it shortly.`);
154
+ ctx.print(`Provider ${name} removed. The file watcher will deregister it shortly.`);
143
155
  } catch (e) {
144
- ctx.print(`Error removing provider file: ${summarizeError(e)}`);
156
+ ctx.print(`Error removing provider file ${summarizeError(e)}`);
145
157
  }
146
158
  return;
147
159
  }
@@ -152,11 +164,12 @@ export function registerLocalProviderRuntimeCommands(registry: CommandRegistry):
152
164
  return;
153
165
  }
154
166
  const providers = requireProviderApi(ctx).listProviderIds();
155
- ctx.print(['Available providers:', ...providers.map((provider) => ` ${provider === ctx.session.runtime.provider ? '▶' : ' '} ${provider}`)].join('\n'));
167
+ ctx.print(['Available providers', ...providers.map((provider) => ` ${provider === ctx.session.runtime.provider ? '▶' : ' '} ${provider}`)].join('\n'));
156
168
  return;
157
169
  }
158
170
 
159
171
  const providerName = commandArgs[0];
172
+ const requestedModel = commandArgs[1];
160
173
  const providerApi = requireProviderApi(ctx);
161
174
  const selectable = await providerApi.listModels({
162
175
  providerId: providerName,
@@ -164,17 +177,26 @@ export function registerLocalProviderRuntimeCommands(registry: CommandRegistry):
164
177
  });
165
178
  const match = selectable[0];
166
179
  if (!match) {
167
- ctx.print(`Unknown provider: ${providerName}. Available: ${providerApi.listProviderIds().join(', ')}`);
180
+ ctx.print([
181
+ `Unknown provider ${providerName}`,
182
+ `available providers ${providerApi.listProviderIds().join(', ')}`,
183
+ ].join('\n'));
168
184
  return;
169
185
  }
170
186
  try {
171
- const selected = await providerApi.selectModel(match.registryKey);
187
+ const registryKey = requestedModel
188
+ ? requestedModel.includes(':') ? requestedModel : `${providerName}:${requestedModel}`
189
+ : match.registryKey;
190
+ const selected = await providerApi.selectModel(registryKey);
172
191
  ctx.session.runtime.model = selected.registryKey;
173
192
  ctx.session.runtime.provider = selected.providerId;
174
193
  ctx.platform.configManager.set('provider.model', selected.registryKey);
175
194
  ctx.print(`Switched to provider: ${selected.providerId} (model: ${selected.modelId})`);
176
195
  } catch (e) {
177
- ctx.print(`Error: ${summarizeError(e)}`);
196
+ ctx.print([
197
+ 'Error',
198
+ ` message ${summarizeError(e)}`,
199
+ ].join('\n'));
178
200
  }
179
201
  },
180
202
  });