@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
@@ -6,9 +6,9 @@ export function registerNotifyRuntimeCommands(registry: CommandRegistry): void {
6
6
  registry.register({
7
7
  name: 'notify',
8
8
  aliases: [],
9
- description: 'Manage webhook notification URLs (ntfy.sh format)',
10
- usage: 'add <url> --yes | remove <url> --yes | list | clear --yes | test --yes',
11
- argsHint: 'list|add --yes|remove --yes|test --yes',
9
+ description: 'Manage and send configured Agent webhook notifications',
10
+ usage: 'add <url> --yes | remove <url> --yes | list | clear --yes | test --yes | send <message> --yes',
11
+ argsHint: 'list|add --yes|remove --yes|test --yes|send --yes',
12
12
  async handler(args, ctx) {
13
13
  const parsed = stripYesFlag(args);
14
14
  const commandArgs = [...parsed.rest];
@@ -18,8 +18,8 @@ export function registerNotifyRuntimeCommands(registry: CommandRegistry): void {
18
18
  const sub = commandArgs[0];
19
19
 
20
20
  if (!sub || sub === 'list') {
21
- if (urls.length === 0) ctx.print('No webhook URLs configured.\nUse: /notify add <url>');
22
- else ctx.print(`Webhook URLs (${urls.length}):\n${urls.map((u, i) => ` ${i + 1}. ${u}`).join('\n')}`);
21
+ if (urls.length === 0) ctx.print('No webhook URLs configured.\nUse /notify add <url>');
22
+ else ctx.print(`Webhook URLs (${urls.length})\n${urls.map((u, i) => ` ${i + 1}. ${u}`).join('\n')}`);
23
23
  return;
24
24
  }
25
25
 
@@ -34,11 +34,11 @@ export function registerNotifyRuntimeCommands(registry: CommandRegistry): void {
34
34
  return;
35
35
  }
36
36
  try { new URL(url); } catch {
37
- ctx.print(`Invalid URL: ${url}`);
37
+ ctx.print(`Invalid URL ${url}`);
38
38
  return;
39
39
  }
40
40
  if (urls.includes(url)) {
41
- ctx.print(`Already configured: ${url}`);
41
+ ctx.print(`Already configured ${url}`);
42
42
  return;
43
43
  }
44
44
  urls.push(url);
@@ -86,7 +86,7 @@ export function registerNotifyRuntimeCommands(registry: CommandRegistry): void {
86
86
  return;
87
87
  }
88
88
  if (urls.length === 0) {
89
- ctx.print('No webhook URLs configured. Use: /notify add <url>');
89
+ ctx.print('No webhook URLs configured. Use /notify add <url>');
90
90
  return;
91
91
  }
92
92
  ctx.print(`Testing ${urls.length} webhook${urls.length !== 1 ? 's' : ''}...`);
@@ -96,7 +96,36 @@ export function registerNotifyRuntimeCommands(registry: CommandRegistry): void {
96
96
  return;
97
97
  }
98
98
 
99
- ctx.print('Usage: /notify add <url> --yes | remove <url> --yes | list | clear --yes | test --yes');
99
+ if (sub === 'send') {
100
+ const message = commandArgs.slice(1).join(' ').trim();
101
+ if (!message) {
102
+ ctx.print('Usage: /notify send <message> --yes');
103
+ return;
104
+ }
105
+ if (!parsed.yes) {
106
+ requireYesFlag(ctx, 'send a notification to configured webhook targets', '/notify send <message> --yes');
107
+ return;
108
+ }
109
+ if (urls.length === 0) {
110
+ ctx.print('No webhook URLs configured. Use /notify add <url>');
111
+ return;
112
+ }
113
+ notifier.setUrls(urls);
114
+ const result = await notifier.send(message);
115
+ ctx.print([
116
+ 'Notification sent',
117
+ ` attempted ${result.attempted}`,
118
+ ` delivered ${result.delivered}`,
119
+ ` failed ${result.failed}`,
120
+ ...result.results.slice(0, 20).map((delivery, index) => (
121
+ ` target ${index + 1} ${delivery.ok ? 'ok' : `failed${delivery.error ? ` (${delivery.error})` : ''}`}`
122
+ )),
123
+ ...(result.results.length > 20 ? [` ${result.results.length - 20} more target(s) omitted.`] : []),
124
+ ].join('\n'));
125
+ return;
126
+ }
127
+
128
+ ctx.print('Usage: /notify add <url> --yes | remove <url> --yes | list | clear --yes | test --yes | send <message> --yes');
100
129
  },
101
130
  });
102
131
  }
@@ -0,0 +1,138 @@
1
+ import type { CommandContext, CommandRegistry } from '../command-registry.ts';
2
+ import {
3
+ buildOperatorActionRequest,
4
+ formatOperatorActionFailure,
5
+ formatOperatorActionPreview,
6
+ formatOperatorActionSuccess,
7
+ postOperatorAction,
8
+ type OperatorActionId,
9
+ } from '../../agent/operator-actions.ts';
10
+ import { resolveAgentConnectedHostConnection } from '../../agent/routine-schedule-promotion.ts';
11
+ import { requireShellPaths } from './runtime-services.ts';
12
+ import { stripYesFlag } from './confirmation.ts';
13
+
14
+ interface OperatorActionOptionParse {
15
+ readonly note?: string;
16
+ readonly remember?: boolean;
17
+ readonly errors: readonly string[];
18
+ }
19
+
20
+ function parseOperatorActionOptions(args: readonly string[]): OperatorActionOptionParse {
21
+ const errors: string[] = [];
22
+ let note: string | undefined;
23
+ let remember: boolean | undefined;
24
+ for (let index = 0; index < args.length; index += 1) {
25
+ const token = args[index];
26
+ if (token === '--note') {
27
+ const value = args[index + 1];
28
+ if (!value) {
29
+ errors.push('--note requires text');
30
+ continue;
31
+ }
32
+ note = value;
33
+ index += 1;
34
+ continue;
35
+ }
36
+ if (token === '--remember') {
37
+ remember = true;
38
+ continue;
39
+ }
40
+ if (token === '--no-remember') {
41
+ remember = false;
42
+ continue;
43
+ }
44
+ errors.push(`Unknown option ${token}`);
45
+ }
46
+ return { note, remember, errors };
47
+ }
48
+
49
+ export async function executeConfirmedOperatorAction(
50
+ ctx: CommandContext,
51
+ action: OperatorActionId,
52
+ targetField: 'approvalId' | 'jobId' | 'runId' | 'scheduleId',
53
+ targetId: string,
54
+ rawArgs: readonly string[],
55
+ usage: string,
56
+ ): Promise<void> {
57
+ const parsed = stripYesFlag(rawArgs);
58
+ const options = parseOperatorActionOptions(parsed.rest);
59
+ if (options.errors.length > 0) {
60
+ ctx.print([`Usage: ${usage}`, ...options.errors.map((error) => ` ${error}`)].join('\n'));
61
+ return;
62
+ }
63
+ const request = buildOperatorActionRequest({
64
+ action,
65
+ [targetField]: targetId,
66
+ note: options.note,
67
+ remember: options.remember,
68
+ });
69
+ if (!request.ok) {
70
+ ctx.print(request.error);
71
+ return;
72
+ }
73
+ const requestSummary = `/${action} ${targetId}`;
74
+ if (!parsed.yes) {
75
+ ctx.print(`${formatOperatorActionPreview(request, requestSummary)}\nUsage: ${usage}`);
76
+ return;
77
+ }
78
+ const shellPaths = requireShellPaths(ctx);
79
+ const connection = resolveAgentConnectedHostConnection(ctx.platform.configManager, shellPaths.homeDirectory);
80
+ const result = await postOperatorAction(connection, request);
81
+ ctx.print(result.ok ? formatOperatorActionSuccess(connection.baseUrl, result) : formatOperatorActionFailure(result));
82
+ }
83
+
84
+ function approvalActionFromSubcommand(sub: string): OperatorActionId | null {
85
+ if (sub === 'approve') return 'approvals.approve';
86
+ if (sub === 'deny') return 'approvals.deny';
87
+ if (sub === 'cancel') return 'approvals.cancel';
88
+ return null;
89
+ }
90
+
91
+ function automationActionFromParts(scope: string, verb: string): {
92
+ readonly action: OperatorActionId;
93
+ readonly targetField: 'jobId' | 'runId' | 'scheduleId';
94
+ } | null {
95
+ if ((scope === 'job' || scope === 'jobs') && verb === 'run') return { action: 'automation.jobs.run', targetField: 'jobId' };
96
+ if ((scope === 'job' || scope === 'jobs') && verb === 'pause') return { action: 'automation.jobs.pause', targetField: 'jobId' };
97
+ if ((scope === 'job' || scope === 'jobs') && verb === 'resume') return { action: 'automation.jobs.resume', targetField: 'jobId' };
98
+ if ((scope === 'run' || scope === 'runs') && verb === 'cancel') return { action: 'automation.runs.cancel', targetField: 'runId' };
99
+ if ((scope === 'run' || scope === 'runs') && verb === 'retry') return { action: 'automation.runs.retry', targetField: 'runId' };
100
+ if ((scope === 'schedule' || scope === 'schedules') && verb === 'run') return { action: 'schedules.run', targetField: 'scheduleId' };
101
+ return null;
102
+ }
103
+
104
+ export async function handleApprovalOperatorAction(args: readonly string[], ctx: CommandContext): Promise<boolean> {
105
+ const sub = (args[0] ?? '').toLowerCase();
106
+ const action = approvalActionFromSubcommand(sub);
107
+ if (!action) return false;
108
+ const approvalId = args[1] ?? '';
109
+ const usage = `/approval ${sub} <approval-id> [--note <text>] [--remember|--no-remember] --yes`;
110
+ if (!approvalId) {
111
+ ctx.print(`Usage: ${usage}`);
112
+ return true;
113
+ }
114
+ await executeConfirmedOperatorAction(ctx, action, 'approvalId', approvalId, args.slice(2), usage);
115
+ return true;
116
+ }
117
+
118
+ export function registerOperatorActionRuntimeCommands(registry: CommandRegistry): void {
119
+ registry.register({
120
+ name: 'automation',
121
+ aliases: ['auto'],
122
+ description: 'Run confirmed connected-host automation actions from the Agent TUI',
123
+ usage: 'job <run|pause|resume> <job-id> --yes | run <cancel|retry> <run-id> --yes | schedule run <schedule-id> --yes',
124
+ argsHint: 'job run <id> --yes | run cancel <id> --yes | schedule run <id> --yes',
125
+ async handler(args, ctx) {
126
+ const scope = (args[0] ?? '').toLowerCase();
127
+ const verb = (args[1] ?? '').toLowerCase();
128
+ const targetId = args[2] ?? '';
129
+ const mapping = automationActionFromParts(scope, verb);
130
+ const usage = '/automation job <run|pause|resume> <job-id> --yes | /automation run <cancel|retry> <run-id> --yes | /automation schedule run <schedule-id> --yes';
131
+ if (!mapping || !targetId) {
132
+ ctx.print(`Usage: ${usage}`);
133
+ return;
134
+ }
135
+ await executeConfirmedOperatorAction(ctx, mapping.action, mapping.targetField, targetId, args.slice(3), usage);
136
+ },
137
+ });
138
+ }
@@ -1,7 +1,5 @@
1
1
  import type { CommandRegistry } from '../command-registry.ts';
2
- import type { ProfileData } from '@pellux/goodvibes-sdk/platform/profiles';
3
2
  import { logger } from '@pellux/goodvibes-sdk/platform/utils';
4
- import { requireProfileManager } from './runtime-services.ts';
5
3
  import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
6
4
  import { requireYesFlag, stripYesFlag } from './confirmation.ts';
7
5
 
@@ -59,78 +57,6 @@ export function registerOperatorRuntimeCommands(registry: CommandRegistry): void
59
57
  else ctx.scrollToLine?.(nextLine);
60
58
  },
61
59
  });
62
-
63
- registry.register({
64
- name: 'profiles',
65
- aliases: ['profile'],
66
- description: 'Browse, save, and delete config profiles',
67
- usage: '[list|open|save <name> --yes|delete <name> --yes]',
68
- argsHint: '[list|open|save --yes|delete --yes]',
69
- handler(args, ctx) {
70
- const parsed = stripYesFlag(args);
71
- const sub = parsed.rest[0] ?? 'open';
72
- const profileManager = requireProfileManager(ctx);
73
- if (sub === 'open') {
74
- if (ctx.openProfilePicker) {
75
- ctx.openProfilePicker();
76
- } else {
77
- const profiles = profileManager.list();
78
- if (profiles.length === 0) ctx.print('No profiles saved. Use /profiles save <name> --yes to save the current settings as a profile.');
79
- else ctx.print(['Saved profiles:', ...profiles.map(p => ` ${p.name}`)].join('\n'));
80
- }
81
- return;
82
- }
83
- if (sub === 'list') {
84
- const profiles = profileManager.list();
85
- if (profiles.length === 0) ctx.print('No profiles saved. Use /profiles save <name> --yes to save the current settings as a profile.');
86
- else ctx.print(['Saved profiles:', ...profiles.map(p => ` ${p.name}`)].join('\n'));
87
- return;
88
- }
89
- if (sub === 'save') {
90
- const name = parsed.rest[1];
91
- if (!name) {
92
- ctx.print('Usage: /profiles save <name> --yes');
93
- return;
94
- }
95
- if (!parsed.yes) {
96
- requireYesFlag(ctx, `save config profile ${name}`, '/profiles save <name> --yes');
97
- return;
98
- }
99
- const all = ctx.platform.configManager.getAll();
100
- const data: ProfileData = {
101
- display: { ...all.display },
102
- provider: {
103
- model: all.provider.model,
104
- reasoningEffort: all.provider.reasoningEffort,
105
- },
106
- behavior: { ...all.behavior },
107
- };
108
- profileManager.save(name, data);
109
- ctx.print(`Profile saved: ${name}`);
110
- return;
111
- }
112
- if (sub === 'delete' || sub === 'remove') {
113
- const name = parsed.rest[1];
114
- if (!name) {
115
- ctx.print('Usage: /profiles delete <name> --yes');
116
- return;
117
- }
118
- if (!parsed.yes) {
119
- requireYesFlag(ctx, `delete config profile ${name}`, '/profiles delete <name> --yes');
120
- return;
121
- }
122
- const deleted = profileManager.delete(name);
123
- ctx.print(deleted ? `Profile deleted: ${name}` : `Profile not found: ${name}`);
124
- return;
125
- }
126
- if (args.length === 0 && ctx.openProfilePicker) {
127
- ctx.openProfilePicker();
128
- return;
129
- }
130
- ctx.print('Usage: /profiles [list|open|save <name> --yes|delete <name> --yes]');
131
- },
132
- });
133
-
134
60
  registry.register({
135
61
  name: 'prev-error',
136
62
  aliases: ['pe'],
@@ -212,7 +138,7 @@ export function registerOperatorRuntimeCommands(registry: CommandRegistry): void
212
138
  return;
213
139
  }
214
140
  if (verbosity !== 'minimal' && verbosity !== 'normal' && verbosity !== 'verbose') {
215
- ctx.print(`Invalid verbosity "${verbosity}". Valid values: minimal, normal, verbose`);
141
+ ctx.print(`Invalid verbosity "${verbosity}". Valid values minimal, normal, verbose.`);
216
142
  return;
217
143
  }
218
144
  if (!parsed.yes) {
@@ -220,7 +146,11 @@ export function registerOperatorRuntimeCommands(registry: CommandRegistry): void
220
146
  return;
221
147
  }
222
148
  mgr.setDomainVerbosity(domain, verbosity as 'minimal' | 'normal' | 'verbose');
223
- ctx.print(`Domain "${domain}" verbosity set to: ${verbosity}`);
149
+ ctx.print([
150
+ 'Domain verbosity set',
151
+ ` domain ${domain}`,
152
+ ` verbosity ${verbosity}`,
153
+ ].join('\n'));
224
154
  return;
225
155
  }
226
156
 
@@ -1,5 +1,6 @@
1
1
  import { discoverPersonas, type DiscoveredPersonaRecord } from '../../agent/persona-discovery.ts';
2
2
  import { AgentPersonaRegistry, type AgentPersonaRecord } from '../../agent/persona-registry.ts';
3
+ import { formatAgentRecordOrigin, formatAgentRecordReviewState } from '../../agent/record-labels.ts';
3
4
  import type { CommandContext, CommandRegistry } from '../command-registry.ts';
4
5
  import { requireShellPaths } from './runtime-services.ts';
5
6
 
@@ -44,21 +45,33 @@ function registryFromContext(ctx: CommandContext): AgentPersonaRegistry {
44
45
  return AgentPersonaRegistry.fromShellPaths(requireShellPaths(ctx));
45
46
  }
46
47
 
48
+ function formatPersonaReceipt(title: string, persona: Pick<AgentPersonaRecord, 'id' | 'name'>, extra: readonly string[] = []): string {
49
+ const activeSuffix = extra.some((line) => line.includes('active yes') || line.includes('active: yes')) ? ' (active)' : '';
50
+ const normalizedExtra = extra.map((line) => line.replace(/^ active /, ' active: '));
51
+ return [
52
+ `${title} ${persona.id}: ${persona.name}${activeSuffix}`,
53
+ ` id ${persona.id}`,
54
+ ` name ${persona.name}`,
55
+ ...normalizedExtra,
56
+ ].join('\n');
57
+ }
58
+
47
59
  function summarizePersona(persona: AgentPersonaRecord, activePersonaId: string | null): string {
48
60
  const active = persona.id === activePersonaId ? 'active' : 'inactive';
49
- const tags = persona.tags.length > 0 ? ` tags=${persona.tags.join(',')}` : '';
50
- return ` ${persona.id} ${active} ${persona.reviewState} ${persona.name} - ${persona.description}${tags}`;
61
+ const review = formatAgentRecordReviewState(persona.reviewState);
62
+ const tags = persona.tags.length > 0 ? ` tags ${persona.tags.join(', ')}` : '';
63
+ return ` ${persona.id} ${active} ${review} ${persona.name} - ${persona.description}${tags}`;
51
64
  }
52
65
 
53
66
  function renderList(title: string, registry: AgentPersonaRegistry, personas: readonly AgentPersonaRecord[]): string {
54
67
  const snapshot = registry.snapshot();
55
68
  if (personas.length === 0) {
56
- return `${title}\n No local Agent personas yet. Create one with /personas create --name <name> --description <summary> --body <instructions>.`;
69
+ return `${title}\n No local Agent personas yet.\n No Agent-local personas yet. Create one with /personas create --name <name> --description <summary> --body <instructions>.`;
57
70
  }
58
71
  return [
59
72
  `${title} (${personas.length})`,
60
- ` store: ${snapshot.path}`,
61
- ` active: ${snapshot.activePersona?.name ?? '(none)'}`,
73
+ ` store ${snapshot.path}`,
74
+ ` active ${snapshot.activePersona?.name ?? '(none)'}`,
62
75
  ...personas.map((persona) => summarizePersona(persona, snapshot.activePersonaId)),
63
76
  ].join('\n');
64
77
  }
@@ -66,11 +79,10 @@ function renderList(title: string, registry: AgentPersonaRegistry, personas: rea
66
79
  function renderPersona(persona: AgentPersonaRecord, activePersonaId: string | null): string {
67
80
  return [
68
81
  `Persona ${persona.name}`,
69
- ` id: ${persona.id}`,
82
+ ` id ${persona.id}`,
70
83
  ` active: ${persona.id === activePersonaId ? 'yes' : 'no'}`,
71
- ` review: ${persona.reviewState}`,
72
- ` source: ${persona.source}`,
73
- ` provenance: ${persona.provenance}`,
84
+ ` review: ${formatAgentRecordReviewState(persona.reviewState)}`,
85
+ ` origin: ${formatAgentRecordOrigin(persona.source, persona.provenance)}`,
74
86
  ` tags: ${persona.tags.join(', ') || '(none)'}`,
75
87
  ` triggers: ${persona.triggers.join(', ') || '(none)'}`,
76
88
  ` created: ${persona.createdAt}`,
@@ -85,7 +97,7 @@ function renderPersona(persona: AgentPersonaRecord, activePersonaId: string | nu
85
97
 
86
98
  function summarizeDiscoveredPersona(persona: DiscoveredPersonaRecord): string {
87
99
  const description = persona.description ? ` - ${persona.description}` : '';
88
- return ` ${persona.name} ${persona.origin}${description}\n path: ${persona.path}`;
100
+ return ` ${persona.name} ${persona.origin}${description}\n path ${persona.path}`;
89
101
  }
90
102
 
91
103
  function renderDiscoveredPersonas(personas: readonly DiscoveredPersonaRecord[]): string {
@@ -93,7 +105,7 @@ function renderDiscoveredPersonas(personas: readonly DiscoveredPersonaRecord[]):
93
105
  return [
94
106
  'Discovered Agent persona files',
95
107
  ' No persona markdown files found in project/global Agent persona folders.',
96
- ' Search roots: .goodvibes/personas, .goodvibes/agent/personas, .goodvibes/agents, ~/.goodvibes/personas, ~/.goodvibes/agent/personas, ~/.goodvibes/agents',
108
+ ' Search roots: .goodvibes/personas, .goodvibes/agent/personas, ~/.goodvibes/personas, ~/.goodvibes/agent/personas',
97
109
  ].join('\n');
98
110
  }
99
111
  return [
@@ -131,18 +143,18 @@ async function importDiscoveredPersona(args: readonly string[], ctx: CommandCont
131
143
  }
132
144
  const discovered = findDiscoveredPersona(await discoverPersonas(requireShellPaths(ctx)), name);
133
145
  if (!discovered) {
134
- ctx.print(`Unknown discovered Agent persona: ${name}\nRun /personas discover to inspect available persona files.`);
146
+ ctx.print(`Unknown discovered Agent persona ${name}\nRun /personas discover to inspect available persona files.`);
135
147
  return;
136
148
  }
137
149
  if (!parsed.yes) {
138
150
  ctx.print([
139
151
  'Agent persona import preview',
140
- ` name: ${discovered.name}`,
141
- ` origin: ${discovered.origin}`,
142
- ` path: ${discovered.path}`,
143
- ` description: ${discovered.description || '(none)'}`,
144
- ` body characters: ${discovered.body.length}`,
145
- ' next: rerun with --yes to import into the Agent-local persona registry',
152
+ ` name ${discovered.name}`,
153
+ ` origin ${discovered.origin}`,
154
+ ` path ${discovered.path}`,
155
+ ` description ${discovered.description || '(none)'}`,
156
+ ` body characters ${discovered.body.length}`,
157
+ ' next rerun with --yes to import into the Agent-local persona registry',
146
158
  ].join('\n'));
147
159
  return;
148
160
  }
@@ -153,10 +165,10 @@ async function importDiscoveredPersona(args: readonly string[], ctx: CommandCont
153
165
  tags: frontmatterList(discovered, 'tags'),
154
166
  triggers: frontmatterList(discovered, 'triggers'),
155
167
  source: 'imported',
156
- provenance: `discovered:${discovered.origin}:${discovered.path}`,
168
+ provenance: `Imported file (${discovered.origin}): ${discovered.path}`,
157
169
  });
158
170
  if (parsed.flags.get('use') === 'true') personaRegistry.setActive(persona.id);
159
- ctx.print(`Imported Agent persona ${persona.id}: ${persona.name}${parsed.flags.get('use') === 'true' ? ' (active)' : ''}`);
171
+ ctx.print(formatPersonaReceipt('Imported Agent persona', persona, [` active ${parsed.flags.get('use') === 'true' ? 'yes' : 'no'}`]));
160
172
  }
161
173
 
162
174
  function requiredFlag(flags: ReadonlyMap<string, string>, key: string): string {
@@ -166,14 +178,17 @@ function requiredFlag(flags: ReadonlyMap<string, string>, key: string): string {
166
178
  }
167
179
 
168
180
  function printError(ctx: CommandContext, error: unknown): void {
169
- ctx.print(`Error: ${error instanceof Error ? error.message : String(error)}`);
181
+ ctx.print([
182
+ 'Error',
183
+ ` message ${error instanceof Error ? error.message : String(error)}`,
184
+ ].join('\n'));
170
185
  }
171
186
 
172
187
  export function registerPersonasRuntimeCommands(registry: CommandRegistry): void {
173
188
  registry.register({
174
189
  name: 'personas',
175
190
  aliases: ['persona'],
176
- description: 'Manage local GoodVibes Agent personas',
191
+ description: 'Manage Agent-local personas',
177
192
  usage: '[list|discover|import-discovered <name> --yes|search <query>|show <id>|create --name <name> --description <summary> --body <instructions>|update <id> [--name ...] [--description ...] [--body ...]|use <id>|active|clear|review <id>|stale <id> <reason...>|delete <id> --yes]',
178
193
  async handler(args, ctx) {
179
194
  const sub = (args[0] ?? 'list').toLowerCase();
@@ -204,7 +219,7 @@ export function registerPersonasRuntimeCommands(registry: CommandRegistry): void
204
219
  }
205
220
  const snapshot = registryStore.snapshot();
206
221
  const persona = registryStore.get(id);
207
- ctx.print(persona ? renderPersona(persona, snapshot.activePersonaId) : `Unknown persona: ${id}`);
222
+ ctx.print(persona ? renderPersona(persona, snapshot.activePersonaId) : `Unknown Agent persona ${id}`);
208
223
  return;
209
224
  }
210
225
  if (sub === 'create') {
@@ -217,9 +232,9 @@ export function registerPersonasRuntimeCommands(registry: CommandRegistry): void
217
232
  tags: splitList(parsed.flags.get('tags')),
218
233
  triggers: splitList(parsed.flags.get('triggers')),
219
234
  source: 'user',
220
- provenance: 'slash-command',
235
+ provenance: 'Command',
221
236
  });
222
- ctx.print(`Created Agent persona ${persona.id}: ${persona.name}`);
237
+ ctx.print(formatPersonaReceipt('Created Agent persona', persona));
223
238
  return;
224
239
  }
225
240
  if (sub === 'update') {
@@ -235,9 +250,9 @@ export function registerPersonasRuntimeCommands(registry: CommandRegistry): void
235
250
  body: parsed.flags.get('body'),
236
251
  tags: parsed.flags.has('tags') ? splitList(parsed.flags.get('tags')) : undefined,
237
252
  triggers: parsed.flags.has('triggers') ? splitList(parsed.flags.get('triggers')) : undefined,
238
- provenance: 'slash-command',
253
+ provenance: 'Command',
239
254
  });
240
- ctx.print(`Updated Agent persona ${updated.id}: ${updated.name}`);
255
+ ctx.print(formatPersonaReceipt('Updated Agent persona', updated));
241
256
  return;
242
257
  }
243
258
  if (sub === 'use') {
@@ -247,7 +262,7 @@ export function registerPersonasRuntimeCommands(registry: CommandRegistry): void
247
262
  return;
248
263
  }
249
264
  const persona = registryStore.setActive(id);
250
- ctx.print(`Active Agent persona: ${persona.name} (${persona.id})`);
265
+ ctx.print(`Active Agent persona: ${persona.name}\n${formatPersonaReceipt('Active Agent persona', persona)}`);
251
266
  return;
252
267
  }
253
268
  if (sub === 'active') {
@@ -267,7 +282,7 @@ export function registerPersonasRuntimeCommands(registry: CommandRegistry): void
267
282
  return;
268
283
  }
269
284
  const persona = registryStore.markReviewed(id);
270
- ctx.print(`Reviewed Agent persona ${persona.id}.`);
285
+ ctx.print(formatPersonaReceipt('Reviewed Agent persona', persona));
271
286
  return;
272
287
  }
273
288
  if (sub === 'stale') {
@@ -277,7 +292,7 @@ export function registerPersonasRuntimeCommands(registry: CommandRegistry): void
277
292
  return;
278
293
  }
279
294
  const persona = registryStore.markStale(id, args.slice(2).join(' '));
280
- ctx.print(`Marked Agent persona ${persona.id} stale.`);
295
+ ctx.print(formatPersonaReceipt('Marked Agent persona stale', persona));
281
296
  return;
282
297
  }
283
298
  if (sub === 'delete' || sub === 'remove') {
@@ -292,7 +307,7 @@ export function registerPersonasRuntimeCommands(registry: CommandRegistry): void
292
307
  return;
293
308
  }
294
309
  const removed = registryStore.deletePersona(id);
295
- ctx.print(`Deleted Agent persona ${removed.id}: ${removed.name}`);
310
+ ctx.print(formatPersonaReceipt('Deleted Agent persona', removed));
296
311
  return;
297
312
  }
298
313
  ctx.print('Usage: /personas [list|discover|import-discovered|search <query>|show <id>|create|update|use|active|clear|review|stale|delete]');
@@ -111,7 +111,7 @@ export function registerPlanningRuntimeCommands(registry: CommandRegistry): void
111
111
  }
112
112
 
113
113
  if (args[0] === 'panel') {
114
- ctx.print('Planning panels are not part of the Agent workspace. Use /plan status or /plan list.');
114
+ ctx.print('Use /plan status or /plan list for compact command output. Open Agent Workspace -> Work -> Planning status or Saved plans for the workspace view.');
115
115
  return;
116
116
  }
117
117