@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
@@ -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,37 +439,37 @@ 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) {
468
469
  return {
469
470
  ok: false,
470
471
  kind: 'auth_required',
471
- error: `No daemon operator token found at ${connection.tokenPath}`,
472
+ error: `No connected-host operator token found at ${connection.tokenPath}`,
472
473
  route: REMINDER_SCHEDULE_ROUTE,
473
474
  baseUrl: connection.baseUrl,
474
475
  };
@@ -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,9 +3,19 @@ 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';
7
+ import {
8
+ evaluateAgentSkillReadiness,
9
+ formatAgentSkillRequirement,
10
+ normalizeAgentSkillRequirements,
11
+ type AgentSkillReadiness,
12
+ type AgentSkillRequirement,
13
+ } from './skill-registry.ts';
6
14
 
7
15
  export type AgentRoutineSource = 'user' | 'agent' | 'imported' | 'system';
8
16
  export type AgentRoutineReviewState = 'fresh' | 'reviewed' | 'stale';
17
+ export type AgentRoutineRequirement = AgentSkillRequirement;
18
+ export type AgentRoutineReadiness = AgentSkillReadiness;
9
19
 
10
20
  export interface AgentRoutineRecord {
11
21
  readonly id: string;
@@ -14,6 +24,7 @@ export interface AgentRoutineRecord {
14
24
  readonly steps: string;
15
25
  readonly triggers: readonly string[];
16
26
  readonly tags: readonly string[];
27
+ readonly requirements: readonly AgentRoutineRequirement[];
17
28
  readonly enabled: boolean;
18
29
  readonly source: AgentRoutineSource;
19
30
  readonly provenance: string;
@@ -32,6 +43,7 @@ export interface AgentRoutineCreateInput {
32
43
  readonly steps: string;
33
44
  readonly triggers?: readonly string[];
34
45
  readonly tags?: readonly string[];
46
+ readonly requirements?: readonly AgentRoutineRequirement[];
35
47
  readonly enabled?: boolean;
36
48
  readonly source?: AgentRoutineSource;
37
49
  readonly provenance?: string;
@@ -43,6 +55,7 @@ export interface AgentRoutineUpdateInput {
43
55
  readonly steps?: string;
44
56
  readonly triggers?: readonly string[];
45
57
  readonly tags?: readonly string[];
58
+ readonly requirements?: readonly AgentRoutineRequirement[];
46
59
  readonly provenance?: string;
47
60
  }
48
61
 
@@ -72,6 +85,18 @@ function readStringArray(value: unknown): string[] {
72
85
  return value.filter((entry): entry is string => typeof entry === 'string').map((entry) => entry.trim()).filter(Boolean);
73
86
  }
74
87
 
88
+ function readRequirementArray(value: unknown): readonly AgentRoutineRequirement[] {
89
+ if (!Array.isArray(value)) return [];
90
+ const requirements = value
91
+ .filter(isRecord)
92
+ .map((entry) => ({
93
+ kind: entry.kind === 'command' ? 'command' as const : 'env' as const,
94
+ name: readString(entry.name).trim(),
95
+ description: readString(entry.description).trim() || undefined,
96
+ }));
97
+ return normalizeAgentSkillRequirements(requirements);
98
+ }
99
+
75
100
  function readNonNegativeInteger(value: unknown): number {
76
101
  const parsed = typeof value === 'number' ? value : Number(value);
77
102
  return Number.isInteger(parsed) && parsed > 0 ? parsed : 0;
@@ -123,6 +148,7 @@ function parseRoutine(value: unknown): AgentRoutineRecord | null {
123
148
  steps,
124
149
  triggers: readStringArray(value.triggers),
125
150
  tags: readStringArray(value.tags),
151
+ requirements: readRequirementArray(value.requirements),
126
152
  enabled: value.enabled === true,
127
153
  source,
128
154
  provenance: readString(value.provenance, source).trim() || source,
@@ -151,14 +177,16 @@ function formatStore(store: RoutineStoreFile): string {
151
177
  return `${JSON.stringify(store, null, 2)}\n`;
152
178
  }
153
179
 
154
- export function routineStorePath(shellPaths: ShellPathService): string {
180
+ type AgentLocalStorePaths = Pick<ShellPathService, 'resolveUserPath'>;
181
+
182
+ export function routineStorePath(shellPaths: AgentLocalStorePaths): string {
155
183
  return shellPaths.resolveUserPath(GOODVIBES_AGENT_SURFACE_ROOT, 'routines', 'routines.json');
156
184
  }
157
185
 
158
186
  export class AgentRoutineRegistry {
159
187
  public constructor(private readonly storePath: string) {}
160
188
 
161
- public static fromShellPaths(shellPaths: ShellPathService): AgentRoutineRegistry {
189
+ public static fromShellPaths(shellPaths: AgentLocalStorePaths): AgentRoutineRegistry {
162
190
  return new AgentRoutineRegistry(routineStorePath(shellPaths));
163
191
  }
164
192
 
@@ -200,9 +228,10 @@ export class AgentRoutineRegistry {
200
228
  const description = input.description.trim();
201
229
  const steps = input.steps.trim();
202
230
  this.validateRequired(name, description, steps);
203
- assertNoSecretLikeText([name, description, steps, ...(input.tags ?? []), ...(input.triggers ?? [])]);
231
+ const requirements = normalizeAgentSkillRequirements(input.requirements);
232
+ assertNoSecretLikeText([name, description, steps, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])], 'Routines');
204
233
  const duplicate = store.routines.find((routine) => routine.name.toLowerCase() === name.toLowerCase());
205
- if (duplicate) throw new Error(`Routine already exists: ${duplicate.id}`);
234
+ if (duplicate) throw new Error(`Routine already exists ${duplicate.id}`);
206
235
  const timestamp = nowIso();
207
236
  const routine: AgentRoutineRecord = {
208
237
  id: this.nextId(name, store.routines),
@@ -211,6 +240,7 @@ export class AgentRoutineRegistry {
211
240
  steps,
212
241
  triggers: normalizeList(input.triggers),
213
242
  tags: normalizeList(input.tags),
243
+ requirements,
214
244
  enabled: input.enabled === true,
215
245
  source: input.source ?? 'user',
216
246
  provenance: input.provenance?.trim() || input.source || 'user',
@@ -226,14 +256,15 @@ export class AgentRoutineRegistry {
226
256
  public update(idOrName: string, input: AgentRoutineUpdateInput): AgentRoutineRecord {
227
257
  const store = this.readStore();
228
258
  const existing = this.findInStore(store, idOrName);
229
- if (!existing) throw new Error(`Unknown routine: ${idOrName}`);
259
+ if (!existing) throw new Error(`Unknown routine ${idOrName}`);
230
260
  const name = input.name === undefined ? existing.name : normalizeName(input.name);
231
261
  const description = input.description === undefined ? existing.description : input.description.trim();
232
262
  const steps = input.steps === undefined ? existing.steps : input.steps.trim();
233
263
  this.validateRequired(name, description, steps);
234
- assertNoSecretLikeText([name, description, steps, ...(input.tags ?? []), ...(input.triggers ?? [])]);
264
+ const requirements = input.requirements === undefined ? existing.requirements : normalizeAgentSkillRequirements(input.requirements);
265
+ assertNoSecretLikeText([name, description, steps, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])], 'Routines');
235
266
  const duplicate = store.routines.find((routine) => routine.id !== existing.id && routine.name.toLowerCase() === name.toLowerCase());
236
- if (duplicate) throw new Error(`Routine already exists: ${duplicate.id}`);
267
+ if (duplicate) throw new Error(`Routine already exists ${duplicate.id}`);
237
268
  const updated: AgentRoutineRecord = {
238
269
  ...existing,
239
270
  name,
@@ -241,6 +272,7 @@ export class AgentRoutineRegistry {
241
272
  steps,
242
273
  triggers: input.triggers === undefined ? existing.triggers : normalizeList(input.triggers),
243
274
  tags: input.tags === undefined ? existing.tags : normalizeList(input.tags),
275
+ requirements,
244
276
  provenance: input.provenance === undefined ? existing.provenance : input.provenance.trim() || existing.provenance,
245
277
  reviewState: 'fresh',
246
278
  staleReason: undefined,
@@ -257,7 +289,7 @@ export class AgentRoutineRegistry {
257
289
  public setEnabled(idOrName: string, enabled: boolean): AgentRoutineRecord {
258
290
  const store = this.readStore();
259
291
  const existing = this.findInStore(store, idOrName);
260
- if (!existing) throw new Error(`Unknown routine: ${idOrName}`);
292
+ if (!existing) throw new Error(`Unknown routine ${idOrName}`);
261
293
  const updated: AgentRoutineRecord = { ...existing, enabled, updatedAt: nowIso() };
262
294
  this.writeStore({
263
295
  ...store,
@@ -269,7 +301,7 @@ export class AgentRoutineRegistry {
269
301
  public markStarted(idOrName: string): AgentRoutineRecord {
270
302
  const store = this.readStore();
271
303
  const existing = this.findInStore(store, idOrName);
272
- if (!existing) throw new Error(`Unknown routine: ${idOrName}`);
304
+ if (!existing) throw new Error(`Unknown routine ${idOrName}`);
273
305
  const timestamp = nowIso();
274
306
  const updated: AgentRoutineRecord = {
275
307
  ...existing,
@@ -287,7 +319,7 @@ export class AgentRoutineRegistry {
287
319
  public markReviewed(idOrName: string): AgentRoutineRecord {
288
320
  const store = this.readStore();
289
321
  const existing = this.findInStore(store, idOrName);
290
- if (!existing) throw new Error(`Unknown routine: ${idOrName}`);
322
+ if (!existing) throw new Error(`Unknown routine ${idOrName}`);
291
323
  const updated: AgentRoutineRecord = {
292
324
  ...existing,
293
325
  reviewState: 'reviewed',
@@ -305,7 +337,7 @@ export class AgentRoutineRegistry {
305
337
  public markStale(idOrName: string, reason: string): AgentRoutineRecord {
306
338
  const store = this.readStore();
307
339
  const existing = this.findInStore(store, idOrName);
308
- if (!existing) throw new Error(`Unknown routine: ${idOrName}`);
340
+ if (!existing) throw new Error(`Unknown routine ${idOrName}`);
309
341
  const updated: AgentRoutineRecord = {
310
342
  ...existing,
311
343
  reviewState: 'stale',
@@ -322,7 +354,7 @@ export class AgentRoutineRegistry {
322
354
  public deleteRoutine(idOrName: string): AgentRoutineRecord {
323
355
  const store = this.readStore();
324
356
  const existing = this.findInStore(store, idOrName);
325
- if (!existing) throw new Error(`Unknown routine: ${idOrName}`);
357
+ if (!existing) throw new Error(`Unknown routine ${idOrName}`);
326
358
  this.writeStore({
327
359
  ...store,
328
360
  routines: store.routines.filter((routine) => routine.id !== existing.id),
@@ -358,7 +390,7 @@ export class AgentRoutineRegistry {
358
390
  try {
359
391
  return parseStore(readFileSync(this.storePath, 'utf-8'));
360
392
  } catch (error) {
361
- 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)}`);
362
394
  }
363
395
  }
364
396
 
@@ -370,6 +402,13 @@ export class AgentRoutineRegistry {
370
402
  }
371
403
  }
372
404
 
405
+ export function evaluateAgentRoutineReadiness(
406
+ routine: Pick<AgentRoutineRecord, 'requirements'>,
407
+ options: Parameters<typeof evaluateAgentSkillReadiness>[1] = {},
408
+ ): AgentRoutineReadiness {
409
+ return evaluateAgentSkillReadiness(routine, options);
410
+ }
411
+
373
412
  export function buildEnabledRoutinesPrompt(shellPaths: ShellPathService): string | null {
374
413
  const enabled = AgentRoutineRegistry.fromShellPaths(shellPaths).snapshot().enabledRoutines;
375
414
  if (enabled.length === 0) return null;
@@ -380,8 +419,9 @@ export function buildEnabledRoutinesPrompt(shellPaths: ShellPathService): string
380
419
  ...enabled.slice(0, 8).flatMap((routine) => [
381
420
  `### ${routine.name}`,
382
421
  `Description: ${routine.description}`,
383
- `Review state: ${routine.reviewState}`,
422
+ `Review: ${formatAgentRecordReviewState(routine.reviewState)}`,
384
423
  `Triggers: ${routine.triggers.join(', ') || '(manual)'}`,
424
+ `Readiness: ${evaluateAgentRoutineReadiness(routine).ready ? 'ready' : `missing ${evaluateAgentRoutineReadiness(routine).missing.map(formatAgentSkillRequirement).join(', ')}`}`,
385
425
  routine.steps,
386
426
  '',
387
427
  ]),
@@ -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) {
@@ -23,6 +23,30 @@ function formatDeliveryTargetKind(target: { readonly kind?: string; readonly sur
23
23
  return `${target.kind ?? 'unknown'}${target.surfaceKind ? `/${target.surfaceKind}` : ''}`;
24
24
  }
25
25
 
26
+ function formatDeliveryTarget(target: {
27
+ readonly kind?: string;
28
+ readonly surfaceKind?: string;
29
+ readonly routeId?: string;
30
+ readonly address?: string;
31
+ readonly label?: string;
32
+ }): string {
33
+ const details = [
34
+ target.routeId ? `route=${target.routeId}` : '',
35
+ target.address ? `address=${target.address}` : '',
36
+ target.label ? `label=${target.label}` : '',
37
+ ].filter(Boolean);
38
+ return `${formatDeliveryTargetKind(target)}${details.length > 0 ? ` ${details.join(' ')}` : ''}`;
39
+ }
40
+
41
+ function formatRoutineScheduleFailureKind(kind: string): string {
42
+ if (kind === 'auth_required') return 'authorization required';
43
+ if (kind === 'connected_host_unavailable') return 'connected host unavailable';
44
+ if (kind === 'connected_host_route_unavailable') return 'connected host route unavailable';
45
+ if (kind === 'connected_host_error') return 'connected host error';
46
+ if (kind === 'version_mismatch') return 'version mismatch';
47
+ return kind.replace(/[_-]+/g, ' ');
48
+ }
49
+
26
50
  export function formatRoutineSchedulePreview(preview: RoutineSchedulePromotionPreview): string {
27
51
  const schedule = preview.payload.kind === 'cron'
28
52
  ? `${preview.payload.cron}${preview.payload.timezone ? ` [${preview.payload.timezone}]` : ''}`
@@ -33,15 +57,15 @@ export function formatRoutineSchedulePreview(preview: RoutineSchedulePromotionPr
33
57
  const deliveryTargetCount = delivery?.targets.length ?? 0;
34
58
  return [
35
59
  'GoodVibes schedule preview for Agent routine',
36
- ` routine: ${preview.routineName} (${preview.routineId})`,
37
- ` route: ${preview.method} ${preview.route}`,
38
- ` name: ${String(preview.payload.name ?? '(runtime default)')}`,
39
- ` schedule: ${preview.payload.kind} ${schedule}`,
40
- ` enabled: ${preview.payload.enabled === false ? 'no' : 'yes'}`,
60
+ ` routine ${preview.routineName} (${preview.routineId})`,
61
+ ` route ${preview.method} ${preview.route}`,
62
+ ` name ${String(preview.payload.name ?? '(connected-host default)')}`,
63
+ ` schedule ${preview.payload.kind} ${schedule}`,
64
+ ` enabled ${preview.payload.enabled === false ? 'no' : 'yes'}`,
41
65
  ` delivery: ${delivery?.mode ?? 'none'}${deliveryTargetCount > 0 ? ` (${deliveryTargetCount} target${deliveryTargetCount === 1 ? '' : 's'})` : ''}`,
42
- ' target: GoodVibes runtime/main conversation route',
43
- ' policy: isolated Agent Knowledge only; no default wiki/non-Agent fallback; no WRFC unless explicitly delegated',
44
- ' next: rerun with --yes to create this connected schedule',
66
+ ' target connected GoodVibes host/main conversation route',
67
+ ' policy isolated Agent Knowledge only; no default knowledge/non-Agent fallback; no delegated review unless explicitly requested',
68
+ ' next rerun with --yes to create this connected schedule',
45
69
  ].join('\n');
46
70
  }
47
71
 
@@ -51,11 +75,11 @@ export function formatRoutineScheduleSuccess(result: RoutineSchedulePromotionSuc
51
75
  const status = readString(record, 'status') ?? (record.enabled === false ? 'paused' : 'enabled');
52
76
  return [
53
77
  'Created GoodVibes schedule for Agent routine',
54
- ` routine: ${result.routineName} (${result.routineId})`,
55
- ` schedule: ${id}`,
56
- ` status: ${status}`,
57
- ` route: ${result.kind} ${result.route}`,
58
- ' next: inspect with /schedule list or schedule observability',
78
+ ` routine ${result.routineName} (${result.routineId})`,
79
+ ` schedule ${id}`,
80
+ ` status ${status}`,
81
+ ` route ${result.kind} ${result.route}`,
82
+ ' next inspect with /schedule list or schedule observability',
59
83
  ].join('\n');
60
84
  }
61
85
 
@@ -64,18 +88,18 @@ export function formatRoutineScheduleReceipts(snapshot: RoutineScheduleReceiptSn
64
88
  if (snapshot.receipts.length === 0) {
65
89
  return [
66
90
  'Agent routine schedule receipts',
67
- ` store: ${snapshot.path}`,
91
+ ` store ${snapshot.path}`,
68
92
  ' No routine schedule promotions have been recorded yet.',
69
93
  ' Create one with /schedule promote-routine <routine-id> --cron <expr> --yes.',
70
94
  ].join('\n');
71
95
  }
72
96
  return [
73
97
  `Agent routine schedule receipts (${snapshot.receipts.length})`,
74
- ` store: ${snapshot.path}`,
98
+ ` store ${snapshot.path}`,
75
99
  ...receipts.map((receipt) => {
76
100
  const schedule = receipt.scheduleId ? ` schedule=${receipt.scheduleId}` : '';
77
- const failure = receipt.status === 'failed' && receipt.failureKind ? ` failure=${receipt.failureKind}` : '';
78
- return ` ${receipt.id} ${receipt.status} ${receipt.scheduleKind} ${receipt.scheduleValue} routine=${receipt.routineId}${schedule}${failure}`;
101
+ const failure = receipt.status === 'failed' && receipt.failureKind ? ` failure ${formatRoutineScheduleFailureKind(receipt.failureKind)}` : '';
102
+ return ` ${receipt.id} ${receipt.status} ${receipt.scheduleKind} ${receipt.scheduleValue} routine ${receipt.routineId}${schedule}${failure}`;
79
103
  }),
80
104
  snapshot.receipts.length > receipts.length ? ` ...${snapshot.receipts.length - receipts.length} more` : '',
81
105
  ].filter((line): line is string => Boolean(line)).join('\n');
@@ -84,20 +108,20 @@ export function formatRoutineScheduleReceipts(snapshot: RoutineScheduleReceiptSn
84
108
  export function formatRoutineScheduleReceipt(receipt: RoutineScheduleReceipt): string {
85
109
  return [
86
110
  `Agent routine schedule receipt ${receipt.id}`,
87
- ` created: ${receipt.createdAt}`,
88
- ` status: ${receipt.status}`,
89
- ` routine: ${receipt.routineName} (${receipt.routineId})`,
90
- ` route: ${receipt.method} ${receipt.route}`,
91
- ` runtime: ${receipt.daemonBaseUrl}`,
92
- ` schedule: ${receipt.scheduleName}${receipt.scheduleId ? ` (${receipt.scheduleId})` : ''}`,
93
- receipt.scheduleStatus ? ` schedule status: ${receipt.scheduleStatus}` : '',
111
+ ` created ${receipt.createdAt}`,
112
+ ` status ${receipt.status}`,
113
+ ` routine ${receipt.routineName} (${receipt.routineId})`,
114
+ ` route ${receipt.method} ${receipt.route}`,
115
+ ` connected host: ${receipt.connectedHostBaseUrl}`,
116
+ ` schedule ${receipt.scheduleName}${receipt.scheduleId ? ` (${receipt.scheduleId})` : ''}`,
117
+ receipt.scheduleStatus ? ` schedule status ${receipt.scheduleStatus}` : '',
94
118
  ` cadence: ${receipt.scheduleKind} ${receipt.scheduleValue}${receipt.timezone ? ` [${receipt.timezone}]` : ''}`,
95
- ` enabled: ${receipt.enabled ? 'yes' : 'no'}`,
96
- receipt.provider ? ` provider: ${receipt.provider}` : '',
97
- receipt.model ? ` model: ${receipt.model}` : '',
98
- ` target: ${formatDeliveryTargetKind(receipt.target)}`,
119
+ ` enabled ${receipt.enabled ? 'yes' : 'no'}`,
120
+ receipt.provider ? ` provider ${receipt.provider}` : '',
121
+ receipt.model ? ` model ${receipt.model}` : '',
122
+ ` target ${formatDeliveryTargetKind(receipt.target)}`,
99
123
  receipt.deliveryMode ? ` delivery: ${receipt.deliveryMode}` : '',
100
- ...(receipt.deliveryTargets ?? []).map((target) => ` delivery target: ${formatDeliveryTargetKind(target)}${target.routeId ? ` route=${target.routeId}` : ''}${target.address ? ` address=${target.address}` : ''}${target.label ? ` label=${target.label}` : ''}`),
124
+ ...(receipt.deliveryTargets ?? []).map((target) => ` delivery target: ${formatDeliveryTarget(target)}`),
101
125
  receipt.failureKind ? ` failure: ${receipt.failureKind}` : '',
102
126
  receipt.failureError ? ` error: ${receipt.failureError}` : '',
103
127
  ].filter((line): line is string => Boolean(line)).join('\n');
@@ -106,18 +130,18 @@ export function formatRoutineScheduleReceipt(receipt: RoutineScheduleReceipt): s
106
130
  export function formatRoutineScheduleCorrelation(result: RoutineScheduleCorrelationResult, limit = 10): string {
107
131
  if (!result.ok) {
108
132
  return [
109
- `GoodVibes schedule reconciliation error: ${result.kind}`,
133
+ `GoodVibes schedule reconciliation error ${formatRoutineScheduleFailureKind(result.kind)}`,
110
134
  ` ${result.error}`,
111
- result.baseUrl ? ` runtime: ${result.baseUrl}` : null,
112
- ` route: ${ROUTINE_SCHEDULE_LIST_METHOD} ${result.route}`,
135
+ result.baseUrl ? ` connected host ${result.baseUrl}` : null,
136
+ ` route ${ROUTINE_SCHEDULE_LIST_METHOD} ${result.route}`,
113
137
  result.kind === 'auth_required'
114
- ? ' next: pair/authenticate with connected GoodVibes services, then retry.'
138
+ ? ' next pair/authenticate with the connected GoodVibes host, then retry.'
115
139
  : null,
116
- result.kind === 'daemon_unavailable'
117
- ? ' next: make connected GoodVibes services available outside Agent, then retry.'
140
+ result.kind === 'connected_host_unavailable'
141
+ ? ' next make the connected GoodVibes host available outside Agent, then retry.'
118
142
  : null,
119
- result.kind === 'version_mismatch' || result.kind === 'daemon_route_unavailable'
120
- ? ' next: update connected GoodVibes services so public schedules.list is available.'
143
+ result.kind === 'version_mismatch' || result.kind === 'connected_host_route_unavailable'
144
+ ? ' next update the connected GoodVibes host so public schedules.list is available.'
121
145
  : null,
122
146
  ].filter((line): line is string => Boolean(line)).join('\n');
123
147
  }
@@ -125,7 +149,7 @@ export function formatRoutineScheduleCorrelation(result: RoutineScheduleCorrelat
125
149
  if (result.receiptCount === 0) {
126
150
  return [
127
151
  'Agent routine schedule reconciliation',
128
- ` runtime: ${result.baseUrl}`,
152
+ ` connected host: ${result.baseUrl}`,
129
153
  ` route: ${result.kind} ${result.route}`,
130
154
  ` live schedules: ${result.scheduleCount}`,
131
155
  ' No local routine promotion receipts exist yet.',
@@ -137,20 +161,20 @@ export function formatRoutineScheduleCorrelation(result: RoutineScheduleCorrelat
137
161
  const failed = result.correlations.filter((entry) => entry.liveStatus === 'failed-receipt').length;
138
162
  return [
139
163
  'Agent routine schedule reconciliation',
140
- ` runtime: ${result.baseUrl}`,
164
+ ` connected host: ${result.baseUrl}`,
141
165
  ` route: ${result.kind} ${result.route}`,
142
166
  ` receipts: ${result.receiptCount}; live schedules: ${result.scheduleCount}; matched: ${matched}; missing: ${missing}; failed receipts: ${failed}`,
143
167
  ...correlations.map((entry) => {
144
168
  const receipt = entry.receipt;
145
169
  const schedule = entry.schedule;
146
170
  const live = schedule
147
- ? ` live=${schedule.id} status=${schedule.status ?? (schedule.enabled === false ? 'paused' : 'enabled')}`
171
+ ? ` live=${schedule.id}; status ${schedule.status ?? (schedule.enabled === false ? 'paused' : 'enabled')}`
148
172
  : '';
149
173
  const runs = schedule && schedule.runCount !== undefined
150
- ? ` runs=${schedule.runCount}/${schedule.successCount ?? 0}/${schedule.failureCount ?? 0}`
174
+ ? ` runs ${schedule.runCount}/${schedule.successCount ?? 0}/${schedule.failureCount ?? 0}`
151
175
  : '';
152
- const next = schedule?.nextRunAt ? ` next=${new Date(schedule.nextRunAt).toISOString()}` : '';
153
- return ` ${receipt.id} ${entry.liveStatus} reason=${entry.matchReason} routine=${receipt.routineId} receiptSchedule=${receipt.scheduleId ?? '(none)'}${live}${runs}${next}`;
176
+ const next = schedule?.nextRunAt ? ` next ${new Date(schedule.nextRunAt).toISOString()}` : '';
177
+ return ` ${receipt.id} ${entry.liveStatus} reason ${entry.matchReason} routine ${receipt.routineId} receipt schedule ${receipt.scheduleId ?? '(none)'}${live}${runs}${next}`;
154
178
  }),
155
179
  result.correlations.length > correlations.length ? ` ...${result.correlations.length - correlations.length} more` : '',
156
180
  ].filter((line): line is string => Boolean(line)).join('\n');
@@ -158,21 +182,21 @@ export function formatRoutineScheduleCorrelation(result: RoutineScheduleCorrelat
158
182
 
159
183
  export function formatRoutineScheduleFailure(failure: RoutineSchedulePromotionFailure): string {
160
184
  return [
161
- `GoodVibes schedule error: ${failure.kind}`,
185
+ `GoodVibes schedule error ${formatRoutineScheduleFailureKind(failure.kind)}`,
162
186
  ` ${failure.error}`,
163
- failure.baseUrl ? ` runtime: ${failure.baseUrl}` : null,
164
- ` route: ${ROUTINE_SCHEDULE_METHOD} ${failure.route}`,
165
- failure.kind === 'version_mismatch' && failure.daemonVersion && failure.expectedSdkVersion
166
- ? ` versions: runtime=${failure.daemonVersion} expected=${failure.expectedSdkVersion}`
187
+ failure.baseUrl ? ` connected host ${failure.baseUrl}` : null,
188
+ ` route ${ROUTINE_SCHEDULE_METHOD} ${failure.route}`,
189
+ failure.kind === 'version_mismatch' && failure.connectedHostVersion && failure.expectedSdkVersion
190
+ ? ` versions connected host ${failure.connectedHostVersion}; expected ${failure.expectedSdkVersion}`
167
191
  : null,
168
192
  failure.kind === 'auth_required'
169
- ? ' next: pair/authenticate with connected GoodVibes services, then retry with --yes.'
193
+ ? ' next pair/authenticate with the connected GoodVibes host, then retry with --yes.'
170
194
  : null,
171
- failure.kind === 'daemon_unavailable'
172
- ? ' next: make connected GoodVibes services available outside Agent, then retry.'
195
+ failure.kind === 'connected_host_unavailable'
196
+ ? ' next make the connected GoodVibes host available outside Agent, then retry.'
173
197
  : null,
174
- failure.kind === 'version_mismatch' || failure.kind === 'daemon_route_unavailable'
175
- ? ' next: update connected GoodVibes services so public schedules.create is available.'
198
+ failure.kind === 'version_mismatch' || failure.kind === 'connected_host_route_unavailable'
199
+ ? ' next update the connected GoodVibes host so public schedules.create is available.'
176
200
  : null,
177
201
  ].filter((line): line is string => Boolean(line)).join('\n');
178
202
  }