@pellux/goodvibes-agent 0.1.117 → 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 (253) hide show
  1. package/CHANGELOG.md +13 -4
  2. package/README.md +35 -14
  3. package/bin/goodvibes-agent.ts +16 -2
  4. package/dist/package/main.js +176073 -170980
  5. package/docs/README.md +11 -5
  6. package/docs/channels-remote-and-api.md +50 -0
  7. package/docs/connected-host.md +3 -3
  8. package/docs/getting-started.md +29 -15
  9. package/docs/knowledge-artifacts-and-multimodal.md +91 -0
  10. package/docs/project-planning.md +79 -0
  11. package/docs/providers-and-routing.md +46 -0
  12. package/docs/release-and-publishing.md +44 -9
  13. package/docs/tools-and-commands.md +123 -0
  14. package/docs/voice-and-live-tts.md +51 -0
  15. package/package.json +2 -5
  16. package/src/agent/channel-delivery.ts +201 -0
  17. package/src/agent/media-generation.ts +159 -0
  18. package/src/agent/memory-prompt.ts +0 -1
  19. package/src/agent/note-registry.ts +329 -0
  20. package/src/agent/operator-actions.ts +343 -0
  21. package/src/agent/persona-registry.ts +15 -14
  22. package/src/agent/record-labels.ts +107 -0
  23. package/src/agent/reminder-schedule-format.ts +33 -24
  24. package/src/agent/reminder-schedule.ts +26 -25
  25. package/src/agent/routine-registry.ts +13 -12
  26. package/src/agent/routine-schedule-args.ts +2 -1
  27. package/src/agent/routine-schedule-format.ts +77 -53
  28. package/src/agent/routine-schedule-promotion.ts +34 -32
  29. package/src/agent/routine-schedule-receipts.ts +28 -26
  30. package/src/agent/runtime-profile-starters.ts +2 -2
  31. package/src/agent/runtime-profile.ts +18 -17
  32. package/src/agent/skill-registry.ts +25 -24
  33. package/src/cli/agent-knowledge-args.ts +5 -1
  34. package/src/cli/agent-knowledge-command.ts +39 -33
  35. package/src/cli/agent-knowledge-format.ts +80 -67
  36. package/src/cli/agent-knowledge-methods.ts +1 -1
  37. package/src/cli/agent-knowledge-runtime.ts +32 -26
  38. package/src/cli/bundle-command.ts +13 -8
  39. package/src/cli/config-overrides.ts +37 -36
  40. package/src/cli/external-runtime.ts +10 -4
  41. package/src/cli/help.ts +29 -11
  42. package/src/cli/local-library-command.ts +134 -68
  43. package/src/cli/management-commands.ts +98 -62
  44. package/src/cli/management.ts +52 -26
  45. package/src/cli/memory-command.ts +66 -32
  46. package/src/cli/parser.ts +37 -24
  47. package/src/cli/profiles-command.ts +52 -35
  48. package/src/cli/provider-auth-routes.ts +2 -1
  49. package/src/cli/routines-command.ts +44 -36
  50. package/src/cli/service-posture.ts +6 -6
  51. package/src/cli/status.ts +46 -121
  52. package/src/core/conversation-message-snapshot.ts +131 -0
  53. package/src/input/agent-workspace-activation.ts +33 -7
  54. package/src/input/agent-workspace-basic-command-editor-submission.ts +7 -3
  55. package/src/input/agent-workspace-basic-command-editors.ts +30 -10
  56. package/src/input/agent-workspace-categories.ts +276 -64
  57. package/src/input/agent-workspace-channel-command-editor-submission.ts +34 -0
  58. package/src/input/agent-workspace-channel-command-editors.ts +23 -5
  59. package/src/input/agent-workspace-channels.ts +35 -2
  60. package/src/input/agent-workspace-command-editor.ts +18 -2
  61. package/src/input/agent-workspace-config-reader.ts +16 -0
  62. package/src/input/agent-workspace-delegation-editor-submission.ts +1 -1
  63. package/src/input/agent-workspace-editors.ts +140 -2
  64. package/src/input/agent-workspace-knowledge-query-editor.ts +1 -1
  65. package/src/input/agent-workspace-learned-behavior.ts +2 -2
  66. package/src/input/agent-workspace-library-command-editor-submission.ts +1 -1
  67. package/src/input/agent-workspace-library-command-editors.ts +2 -2
  68. package/src/input/agent-workspace-local-operations.ts +218 -0
  69. package/src/input/agent-workspace-local-selection.ts +75 -0
  70. package/src/input/agent-workspace-media-command-editor-submission.ts +62 -0
  71. package/src/input/agent-workspace-media-command-editors.ts +27 -0
  72. package/src/input/agent-workspace-memory-command-editors.ts +1 -1
  73. package/src/input/agent-workspace-memory-editor.ts +2 -2
  74. package/src/input/agent-workspace-navigation.ts +38 -2
  75. package/src/input/agent-workspace-notify-editor-submission.ts +16 -2
  76. package/src/input/agent-workspace-operations-command-editor-submission.ts +63 -1
  77. package/src/input/agent-workspace-operations-command-editors.ts +80 -3
  78. package/src/input/agent-workspace-panel-route.ts +43 -0
  79. package/src/input/agent-workspace-reminder-schedule-editor.ts +1 -1
  80. package/src/input/agent-workspace-routine-schedule-editor.ts +2 -2
  81. package/src/input/agent-workspace-search.ts +169 -0
  82. package/src/input/agent-workspace-setup.ts +22 -11
  83. package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +6 -6
  84. package/src/input/agent-workspace-skill-bundle-command-editors.ts +10 -10
  85. package/src/input/agent-workspace-snapshot.ts +112 -13
  86. package/src/input/agent-workspace-task-command-editors.ts +4 -4
  87. package/src/input/agent-workspace-token.ts +18 -2
  88. package/src/input/agent-workspace-types.ts +92 -4
  89. package/src/input/agent-workspace-voice-media.ts +3 -6
  90. package/src/input/agent-workspace-web-research-editor.ts +104 -0
  91. package/src/input/agent-workspace.ts +136 -208
  92. package/src/input/command-registry.ts +4 -1
  93. package/src/input/commands/agent-runtime-profile-runtime.ts +45 -41
  94. package/src/input/commands/agent-skills-runtime.ts +83 -70
  95. package/src/input/commands/agent-workspace-runtime.ts +23 -7
  96. package/src/input/commands/brief-runtime.ts +25 -24
  97. package/src/input/commands/channels-runtime.ts +145 -31
  98. package/src/input/commands/delegation-runtime.ts +29 -23
  99. package/src/input/commands/experience-runtime.ts +6 -4
  100. package/src/input/commands/guidance-runtime.ts +4 -4
  101. package/src/input/commands/health-runtime.ts +140 -115
  102. package/src/input/commands/knowledge.ts +57 -56
  103. package/src/input/commands/local-provider-runtime.ts +36 -18
  104. package/src/input/commands/local-runtime.ts +138 -16
  105. package/src/input/commands/local-setup-review.ts +7 -7
  106. package/src/input/commands/mcp-runtime.ts +56 -35
  107. package/src/input/commands/notify-runtime.ts +38 -9
  108. package/src/input/commands/operator-actions-runtime.ts +138 -0
  109. package/src/input/commands/operator-runtime.ts +6 -17
  110. package/src/input/commands/personas-runtime.ts +45 -30
  111. package/src/input/commands/planning-runtime.ts +1 -1
  112. package/src/input/commands/platform-access-runtime.ts +29 -29
  113. package/src/input/commands/provider-accounts-runtime.ts +60 -39
  114. package/src/input/commands/qrcode-runtime.ts +45 -6
  115. package/src/input/commands/recall-bundle.ts +11 -11
  116. package/src/input/commands/recall-capture.ts +13 -11
  117. package/src/input/commands/recall-query.ts +29 -21
  118. package/src/input/commands/recall-review.ts +2 -1
  119. package/src/input/commands/routines-runtime.ts +59 -43
  120. package/src/input/commands/schedule-runtime.ts +33 -20
  121. package/src/input/commands/security-runtime.ts +4 -4
  122. package/src/input/commands/session-content.ts +80 -78
  123. package/src/input/commands/session-workflow.ts +132 -93
  124. package/src/input/commands/session.ts +3 -174
  125. package/src/input/commands/shell-core.ts +32 -17
  126. package/src/input/commands/subscription-runtime.ts +53 -179
  127. package/src/input/commands/tasks-runtime.ts +20 -20
  128. package/src/input/commands/work-plan-runtime.ts +33 -8
  129. package/src/input/commands.ts +2 -2
  130. package/src/input/feed-context-factory.ts +2 -1
  131. package/src/input/file-picker.ts +3 -2
  132. package/src/input/handler-command-route.ts +4 -7
  133. package/src/input/handler-content-actions.ts +89 -1
  134. package/src/input/handler-feed-routes.ts +19 -12
  135. package/src/input/handler-feed.ts +6 -3
  136. package/src/input/handler-interactions.ts +7 -5
  137. package/src/input/handler-modal-stack.ts +3 -3
  138. package/src/input/handler-onboarding.ts +24 -80
  139. package/src/input/handler-shortcuts.ts +15 -4
  140. package/src/input/handler.ts +47 -17
  141. package/src/input/input-history.ts +5 -6
  142. package/src/input/keybindings.ts +22 -11
  143. package/src/input/onboarding/onboarding-wizard-apply.ts +13 -0
  144. package/src/input/onboarding/onboarding-wizard-constants.ts +1 -0
  145. package/src/input/onboarding/onboarding-wizard-operator-steps.ts +147 -57
  146. package/src/input/onboarding/onboarding-wizard-steps.ts +46 -29
  147. package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
  148. package/src/input/onboarding/onboarding-wizard.ts +68 -0
  149. package/src/input/profile-picker-modal.ts +31 -12
  150. package/src/input/session-picker-modal.ts +21 -4
  151. package/src/input/settings-modal-behavior.ts +0 -3
  152. package/src/input/settings-modal-subscriptions.ts +3 -3
  153. package/src/input/settings-modal-types.ts +2 -13
  154. package/src/input/settings-modal.ts +6 -52
  155. package/src/input/submission-intent.ts +0 -1
  156. package/src/input/submission-router.ts +3 -3
  157. package/src/main.ts +18 -8
  158. package/src/panels/approval-panel.ts +8 -8
  159. package/src/panels/automation-control-panel.ts +2 -2
  160. package/src/panels/base-panel.ts +1 -1
  161. package/src/panels/builtin/agent.ts +1 -1
  162. package/src/panels/builtin/operations.ts +1 -10
  163. package/src/panels/builtin/session.ts +9 -9
  164. package/src/panels/builtin/shared.ts +2 -2
  165. package/src/panels/cost-tracker-panel.ts +1 -1
  166. package/src/panels/docs-panel.ts +5 -3
  167. package/src/panels/index.ts +0 -1
  168. package/src/panels/knowledge-panel.ts +6 -5
  169. package/src/panels/memory-panel.ts +7 -6
  170. package/src/panels/panel-list-panel.ts +36 -80
  171. package/src/panels/project-planning-panel.ts +18 -11
  172. package/src/panels/provider-account-snapshot.ts +51 -25
  173. package/src/panels/provider-accounts-panel.ts +33 -18
  174. package/src/panels/provider-health-domains.ts +45 -4
  175. package/src/panels/provider-health-panel.ts +5 -4
  176. package/src/panels/qr-panel.ts +1 -1
  177. package/src/panels/schedule-panel.ts +9 -17
  178. package/src/panels/security-panel.ts +8 -8
  179. package/src/panels/session-browser-panel.ts +10 -10
  180. package/src/panels/subscription-panel.ts +3 -3
  181. package/src/panels/system-messages-panel.ts +1 -1
  182. package/src/panels/tasks-panel.ts +20 -13
  183. package/src/panels/tool-inspector-panel.ts +19 -12
  184. package/src/panels/work-plan-panel.ts +5 -5
  185. package/src/planning/project-planning-coordinator.ts +1 -1
  186. package/src/provider-auth-route-display.ts +9 -0
  187. package/src/renderer/agent-workspace-style.ts +34 -0
  188. package/src/renderer/agent-workspace.ts +228 -52
  189. package/src/renderer/autocomplete-overlay.ts +25 -6
  190. package/src/renderer/bookmark-modal.ts +19 -4
  191. package/src/renderer/buffer.ts +4 -2
  192. package/src/renderer/context-inspector.ts +50 -13
  193. package/src/renderer/diff.ts +1 -1
  194. package/src/renderer/file-picker-overlay.ts +19 -6
  195. package/src/renderer/help-overlay.ts +106 -33
  196. package/src/renderer/history-search-overlay.ts +19 -4
  197. package/src/renderer/live-tail-modal.ts +27 -5
  198. package/src/renderer/mcp-workspace.ts +164 -50
  199. package/src/renderer/model-picker-overlay.ts +58 -2
  200. package/src/renderer/model-workspace.ts +104 -20
  201. package/src/renderer/process-modal.ts +27 -6
  202. package/src/renderer/profile-picker-modal.ts +20 -4
  203. package/src/renderer/search-overlay.ts +25 -5
  204. package/src/renderer/selection-modal-overlay.ts +46 -14
  205. package/src/renderer/session-picker-modal.ts +18 -1
  206. package/src/renderer/settings-modal-helpers.ts +2 -40
  207. package/src/renderer/settings-modal.ts +83 -50
  208. package/src/renderer/tool-call.ts +20 -11
  209. package/src/runtime/agent-runtime-events.ts +6 -6
  210. package/src/runtime/bootstrap-command-context.ts +7 -1
  211. package/src/runtime/bootstrap-command-parts.ts +9 -7
  212. package/src/runtime/bootstrap-core.ts +19 -1
  213. package/src/runtime/bootstrap-hook-bridge.ts +7 -18
  214. package/src/runtime/bootstrap-shell.ts +4 -4
  215. package/src/runtime/bootstrap.ts +31 -22
  216. package/src/runtime/connected-host-auth.ts +7 -2
  217. package/src/runtime/diagnostics/panels/index.ts +2 -2
  218. package/src/runtime/diagnostics/panels/policy.ts +7 -7
  219. package/src/runtime/index.ts +2 -1
  220. package/src/runtime/onboarding/apply-file-helpers.ts +66 -0
  221. package/src/runtime/onboarding/apply.ts +80 -79
  222. package/src/runtime/onboarding/derivation.ts +5 -8
  223. package/src/runtime/onboarding/snapshot.ts +0 -15
  224. package/src/runtime/onboarding/types.ts +8 -19
  225. package/src/runtime/onboarding/verify.ts +32 -10
  226. package/src/runtime/operator-token-cleanup.ts +1 -1
  227. package/src/runtime/services.ts +91 -22
  228. package/src/runtime/store/selectors/index.ts +3 -3
  229. package/src/runtime/store/state.ts +1 -4
  230. package/src/runtime/surface-feature-flags.ts +41 -6
  231. package/src/runtime/ui-read-models.ts +3 -4
  232. package/src/runtime/ui-services.ts +6 -6
  233. package/src/shell/blocking-input.ts +2 -1
  234. package/src/shell/ui-openers.ts +3 -13
  235. package/src/tools/agent-analysis-registry-policy.ts +0 -1
  236. package/src/tools/agent-channel-send-tool.ts +133 -0
  237. package/src/tools/agent-context-policy.ts +1 -1
  238. package/src/tools/agent-knowledge-ingest-tool.ts +405 -0
  239. package/src/tools/agent-knowledge-tool.ts +170 -0
  240. package/src/tools/agent-local-registry-tool.ts +269 -69
  241. package/src/tools/agent-media-generate-tool.ts +133 -0
  242. package/src/tools/agent-notify-tool.ts +143 -0
  243. package/src/tools/agent-operator-action-tool.ts +137 -0
  244. package/src/tools/agent-operator-briefing-tool.ts +217 -0
  245. package/src/tools/agent-reminder-schedule-tool.ts +237 -0
  246. package/src/tools/agent-tool-policy-guard.ts +8 -8
  247. package/src/tools/agent-work-plan-tool.ts +256 -0
  248. package/src/version.ts +1 -1
  249. package/src/input/commands/policy-dispatch.ts +0 -339
  250. package/src/input/commands/policy.ts +0 -13
  251. package/src/panels/panel-picker.ts +0 -105
  252. package/src/panels/policy-panel.ts +0 -308
  253. package/src/renderer/panel-picker-overlay.ts +0 -202
@@ -8,7 +8,7 @@ import {
8
8
  buildReminderSchedulePreview,
9
9
  createReminderSchedule,
10
10
  parseReminderScheduleArgs,
11
- resolveReminderDaemonConnection,
11
+ resolveReminderConnectedHostConnection,
12
12
  } from '../../agent/reminder-schedule.ts';
13
13
  import {
14
14
  formatReminderScheduleFailure,
@@ -18,8 +18,8 @@ import {
18
18
  import { AgentRoutineRegistry } from '../../agent/routine-registry.ts';
19
19
  import {
20
20
  buildRoutineSchedulePreview,
21
- promoteRoutineToDaemonSchedule,
22
- resolveAgentDaemonConnection,
21
+ promoteRoutineToConnectedSchedule,
22
+ resolveAgentConnectedHostConnection,
23
23
  } from '../../agent/routine-schedule-promotion.ts';
24
24
  import { parseRoutineSchedulePromotionArgs } from '../../agent/routine-schedule-args.ts';
25
25
  import {
@@ -36,6 +36,7 @@ import {
36
36
  } from '../../agent/routine-schedule-receipts.ts';
37
37
  import type { CommandContext } from '../command-registry.ts';
38
38
  import { requireShellPaths } from './runtime-services.ts';
39
+ import { executeConfirmedOperatorAction } from './operator-actions-runtime.ts';
39
40
 
40
41
  function formatSchedule(schedule: AutomationScheduleDefinition): string {
41
42
  switch (schedule.kind) {
@@ -64,10 +65,11 @@ function formatPrompt(job: AutomationJob): string {
64
65
  function printReadOnlyScheduleBoundary(print: (text: string) => void, requestedAction: string): void {
65
66
  print([
66
67
  'GoodVibes Agent local schedule commands are read-only in this runtime.',
67
- ` requested: ${requestedAction}`,
68
- ' policy: no local Agent automation jobs, scheduled spawns, or immediate automation runs',
69
- ' use: /schedule list',
70
- ' schedule route: use /schedule promote-routine <routine> --cron <expr> --yes to create a connected schedule explicitly',
68
+ ` requested ${requestedAction}`,
69
+ ' policy no hidden local Agent automation jobs or immediate automation runs',
70
+ ' use /schedule list',
71
+ ' connected run route /automation schedule run <schedule-id> --yes',
72
+ ' schedule route use /schedule promote-routine <routine> --cron <expr> --yes to create a connected schedule explicitly',
71
73
  ].join('\n'));
72
74
  }
73
75
 
@@ -83,7 +85,7 @@ async function promoteRoutineSchedule(args: readonly string[], ctx: CommandConte
83
85
  const shellPaths = requireShellPaths(ctx);
84
86
  const routine = AgentRoutineRegistry.fromShellPaths(shellPaths).get(parsed.routineId ?? '');
85
87
  if (!routine) {
86
- ctx.print(`Unknown Agent routine: ${parsed.routineId ?? ''}`);
88
+ ctx.print(`Unknown Agent routine ${parsed.routineId ?? ''}`);
87
89
  return;
88
90
  }
89
91
  const preview = buildRoutineSchedulePreview(routine, parsed);
@@ -91,8 +93,8 @@ async function promoteRoutineSchedule(args: readonly string[], ctx: CommandConte
91
93
  ctx.print(formatRoutineSchedulePreview(preview));
92
94
  return;
93
95
  }
94
- const connection = resolveAgentDaemonConnection(ctx.platform.configManager, shellPaths.homeDirectory);
95
- const result = await promoteRoutineToDaemonSchedule(connection, preview);
96
+ const connection = resolveAgentConnectedHostConnection(ctx.platform.configManager, shellPaths.homeDirectory);
97
+ const result = await promoteRoutineToConnectedSchedule(connection, preview);
96
98
  const receipt = RoutineScheduleReceiptStore.fromShellPaths(shellPaths).append(connection, preview, result);
97
99
  ctx.print(result.ok ? `${formatRoutineScheduleSuccess(result)}\n receipt: ${receipt.id}` : `${formatRoutineScheduleFailure(result)}\n receipt: ${receipt.id}`);
98
100
  }
@@ -112,7 +114,7 @@ async function createReminder(args: readonly string[], ctx: CommandContext): Pro
112
114
  return;
113
115
  }
114
116
  const shellPaths = requireShellPaths(ctx);
115
- const connection = resolveReminderDaemonConnection(ctx.platform.configManager, shellPaths.homeDirectory);
117
+ const connection = resolveReminderConnectedHostConnection(ctx.platform.configManager, shellPaths.homeDirectory);
116
118
  const result = await createReminderSchedule(connection, preview);
117
119
  ctx.print(result.ok ? formatReminderScheduleSuccess(result) : formatReminderScheduleFailure(result));
118
120
  }
@@ -121,7 +123,7 @@ export function registerScheduleRuntimeCommands(registry: CommandRegistry): void
121
123
  registry.register({
122
124
  name: 'schedule',
123
125
  aliases: ['sched'],
124
- description: 'Inspect schedules, create confirmed reminders, and explicitly promote local Agent routines to connected schedules',
126
+ description: 'Inspect schedules, create confirmed reminders, and explicitly promote Agent-local routines to connected schedules',
125
127
  usage: 'list | remind --at <iso> --message <text> --yes | receipts | reconcile | receipt <id> | promote-routine <routine-id> --cron <expr> [--delivery-channel slack] --yes',
126
128
  argsHint: 'list | remind --at <iso> --message <text> --yes | receipts | reconcile | receipt <id> | promote-routine <routine-id> --cron <expr> [--delivery-channel slack] --yes',
127
129
  async handler(args, ctx) {
@@ -144,7 +146,7 @@ export function registerScheduleRuntimeCommands(registry: CommandRegistry): void
144
146
 
145
147
  if (sub === 'reconcile' || sub === 'sync' || sub === 'status') {
146
148
  const shellPaths = requireShellPaths(ctx);
147
- const connection = resolveAgentDaemonConnection(ctx.platform.configManager, shellPaths.homeDirectory);
149
+ const connection = resolveAgentConnectedHostConnection(ctx.platform.configManager, shellPaths.homeDirectory);
148
150
  const result = await reconcileRoutineScheduleReceipts(connection, RoutineScheduleReceiptStore.fromShellPaths(shellPaths).snapshot());
149
151
  ctx.print(formatRoutineScheduleCorrelation(result));
150
152
  return;
@@ -157,7 +159,7 @@ export function registerScheduleRuntimeCommands(registry: CommandRegistry): void
157
159
  return;
158
160
  }
159
161
  const receipt = RoutineScheduleReceiptStore.fromShellPaths(requireShellPaths(ctx)).get(id);
160
- ctx.print(receipt ? formatRoutineScheduleReceipt(receipt) : `Unknown routine schedule receipt: ${id}`);
162
+ ctx.print(receipt ? formatRoutineScheduleReceipt(receipt) : `Unknown routine schedule receipt ${id}`);
161
163
  return;
162
164
  }
163
165
 
@@ -176,20 +178,30 @@ export function registerScheduleRuntimeCommands(registry: CommandRegistry): void
176
178
  );
177
179
  return;
178
180
  }
179
- const lines = ['Automation jobs:', ''];
181
+ const lines = ['Automation jobs', ''];
180
182
  for (const job of jobs) {
181
183
  const status = job.enabled ? '● enabled ' : '○ paused ';
182
184
  const next = formatNextRun(job.nextRunAt);
183
185
  const last = job.lastRunAt ? new Date(job.lastRunAt).toLocaleString() : 'never';
184
- lines.push(` ${job.id.slice(0, 12)} ${status} runs:${job.runCount} next:${next} last:${last}`);
185
- lines.push(` name: ${job.name} schedule: ${formatSchedule(job.schedule)}`);
186
- lines.push(` prompt: ${formatPrompt(job)}`);
186
+ lines.push(` ${job.id.slice(0, 12)} ${status} runs ${job.runCount} next ${next} last ${last}`);
187
+ lines.push(` name ${job.name} schedule ${formatSchedule(job.schedule)}`);
188
+ lines.push(` prompt ${formatPrompt(job)}`);
187
189
  }
188
190
  ctx.print(lines.join('\n'));
189
191
  return;
190
192
  }
191
193
 
192
- if (sub === 'add' || sub === 'remove' || sub === 'enable' || sub === 'disable' || sub === 'run') {
194
+ if (sub === 'run') {
195
+ const scheduleId = args[1] ?? '';
196
+ if (!scheduleId) {
197
+ ctx.print('Usage: /schedule run <schedule-id> --yes');
198
+ return;
199
+ }
200
+ await executeConfirmedOperatorAction(ctx, 'schedules.run', 'scheduleId', scheduleId, args.slice(2), '/schedule run <schedule-id> --yes');
201
+ return;
202
+ }
203
+
204
+ if (sub === 'add' || sub === 'remove' || sub === 'enable' || sub === 'disable') {
193
205
  printReadOnlyScheduleBoundary(ctx.print, `/schedule ${args.join(' ')}`.trim());
194
206
  return;
195
207
  }
@@ -200,9 +212,10 @@ export function registerScheduleRuntimeCommands(registry: CommandRegistry): void
200
212
  + ' /schedule receipts\n'
201
213
  + ' /schedule reconcile\n'
202
214
  + ' /schedule receipt <receipt-id>\n'
215
+ + ' /schedule run <schedule-id> --yes\n'
203
216
  + ' /schedule remind (--cron <expr>|--every <interval>|--at <iso-time>) (--message <text>|<text...>) [--delivery-channel <channel>|--delivery-route <route>|--delivery-webhook <url>] --yes\n'
204
217
  + ' /schedule promote-routine <routine-id> (--cron <expr>|--every <interval>|--at <iso-time>) [--delivery-channel <channel>|--delivery-route <route>|--delivery-webhook <url>] --yes\n'
205
- + ' Local schedule mutations and runs remain blocked.'
218
+ + ' Local schedule mutations remain blocked; schedule run is a confirmed connected-host action.'
206
219
  );
207
220
  },
208
221
  });
@@ -12,7 +12,7 @@ export function registerSecurityRuntimeCommands(registry: CommandRegistry): void
12
12
  handler(args, ctx) {
13
13
  const subcommand = args[0]?.toLowerCase() ?? 'review';
14
14
  if (subcommand === 'open' || subcommand === 'panel') {
15
- ctx.print('Security panels are not part of the Agent workspace. Use /security review.');
15
+ ctx.print('Open Agent Workspace -> Tools & MCP -> Security review for the workspace view, or run /security review for compact command output.');
16
16
  return;
17
17
  }
18
18
  const audit = requireTokenAuditor(ctx).auditAll(Date.now());
@@ -29,13 +29,13 @@ export function registerSecurityRuntimeCommands(registry: CommandRegistry): void
29
29
 
30
30
  if (subcommand === 'tokens') {
31
31
  if (audit.results.length === 0) {
32
- ctx.print('No registered API tokens are currently under audit.');
32
+ ctx.print('No registered API tokens are currently available for security review.');
33
33
  return;
34
34
  }
35
35
  ctx.print([
36
- `Token Audit (${audit.results.length})`,
36
+ `Token Review (${audit.results.length})`,
37
37
  ...audit.results.map((result) => (
38
- ` ${result.label} policy=${result.scope.policyId} scope=${result.scope.outcome} rotation=${result.rotation.outcome} blocked=${result.blocked ? 'yes' : 'no'}`
38
+ ` ${result.label} policy ${result.scope.policyId} scope ${result.scope.outcome} rotation ${result.rotation.outcome} blocked ${result.blocked ? 'yes' : 'no'}`
39
39
  )),
40
40
  ].join('\n'));
41
41
  return;
@@ -1,12 +1,43 @@
1
1
  import { join, resolve } from 'path';
2
2
  import { existsSync, mkdirSync } from 'node:fs';
3
3
  import { writeFile } from 'node:fs/promises';
4
- import type { CommandRegistry } from '../command-registry.ts';
4
+ import type { CommandContext, CommandRegistry } from '../command-registry.ts';
5
5
  import type { SelectionItem } from '../selection-modal.ts';
6
- import { exportToMarkdown } from '@pellux/goodvibes-sdk/platform/export';
7
- import { requireSessionManager, requireSessionMemoryStore, requireShellPaths } from './runtime-services.ts';
6
+ import type { SessionMeta } from '@pellux/goodvibes-sdk/platform/sessions';
7
+ import { exportToMarkdown, extractText } from '@pellux/goodvibes-sdk/platform/export';
8
+ import { requireSessionManager, requireShellPaths } from './runtime-services.ts';
8
9
  import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
9
10
  import { requireYesFlag, stripYesFlag } from './confirmation.ts';
11
+ import { readConversationMessageSnapshots } from '../../core/conversation-message-snapshot.ts';
12
+
13
+ function formatSessionFailure(action: string, error: unknown): string {
14
+ return [
15
+ `Failed to ${action}`,
16
+ ` error ${summarizeError(error)}`,
17
+ ].join('\n');
18
+ }
19
+
20
+ type ConversationSnapshotReader = {
21
+ readonly getMessageSnapshot?: () => ReturnType<typeof readConversationMessageSnapshots>;
22
+ readonly toJSON?: () => unknown;
23
+ };
24
+
25
+ function isObjectArray(value: unknown): value is readonly object[] {
26
+ return Array.isArray(value) && value.every((entry) => typeof entry === 'object' && entry !== null);
27
+ }
28
+
29
+ function getCurrentConversationMessages(ctx: CommandContext): ReturnType<typeof readConversationMessageSnapshots> {
30
+ const manager = ctx.session.conversationManager as unknown as ConversationSnapshotReader;
31
+ if (typeof manager.getMessageSnapshot === 'function') return manager.getMessageSnapshot();
32
+ if (typeof manager.toJSON === 'function') {
33
+ const serialized = manager.toJSON();
34
+ if (serialized && typeof serialized === 'object' && 'messages' in serialized) {
35
+ const messages = (serialized as { readonly messages?: unknown }).messages;
36
+ if (isObjectArray(messages)) return readConversationMessageSnapshots(messages);
37
+ }
38
+ }
39
+ throw new Error('Conversation manager cannot export message snapshots.');
40
+ }
10
41
 
11
42
  export function registerSessionContentCommands(registry: CommandRegistry): void {
12
43
  registry.register({
@@ -32,7 +63,10 @@ export function registerSessionContentCommands(registry: CommandRegistry): void
32
63
  }
33
64
  const resolvedPath = shellPaths.resolveWorkspacePath(outPath);
34
65
  if (!shellPaths.isWithinWorkingDirectory(resolvedPath)) {
35
- ctx.print('Error: Export path must be within the current directory.');
66
+ ctx.print([
67
+ 'Error',
68
+ ' message Export path must be within the current directory.',
69
+ ].join('\n'));
36
70
  return;
37
71
  }
38
72
  if (!yes) {
@@ -41,23 +75,10 @@ export function registerSessionContentCommands(registry: CommandRegistry): void
41
75
  }
42
76
 
43
77
  try {
44
- const data = ctx.session.conversationManager.toJSON() as { messages: Array<Record<string, unknown>> };
45
- const msgs = data.messages ?? [];
78
+ const msgs = getCurrentConversationMessages(ctx);
46
79
  let fileContent: string;
47
80
  if (format === 'markdown') {
48
- const exportMsgs = msgs.map(m => ({
49
- role: String(m.role ?? 'user') as 'user' | 'assistant' | 'system' | 'tool',
50
- content: Array.isArray(m.content)
51
- ? m.content as import('@pellux/goodvibes-sdk/platform/providers').ContentPart[]
52
- : String(m.content ?? ''),
53
- toolCalls: m.toolCalls as import('@pellux/goodvibes-sdk/platform/types').ToolCall[] | undefined,
54
- callId: m.callId as string | undefined,
55
- toolName: m.toolName as string | undefined,
56
- reasoningContent: m.reasoningContent as string | undefined,
57
- reasoningSummary: m.reasoningSummary as string | undefined,
58
- usage: m.usage as { inputTokens: number; outputTokens: number; cacheReadTokens?: number; cacheWriteTokens?: number } | undefined,
59
- }));
60
- fileContent = exportToMarkdown(exportMsgs, {
81
+ fileContent = exportToMarkdown(msgs, {
61
82
  model: ctx.session.runtime.model,
62
83
  provider: ctx.session.runtime.provider,
63
84
  sessionId: ctx.session.runtime.sessionId,
@@ -66,8 +87,8 @@ export function registerSessionContentCommands(registry: CommandRegistry): void
66
87
  } else {
67
88
  const lines: string[] = [];
68
89
  for (const m of msgs) {
69
- const role = String(m.role ?? 'unknown').toUpperCase();
70
- const content = typeof m.content === 'string' ? m.content : '';
90
+ const role = m.role.toUpperCase();
91
+ const content = extractText(m.content);
71
92
  if (!content.trim()) continue;
72
93
  lines.push(`[${role}]`);
73
94
  lines.push(content);
@@ -79,9 +100,13 @@ export function registerSessionContentCommands(registry: CommandRegistry): void
79
100
  const dir = dirname(resolvedPath);
80
101
  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
81
102
  await writeFile(resolvedPath, fileContent, 'utf-8');
82
- ctx.print(`Exported ${msgs.length} messages to: ${resolvedPath}`);
103
+ ctx.print([
104
+ 'Exported conversation',
105
+ ` messages ${msgs.length}`,
106
+ ` path ${resolvedPath}`,
107
+ ].join('\n'));
83
108
  } catch (err) {
84
- ctx.print(`Export failed: ${summarizeError(err)}`);
109
+ ctx.print(formatSessionFailure('export conversation', err));
85
110
  }
86
111
  },
87
112
  });
@@ -92,10 +117,13 @@ export function registerSessionContentCommands(registry: CommandRegistry): void
92
117
  usage: '[text]',
93
118
  argsHint: '[text]',
94
119
  handler(args, ctx) {
95
- if (args.length === 0) ctx.print(ctx.session.conversationManager.title ? `Conversation title: ${ctx.session.conversationManager.title}` : 'No title set.');
120
+ if (args.length === 0) ctx.print(ctx.session.conversationManager.title ? `Conversation title\n title ${ctx.session.conversationManager.title}` : 'No title set.');
96
121
  else {
97
122
  ctx.session.conversationManager.title = args.join(' ');
98
- ctx.print(`Title set to: ${ctx.session.conversationManager.title}`);
123
+ ctx.print([
124
+ 'Title set',
125
+ ` title ${ctx.session.conversationManager.title}`,
126
+ ].join('\n'));
99
127
  ctx.renderRequest();
100
128
  }
101
129
  },
@@ -109,19 +137,24 @@ export function registerSessionContentCommands(registry: CommandRegistry): void
109
137
  handler(args, ctx) {
110
138
  const sessionManager = requireSessionManager(ctx);
111
139
  const rawName = args[0] || ctx.session.conversationManager.title || `session-${Date.now()}`;
112
- const exportData = ctx.session.conversationManager.toJSON() as { messages: object[] };
113
- const messages = exportData.messages ?? [];
114
- const meta = {
140
+ const messages = ctx.session.conversationManager.getMessageSnapshot();
141
+ const meta: SessionMeta = {
115
142
  title: ctx.session.conversationManager.title,
116
143
  model: ctx.session.runtime.model,
117
144
  provider: ctx.session.runtime.provider,
118
145
  timestamp: Date.now(),
146
+ titleSource: ctx.session.conversationManager.getTitleSource(),
119
147
  };
120
148
  try {
121
149
  const { filePath, sanitizedName } = sessionManager.save(rawName, messages, meta);
122
- ctx.print(`Session saved: ${rawName}${sanitizedName !== rawName ? ` (saved as "${sanitizedName}")` : ''}\n → ${filePath}`);
150
+ ctx.print([
151
+ 'Session saved',
152
+ ` name ${rawName}`,
153
+ ...(sanitizedName !== rawName ? [` saved as ${sanitizedName}`] : []),
154
+ ` path ${filePath}`,
155
+ ].join('\n'));
123
156
  } catch (e) {
124
- ctx.print(`Failed to save session: ${summarizeError(e)}`);
157
+ ctx.print(formatSessionFailure('save session', e));
125
158
  }
126
159
  },
127
160
  });
@@ -140,13 +173,18 @@ export function registerSessionContentCommands(registry: CommandRegistry): void
140
173
  try {
141
174
  const { meta, messages, agentRecords } = sessionManager.load(args[0]);
142
175
  ctx.session.conversationManager.resetAll();
143
- ctx.session.conversationManager.fromJSON({ messages: messages as never[] });
176
+ ctx.session.conversationManager.fromJSON({ messages: readConversationMessageSnapshots(messages) });
144
177
  if (meta.title) ctx.session.conversationManager.title = meta.title;
145
178
  ctx.session.conversationManager.rebuildHistory();
146
179
  ctx.renderRequest();
147
- ctx.print(`Session loaded: ${args[0]} (${messages.length} messages)${agentRecords.length > 0 ? ` [ignored ${agentRecords.length} host-owned local agent record${agentRecords.length !== 1 ? 's' : ''}]` : ''}`);
180
+ ctx.print([
181
+ 'Session loaded',
182
+ ` id ${args[0]}`,
183
+ ` messages ${messages.length}`,
184
+ ...(agentRecords.length > 0 ? [` ignored connected-host records ${agentRecords.length}`] : []),
185
+ ].join('\n'));
148
186
  } catch (e) {
149
- ctx.print(`Failed to load session: ${summarizeError(e)}`);
187
+ ctx.print(formatSessionFailure('load session', e));
150
188
  }
151
189
  },
152
190
  });
@@ -217,67 +255,31 @@ export function registerSessionContentCommands(registry: CommandRegistry): void
217
255
  const sessions = sessionManager.list();
218
256
  if (ctx.openSelection) {
219
257
  const items: SelectionItem[] = sessions.length === 0
220
- ? [{ id: '_empty', label: 'No saved sessions', detail: 'Use /save [name] to save' }]
258
+ ? [{ id: '_empty', label: 'No saved sessions', detail: 'Open Agent Workspace -> Conversation -> Save current session' }]
221
259
  : sessions.map(s => ({ id: s.name, label: s.name, detail: s.title || '(untitled)', actions: 'Enter to load' }));
222
260
  ctx.openSelection('Sessions', items, { allowSearch: true }, (result) => {
223
261
  if (!result) return;
224
262
  try {
225
263
  const { meta, messages } = sessionManager.load(result.item.id);
226
264
  ctx.session.conversationManager.resetAll();
227
- ctx.session.conversationManager.fromJSON({ messages: messages as never[] });
265
+ ctx.session.conversationManager.fromJSON({ messages: readConversationMessageSnapshots(messages) });
228
266
  if (meta.title) ctx.session.conversationManager.title = meta.title;
229
267
  ctx.session.conversationManager.rebuildHistory();
230
268
  ctx.renderRequest();
231
- ctx.print(`Session loaded: ${result.item.id} (${messages.length} messages)`);
269
+ ctx.print([
270
+ 'Session loaded',
271
+ ` id ${result.item.id}`,
272
+ ` messages ${messages.length}`,
273
+ ].join('\n'));
232
274
  } catch (e) {
233
- ctx.print(`Failed to load session: ${summarizeError(e)}`);
275
+ ctx.print(formatSessionFailure('load session', e));
234
276
  }
235
277
  });
236
278
  return;
237
279
  }
238
- const lines = ['Saved sessions:', ''];
280
+ const lines = ['Saved sessions', ''];
239
281
  for (const s of sessions) lines.push(` ${s.name.padEnd(30)} ${(s.title || '(untitled)').padEnd(24)} ${new Date(s.timestamp).toLocaleString()} (${s.messageCount} msgs)`);
240
282
  ctx.print(lines.join('\n'));
241
283
  },
242
284
  });
243
-
244
- registry.register({
245
- name: 'session-memory',
246
- aliases: ['smemory'],
247
- description: 'Manage conversation-pinned memories used during context compaction',
248
- usage: '[list|add <text>|remove <id> --yes]',
249
- argsHint: '[list|add|remove]',
250
- handler(args, ctx) {
251
- const sub = args[0] ?? 'list';
252
- if (sub === 'list' || args.length === 0) {
253
- const memories = requireSessionMemoryStore(ctx).list();
254
- ctx.print(memories.length === 0
255
- ? 'No conversation-pinned memories. Use !# prefix or /session-memory add <text> to create one.'
256
- : [`Conversation-Pinned Memories (${memories.length}):`, ...memories.map(m => ` [${m.id}] ${m.text}`)].join('\n'));
257
- } else if (sub === 'add') {
258
- const text = args.slice(1).join(' ').trim();
259
- if (!text) {
260
- ctx.print('Usage: /session-memory add <text>');
261
- return;
262
- }
263
- const id = requireSessionMemoryStore(ctx).add(text);
264
- ctx.print(`Memory added: [${id}] ${text}`);
265
- } else if (sub === 'remove') {
266
- const parsed = stripYesFlag(args);
267
- const id = parsed.rest[1];
268
- if (!id) {
269
- ctx.print('Usage: /session-memory remove <id> --yes');
270
- return;
271
- }
272
- if (!parsed.yes) {
273
- requireYesFlag(ctx, `remove conversation-pinned memory ${id}`, '/session-memory remove <id> --yes');
274
- return;
275
- }
276
- const store = requireSessionMemoryStore(ctx);
277
- ctx.print(store.remove(id) ? `Memory removed: [${id}]` : `Memory not found: ${id}`);
278
- } else {
279
- ctx.print('Usage: /session-memory [list|add <text>|remove <id> --yes]\n /session-memory — list conversation-pinned memories\n /session-memory list — list conversation-pinned memories\n /session-memory add <text> — add a memory without sending a message\n /session-memory remove <id> --yes — remove a specific memory');
280
- }
281
- },
282
- });
283
285
  }