@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,16 +1,25 @@
1
1
  import { mkdirSync, writeFileSync } from 'node:fs';
2
- import { dirname, join } from 'node:path';
2
+ import { dirname } from 'node:path';
3
3
  import { SecretsManager } from '../config/secrets.ts';
4
4
  import { BUILTIN_SECRET_PROVIDER_SOURCES, describeSecretRef, isSecretRefInput, resolveSecretRef } from '@pellux/goodvibes-sdk/platform/config';
5
5
  import { getSubscriptionProviderConfig, listAvailableSubscriptionProviders } from '@pellux/goodvibes-sdk/platform/config';
6
+ import type { OAuthProviderConfig } from '@pellux/goodvibes-sdk/platform/config';
6
7
  import { beginOpenAICodexLogin, exchangeOpenAICodexCode } from '@pellux/goodvibes-sdk/platform/config';
7
8
  import { inspectProviderAuth } from '@/runtime/index.ts';
8
- import { getOrCreateCompanionToken, buildCompanionConnectionInfo, encodeConnectionPayload, formatConnectionBlock } from '@pellux/goodvibes-sdk/platform/pairing';
9
+ import { buildCompanionConnectionInfo, encodeConnectionPayload, formatConnectionBlock } from '@pellux/goodvibes-sdk/platform/pairing';
9
10
  import { generateQrMatrix, renderQrToString } from '@pellux/goodvibes-sdk/platform/pairing';
10
11
  import { resolveRuntimeEndpointBinding } from './endpoints.ts';
11
12
  import type { CliCommandRuntime } from './management.ts';
12
13
  import { extractAuthorizationCode, formatJsonOrText, hasCommandFlag, openBrowser, urlHostForBindHost, withRuntimeServices, yesNo } from './management.ts';
13
14
  import { GOODVIBES_AGENT_PAIRING_SURFACE } from '../config/surface.ts';
15
+ import { connectedHostTokenRequiredMessage, readConnectedHostOperatorToken } from '../runtime/connected-host-auth.ts';
16
+ import { formatAgentRecordSource } from '../agent/record-labels.ts';
17
+
18
+ function resolveManualSubscriptionConfig(config: OAuthProviderConfig): OAuthProviderConfig {
19
+ return config.manualRedirectUri
20
+ ? { ...config, redirectUri: config.manualRedirectUri }
21
+ : config;
22
+ }
14
23
 
15
24
  export async function renderSubscriptions(runtime: CliCommandRuntime): Promise<string> {
16
25
  return await withRuntimeServices(runtime, async (services) => {
@@ -22,7 +31,7 @@ export async function renderSubscriptions(runtime: CliCommandRuntime): Promise<s
22
31
  if (sub === 'providers') {
23
32
  return formatJsonOrText(runtime.cli)(available, [
24
33
  'GoodVibes subscription providers',
25
- ...available.map((provider) => ` ${provider.provider} source=${provider.source} redirect=${provider.oauth.redirectUri}`),
34
+ ...available.map((provider) => ` ${provider.provider} origin ${provider.source} redirect ${provider.oauth.redirectUri}`),
26
35
  ].join('\n'));
27
36
  }
28
37
  if (sub === 'inspect' || sub === 'show') {
@@ -40,21 +49,21 @@ export async function renderSubscriptions(runtime: CliCommandRuntime): Promise<s
40
49
  const stored = services.subscriptionManager.get(provider);
41
50
  return formatJsonOrText(runtime.cli)({ provider, resolved, inspection, stored }, [
42
51
  `GoodVibes subscription ${provider}`,
43
- ` configured: ${yesNo(inspection.configured)}`,
44
- ` freshness: ${inspection.freshness}`,
45
- ` callbackMode: ${inspection.callbackMode}`,
52
+ ` configured ${yesNo(inspection.configured)}`,
53
+ ` freshness ${inspection.freshness}`,
54
+ ' finish mode explicit manual',
46
55
  ...(resolved ? [
47
- ` source: ${resolved.source}`,
48
- ` redirectUri: ${resolved.oauth.redirectUri}`,
56
+ ` origin ${formatAgentRecordSource(resolved.source)}`,
57
+ ` redirect URI ${resolved.oauth.redirectUri}`,
49
58
  ] : []),
50
59
  ...(stored ? [
51
- ` tokenType: ${stored.tokenType}`,
52
- ` expiresAt: ${stored.expiresAt ? new Date(stored.expiresAt).toISOString() : 'n/a'}`,
53
- ` refreshToken: ${stored.refreshToken ? 'present' : 'absent'}`,
54
- ` overrideAmbient: ${yesNo(stored.overrideAmbientApiKeys)}`,
55
- ] : [' stored: no']),
56
- ...inspection.issues.map((issue) => ` issue: ${issue}`),
57
- ...inspection.nextActions.map((action) => ` next: ${action}`),
60
+ ` token type ${stored.tokenType}`,
61
+ ` expires ${stored.expiresAt ? new Date(stored.expiresAt).toISOString() : 'n/a'}`,
62
+ ` refresh token ${stored.refreshToken ? 'present' : 'absent'}`,
63
+ ` override ambient ${yesNo(stored.overrideAmbientApiKeys)}`,
64
+ ] : [' stored no']),
65
+ ...inspection.issues.map((issue) => ` issue ${issue}`),
66
+ ...inspection.nextActions.map((action) => ` next ${action}`),
58
67
  ].join('\n'));
59
68
  }
60
69
  if (sub === 'login' || sub === 'start') {
@@ -62,7 +71,7 @@ export async function renderSubscriptions(runtime: CliCommandRuntime): Promise<s
62
71
  const mode = sub === 'start' ? 'start' : rest[1]?.toLowerCase();
63
72
  if (!provider || mode !== 'start') return `Usage: ${binary} subscription login <provider> start [--open]`;
64
73
  const resolved = getSubscriptionProviderConfig(provider, services.serviceRegistry.get(provider));
65
- if (!resolved) return `No subscription provider found: ${provider}`;
74
+ if (!resolved) return `No subscription provider found ${provider}`;
66
75
  if (provider === 'openai' && resolved.source === 'builtin') {
67
76
  const started = await beginOpenAICodexLogin();
68
77
  services.subscriptionManager.savePending({
@@ -74,26 +83,29 @@ export async function renderSubscriptions(runtime: CliCommandRuntime): Promise<s
74
83
  });
75
84
  const openResult = runtime.cli.flags.open || hasCommandFlag(rest, '--open') ? openBrowser(started.authorizationUrl) : null;
76
85
  return [
77
- `Subscription OAuth started: ${provider}`,
78
- ` source: ${resolved.source}`,
79
- ` state: ${started.state}`,
80
- ` redirectUri: ${started.redirectUri}`,
81
- ...(openResult ? [` open: ${openResult}`] : []),
82
- ` next: ${binary} subscription login ${provider} finish <code-or-url>`,
83
- ' authorizationUrl:',
86
+ `Subscription OAuth started ${provider}`,
87
+ ` origin ${formatAgentRecordSource(resolved.source)}`,
88
+ ` state ${started.state}`,
89
+ ` redirect URI ${started.redirectUri}`,
90
+ ...(openResult ? [` open ${openResult}`] : []),
91
+ ' completion paste the callback code or redirect URL into the finish command',
92
+ ` next ${binary} subscription login ${provider} finish <code-or-url>`,
93
+ ' authorization URL',
84
94
  ` ${started.authorizationUrl}`,
85
95
  ].join('\n');
86
96
  }
87
- const started = await services.subscriptionManager.beginOAuthLogin(provider, resolved.oauth);
97
+ const activeConfig = resolveManualSubscriptionConfig(resolved.oauth);
98
+ const started = await services.subscriptionManager.beginOAuthLogin(provider, activeConfig);
88
99
  const openResult = runtime.cli.flags.open || hasCommandFlag(rest, '--open') ? openBrowser(started.authorizationUrl) : null;
89
100
  return [
90
- `Subscription OAuth started: ${provider}`,
91
- ` source: ${resolved.source}`,
92
- ` state: ${started.pending.state}`,
93
- ` redirectUri: ${started.pending.redirectUri}`,
94
- ...(openResult ? [` open: ${openResult}`] : []),
95
- ` next: ${binary} subscription login ${provider} finish <code-or-url>`,
96
- ' authorizationUrl:',
101
+ `Subscription OAuth started ${provider}`,
102
+ ` origin ${formatAgentRecordSource(resolved.source)}`,
103
+ ` state ${started.pending.state}`,
104
+ ` redirect URI ${started.pending.redirectUri}`,
105
+ ...(openResult ? [` open ${openResult}`] : []),
106
+ ' completion paste the callback code or redirect URL into the finish command',
107
+ ` next ${binary} subscription login ${provider} finish <code-or-url>`,
108
+ ' authorization URL',
97
109
  ` ${started.authorizationUrl}`,
98
110
  ].join('\n');
99
111
  }
@@ -102,7 +114,7 @@ export async function renderSubscriptions(runtime: CliCommandRuntime): Promise<s
102
114
  const codeInput = sub === 'finish' ? rest[1] : rest[2];
103
115
  if (!provider || !codeInput) return `Usage: ${binary} subscription login <provider> finish <code-or-url>`;
104
116
  const resolved = getSubscriptionProviderConfig(provider, services.serviceRegistry.get(provider));
105
- if (!resolved) return `No subscription provider found: ${provider}`;
117
+ if (!resolved) return `No subscription provider found ${provider}`;
106
118
  const code = extractAuthorizationCode(codeInput);
107
119
  if (provider === 'openai' && resolved.source === 'builtin') {
108
120
  const pendingLogin = services.subscriptionManager.getPending(provider);
@@ -121,24 +133,36 @@ export async function renderSubscriptions(runtime: CliCommandRuntime): Promise<s
121
133
  createdAt: services.subscriptionManager.get(provider)?.createdAt ?? now,
122
134
  updatedAt: now,
123
135
  });
124
- return `Subscription stored: ${provider} token=${record.tokenType} expires=${record.expiresAt ? new Date(record.expiresAt).toISOString() : 'n/a'}`;
136
+ return [
137
+ `Subscription stored ${provider}`,
138
+ ` token type ${record.tokenType}`,
139
+ ` expires ${record.expiresAt ? new Date(record.expiresAt).toISOString() : 'n/a'}`,
140
+ ].join('\n');
125
141
  }
126
- const record = await services.subscriptionManager.completeOAuthLogin(provider, resolved.oauth, code);
127
- return `Subscription stored: ${provider} token=${record.tokenType} expires=${record.expiresAt ? new Date(record.expiresAt).toISOString() : 'n/a'}`;
142
+ const activeConfig = resolveManualSubscriptionConfig(resolved.oauth);
143
+ const record = await services.subscriptionManager.completeOAuthLogin(provider, activeConfig, code);
144
+ return [
145
+ `Subscription stored ${provider}`,
146
+ ` token type ${record.tokenType}`,
147
+ ` expires ${record.expiresAt ? new Date(record.expiresAt).toISOString() : 'n/a'}`,
148
+ ].join('\n');
128
149
  }
129
150
  if (sub === 'refresh') {
130
151
  const provider = rest[0];
131
152
  if (!provider) return `Usage: ${binary} subscription refresh <provider>`;
132
153
  const resolved = getSubscriptionProviderConfig(provider, services.serviceRegistry.get(provider));
133
- if (!resolved) return `No subscription provider found: ${provider}`;
154
+ if (!resolved) return `No subscription provider found ${provider}`;
134
155
  const record = await services.subscriptionManager.refreshOAuthToken(provider, resolved.oauth);
135
- return `Subscription refreshed: ${provider} expires=${record.expiresAt ? new Date(record.expiresAt).toISOString() : 'n/a'}`;
156
+ return [
157
+ `Subscription refreshed ${provider}`,
158
+ ` expires ${record.expiresAt ? new Date(record.expiresAt).toISOString() : 'n/a'}`,
159
+ ].join('\n');
136
160
  }
137
161
  if (sub === 'logout' || sub === 'remove') {
138
162
  const provider = rest[0];
139
163
  if (!provider) return `Usage: ${binary} subscription logout <provider>`;
140
164
  const removed = services.subscriptionManager.logout(provider);
141
- return removed ? `Subscription removed: ${provider}` : `No stored subscription session existed for ${provider}.`;
165
+ return removed ? `Subscription removed ${provider}` : `No stored subscription session existed for ${provider}.`;
142
166
  }
143
167
  if (sub !== 'list' && sub !== 'status' && sub !== 'review') {
144
168
  return `Usage: ${binary} subscription [list|providers|inspect <provider>|login <provider> start|finish <code-or-url>|refresh <provider>|logout <provider>]`;
@@ -154,10 +178,10 @@ export async function renderSubscriptions(runtime: CliCommandRuntime): Promise<s
154
178
  };
155
179
  return formatJsonOrText(runtime.cli)(value, [
156
180
  'GoodVibes subscriptions',
157
- subscriptions.length === 0 ? ' active: none' : ' active:',
158
- ...subscriptions.map((sub) => ` ${sub.provider} token=${sub.tokenType} expires=${sub.expiresAt ? new Date(sub.expiresAt).toISOString() : 'n/a'} overrideAmbient=${yesNo(sub.overrideAmbientApiKeys)}`),
159
- pending.length === 0 ? ' pending: none' : ' pending:',
160
- ...pending.map((sub) => ` ${sub.provider} created=${new Date(sub.createdAt).toISOString()}`),
181
+ subscriptions.length === 0 ? ' active none' : ' active',
182
+ ...subscriptions.map((sub) => ` ${sub.provider} token ${sub.tokenType} expires ${sub.expiresAt ? new Date(sub.expiresAt).toISOString() : 'n/a'} override ambient ${yesNo(sub.overrideAmbientApiKeys)}`),
183
+ pending.length === 0 ? ' pending none' : ' pending',
184
+ ...pending.map((sub) => ` ${sub.provider} created ${new Date(sub.createdAt).toISOString()}`),
161
185
  ].join('\n'));
162
186
  });
163
187
  }
@@ -185,7 +209,11 @@ export async function handleSecrets(runtime: CliCommandRuntime): Promise<string>
185
209
  }
186
210
  const resolved = await resolveSecretRef(ref, { resolveLocalSecret: (key) => secrets.get(key) });
187
211
  const value = { ref: describeSecretRef(ref), resolved: Boolean(resolved.value) };
188
- return formatJsonOrText(runtime.cli)(value, `[secrets] ${value.ref}: ${value.resolved ? 'resolved <redacted>' : 'missing'}`);
212
+ return formatJsonOrText(runtime.cli)(value, [
213
+ 'GoodVibes secret test',
214
+ ` ref ${value.ref}`,
215
+ ` result ${value.resolved ? 'resolved <redacted>' : 'missing'}`,
216
+ ].join('\n'));
189
217
  }
190
218
  if (sub === 'set' || sub === 'link') {
191
219
  const flags = new Set(rest.filter((arg) => arg.startsWith('--')));
@@ -200,7 +228,10 @@ export async function handleSecrets(runtime: CliCommandRuntime): Promise<string>
200
228
  scope: flags.has('--user') ? 'user' : 'project',
201
229
  medium: flags.has('--plaintext') ? 'plaintext' : 'secure',
202
230
  });
203
- return `[secrets] ${sub === 'link' ? 'Linked' : 'Stored'}: ${key}`;
231
+ return [
232
+ `Secret ${sub === 'link' ? 'linked' : 'stored'}`,
233
+ ` key ${key}`,
234
+ ].join('\n');
204
235
  }
205
236
  if (sub === 'delete') {
206
237
  const key = rest.find((arg) => !arg.startsWith('--'));
@@ -210,18 +241,21 @@ export async function handleSecrets(runtime: CliCommandRuntime): Promise<string>
210
241
  scope: flags.has('--user') ? 'user' : flags.has('--project') ? 'project' : undefined,
211
242
  medium: flags.has('--secure') ? 'secure' : flags.has('--plaintext') ? 'plaintext' : undefined,
212
243
  });
213
- return `[secrets] Deleted: ${key}`;
244
+ return [
245
+ 'Secret deleted',
246
+ ` key ${key}`,
247
+ ].join('\n');
214
248
  }
215
249
  const [records, review] = await Promise.all([secrets.listDetailed(), secrets.inspect()]);
216
250
  const stored = records.filter((record) => record.source !== 'env');
217
251
  const value = { policy: review.policy, records: stored, warnings: review.warnings };
218
252
  return formatJsonOrText(runtime.cli)(value, [
219
253
  'GoodVibes secrets',
220
- ` policy: ${review.policy}`,
221
- ` secure available: ${yesNo(review.secureAvailable)}`,
222
- ` stored keys: ${stored.length}`,
254
+ ` policy ${review.policy}`,
255
+ ` secure available ${yesNo(review.secureAvailable)}`,
256
+ ` stored keys ${stored.length}`,
223
257
  ...stored.map((record) => ` ${record.key} (${record.source}${record.refSource ? `, ref:${record.refSource}` : ''}${record.overriddenByEnv ? ', env override' : ''})`),
224
- ...review.warnings.map((warning) => ` warning: ${warning}`),
258
+ ...review.warnings.map((warning) => ` warning ${warning}`),
225
259
  ].join('\n'));
226
260
  }
227
261
 
@@ -234,21 +268,21 @@ export async function handleSessions(runtime: CliCommandRuntime): Promise<string
234
268
  const value = sessions;
235
269
  return formatJsonOrText(runtime.cli)(value, [
236
270
  `GoodVibes sessions (${sessions.length})`,
237
- ...sessions.slice(0, 50).map((session) => ` ${session.name} messages=${session.messageCount} ${new Date(session.timestamp).toISOString()} ${session.title || '(untitled)'}`),
271
+ ...sessions.slice(0, 50).map((session) => ` ${session.name} messages ${session.messageCount} ${new Date(session.timestamp).toISOString()} ${session.title || '(untitled)'}`),
238
272
  ].join('\n'));
239
273
  }
240
274
  if (sub === 'show' || sub === 'info') {
241
275
  const target = rest.join(' ').trim();
242
276
  if (!target) return `Usage: ${binary} sessions show <id|name>`;
243
277
  const found = sessions.find((session) => session.name === target || session.name.startsWith(target) || session.title.toLowerCase() === target.toLowerCase());
244
- if (!found) return `Session not found: ${target}`;
278
+ if (!found) return `Session not found ${target}`;
245
279
  return formatJsonOrText(runtime.cli)(found, [
246
280
  `Session ${found.name}`,
247
- ` title: ${found.title || '(untitled)'}`,
248
- ` messages: ${found.messageCount}`,
249
- ` provider/model: ${found.provider}/${found.model}`,
250
- ` updated: ${new Date(found.timestamp).toISOString()}`,
251
- ` file: ${found.filePath}`,
281
+ ` title ${found.title || '(untitled)'}`,
282
+ ` messages ${found.messageCount}`,
283
+ ` provider/model ${found.provider}/${found.model}`,
284
+ ` updated ${new Date(found.timestamp).toISOString()}`,
285
+ ` file ${found.filePath}`,
252
286
  ].join('\n'));
253
287
  }
254
288
  if (sub === 'export') {
@@ -256,14 +290,17 @@ export async function handleSessions(runtime: CliCommandRuntime): Promise<string
256
290
  const outputPath = rest[1];
257
291
  if (!target) return `Usage: ${binary} sessions export <id|name> [path]`;
258
292
  const found = sessions.find((session) => session.name === target || session.name.startsWith(target) || session.title.toLowerCase() === target.toLowerCase());
259
- if (!found) return `Session not found: ${target}`;
293
+ if (!found) return `Session not found ${target}`;
260
294
  const data = services.sessionManager.load(found.name);
261
295
  const text = JSON.stringify({ name: found.name, ...data }, null, 2) + '\n';
262
296
  if (outputPath) {
263
297
  const targetPath = services.shellPaths.resolveWorkspacePath(outputPath);
264
298
  mkdirSync(dirname(targetPath), { recursive: true });
265
299
  writeFileSync(targetPath, text, 'utf-8');
266
- return `Session exported: ${targetPath}`;
300
+ return [
301
+ 'Session exported',
302
+ ` path ${targetPath}`,
303
+ ].join('\n');
267
304
  }
268
305
  return text.trimEnd();
269
306
  }
@@ -279,35 +316,35 @@ export async function handleTasks(runtime: CliCommandRuntime): Promise<string> {
279
316
  const [sub = 'list', ...rest] = runtime.cli.commandArgs;
280
317
  if (sub === 'submit') {
281
318
  return [
282
- 'GoodVibes Agent blocks CLI task submission from the service-owned task workflow.',
283
- ' policy: do normal assistant work in the main Agent conversation or use `goodvibes-agent run <prompt>` for an explicit one-shot run.',
284
- ' build/fix/review: use `goodvibes-agent delegate <task>` for explicit GoodVibes TUI handoff.',
285
- ' result: no local task was started.',
319
+ 'GoodVibes Agent blocks CLI task submission from the host-owned task workflow.',
320
+ ' policy do normal assistant work in the main Agent conversation or use `goodvibes-agent run <prompt>` for an explicit one-shot run.',
321
+ ' build/fix/review use `goodvibes-agent delegate <task>` for explicit GoodVibes TUI handoff.',
322
+ ' result no local task was started.',
286
323
  ].join('\n');
287
324
  }
288
325
  return await withRuntimeServices(runtime, (services) => {
289
326
  const tasks = [...services.runtimeStore.getState().tasks.tasks.values()];
290
327
  if (sub === 'list') {
291
328
  return tasks.length === 0
292
- ? 'GoodVibes tasks\n No connected-service tasks are currently recorded.'
329
+ ? 'GoodVibes tasks\n No connected-host tasks are currently recorded.'
293
330
  : ['GoodVibes tasks', ...tasks.map((task) => ` ${task.id} ${task.status} ${task.kind} ${task.title}`)].join('\n');
294
331
  }
295
332
  if (sub === 'show') {
296
333
  if (!rest[0]) return `Usage: ${runtime.cli.binary} tasks show <taskId>`;
297
334
  const task = tasks.find((candidate) => candidate.id === rest[0]);
298
- return task ? JSON.stringify(task, null, 2) : `Unknown task: ${rest[0] ?? ''}`;
335
+ return task ? JSON.stringify(task, null, 2) : `Unknown task ${rest[0] ?? ''}`;
299
336
  }
300
337
  return `Usage: ${runtime.cli.binary} tasks list|show <taskId>`;
301
338
  });
302
339
  }
303
340
 
304
341
  export async function renderPairing(runtime: CliCommandRuntime): Promise<string> {
305
- const daemonHomeDir = join(runtime.homeDirectory, '.goodvibes', 'daemon');
306
- const tokenRecord = getOrCreateCompanionToken(GOODVIBES_AGENT_PAIRING_SURFACE, { daemonHomeDir });
342
+ const tokenRecord = readConnectedHostOperatorToken(runtime.homeDirectory);
343
+ if (!tokenRecord.token) return connectedHostTokenRequiredMessage(tokenRecord.path);
307
344
  const binding = resolveRuntimeEndpointBinding(runtime.configManager, 'controlPlane');
308
- const daemonUrl = `http://${urlHostForBindHost(binding.host)}:${binding.port}`;
345
+ const connectedHostUrl = `http://${urlHostForBindHost(binding.host)}:${binding.port}`;
309
346
  const info = buildCompanionConnectionInfo({
310
- daemonUrl,
347
+ daemonUrl: connectedHostUrl,
311
348
  token: tokenRecord.token,
312
349
  username: 'admin',
313
350
  surface: GOODVIBES_AGENT_PAIRING_SURFACE,
@@ -13,20 +13,18 @@ import type { RuntimeServices } from '../runtime/services.ts';
13
13
  import { SecretsManager } from '../config/secrets.ts';
14
14
  import { RuntimeEventBus, type TurnEvent } from '@/runtime/index.ts';
15
15
  import { createShellPathService } from '@/runtime/index.ts';
16
+ import { buildPersistedSessionContext } from '@/runtime/index.ts';
17
+ import type { SessionSnapshot } from '@/runtime/index.ts';
18
+ import { conversationMessagesAsSessionRecords } from '../core/conversation-message-snapshot.ts';
16
19
  import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
17
20
  import { listProviderRuntimeSnapshots } from '@pellux/goodvibes-sdk/platform/providers';
18
21
  import { BUILTIN_SECRET_PROVIDER_SOURCES, describeSecretRef, isSecretRefInput, resolveSecretRef } from '@pellux/goodvibes-sdk/platform/config';
19
22
  import { getSubscriptionProviderConfig, listAvailableSubscriptionProviders } from '@pellux/goodvibes-sdk/platform/config';
20
23
  import { beginOpenAICodexLogin, exchangeOpenAICodexCode } from '@pellux/goodvibes-sdk/platform/config';
21
24
  import { inspectProviderAuth } from '@/runtime/index.ts';
22
- import { getOrCreateCompanionToken, buildCompanionConnectionInfo, encodeConnectionPayload, formatConnectionBlock } from '@pellux/goodvibes-sdk/platform/pairing';
23
- import { generateQrMatrix, renderQrToString } from '@pellux/goodvibes-sdk/platform/pairing';
24
25
  import type { GoodVibesCliParseResult } from './types.ts';
25
26
  import { formatProviderAuthRoute, summarizeProviderAuthRoutes } from './provider-auth-routes.ts';
26
27
  import { classifyProviderSetup } from './provider-classification.ts';
27
- import { resolveRuntimeEndpointBinding } from './endpoints.ts';
28
- import { applyRuntimeEndpointFlagOverrides } from './config-overrides.ts';
29
- import type { RuntimeEndpointId } from './endpoints.ts';
30
28
  import { handleBundleCommand } from './bundle-command.ts';
31
29
  import { handleSecrets, handleSessions, handleTasks, renderPairing, renderSubscriptions } from './management-commands.ts';
32
30
  import { handleAgentKnowledgeCommand, handleAgentKnowledgeShortcutCommand, handleCompatCommand, handleDelegateCommand } from './agent-knowledge-command.ts';
@@ -61,8 +59,8 @@ export function formatJsonOrText(cli: GoodVibesCliParseResult): Formatter {
61
59
  }
62
60
 
63
61
  function exitCodeForText(output: string): number {
64
- if (output.startsWith('Usage:') || output.startsWith('Invalid ') || output.startsWith('Unsupported:')) return 2;
65
- if (output.startsWith('Session not found:') || output.startsWith('Unknown task:') || output.startsWith('Task submit failed ')) return 1;
62
+ if (output.startsWith('Usage:') || output.startsWith('Invalid ') || output.startsWith('Unsupported')) return 2;
63
+ if (output.startsWith('Session not found') || output.startsWith('Unknown task') || output.startsWith('Task submit failed ')) return 1;
66
64
  if (output.startsWith('No stored ') || output.startsWith('No pending ') || output.startsWith('No model ') || output.startsWith('No provider ') || output.startsWith('No auth ')) return 1;
67
65
  if (output.startsWith('Unknown ')) return 1;
68
66
  if (output === 'Bundle has no config object to import.') return 1;
@@ -103,7 +101,7 @@ export function getNestedValue(source: unknown, key: string): unknown {
103
101
  let cursor = source;
104
102
  for (const part of key.split('.')) {
105
103
  if (cursor == null || typeof cursor !== 'object') return undefined;
106
- cursor = (cursor as Record<string, unknown>)[part];
104
+ cursor = Reflect.get(cursor, part);
107
105
  }
108
106
  return cursor;
109
107
  }
@@ -132,46 +130,6 @@ export function urlHostForBindHost(host: string): string {
132
130
  return host || '127.0.0.1';
133
131
  }
134
132
 
135
- export function enableServicePosture(config: ConfigManager): void {
136
- config.setDynamic('service.enabled', true);
137
- config.setDynamic('service.autostart', true);
138
- config.setDynamic('service.restartOnFailure', true);
139
- }
140
-
141
- export function enableEndpointLanDefault(config: ConfigManager, endpoint: RuntimeEndpointId): void {
142
- const binding = resolveRuntimeEndpointBinding(config, endpoint);
143
- if (binding.hostMode === 'custom') return;
144
- if (endpoint === 'controlPlane') {
145
- config.setDynamic('controlPlane.hostMode', 'network');
146
- config.setDynamic('controlPlane.host', '0.0.0.0');
147
- config.setDynamic('controlPlane.allowRemote', true);
148
- return;
149
- }
150
- if (endpoint === 'httpListener') {
151
- config.setDynamic('httpListener.hostMode', 'network');
152
- config.setDynamic('httpListener.host', '0.0.0.0');
153
- return;
154
- }
155
- config.setDynamic('web.hostMode', 'network');
156
- config.setDynamic('web.host', '0.0.0.0');
157
- }
158
-
159
- export function applyTargetEndpointFlagsOrDefault(
160
- runtime: CliCommandRuntime,
161
- endpoint: RuntimeEndpointId,
162
- ): string | null {
163
- const errors = applyRuntimeEndpointFlagOverrides(runtime.configManager, endpoint, runtime.cli.flags);
164
- if (errors.length > 0) return errors.join('\n');
165
- if (runtime.cli.flags.hostname === undefined) {
166
- enableEndpointLanDefault(runtime.configManager, endpoint);
167
- }
168
- if (endpoint === 'controlPlane') {
169
- const binding = resolveRuntimeEndpointBinding(runtime.configManager, endpoint);
170
- runtime.configManager.setDynamic('controlPlane.allowRemote', binding.hostMode !== 'local');
171
- }
172
- return null;
173
- }
174
-
175
133
  export function openBrowser(url: string): string {
176
134
  const platform = process.platform;
177
135
  const command = platform === 'darwin' ? 'open' : platform === 'win32' ? 'cmd' : 'xdg-open';
@@ -323,7 +281,13 @@ export async function runNonInteractiveAgent(runtime: CliCommandRuntime): Promis
323
281
  }) + '\n');
324
282
  }
325
283
  } finally {
326
- const snapshot = ctx.conversation.toJSON() as Parameters<typeof ctx.shutdown>[0];
284
+ const messages = ctx.conversation.getMessageSnapshot();
285
+ const snapshot: SessionSnapshot = {
286
+ messages: conversationMessagesAsSessionRecords(messages),
287
+ timestamp: Date.now(),
288
+ title: ctx.conversation.title,
289
+ ...buildPersistedSessionContext(messages, ctx.conversation.getTitleSource()),
290
+ };
327
291
  await ctx.shutdown(snapshot);
328
292
  }
329
293
  return exitCode;
@@ -371,18 +335,25 @@ async function renderProviders(runtime: CliCommandRuntime): Promise<string> {
371
335
  } else {
372
336
  runtime.configManager.setDynamic('provider.model', formatProviderModel(provider, getModelIdFromProviderModel(runtime.configManager.get('provider.model'))));
373
337
  }
374
- return requestedModel
375
- ? `Provider selected: ${provider} (${requestedModel})\n warning: model catalog entry was not available locally; saved explicit selection.`
376
- : `Provider selected: ${provider}\n warning: model catalog entry was not available locally; model selection was left unchanged.`;
338
+ return [
339
+ 'Provider selected',
340
+ ` provider ${provider}`,
341
+ ...(requestedModel ? [` model ${requestedModel}`] : []),
342
+ ` warning model catalog entry was not available locally; ${requestedModel ? 'saved explicit selection' : 'model selection was left unchanged'}.`,
343
+ ].join('\n');
377
344
  }
378
345
  runtime.configManager.setDynamic('provider.model', selected.registryKey);
379
- return `Provider selected: ${selected.provider} (${selected.registryKey})`;
346
+ return [
347
+ 'Provider selected',
348
+ ` provider ${selected.provider}`,
349
+ ` model ${selected.registryKey}`,
350
+ ].join('\n');
380
351
  }
381
352
  if (sub === 'inspect' || sub === 'show') {
382
353
  const provider = rest[0];
383
354
  if (!provider) return `Usage: ${binary} providers inspect <provider>`;
384
355
  const snapshot = snapshots.find((candidate) => candidate.providerId === provider);
385
- if (!snapshot) return `No provider found: ${provider}`;
356
+ if (!snapshot) return `No provider found ${provider}`;
386
357
  const setup = classifyProviderSetup({
387
358
  providerId: snapshot.providerId,
388
359
  authMode: snapshot.runtime.auth?.mode,
@@ -428,7 +399,7 @@ async function renderProviders(runtime: CliCommandRuntime): Promise<string> {
428
399
  return formatJsonOrText(runtime.cli)(value, [
429
400
  'GoodVibes providers',
430
401
  ...value.map((provider) =>
431
- ` ${provider.current ? '*' : ' '} ${provider.provider.padEnd(18)} setup=${provider.setupClass} configured=${yesNo(provider.configured)} via=${provider.configuredVia ?? 'n/a'} models=${provider.models} routes=${provider.authRouteSummary} ${provider.detail ?? ''}`.trimEnd(),
402
+ ` ${provider.current ? '*' : ' '} ${provider.provider.padEnd(18)} setup ${provider.setupClass} configured ${yesNo(provider.configured)} via ${provider.configuredVia ?? 'n/a'} models ${provider.models} routes ${provider.authRouteSummary} ${provider.detail ?? ''}`.trimEnd(),
432
403
  ),
433
404
  ].join('\n'));
434
405
  });
@@ -481,18 +452,28 @@ async function renderModels(runtime: CliCommandRuntime): Promise<string> {
481
452
  const provider = inferProviderFromRegistryKey(modelKey);
482
453
  runtime.configManager.setDynamic('provider.model', formatProviderModel(provider, modelKey));
483
454
  await services.favoritesStore.recordUsage(modelKey);
484
- return `Model selected: ${modelKey}\n warning: model catalog entry was not available locally; saved explicit selection.`;
455
+ return [
456
+ 'Model selected',
457
+ ` model ${modelKey}`,
458
+ ' warning model catalog entry was not available locally; saved explicit selection.',
459
+ ].join('\n');
485
460
  }
486
461
  runtime.configManager.setDynamic('provider.model', model.registryKey);
487
462
  await services.favoritesStore.recordUsage(model.registryKey);
488
- return `Model selected: ${model.registryKey}`;
463
+ return [
464
+ 'Model selected',
465
+ ` model ${model.registryKey}`,
466
+ ].join('\n');
489
467
  }
490
468
  if (subOrFilter === 'pin' || subOrFilter === 'unpin') {
491
469
  const modelKey = rest[0];
492
470
  if (!modelKey) return `Usage: ${binary} models ${subOrFilter} <registryKey>`;
493
471
  if (subOrFilter === 'pin') await services.favoritesStore.pinModel(modelKey);
494
472
  else await services.favoritesStore.unpinModel(modelKey);
495
- return `Model ${subOrFilter === 'pin' ? 'pinned' : 'unpinned'}: ${modelKey}`;
473
+ return [
474
+ `Model ${subOrFilter === 'pin' ? 'pinned' : 'unpinned'}`,
475
+ ` model ${modelKey}`,
476
+ ].join('\n');
496
477
  }
497
478
  if (subOrFilter === 'pinned') {
498
479
  const pinned = await services.favoritesStore.getPinned();
@@ -524,7 +505,7 @@ async function renderModels(runtime: CliCommandRuntime): Promise<string> {
524
505
  }));
525
506
  return formatJsonOrText(runtime.cli)(value, [
526
507
  `GoodVibes models${filter ? ` (${filter})` : ''}`,
527
- ...value.map((model) => ` ${model.current ? '*' : ' '} ${model.registryKey.padEnd(42)} setup=${model.setupClass} ctx=${model.contextWindow.toLocaleString()} ${model.displayName}`),
508
+ ...value.map((model) => ` ${model.current ? '*' : ' '} ${model.registryKey.padEnd(42)} setup ${model.setupClass} context ${model.contextWindow.toLocaleString()} ${model.displayName}`),
528
509
  ].join('\n'));
529
510
  });
530
511
  }
@@ -544,8 +525,8 @@ async function renderAuth(runtime: CliCommandRuntime): Promise<string> {
544
525
  ]);
545
526
  if (blocked.has(sub)) {
546
527
  return [
547
- 'Unsupported: connected-service auth user/session administration is outside GoodVibes Agent.',
548
- 'GoodVibes Agent does not create, delete, rotate, revoke, or clear connected-service users, sessions, or bootstrap credentials.',
528
+ 'Unsupported: connected-host auth user/session administration is outside GoodVibes Agent.',
529
+ 'GoodVibes Agent does not create, delete, rotate, revoke, or clear connected-host users, sessions, or bootstrap credentials.',
549
530
  'Use the owning GoodVibes host for auth administration.',
550
531
  ].join('\n');
551
532
  }
@@ -566,23 +547,23 @@ async function renderAuth(runtime: CliCommandRuntime): Promise<string> {
566
547
  if (sub === 'users' || sub === 'sessions') {
567
548
  return formatJsonOrText(runtime.cli)(value, [
568
549
  `GoodVibes Agent auth ${sub}`,
569
- ' owner: connected GoodVibes services',
570
- ` operator token: ${paths.operatorTokenPresent ? 'present' : 'missing'}`,
571
- ` operator token path: ${paths.operatorTokenPath}`,
572
- ` ${sub}: managed outside Agent`,
573
- ' Agent does not enumerate or mutate connected-service users/sessions from the local CLI.',
550
+ ' owner connected GoodVibes host',
551
+ ` operator token ${paths.operatorTokenPresent ? 'present' : 'missing'}`,
552
+ ` operator token path ${paths.operatorTokenPath}`,
553
+ ` ${sub} managed outside Agent`,
554
+ ' Agent does not enumerate or mutate connected-host users/sessions from the local CLI.',
574
555
  ].join('\n'));
575
556
  }
576
557
  return formatJsonOrText(runtime.cli)(value, [
577
558
  'GoodVibes Agent auth',
578
- ' owner: connected GoodVibes services',
579
- ` permission mode: ${String(value.permissionMode)}`,
580
- ` operator token: ${paths.operatorTokenPresent ? 'present' : 'missing'} (${paths.operatorTokenPath})`,
581
- ` compatibility user store: ${paths.userStorePresent ? 'present' : 'missing'} (${paths.userStorePath})`,
582
- ` compatibility bootstrap credential: ${paths.bootstrapCredentialPresent ? 'present' : 'missing'} (${paths.bootstrapCredentialPath})`,
583
- ' connected-service user/session administration: outside Agent',
584
- ' next: goodvibes-agent providers',
585
- ' next: goodvibes-agent subscription providers',
559
+ ' owner connected GoodVibes host',
560
+ ` permission mode ${String(value.permissionMode)}`,
561
+ ` operator token ${paths.operatorTokenPresent ? 'present' : 'missing'} (${paths.operatorTokenPath})`,
562
+ ` compatibility user store ${paths.userStorePresent ? 'present' : 'missing'} (${paths.userStorePath})`,
563
+ ` compatibility bootstrap credential ${paths.bootstrapCredentialPresent ? 'present' : 'missing'} (${paths.bootstrapCredentialPath})`,
564
+ ' connected-host user/session administration outside Agent',
565
+ ' next goodvibes-agent providers',
566
+ ' next goodvibes-agent subscription providers',
586
567
  ].join('\n'));
587
568
  }
588
569