@pellux/goodvibes-agent 0.1.117 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. package/CHANGELOG.md +20 -4
  2. package/README.md +35 -14
  3. package/bin/goodvibes-agent.ts +16 -2
  4. package/dist/package/main.js +176093 -171065
  5. package/docs/README.md +11 -5
  6. package/docs/channels-remote-and-api.md +50 -0
  7. package/docs/connected-host.md +3 -3
  8. package/docs/getting-started.md +29 -15
  9. package/docs/knowledge-artifacts-and-multimodal.md +91 -0
  10. package/docs/project-planning.md +79 -0
  11. package/docs/providers-and-routing.md +46 -0
  12. package/docs/release-and-publishing.md +44 -9
  13. package/docs/tools-and-commands.md +123 -0
  14. package/docs/voice-and-live-tts.md +51 -0
  15. package/package.json +2 -5
  16. package/src/agent/channel-delivery.ts +201 -0
  17. package/src/agent/media-generation.ts +159 -0
  18. package/src/agent/memory-prompt.ts +0 -1
  19. package/src/agent/note-registry.ts +329 -0
  20. package/src/agent/operator-actions.ts +343 -0
  21. package/src/agent/persona-registry.ts +15 -14
  22. package/src/agent/record-labels.ts +107 -0
  23. package/src/agent/reminder-schedule-format.ts +33 -24
  24. package/src/agent/reminder-schedule.ts +26 -25
  25. package/src/agent/routine-registry.ts +13 -12
  26. package/src/agent/routine-schedule-args.ts +2 -1
  27. package/src/agent/routine-schedule-format.ts +77 -53
  28. package/src/agent/routine-schedule-promotion.ts +34 -32
  29. package/src/agent/routine-schedule-receipts.ts +28 -26
  30. package/src/agent/runtime-profile-starters.ts +2 -2
  31. package/src/agent/runtime-profile.ts +18 -17
  32. package/src/agent/skill-registry.ts +25 -24
  33. package/src/cli/agent-knowledge-args.ts +5 -1
  34. package/src/cli/agent-knowledge-command.ts +39 -33
  35. package/src/cli/agent-knowledge-format.ts +80 -67
  36. package/src/cli/agent-knowledge-methods.ts +1 -1
  37. package/src/cli/agent-knowledge-runtime.ts +32 -26
  38. package/src/cli/bundle-command.ts +13 -8
  39. package/src/cli/config-overrides.ts +37 -36
  40. package/src/cli/external-runtime.ts +10 -4
  41. package/src/cli/help.ts +29 -11
  42. package/src/cli/local-library-command.ts +159 -73
  43. package/src/cli/management-commands.ts +98 -62
  44. package/src/cli/management.ts +52 -26
  45. package/src/cli/memory-command.ts +66 -32
  46. package/src/cli/parser.ts +37 -24
  47. package/src/cli/profiles-command.ts +52 -35
  48. package/src/cli/provider-auth-routes.ts +2 -1
  49. package/src/cli/routines-command.ts +59 -39
  50. package/src/cli/service-posture.ts +6 -6
  51. package/src/cli/status.ts +46 -121
  52. package/src/core/conversation-message-snapshot.ts +131 -0
  53. package/src/input/agent-workspace-activation.ts +33 -7
  54. package/src/input/agent-workspace-basic-command-editor-submission.ts +7 -3
  55. package/src/input/agent-workspace-basic-command-editors.ts +30 -10
  56. package/src/input/agent-workspace-categories.ts +276 -64
  57. package/src/input/agent-workspace-channel-command-editor-submission.ts +34 -0
  58. package/src/input/agent-workspace-channel-command-editors.ts +23 -5
  59. package/src/input/agent-workspace-channels.ts +35 -2
  60. package/src/input/agent-workspace-command-editor.ts +18 -2
  61. package/src/input/agent-workspace-config-reader.ts +16 -0
  62. package/src/input/agent-workspace-delegation-editor-submission.ts +1 -1
  63. package/src/input/agent-workspace-editors.ts +140 -2
  64. package/src/input/agent-workspace-knowledge-query-editor.ts +1 -1
  65. package/src/input/agent-workspace-knowledge-url-editor.ts +4 -11
  66. package/src/input/agent-workspace-learned-behavior.ts +2 -2
  67. package/src/input/agent-workspace-library-command-editor-submission.ts +1 -1
  68. package/src/input/agent-workspace-library-command-editors.ts +2 -2
  69. package/src/input/agent-workspace-local-operations.ts +218 -0
  70. package/src/input/agent-workspace-local-selection.ts +75 -0
  71. package/src/input/agent-workspace-media-command-editor-submission.ts +62 -0
  72. package/src/input/agent-workspace-media-command-editors.ts +27 -0
  73. package/src/input/agent-workspace-memory-command-editors.ts +1 -1
  74. package/src/input/agent-workspace-memory-editor.ts +2 -2
  75. package/src/input/agent-workspace-navigation.ts +38 -2
  76. package/src/input/agent-workspace-notify-editor-submission.ts +16 -2
  77. package/src/input/agent-workspace-operations-command-editor-submission.ts +63 -1
  78. package/src/input/agent-workspace-operations-command-editors.ts +80 -3
  79. package/src/input/agent-workspace-panel-route.ts +43 -0
  80. package/src/input/agent-workspace-reminder-schedule-editor.ts +1 -1
  81. package/src/input/agent-workspace-routine-schedule-editor.ts +2 -2
  82. package/src/input/agent-workspace-search.ts +169 -0
  83. package/src/input/agent-workspace-setup.ts +22 -11
  84. package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +6 -6
  85. package/src/input/agent-workspace-skill-bundle-command-editors.ts +10 -10
  86. package/src/input/agent-workspace-snapshot.ts +112 -13
  87. package/src/input/agent-workspace-task-command-editors.ts +4 -4
  88. package/src/input/agent-workspace-token.ts +18 -2
  89. package/src/input/agent-workspace-types.ts +92 -4
  90. package/src/input/agent-workspace-voice-media.ts +3 -6
  91. package/src/input/agent-workspace-web-research-editor.ts +104 -0
  92. package/src/input/agent-workspace.ts +136 -208
  93. package/src/input/command-registry.ts +4 -1
  94. package/src/input/commands/agent-local-library-args.ts +52 -0
  95. package/src/input/commands/agent-runtime-profile-runtime.ts +45 -41
  96. package/src/input/commands/agent-skills-runtime.ts +87 -99
  97. package/src/input/commands/agent-workspace-runtime.ts +23 -7
  98. package/src/input/commands/brief-runtime.ts +25 -24
  99. package/src/input/commands/channels-runtime.ts +145 -31
  100. package/src/input/commands/delegation-runtime.ts +29 -23
  101. package/src/input/commands/experience-runtime.ts +6 -4
  102. package/src/input/commands/guidance-runtime.ts +4 -4
  103. package/src/input/commands/health-runtime.ts +140 -115
  104. package/src/input/commands/knowledge.ts +57 -56
  105. package/src/input/commands/local-provider-runtime.ts +36 -18
  106. package/src/input/commands/local-runtime.ts +138 -16
  107. package/src/input/commands/local-setup-review.ts +7 -7
  108. package/src/input/commands/mcp-runtime.ts +118 -50
  109. package/src/input/commands/notify-runtime.ts +38 -9
  110. package/src/input/commands/operator-actions-runtime.ts +138 -0
  111. package/src/input/commands/operator-runtime.ts +6 -17
  112. package/src/input/commands/personas-runtime.ts +49 -59
  113. package/src/input/commands/planning-runtime.ts +1 -1
  114. package/src/input/commands/platform-access-runtime.ts +29 -29
  115. package/src/input/commands/provider-accounts-runtime.ts +60 -39
  116. package/src/input/commands/qrcode-runtime.ts +45 -6
  117. package/src/input/commands/recall-bundle.ts +11 -11
  118. package/src/input/commands/recall-capture.ts +13 -11
  119. package/src/input/commands/recall-query.ts +29 -21
  120. package/src/input/commands/recall-review.ts +2 -1
  121. package/src/input/commands/routines-runtime.ts +63 -72
  122. package/src/input/commands/schedule-runtime.ts +33 -20
  123. package/src/input/commands/security-runtime.ts +4 -4
  124. package/src/input/commands/session-content.ts +80 -78
  125. package/src/input/commands/session-workflow.ts +132 -93
  126. package/src/input/commands/session.ts +3 -174
  127. package/src/input/commands/shell-core.ts +32 -17
  128. package/src/input/commands/subscription-runtime.ts +53 -179
  129. package/src/input/commands/tasks-runtime.ts +20 -20
  130. package/src/input/commands/work-plan-runtime.ts +33 -8
  131. package/src/input/commands.ts +2 -2
  132. package/src/input/feed-context-factory.ts +2 -1
  133. package/src/input/file-picker.ts +3 -2
  134. package/src/input/handler-command-route.ts +4 -7
  135. package/src/input/handler-content-actions.ts +89 -1
  136. package/src/input/handler-feed-routes.ts +19 -12
  137. package/src/input/handler-feed.ts +6 -3
  138. package/src/input/handler-interactions.ts +7 -5
  139. package/src/input/handler-modal-stack.ts +3 -3
  140. package/src/input/handler-onboarding.ts +24 -80
  141. package/src/input/handler-shortcuts.ts +15 -4
  142. package/src/input/handler.ts +47 -17
  143. package/src/input/input-history.ts +5 -6
  144. package/src/input/keybindings.ts +22 -11
  145. package/src/input/onboarding/onboarding-wizard-apply.ts +13 -0
  146. package/src/input/onboarding/onboarding-wizard-constants.ts +1 -0
  147. package/src/input/onboarding/onboarding-wizard-operator-steps.ts +147 -57
  148. package/src/input/onboarding/onboarding-wizard-steps.ts +46 -29
  149. package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
  150. package/src/input/onboarding/onboarding-wizard.ts +68 -0
  151. package/src/input/profile-picker-modal.ts +31 -12
  152. package/src/input/session-picker-modal.ts +22 -4
  153. package/src/input/settings-modal-behavior.ts +0 -3
  154. package/src/input/settings-modal-subscriptions.ts +3 -3
  155. package/src/input/settings-modal-types.ts +2 -13
  156. package/src/input/settings-modal.ts +6 -52
  157. package/src/input/submission-intent.ts +0 -1
  158. package/src/input/submission-router.ts +3 -3
  159. package/src/main.ts +18 -8
  160. package/src/panels/approval-panel.ts +8 -8
  161. package/src/panels/automation-control-panel.ts +2 -2
  162. package/src/panels/base-panel.ts +1 -1
  163. package/src/panels/builtin/agent.ts +1 -1
  164. package/src/panels/builtin/operations.ts +1 -10
  165. package/src/panels/builtin/session.ts +9 -9
  166. package/src/panels/builtin/shared.ts +2 -2
  167. package/src/panels/cost-tracker-panel.ts +1 -1
  168. package/src/panels/docs-panel.ts +5 -3
  169. package/src/panels/index.ts +0 -1
  170. package/src/panels/knowledge-panel.ts +6 -5
  171. package/src/panels/memory-panel.ts +7 -6
  172. package/src/panels/panel-list-panel.ts +36 -80
  173. package/src/panels/project-planning-panel.ts +18 -11
  174. package/src/panels/provider-account-snapshot.ts +51 -25
  175. package/src/panels/provider-accounts-panel.ts +33 -18
  176. package/src/panels/provider-health-domains.ts +45 -4
  177. package/src/panels/provider-health-panel.ts +5 -4
  178. package/src/panels/qr-panel.ts +1 -1
  179. package/src/panels/schedule-panel.ts +9 -17
  180. package/src/panels/security-panel.ts +8 -8
  181. package/src/panels/session-browser-panel.ts +10 -10
  182. package/src/panels/subscription-panel.ts +3 -3
  183. package/src/panels/system-messages-panel.ts +1 -1
  184. package/src/panels/tasks-panel.ts +20 -13
  185. package/src/panels/tool-inspector-panel.ts +19 -12
  186. package/src/panels/work-plan-panel.ts +5 -5
  187. package/src/planning/project-planning-coordinator.ts +1 -1
  188. package/src/provider-auth-route-display.ts +9 -0
  189. package/src/renderer/agent-workspace-style.ts +34 -0
  190. package/src/renderer/agent-workspace.ts +228 -52
  191. package/src/renderer/autocomplete-overlay.ts +25 -6
  192. package/src/renderer/bookmark-modal.ts +19 -4
  193. package/src/renderer/buffer.ts +4 -2
  194. package/src/renderer/context-inspector.ts +50 -13
  195. package/src/renderer/diff.ts +1 -1
  196. package/src/renderer/file-picker-overlay.ts +19 -6
  197. package/src/renderer/help-overlay.ts +106 -33
  198. package/src/renderer/history-search-overlay.ts +19 -4
  199. package/src/renderer/live-tail-modal.ts +27 -5
  200. package/src/renderer/mcp-workspace.ts +164 -50
  201. package/src/renderer/model-picker-overlay.ts +58 -2
  202. package/src/renderer/model-workspace.ts +104 -20
  203. package/src/renderer/process-modal.ts +27 -6
  204. package/src/renderer/profile-picker-modal.ts +20 -4
  205. package/src/renderer/search-overlay.ts +25 -5
  206. package/src/renderer/selection-modal-overlay.ts +46 -14
  207. package/src/renderer/session-picker-modal.ts +18 -1
  208. package/src/renderer/settings-modal-helpers.ts +2 -40
  209. package/src/renderer/settings-modal.ts +83 -50
  210. package/src/renderer/tool-call.ts +20 -11
  211. package/src/runtime/agent-runtime-events.ts +6 -6
  212. package/src/runtime/bootstrap-command-context.ts +7 -1
  213. package/src/runtime/bootstrap-command-parts.ts +9 -7
  214. package/src/runtime/bootstrap-core.ts +19 -1
  215. package/src/runtime/bootstrap-hook-bridge.ts +7 -18
  216. package/src/runtime/bootstrap-shell.ts +4 -4
  217. package/src/runtime/bootstrap.ts +31 -22
  218. package/src/runtime/connected-host-auth.ts +7 -2
  219. package/src/runtime/diagnostics/panels/index.ts +2 -2
  220. package/src/runtime/diagnostics/panels/policy.ts +7 -7
  221. package/src/runtime/index.ts +2 -1
  222. package/src/runtime/onboarding/apply-file-helpers.ts +66 -0
  223. package/src/runtime/onboarding/apply.ts +80 -79
  224. package/src/runtime/onboarding/derivation.ts +5 -8
  225. package/src/runtime/onboarding/snapshot.ts +0 -15
  226. package/src/runtime/onboarding/types.ts +8 -19
  227. package/src/runtime/onboarding/verify.ts +32 -10
  228. package/src/runtime/operator-token-cleanup.ts +1 -1
  229. package/src/runtime/services.ts +91 -22
  230. package/src/runtime/store/selectors/index.ts +3 -3
  231. package/src/runtime/store/state.ts +1 -4
  232. package/src/runtime/surface-feature-flags.ts +41 -6
  233. package/src/runtime/ui-read-models.ts +3 -4
  234. package/src/runtime/ui-services.ts +6 -6
  235. package/src/shell/blocking-input.ts +2 -1
  236. package/src/shell/ui-openers.ts +3 -13
  237. package/src/tools/agent-analysis-registry-policy.ts +0 -1
  238. package/src/tools/agent-channel-send-tool.ts +133 -0
  239. package/src/tools/agent-context-policy.ts +1 -1
  240. package/src/tools/agent-knowledge-ingest-tool.ts +405 -0
  241. package/src/tools/agent-knowledge-tool.ts +170 -0
  242. package/src/tools/agent-local-registry-tool.ts +269 -69
  243. package/src/tools/agent-media-generate-tool.ts +133 -0
  244. package/src/tools/agent-notify-tool.ts +143 -0
  245. package/src/tools/agent-operator-action-tool.ts +137 -0
  246. package/src/tools/agent-operator-briefing-tool.ts +217 -0
  247. package/src/tools/agent-reminder-schedule-tool.ts +237 -0
  248. package/src/tools/agent-tool-policy-guard.ts +8 -8
  249. package/src/tools/agent-work-plan-tool.ts +256 -0
  250. package/src/version.ts +1 -1
  251. package/src/input/commands/policy-dispatch.ts +0 -339
  252. package/src/input/commands/policy.ts +0 -13
  253. package/src/panels/panel-picker.ts +0 -105
  254. package/src/panels/policy-panel.ts +0 -308
  255. package/src/renderer/panel-picker-overlay.ts +0 -202
@@ -2,6 +2,7 @@ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from '
2
2
  import { dirname } from 'node:path';
3
3
  import type { ShellPathService } from '@/runtime/index.ts';
4
4
  import { GOODVIBES_AGENT_SURFACE_ROOT } from '../config/surface.ts';
5
+ import { formatAgentRecordReviewState } from './record-labels.ts';
5
6
 
6
7
  export type AgentPersonaSource = 'user' | 'agent' | 'imported' | 'system';
7
8
  export type AgentPersonaReviewState = 'fresh' | 'reviewed' | 'stale';
@@ -106,9 +107,9 @@ function containsSecretLikeText(text: string): boolean {
106
107
  return SECRET_PATTERNS.some((pattern) => pattern.test(text));
107
108
  }
108
109
 
109
- export function assertNoSecretLikeText(fields: readonly string[]): void {
110
+ export function assertNoSecretLikeText(fields: readonly string[], ownerLabel = 'Agent local records'): void {
110
111
  if (fields.some((field) => containsSecretLikeText(field))) {
111
- throw new Error('Personas cannot store secret-looking values. Store a secret reference or remove the sensitive text.');
112
+ throw new Error(`${ownerLabel} cannot store secret-looking values. Store a secret reference or remove the sensitive text.`);
112
113
  }
113
114
  }
114
115
 
@@ -213,9 +214,9 @@ export class AgentPersonaRegistry {
213
214
  const description = input.description.trim();
214
215
  const body = input.body.trim();
215
216
  this.validateRequired(name, description, body);
216
- assertNoSecretLikeText([name, description, body, ...(input.tags ?? []), ...(input.triggers ?? [])]);
217
+ assertNoSecretLikeText([name, description, body, ...(input.tags ?? []), ...(input.triggers ?? [])], 'Personas');
217
218
  const duplicate = store.personas.find((persona) => persona.name.toLowerCase() === name.toLowerCase());
218
- if (duplicate) throw new Error(`Persona already exists: ${duplicate.id}`);
219
+ if (duplicate) throw new Error(`Persona already exists ${duplicate.id}`);
219
220
  const timestamp = nowIso();
220
221
  const persona: AgentPersonaRecord = {
221
222
  id: this.nextId(name, store.personas),
@@ -237,14 +238,14 @@ export class AgentPersonaRegistry {
237
238
  public update(idOrName: string, input: AgentPersonaUpdateInput): AgentPersonaRecord {
238
239
  const store = this.readStore();
239
240
  const existing = this.findInStore(store, idOrName);
240
- if (!existing) throw new Error(`Unknown persona: ${idOrName}`);
241
+ if (!existing) throw new Error(`Unknown persona ${idOrName}`);
241
242
  const name = input.name === undefined ? existing.name : normalizeName(input.name);
242
243
  const description = input.description === undefined ? existing.description : input.description.trim();
243
244
  const body = input.body === undefined ? existing.body : input.body.trim();
244
245
  this.validateRequired(name, description, body);
245
- assertNoSecretLikeText([name, description, body, ...(input.tags ?? []), ...(input.triggers ?? [])]);
246
+ assertNoSecretLikeText([name, description, body, ...(input.tags ?? []), ...(input.triggers ?? [])], 'Personas');
246
247
  const duplicate = store.personas.find((persona) => persona.id !== existing.id && persona.name.toLowerCase() === name.toLowerCase());
247
- if (duplicate) throw new Error(`Persona already exists: ${duplicate.id}`);
248
+ if (duplicate) throw new Error(`Persona already exists ${duplicate.id}`);
248
249
  const updated: AgentPersonaRecord = {
249
250
  ...existing,
250
251
  name,
@@ -268,7 +269,7 @@ export class AgentPersonaRegistry {
268
269
  public setActive(idOrName: string): AgentPersonaRecord {
269
270
  const store = this.readStore();
270
271
  const persona = this.findInStore(store, idOrName);
271
- if (!persona) throw new Error(`Unknown persona: ${idOrName}`);
272
+ if (!persona) throw new Error(`Unknown persona ${idOrName}`);
272
273
  this.writeStore({ ...store, activePersonaId: persona.id });
273
274
  return persona;
274
275
  }
@@ -281,7 +282,7 @@ export class AgentPersonaRegistry {
281
282
  public markReviewed(idOrName: string): AgentPersonaRecord {
282
283
  const store = this.readStore();
283
284
  const existing = this.findInStore(store, idOrName);
284
- if (!existing) throw new Error(`Unknown persona: ${idOrName}`);
285
+ if (!existing) throw new Error(`Unknown persona ${idOrName}`);
285
286
  const updated: AgentPersonaRecord = {
286
287
  ...existing,
287
288
  reviewState: 'reviewed',
@@ -299,7 +300,7 @@ export class AgentPersonaRegistry {
299
300
  public markStale(idOrName: string, reason: string): AgentPersonaRecord {
300
301
  const store = this.readStore();
301
302
  const existing = this.findInStore(store, idOrName);
302
- if (!existing) throw new Error(`Unknown persona: ${idOrName}`);
303
+ if (!existing) throw new Error(`Unknown persona ${idOrName}`);
303
304
  const updated: AgentPersonaRecord = {
304
305
  ...existing,
305
306
  reviewState: 'stale',
@@ -316,7 +317,7 @@ export class AgentPersonaRegistry {
316
317
  public deletePersona(idOrName: string): AgentPersonaRecord {
317
318
  const store = this.readStore();
318
319
  const existing = this.findInStore(store, idOrName);
319
- if (!existing) throw new Error(`Unknown persona: ${idOrName}`);
320
+ if (!existing) throw new Error(`Unknown persona ${idOrName}`);
320
321
  this.writeStore({
321
322
  ...store,
322
323
  activePersonaId: store.activePersonaId === existing.id ? null : store.activePersonaId,
@@ -353,7 +354,7 @@ export class AgentPersonaRegistry {
353
354
  try {
354
355
  return parseStore(readFileSync(this.storePath, 'utf-8'));
355
356
  } catch (error) {
356
- throw new Error(`Could not read Agent persona store: ${error instanceof Error ? error.message : String(error)}`);
357
+ throw new Error(`Could not read Agent persona store ${error instanceof Error ? error.message : String(error)}`);
357
358
  }
358
359
  }
359
360
 
@@ -372,10 +373,10 @@ export function buildActivePersonaPrompt(shellPaths: ShellPathService): string |
372
373
  '## Active GoodVibes Agent Persona',
373
374
  `Name: ${active.name}`,
374
375
  `Description: ${active.description}`,
375
- `Review state: ${active.reviewState}`,
376
+ `Review: ${formatAgentRecordReviewState(active.reviewState)}`,
376
377
  '',
377
378
  active.body,
378
379
  '',
379
- 'Apply this persona inside the same serial assistant conversation. Do not launch local workers because a persona is active.',
380
+ 'Apply this persona inside the same serial assistant conversation. Do not spawn separate Agent jobs because a persona is active.',
380
381
  ].join('\n');
381
382
  }
@@ -0,0 +1,107 @@
1
+ const SOURCE_LABELS = {
2
+ user: 'User',
3
+ agent: 'Agent',
4
+ imported: 'Imported',
5
+ system: 'Built-in',
6
+ builtin: 'Built-in',
7
+ local: 'Local',
8
+ 'agent-memory': 'Agent memory',
9
+ } as const satisfies Readonly<Record<string, string>>;
10
+
11
+ const USER_PROVENANCE_LABELS = new Set<string>([
12
+ 'Workspace',
13
+ 'Workspace capture',
14
+ 'Command',
15
+ 'Manual',
16
+ 'Main conversation',
17
+ ]);
18
+
19
+ const REVIEW_STATE_LABELS = {
20
+ fresh: 'Needs review',
21
+ reviewed: 'Reviewed',
22
+ stale: 'Needs refresh',
23
+ contradicted: 'Contradicted',
24
+ } as const satisfies Readonly<Record<string, string>>;
25
+
26
+ type SourceLabelKey = keyof typeof SOURCE_LABELS;
27
+ type ReviewStateLabelKey = keyof typeof REVIEW_STATE_LABELS;
28
+
29
+ export interface AgentRecordReference {
30
+ readonly kind: string;
31
+ readonly ref: string;
32
+ readonly label?: string;
33
+ }
34
+
35
+ function titleCaseWords(value: string): string {
36
+ return value
37
+ .trim()
38
+ .replace(/[_-]+/g, ' ')
39
+ .replace(/\s+/g, ' ')
40
+ .replace(/\b\w/g, (match) => match.toUpperCase());
41
+ }
42
+
43
+ export function formatAgentRecordSource(source: string | undefined): string {
44
+ const normalized = (source ?? '').trim().toLowerCase();
45
+ if (!normalized) return 'Unknown';
46
+ if (normalized in SOURCE_LABELS) return SOURCE_LABELS[normalized as SourceLabelKey];
47
+ return titleCaseWords(source ?? '');
48
+ }
49
+
50
+ export function formatAgentRecordReviewState(reviewState: string | undefined): string {
51
+ const normalized = (reviewState ?? '').trim().toLowerCase();
52
+ if (!normalized) return REVIEW_STATE_LABELS.fresh;
53
+ if (normalized in REVIEW_STATE_LABELS) return REVIEW_STATE_LABELS[normalized as ReviewStateLabelKey];
54
+ return titleCaseWords(reviewState ?? '');
55
+ }
56
+
57
+ export function formatAgentRecordProvenance(provenance: string | undefined): string {
58
+ const raw = (provenance ?? '').trim();
59
+ const normalized = raw.toLowerCase();
60
+ if (!raw) return '';
61
+ if (normalized === 'agent-workspace') return 'Workspace';
62
+ if (normalized === 'agent-workspace-learned-behavior') return 'Workspace capture';
63
+ if (normalized === 'workspace capture') return 'Workspace capture';
64
+ if (normalized === 'slash-command') return 'Command';
65
+ if (normalized === 'agent-memory') return 'Agent memory';
66
+ if (normalized === 'manual') return 'Manual';
67
+ if (normalized === 'main-conversation') return 'Main conversation';
68
+ if (normalized === 'plan-command') return 'Planning command';
69
+ if (normalized === 'agent-media-generation') return 'Media generation';
70
+ if (normalized === 'agent-channel-send') return 'Channel delivery';
71
+ if (normalized === 'project-memory') return 'Project memory';
72
+
73
+ const discovered = raw.match(/^discovered:([^:]+):(.+)$/i);
74
+ if (discovered) {
75
+ return `Imported file (${discovered[1]}): ${discovered[2]}`;
76
+ }
77
+
78
+ const starter = raw.match(/^goodvibes-agent starter:(.+)$/i);
79
+ if (starter) {
80
+ return `Starter profile: ${starter[1]}`;
81
+ }
82
+
83
+ return titleCaseWords(raw);
84
+ }
85
+
86
+ export function formatAgentRecordOrigin(source: string | undefined, provenance: string | undefined): string {
87
+ const sourceLabel = formatAgentRecordSource(source);
88
+ const provenanceLabel = formatAgentRecordProvenance(provenance);
89
+ if (!provenanceLabel || provenanceLabel === sourceLabel) return sourceLabel;
90
+ if (sourceLabel === 'User' && USER_PROVENANCE_LABELS.has(provenanceLabel)) {
91
+ return provenanceLabel;
92
+ }
93
+ if (sourceLabel === 'Imported' && provenanceLabel.startsWith('Imported file')) {
94
+ return provenanceLabel;
95
+ }
96
+ return `${sourceLabel} (${provenanceLabel})`;
97
+ }
98
+
99
+ export function formatAgentRecordReference(reference: AgentRecordReference): string {
100
+ const kind = titleCaseWords(reference.kind);
101
+ const label = reference.label?.trim();
102
+ return `${kind}: ${reference.ref}${label ? ` (${label})` : ''}`;
103
+ }
104
+
105
+ export function formatAgentRecordReferences(references: readonly AgentRecordReference[]): string {
106
+ return references.map(formatAgentRecordReference).join(', ');
107
+ }
@@ -22,20 +22,29 @@ function scheduleValue(preview: ReminderSchedulePreview): string {
22
22
  return String(preview.payload.at);
23
23
  }
24
24
 
25
+ function formatReminderScheduleFailureKind(kind: string): string {
26
+ if (kind === 'auth_required') return 'authorization required';
27
+ if (kind === 'connected_host_unavailable') return 'connected host unavailable';
28
+ if (kind === 'connected_host_route_unavailable') return 'connected host route unavailable';
29
+ if (kind === 'connected_host_error') return 'connected host error';
30
+ if (kind === 'version_mismatch') return 'version mismatch';
31
+ return kind.replace(/[_-]+/g, ' ');
32
+ }
33
+
25
34
  export function formatReminderSchedulePreview(preview: ReminderSchedulePreview): string {
26
35
  const delivery = preview.payload.delivery;
27
36
  const deliveryTargetCount = delivery?.targets.length ?? 0;
28
37
  return [
29
38
  'GoodVibes schedule preview for Agent reminder',
30
- ` route: ${preview.method} ${preview.route}`,
31
- ` name: ${String(preview.payload.name ?? '(service default)')}`,
32
- ` reminder: ${preview.message}`,
33
- ` schedule: ${preview.payload.kind} ${scheduleValue(preview)}`,
34
- ` enabled: ${preview.payload.enabled === false ? 'no' : 'yes'}`,
35
- ` delivery: ${delivery?.mode ?? 'none'}${deliveryTargetCount > 0 ? ` (${deliveryTargetCount} target${deliveryTargetCount === 1 ? '' : 's'})` : ''}`,
36
- ' target: connected GoodVibes host/main conversation route',
37
- ' policy: reminder delivery only; isolated Agent Knowledge only; no default wiki/non-Agent fallback',
38
- ' next: rerun with --yes to create this connected reminder schedule',
39
+ ` route ${preview.method} ${preview.route}`,
40
+ ` name ${String(preview.payload.name ?? '(service default)')}`,
41
+ ` reminder ${preview.message}`,
42
+ ` schedule ${preview.payload.kind} ${scheduleValue(preview)}`,
43
+ ` enabled ${preview.payload.enabled === false ? 'no' : 'yes'}`,
44
+ ` delivery ${delivery?.mode ?? 'none'}${deliveryTargetCount > 0 ? ` (${deliveryTargetCount} target${deliveryTargetCount === 1 ? '' : 's'})` : ''}`,
45
+ ' target connected GoodVibes host/main conversation route',
46
+ ' policy reminder delivery only; isolated Agent Knowledge only; no default knowledge/non-Agent fallback',
47
+ ' next rerun with --yes to create this connected reminder schedule',
39
48
  ].join('\n');
40
49
  }
41
50
 
@@ -45,31 +54,31 @@ export function formatReminderScheduleSuccess(result: ReminderScheduleSuccess):
45
54
  const status = readString(record, 'status') ?? (record.enabled === false ? 'paused' : 'enabled');
46
55
  return [
47
56
  'Created GoodVibes schedule for Agent reminder',
48
- ` schedule: ${id}`,
49
- ` status: ${status}`,
50
- ` route: ${result.kind} ${result.route}`,
51
- ` reminder: ${result.message}`,
52
- ' next: inspect with /schedule list',
57
+ ` schedule ${id}`,
58
+ ` status ${status}`,
59
+ ` route ${result.kind} ${result.route}`,
60
+ ` reminder ${result.message}`,
61
+ ' next inspect with /schedule list',
53
62
  ].join('\n');
54
63
  }
55
64
 
56
65
  export function formatReminderScheduleFailure(failure: ReminderScheduleFailure): string {
57
66
  return [
58
- `GoodVibes reminder schedule error: ${failure.kind}`,
67
+ `GoodVibes reminder schedule error ${formatReminderScheduleFailureKind(failure.kind)}`,
59
68
  ` ${failure.error}`,
60
- failure.baseUrl ? ` host: ${failure.baseUrl}` : null,
61
- ` route: ${REMINDER_SCHEDULE_METHOD} ${failure.route}`,
62
- failure.kind === 'version_mismatch' && failure.daemonVersion && failure.expectedSdkVersion
63
- ? ` versions: host=${failure.daemonVersion} expected=${failure.expectedSdkVersion}`
69
+ failure.baseUrl ? ` connected host ${failure.baseUrl}` : null,
70
+ ` route ${REMINDER_SCHEDULE_METHOD} ${failure.route}`,
71
+ failure.kind === 'version_mismatch' && failure.connectedHostVersion && failure.expectedSdkVersion
72
+ ? ` versions connected host ${failure.connectedHostVersion}; expected ${failure.expectedSdkVersion}`
64
73
  : null,
65
74
  failure.kind === 'auth_required'
66
- ? ' next: pair/authenticate with the connected GoodVibes host, then retry with --yes.'
75
+ ? ' next pair/authenticate with the connected GoodVibes host, then retry with --yes.'
67
76
  : null,
68
- failure.kind === 'daemon_unavailable'
69
- ? ' next: make the connected GoodVibes host available outside Agent, then retry.'
77
+ failure.kind === 'connected_host_unavailable'
78
+ ? ' next make the connected GoodVibes host available outside Agent, then retry.'
70
79
  : null,
71
- failure.kind === 'version_mismatch' || failure.kind === 'daemon_route_unavailable'
72
- ? ' next: update the connected GoodVibes host so public schedules.create is available.'
80
+ failure.kind === 'version_mismatch' || failure.kind === 'connected_host_route_unavailable'
81
+ ? ' next update the connected GoodVibes host so public schedules.create is available.'
73
82
  : null,
74
83
  ].filter((line): line is string => Boolean(line)).join('\n');
75
84
  }
@@ -4,11 +4,11 @@ import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
4
4
  import { getModelIdFromProviderModel, getProviderIdFromModel } from '../config/provider-model.ts';
5
5
  import { SDK_VERSION } from '../version.ts';
6
6
  import {
7
- resolveAgentDaemonConnection,
7
+ resolveAgentConnectedHostConnection,
8
8
  ROUTINE_SCHEDULE_METHOD,
9
9
  ROUTINE_SCHEDULE_ROUTE,
10
- type AgentDaemonConfigReader,
11
- type AgentDaemonConnection,
10
+ type AgentConnectedHostConfigReader,
11
+ type AgentConnectedHostConnection,
12
12
  type RoutineScheduleDeliveryKind,
13
13
  type RoutineScheduleDeliverySurfaceKind,
14
14
  type RoutineScheduleDeliveryTargetSpec,
@@ -58,14 +58,14 @@ export interface ReminderScheduleFailure {
58
58
  readonly kind:
59
59
  | 'confirmation_required'
60
60
  | 'auth_required'
61
- | 'daemon_unavailable'
61
+ | 'connected_host_unavailable'
62
62
  | 'version_mismatch'
63
- | 'daemon_route_unavailable'
64
- | 'daemon_error';
63
+ | 'connected_host_route_unavailable'
64
+ | 'connected_host_error';
65
65
  readonly error: string;
66
66
  readonly route: typeof REMINDER_SCHEDULE_ROUTE;
67
67
  readonly baseUrl?: string;
68
- readonly daemonVersion?: string;
68
+ readonly connectedHostVersion?: string;
69
69
  readonly expectedSdkVersion?: string;
70
70
  }
71
71
 
@@ -82,6 +82,7 @@ const DELIVERY_SURFACE_KINDS: readonly RoutineScheduleDeliverySurfaceKind[] = [
82
82
  'google-chat',
83
83
  'signal',
84
84
  'whatsapp',
85
+ 'telephony',
85
86
  'imessage',
86
87
  'msteams',
87
88
  'bluebubbles',
@@ -186,7 +187,7 @@ function deliveryModeFromTargets(targets: readonly RoutineScheduleDeliveryTarget
186
187
  function toDeliveryTargetInput(target: RoutineScheduleDeliveryTargetSpec): ScheduleDeliveryTargetInput {
187
188
  return {
188
189
  kind: target.kind,
189
- surfaceKind: target.surfaceKind,
190
+ surfaceKind: target.surfaceKind as ScheduleDeliveryTargetInput['surfaceKind'],
190
191
  address: target.address,
191
192
  routeId: target.routeId,
192
193
  label: target.label,
@@ -312,7 +313,7 @@ export function parseReminderScheduleArgs(args: readonly string[]): ParsedRemind
312
313
  continue;
313
314
  }
314
315
  if (raw.startsWith('--')) {
315
- errors.push(`Unknown option: ${raw}`);
316
+ errors.push(`Unknown option ${raw}`);
316
317
  continue;
317
318
  }
318
319
  positional.push(raw);
@@ -335,9 +336,9 @@ export function buildReminderSchedulePrompt(message: string): string {
335
336
  '',
336
337
  'Operator policy:',
337
338
  '- Treat this as a reminder delivery, not an autonomous hidden workflow.',
338
- '- Use isolated Agent Knowledge routes only when lookup is needed; never use default Knowledge/Wiki or non-Agent knowledge spaces as fallback.',
339
+ '- Use isolated Agent Knowledge routes only when lookup is needed; never use default knowledge or non-Agent knowledge spaces as fallback.',
339
340
  '- Do not perform destructive, costly, externally visible, or secret-handling actions from this reminder without explicit approval.',
340
- '- Do not request WRFC from a reminder. If build/fix/review work is needed, ask the user to delegate it explicitly to GoodVibes TUI.',
341
+ '- Do not request GoodVibes TUI delegation from a reminder. If build/fix/review work is needed, ask the user to delegate it explicitly to GoodVibes TUI.',
341
342
  '- Keep the reminder concise and state any next action the user can take.',
342
343
  ].join('\n');
343
344
  }
@@ -406,7 +407,7 @@ export function buildReminderSchedulePreview(parsed: ParsedReminderScheduleArgs)
406
407
  };
407
408
  }
408
409
 
409
- async function fetchDaemonStatus(connection: AgentDaemonConnection): Promise<{
410
+ async function fetchConnectedHostStatus(connection: AgentConnectedHostConnection): Promise<{
410
411
  readonly ok: boolean;
411
412
  readonly status: number;
412
413
  readonly body: unknown;
@@ -430,7 +431,7 @@ async function fetchDaemonStatus(connection: AgentDaemonConnection): Promise<{
430
431
 
431
432
  async function classifyReminderScheduleError(
432
433
  error: unknown,
433
- connection: AgentDaemonConnection,
434
+ connection: AgentConnectedHostConnection,
434
435
  ): Promise<ReminderScheduleFailure> {
435
436
  const message = summarizeError(error);
436
437
  const lower = message.toLowerCase();
@@ -438,30 +439,30 @@ async function classifyReminderScheduleError(
438
439
  return { ok: false, kind: 'auth_required', error: message, route: REMINDER_SCHEDULE_ROUTE, baseUrl: connection.baseUrl };
439
440
  }
440
441
  if (lower.includes('404') || lower.includes('not found')) {
441
- const daemon = await fetchDaemonStatus(connection);
442
- const record = isRecord(daemon.body) ? daemon.body : {};
443
- const daemonVersion = readString(record, 'version') ?? 'unknown';
444
- if (daemon.ok && daemonVersion !== SDK_VERSION) {
442
+ const connectedHost = await fetchConnectedHostStatus(connection);
443
+ const record = isRecord(connectedHost.body) ? connectedHost.body : {};
444
+ const connectedHostVersion = readString(record, 'version') ?? 'unknown';
445
+ if (connectedHost.ok && connectedHostVersion !== SDK_VERSION) {
445
446
  return {
446
447
  ok: false,
447
448
  kind: 'version_mismatch',
448
- error: `Connected GoodVibes service SDK version ${daemonVersion} does not match Agent SDK pin ${SDK_VERSION}; schedules.create is unavailable.`,
449
+ error: `Connected GoodVibes host SDK version ${connectedHostVersion} does not match Agent SDK pin ${SDK_VERSION}; schedules.create is unavailable.`,
449
450
  route: REMINDER_SCHEDULE_ROUTE,
450
451
  baseUrl: connection.baseUrl,
451
- daemonVersion,
452
+ connectedHostVersion,
452
453
  expectedSdkVersion: SDK_VERSION,
453
454
  };
454
455
  }
455
- return { ok: false, kind: 'daemon_route_unavailable', error: message, route: REMINDER_SCHEDULE_ROUTE, baseUrl: connection.baseUrl };
456
+ return { ok: false, kind: 'connected_host_route_unavailable', error: message, route: REMINDER_SCHEDULE_ROUTE, baseUrl: connection.baseUrl };
456
457
  }
457
458
  if (lower.includes('fetch') || lower.includes('connect') || lower.includes('econnrefused')) {
458
- return { ok: false, kind: 'daemon_unavailable', error: message, route: REMINDER_SCHEDULE_ROUTE, baseUrl: connection.baseUrl };
459
+ return { ok: false, kind: 'connected_host_unavailable', error: message, route: REMINDER_SCHEDULE_ROUTE, baseUrl: connection.baseUrl };
459
460
  }
460
- return { ok: false, kind: 'daemon_error', error: message, route: REMINDER_SCHEDULE_ROUTE, baseUrl: connection.baseUrl };
461
+ return { ok: false, kind: 'connected_host_error', error: message, route: REMINDER_SCHEDULE_ROUTE, baseUrl: connection.baseUrl };
461
462
  }
462
463
 
463
464
  export async function createReminderSchedule(
464
- connection: AgentDaemonConnection,
465
+ connection: AgentConnectedHostConnection,
465
466
  preview: ReminderSchedulePreview,
466
467
  ): Promise<ReminderScheduleResult> {
467
468
  if (!connection.token) {
@@ -489,6 +490,6 @@ export async function createReminderSchedule(
489
490
  }
490
491
  }
491
492
 
492
- export function resolveReminderDaemonConnection(configManager: AgentDaemonConfigReader, homeDirectory: string): AgentDaemonConnection {
493
- return resolveAgentDaemonConnection(configManager, homeDirectory);
493
+ export function resolveReminderConnectedHostConnection(configManager: AgentConnectedHostConfigReader, homeDirectory: string): AgentConnectedHostConnection {
494
+ return resolveAgentConnectedHostConnection(configManager, homeDirectory);
494
495
  }
@@ -3,6 +3,7 @@ import { dirname } from 'node:path';
3
3
  import type { ShellPathService } from '@/runtime/index.ts';
4
4
  import { GOODVIBES_AGENT_SURFACE_ROOT } from '../config/surface.ts';
5
5
  import { assertNoSecretLikeText } from './persona-registry.ts';
6
+ import { formatAgentRecordReviewState } from './record-labels.ts';
6
7
  import {
7
8
  evaluateAgentSkillReadiness,
8
9
  formatAgentSkillRequirement,
@@ -228,9 +229,9 @@ export class AgentRoutineRegistry {
228
229
  const steps = input.steps.trim();
229
230
  this.validateRequired(name, description, steps);
230
231
  const requirements = normalizeAgentSkillRequirements(input.requirements);
231
- assertNoSecretLikeText([name, description, steps, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])]);
232
+ assertNoSecretLikeText([name, description, steps, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])], 'Routines');
232
233
  const duplicate = store.routines.find((routine) => routine.name.toLowerCase() === name.toLowerCase());
233
- if (duplicate) throw new Error(`Routine already exists: ${duplicate.id}`);
234
+ if (duplicate) throw new Error(`Routine already exists ${duplicate.id}`);
234
235
  const timestamp = nowIso();
235
236
  const routine: AgentRoutineRecord = {
236
237
  id: this.nextId(name, store.routines),
@@ -255,15 +256,15 @@ export class AgentRoutineRegistry {
255
256
  public update(idOrName: string, input: AgentRoutineUpdateInput): AgentRoutineRecord {
256
257
  const store = this.readStore();
257
258
  const existing = this.findInStore(store, idOrName);
258
- if (!existing) throw new Error(`Unknown routine: ${idOrName}`);
259
+ if (!existing) throw new Error(`Unknown routine ${idOrName}`);
259
260
  const name = input.name === undefined ? existing.name : normalizeName(input.name);
260
261
  const description = input.description === undefined ? existing.description : input.description.trim();
261
262
  const steps = input.steps === undefined ? existing.steps : input.steps.trim();
262
263
  this.validateRequired(name, description, steps);
263
264
  const requirements = input.requirements === undefined ? existing.requirements : normalizeAgentSkillRequirements(input.requirements);
264
- assertNoSecretLikeText([name, description, steps, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])]);
265
+ assertNoSecretLikeText([name, description, steps, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])], 'Routines');
265
266
  const duplicate = store.routines.find((routine) => routine.id !== existing.id && routine.name.toLowerCase() === name.toLowerCase());
266
- if (duplicate) throw new Error(`Routine already exists: ${duplicate.id}`);
267
+ if (duplicate) throw new Error(`Routine already exists ${duplicate.id}`);
267
268
  const updated: AgentRoutineRecord = {
268
269
  ...existing,
269
270
  name,
@@ -288,7 +289,7 @@ export class AgentRoutineRegistry {
288
289
  public setEnabled(idOrName: string, enabled: boolean): AgentRoutineRecord {
289
290
  const store = this.readStore();
290
291
  const existing = this.findInStore(store, idOrName);
291
- if (!existing) throw new Error(`Unknown routine: ${idOrName}`);
292
+ if (!existing) throw new Error(`Unknown routine ${idOrName}`);
292
293
  const updated: AgentRoutineRecord = { ...existing, enabled, updatedAt: nowIso() };
293
294
  this.writeStore({
294
295
  ...store,
@@ -300,7 +301,7 @@ export class AgentRoutineRegistry {
300
301
  public markStarted(idOrName: string): AgentRoutineRecord {
301
302
  const store = this.readStore();
302
303
  const existing = this.findInStore(store, idOrName);
303
- if (!existing) throw new Error(`Unknown routine: ${idOrName}`);
304
+ if (!existing) throw new Error(`Unknown routine ${idOrName}`);
304
305
  const timestamp = nowIso();
305
306
  const updated: AgentRoutineRecord = {
306
307
  ...existing,
@@ -318,7 +319,7 @@ export class AgentRoutineRegistry {
318
319
  public markReviewed(idOrName: string): AgentRoutineRecord {
319
320
  const store = this.readStore();
320
321
  const existing = this.findInStore(store, idOrName);
321
- if (!existing) throw new Error(`Unknown routine: ${idOrName}`);
322
+ if (!existing) throw new Error(`Unknown routine ${idOrName}`);
322
323
  const updated: AgentRoutineRecord = {
323
324
  ...existing,
324
325
  reviewState: 'reviewed',
@@ -336,7 +337,7 @@ export class AgentRoutineRegistry {
336
337
  public markStale(idOrName: string, reason: string): AgentRoutineRecord {
337
338
  const store = this.readStore();
338
339
  const existing = this.findInStore(store, idOrName);
339
- if (!existing) throw new Error(`Unknown routine: ${idOrName}`);
340
+ if (!existing) throw new Error(`Unknown routine ${idOrName}`);
340
341
  const updated: AgentRoutineRecord = {
341
342
  ...existing,
342
343
  reviewState: 'stale',
@@ -353,7 +354,7 @@ export class AgentRoutineRegistry {
353
354
  public deleteRoutine(idOrName: string): AgentRoutineRecord {
354
355
  const store = this.readStore();
355
356
  const existing = this.findInStore(store, idOrName);
356
- if (!existing) throw new Error(`Unknown routine: ${idOrName}`);
357
+ if (!existing) throw new Error(`Unknown routine ${idOrName}`);
357
358
  this.writeStore({
358
359
  ...store,
359
360
  routines: store.routines.filter((routine) => routine.id !== existing.id),
@@ -389,7 +390,7 @@ export class AgentRoutineRegistry {
389
390
  try {
390
391
  return parseStore(readFileSync(this.storePath, 'utf-8'));
391
392
  } catch (error) {
392
- throw new Error(`Could not read Agent routine store: ${error instanceof Error ? error.message : String(error)}`);
393
+ throw new Error(`Could not read Agent routine store ${error instanceof Error ? error.message : String(error)}`);
393
394
  }
394
395
  }
395
396
 
@@ -418,7 +419,7 @@ export function buildEnabledRoutinesPrompt(shellPaths: ShellPathService): string
418
419
  ...enabled.slice(0, 8).flatMap((routine) => [
419
420
  `### ${routine.name}`,
420
421
  `Description: ${routine.description}`,
421
- `Review state: ${routine.reviewState}`,
422
+ `Review: ${formatAgentRecordReviewState(routine.reviewState)}`,
422
423
  `Triggers: ${routine.triggers.join(', ') || '(manual)'}`,
423
424
  `Readiness: ${evaluateAgentRoutineReadiness(routine).ready ? 'ready' : `missing ${evaluateAgentRoutineReadiness(routine).missing.map(formatAgentSkillRequirement).join(', ')}`}`,
424
425
  routine.steps,
@@ -15,6 +15,7 @@ const DELIVERY_SURFACE_KINDS: readonly RoutineScheduleDeliverySurfaceKind[] = [
15
15
  'google-chat',
16
16
  'signal',
17
17
  'whatsapp',
18
+ 'telephony',
18
19
  'imessage',
19
20
  'msteams',
20
21
  'bluebubbles',
@@ -204,7 +205,7 @@ export function parseRoutineSchedulePromotionArgs(args: readonly string[]): Pars
204
205
  continue;
205
206
  }
206
207
  if (raw.startsWith('--')) {
207
- errors.push(`Unknown option: ${raw}`);
208
+ errors.push(`Unknown option ${raw}`);
208
209
  continue;
209
210
  }
210
211
  if (!routineId) {