@pellux/goodvibes-agent 0.1.117 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/CHANGELOG.md +13 -4
  2. package/README.md +35 -14
  3. package/bin/goodvibes-agent.ts +16 -2
  4. package/dist/package/main.js +176073 -170980
  5. package/docs/README.md +11 -5
  6. package/docs/channels-remote-and-api.md +50 -0
  7. package/docs/connected-host.md +3 -3
  8. package/docs/getting-started.md +29 -15
  9. package/docs/knowledge-artifacts-and-multimodal.md +91 -0
  10. package/docs/project-planning.md +79 -0
  11. package/docs/providers-and-routing.md +46 -0
  12. package/docs/release-and-publishing.md +44 -9
  13. package/docs/tools-and-commands.md +123 -0
  14. package/docs/voice-and-live-tts.md +51 -0
  15. package/package.json +2 -5
  16. package/src/agent/channel-delivery.ts +201 -0
  17. package/src/agent/media-generation.ts +159 -0
  18. package/src/agent/memory-prompt.ts +0 -1
  19. package/src/agent/note-registry.ts +329 -0
  20. package/src/agent/operator-actions.ts +343 -0
  21. package/src/agent/persona-registry.ts +15 -14
  22. package/src/agent/record-labels.ts +107 -0
  23. package/src/agent/reminder-schedule-format.ts +33 -24
  24. package/src/agent/reminder-schedule.ts +26 -25
  25. package/src/agent/routine-registry.ts +13 -12
  26. package/src/agent/routine-schedule-args.ts +2 -1
  27. package/src/agent/routine-schedule-format.ts +77 -53
  28. package/src/agent/routine-schedule-promotion.ts +34 -32
  29. package/src/agent/routine-schedule-receipts.ts +28 -26
  30. package/src/agent/runtime-profile-starters.ts +2 -2
  31. package/src/agent/runtime-profile.ts +18 -17
  32. package/src/agent/skill-registry.ts +25 -24
  33. package/src/cli/agent-knowledge-args.ts +5 -1
  34. package/src/cli/agent-knowledge-command.ts +39 -33
  35. package/src/cli/agent-knowledge-format.ts +80 -67
  36. package/src/cli/agent-knowledge-methods.ts +1 -1
  37. package/src/cli/agent-knowledge-runtime.ts +32 -26
  38. package/src/cli/bundle-command.ts +13 -8
  39. package/src/cli/config-overrides.ts +37 -36
  40. package/src/cli/external-runtime.ts +10 -4
  41. package/src/cli/help.ts +29 -11
  42. package/src/cli/local-library-command.ts +134 -68
  43. package/src/cli/management-commands.ts +98 -62
  44. package/src/cli/management.ts +52 -26
  45. package/src/cli/memory-command.ts +66 -32
  46. package/src/cli/parser.ts +37 -24
  47. package/src/cli/profiles-command.ts +52 -35
  48. package/src/cli/provider-auth-routes.ts +2 -1
  49. package/src/cli/routines-command.ts +44 -36
  50. package/src/cli/service-posture.ts +6 -6
  51. package/src/cli/status.ts +46 -121
  52. package/src/core/conversation-message-snapshot.ts +131 -0
  53. package/src/input/agent-workspace-activation.ts +33 -7
  54. package/src/input/agent-workspace-basic-command-editor-submission.ts +7 -3
  55. package/src/input/agent-workspace-basic-command-editors.ts +30 -10
  56. package/src/input/agent-workspace-categories.ts +276 -64
  57. package/src/input/agent-workspace-channel-command-editor-submission.ts +34 -0
  58. package/src/input/agent-workspace-channel-command-editors.ts +23 -5
  59. package/src/input/agent-workspace-channels.ts +35 -2
  60. package/src/input/agent-workspace-command-editor.ts +18 -2
  61. package/src/input/agent-workspace-config-reader.ts +16 -0
  62. package/src/input/agent-workspace-delegation-editor-submission.ts +1 -1
  63. package/src/input/agent-workspace-editors.ts +140 -2
  64. package/src/input/agent-workspace-knowledge-query-editor.ts +1 -1
  65. package/src/input/agent-workspace-learned-behavior.ts +2 -2
  66. package/src/input/agent-workspace-library-command-editor-submission.ts +1 -1
  67. package/src/input/agent-workspace-library-command-editors.ts +2 -2
  68. package/src/input/agent-workspace-local-operations.ts +218 -0
  69. package/src/input/agent-workspace-local-selection.ts +75 -0
  70. package/src/input/agent-workspace-media-command-editor-submission.ts +62 -0
  71. package/src/input/agent-workspace-media-command-editors.ts +27 -0
  72. package/src/input/agent-workspace-memory-command-editors.ts +1 -1
  73. package/src/input/agent-workspace-memory-editor.ts +2 -2
  74. package/src/input/agent-workspace-navigation.ts +38 -2
  75. package/src/input/agent-workspace-notify-editor-submission.ts +16 -2
  76. package/src/input/agent-workspace-operations-command-editor-submission.ts +63 -1
  77. package/src/input/agent-workspace-operations-command-editors.ts +80 -3
  78. package/src/input/agent-workspace-panel-route.ts +43 -0
  79. package/src/input/agent-workspace-reminder-schedule-editor.ts +1 -1
  80. package/src/input/agent-workspace-routine-schedule-editor.ts +2 -2
  81. package/src/input/agent-workspace-search.ts +169 -0
  82. package/src/input/agent-workspace-setup.ts +22 -11
  83. package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +6 -6
  84. package/src/input/agent-workspace-skill-bundle-command-editors.ts +10 -10
  85. package/src/input/agent-workspace-snapshot.ts +112 -13
  86. package/src/input/agent-workspace-task-command-editors.ts +4 -4
  87. package/src/input/agent-workspace-token.ts +18 -2
  88. package/src/input/agent-workspace-types.ts +92 -4
  89. package/src/input/agent-workspace-voice-media.ts +3 -6
  90. package/src/input/agent-workspace-web-research-editor.ts +104 -0
  91. package/src/input/agent-workspace.ts +136 -208
  92. package/src/input/command-registry.ts +4 -1
  93. package/src/input/commands/agent-runtime-profile-runtime.ts +45 -41
  94. package/src/input/commands/agent-skills-runtime.ts +83 -70
  95. package/src/input/commands/agent-workspace-runtime.ts +23 -7
  96. package/src/input/commands/brief-runtime.ts +25 -24
  97. package/src/input/commands/channels-runtime.ts +145 -31
  98. package/src/input/commands/delegation-runtime.ts +29 -23
  99. package/src/input/commands/experience-runtime.ts +6 -4
  100. package/src/input/commands/guidance-runtime.ts +4 -4
  101. package/src/input/commands/health-runtime.ts +140 -115
  102. package/src/input/commands/knowledge.ts +57 -56
  103. package/src/input/commands/local-provider-runtime.ts +36 -18
  104. package/src/input/commands/local-runtime.ts +138 -16
  105. package/src/input/commands/local-setup-review.ts +7 -7
  106. package/src/input/commands/mcp-runtime.ts +56 -35
  107. package/src/input/commands/notify-runtime.ts +38 -9
  108. package/src/input/commands/operator-actions-runtime.ts +138 -0
  109. package/src/input/commands/operator-runtime.ts +6 -17
  110. package/src/input/commands/personas-runtime.ts +45 -30
  111. package/src/input/commands/planning-runtime.ts +1 -1
  112. package/src/input/commands/platform-access-runtime.ts +29 -29
  113. package/src/input/commands/provider-accounts-runtime.ts +60 -39
  114. package/src/input/commands/qrcode-runtime.ts +45 -6
  115. package/src/input/commands/recall-bundle.ts +11 -11
  116. package/src/input/commands/recall-capture.ts +13 -11
  117. package/src/input/commands/recall-query.ts +29 -21
  118. package/src/input/commands/recall-review.ts +2 -1
  119. package/src/input/commands/routines-runtime.ts +59 -43
  120. package/src/input/commands/schedule-runtime.ts +33 -20
  121. package/src/input/commands/security-runtime.ts +4 -4
  122. package/src/input/commands/session-content.ts +80 -78
  123. package/src/input/commands/session-workflow.ts +132 -93
  124. package/src/input/commands/session.ts +3 -174
  125. package/src/input/commands/shell-core.ts +32 -17
  126. package/src/input/commands/subscription-runtime.ts +53 -179
  127. package/src/input/commands/tasks-runtime.ts +20 -20
  128. package/src/input/commands/work-plan-runtime.ts +33 -8
  129. package/src/input/commands.ts +2 -2
  130. package/src/input/feed-context-factory.ts +2 -1
  131. package/src/input/file-picker.ts +3 -2
  132. package/src/input/handler-command-route.ts +4 -7
  133. package/src/input/handler-content-actions.ts +89 -1
  134. package/src/input/handler-feed-routes.ts +19 -12
  135. package/src/input/handler-feed.ts +6 -3
  136. package/src/input/handler-interactions.ts +7 -5
  137. package/src/input/handler-modal-stack.ts +3 -3
  138. package/src/input/handler-onboarding.ts +24 -80
  139. package/src/input/handler-shortcuts.ts +15 -4
  140. package/src/input/handler.ts +47 -17
  141. package/src/input/input-history.ts +5 -6
  142. package/src/input/keybindings.ts +22 -11
  143. package/src/input/onboarding/onboarding-wizard-apply.ts +13 -0
  144. package/src/input/onboarding/onboarding-wizard-constants.ts +1 -0
  145. package/src/input/onboarding/onboarding-wizard-operator-steps.ts +147 -57
  146. package/src/input/onboarding/onboarding-wizard-steps.ts +46 -29
  147. package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
  148. package/src/input/onboarding/onboarding-wizard.ts +68 -0
  149. package/src/input/profile-picker-modal.ts +31 -12
  150. package/src/input/session-picker-modal.ts +21 -4
  151. package/src/input/settings-modal-behavior.ts +0 -3
  152. package/src/input/settings-modal-subscriptions.ts +3 -3
  153. package/src/input/settings-modal-types.ts +2 -13
  154. package/src/input/settings-modal.ts +6 -52
  155. package/src/input/submission-intent.ts +0 -1
  156. package/src/input/submission-router.ts +3 -3
  157. package/src/main.ts +18 -8
  158. package/src/panels/approval-panel.ts +8 -8
  159. package/src/panels/automation-control-panel.ts +2 -2
  160. package/src/panels/base-panel.ts +1 -1
  161. package/src/panels/builtin/agent.ts +1 -1
  162. package/src/panels/builtin/operations.ts +1 -10
  163. package/src/panels/builtin/session.ts +9 -9
  164. package/src/panels/builtin/shared.ts +2 -2
  165. package/src/panels/cost-tracker-panel.ts +1 -1
  166. package/src/panels/docs-panel.ts +5 -3
  167. package/src/panels/index.ts +0 -1
  168. package/src/panels/knowledge-panel.ts +6 -5
  169. package/src/panels/memory-panel.ts +7 -6
  170. package/src/panels/panel-list-panel.ts +36 -80
  171. package/src/panels/project-planning-panel.ts +18 -11
  172. package/src/panels/provider-account-snapshot.ts +51 -25
  173. package/src/panels/provider-accounts-panel.ts +33 -18
  174. package/src/panels/provider-health-domains.ts +45 -4
  175. package/src/panels/provider-health-panel.ts +5 -4
  176. package/src/panels/qr-panel.ts +1 -1
  177. package/src/panels/schedule-panel.ts +9 -17
  178. package/src/panels/security-panel.ts +8 -8
  179. package/src/panels/session-browser-panel.ts +10 -10
  180. package/src/panels/subscription-panel.ts +3 -3
  181. package/src/panels/system-messages-panel.ts +1 -1
  182. package/src/panels/tasks-panel.ts +20 -13
  183. package/src/panels/tool-inspector-panel.ts +19 -12
  184. package/src/panels/work-plan-panel.ts +5 -5
  185. package/src/planning/project-planning-coordinator.ts +1 -1
  186. package/src/provider-auth-route-display.ts +9 -0
  187. package/src/renderer/agent-workspace-style.ts +34 -0
  188. package/src/renderer/agent-workspace.ts +228 -52
  189. package/src/renderer/autocomplete-overlay.ts +25 -6
  190. package/src/renderer/bookmark-modal.ts +19 -4
  191. package/src/renderer/buffer.ts +4 -2
  192. package/src/renderer/context-inspector.ts +50 -13
  193. package/src/renderer/diff.ts +1 -1
  194. package/src/renderer/file-picker-overlay.ts +19 -6
  195. package/src/renderer/help-overlay.ts +106 -33
  196. package/src/renderer/history-search-overlay.ts +19 -4
  197. package/src/renderer/live-tail-modal.ts +27 -5
  198. package/src/renderer/mcp-workspace.ts +164 -50
  199. package/src/renderer/model-picker-overlay.ts +58 -2
  200. package/src/renderer/model-workspace.ts +104 -20
  201. package/src/renderer/process-modal.ts +27 -6
  202. package/src/renderer/profile-picker-modal.ts +20 -4
  203. package/src/renderer/search-overlay.ts +25 -5
  204. package/src/renderer/selection-modal-overlay.ts +46 -14
  205. package/src/renderer/session-picker-modal.ts +18 -1
  206. package/src/renderer/settings-modal-helpers.ts +2 -40
  207. package/src/renderer/settings-modal.ts +83 -50
  208. package/src/renderer/tool-call.ts +20 -11
  209. package/src/runtime/agent-runtime-events.ts +6 -6
  210. package/src/runtime/bootstrap-command-context.ts +7 -1
  211. package/src/runtime/bootstrap-command-parts.ts +9 -7
  212. package/src/runtime/bootstrap-core.ts +19 -1
  213. package/src/runtime/bootstrap-hook-bridge.ts +7 -18
  214. package/src/runtime/bootstrap-shell.ts +4 -4
  215. package/src/runtime/bootstrap.ts +31 -22
  216. package/src/runtime/connected-host-auth.ts +7 -2
  217. package/src/runtime/diagnostics/panels/index.ts +2 -2
  218. package/src/runtime/diagnostics/panels/policy.ts +7 -7
  219. package/src/runtime/index.ts +2 -1
  220. package/src/runtime/onboarding/apply-file-helpers.ts +66 -0
  221. package/src/runtime/onboarding/apply.ts +80 -79
  222. package/src/runtime/onboarding/derivation.ts +5 -8
  223. package/src/runtime/onboarding/snapshot.ts +0 -15
  224. package/src/runtime/onboarding/types.ts +8 -19
  225. package/src/runtime/onboarding/verify.ts +32 -10
  226. package/src/runtime/operator-token-cleanup.ts +1 -1
  227. package/src/runtime/services.ts +91 -22
  228. package/src/runtime/store/selectors/index.ts +3 -3
  229. package/src/runtime/store/state.ts +1 -4
  230. package/src/runtime/surface-feature-flags.ts +41 -6
  231. package/src/runtime/ui-read-models.ts +3 -4
  232. package/src/runtime/ui-services.ts +6 -6
  233. package/src/shell/blocking-input.ts +2 -1
  234. package/src/shell/ui-openers.ts +3 -13
  235. package/src/tools/agent-analysis-registry-policy.ts +0 -1
  236. package/src/tools/agent-channel-send-tool.ts +133 -0
  237. package/src/tools/agent-context-policy.ts +1 -1
  238. package/src/tools/agent-knowledge-ingest-tool.ts +405 -0
  239. package/src/tools/agent-knowledge-tool.ts +170 -0
  240. package/src/tools/agent-local-registry-tool.ts +269 -69
  241. package/src/tools/agent-media-generate-tool.ts +133 -0
  242. package/src/tools/agent-notify-tool.ts +143 -0
  243. package/src/tools/agent-operator-action-tool.ts +137 -0
  244. package/src/tools/agent-operator-briefing-tool.ts +217 -0
  245. package/src/tools/agent-reminder-schedule-tool.ts +237 -0
  246. package/src/tools/agent-tool-policy-guard.ts +8 -8
  247. package/src/tools/agent-work-plan-tool.ts +256 -0
  248. package/src/version.ts +1 -1
  249. package/src/input/commands/policy-dispatch.ts +0 -339
  250. package/src/input/commands/policy.ts +0 -13
  251. package/src/panels/panel-picker.ts +0 -105
  252. package/src/panels/policy-panel.ts +0 -308
  253. package/src/renderer/panel-picker-overlay.ts +0 -202
@@ -1,11 +1,12 @@
1
1
  import { createShellPathService } from '@/runtime/index.ts';
2
+ import { formatAgentRecordOrigin, formatAgentRecordReviewState } from '../agent/record-labels.ts';
2
3
  import { discoverRoutines, type DiscoveredRoutineRecord } from '../agent/routine-discovery.ts';
3
4
  import { AgentRoutineRegistry, evaluateAgentRoutineReadiness, type AgentRoutineRecord } from '../agent/routine-registry.ts';
4
5
  import { buildAgentSkillRequirements, formatAgentSkillRequirement } from '../agent/skill-registry.ts';
5
6
  import {
6
7
  buildRoutineSchedulePreview,
7
- promoteRoutineToDaemonSchedule,
8
- resolveAgentDaemonConnection,
8
+ promoteRoutineToConnectedSchedule,
9
+ resolveAgentConnectedHostConnection,
9
10
  } from '../agent/routine-schedule-promotion.ts';
10
11
  import { parseRoutineSchedulePromotionArgs } from '../agent/routine-schedule-args.ts';
11
12
  import {
@@ -146,17 +147,17 @@ function parseImportFlags(args: readonly string[]): {
146
147
 
147
148
  function summarizeRoutine(routine: AgentRoutineRecord): string {
148
149
  const enabled = routine.enabled ? 'enabled' : 'disabled';
149
- const tags = routine.tags.length > 0 ? ` tags=${routine.tags.join(',')}` : '';
150
+ const tags = routine.tags.length > 0 ? ` tags ${routine.tags.join(', ')}` : '';
150
151
  const readiness = evaluateAgentRoutineReadiness(routine);
151
152
  const ready = readiness.ready ? 'ready' : `needs ${readiness.missing.map(formatAgentSkillRequirement).join(',')}`;
152
- return ` ${routine.id} ${enabled} ${routine.reviewState} ${ready} starts=${routine.startCount} ${routine.name} - ${routine.description}${tags}`;
153
+ return ` ${routine.id} ${enabled} ${formatAgentRecordReviewState(routine.reviewState)} ${ready} starts ${routine.startCount} ${routine.name} - ${routine.description}${tags}`;
153
154
  }
154
155
 
155
156
  function summarizeDiscoveredRoutine(routine: DiscoveredRoutineRecord): string {
156
157
  const description = routine.description ? ` - ${routine.description}` : '';
157
158
  return [
158
159
  ` ${routine.name} ${routine.origin}${description}`,
159
- ` path: ${routine.path}`,
160
+ ` path ${routine.path}`,
160
161
  ].join('\n');
161
162
  }
162
163
 
@@ -208,13 +209,13 @@ function renderRoutineList(title: string, path: string, routines: readonly Agent
208
209
  if (routines.length === 0) {
209
210
  return [
210
211
  title,
211
- ` ${emptyMessage ?? 'No local Agent routines yet.'}`,
212
+ ` ${emptyMessage ?? 'No Agent-local routines yet.'}`,
212
213
  emptyMessage ? '' : ' Open /agent routines in the TUI, or use goodvibes-agent routines create for scripted setup.',
213
214
  ].filter(Boolean).join('\n');
214
215
  }
215
216
  return [
216
217
  `${title} (${routines.length})`,
217
- ` store: ${path}`,
218
+ ` store ${path}`,
218
219
  ...routines.map(summarizeRoutine),
219
220
  ].join('\n');
220
221
  }
@@ -223,20 +224,19 @@ function renderRoutine(routine: AgentRoutineRecord): string {
223
224
  const readiness = evaluateAgentRoutineReadiness(routine);
224
225
  return [
225
226
  `Routine ${routine.name}`,
226
- ` id: ${routine.id}`,
227
- ` enabled: ${routine.enabled ? 'yes' : 'no'}`,
228
- ` readiness: ${readiness.ready ? 'ready' : 'needs setup'}`,
229
- ` requirements: ${routine.requirements.map(formatAgentSkillRequirement).join(', ') || '(none)'}`,
230
- readiness.missing.length > 0 ? ` missing: ${readiness.missing.map(formatAgentSkillRequirement).join(', ')}` : '',
231
- ` review: ${routine.reviewState}`,
232
- ` source: ${routine.source}`,
233
- ` provenance: ${routine.provenance}`,
234
- ` tags: ${routine.tags.join(', ') || '(none)'}`,
235
- ` triggers: ${routine.triggers.join(', ') || '(manual)'}`,
236
- ` started: ${routine.startCount}${routine.lastStartedAt ? `; last ${routine.lastStartedAt}` : ''}`,
237
- ` created: ${routine.createdAt}`,
238
- ` updated: ${routine.updatedAt}`,
239
- routine.staleReason ? ` stale reason: ${routine.staleReason}` : '',
227
+ ` id ${routine.id}`,
228
+ ` enabled ${routine.enabled ? 'yes' : 'no'}`,
229
+ ` readiness ${readiness.ready ? 'ready' : 'needs setup'}`,
230
+ ` requirements ${routine.requirements.map(formatAgentSkillRequirement).join(', ') || '(none)'}`,
231
+ readiness.missing.length > 0 ? ` missing ${readiness.missing.map(formatAgentSkillRequirement).join(', ')}` : '',
232
+ ` review ${formatAgentRecordReviewState(routine.reviewState)}`,
233
+ ` origin ${formatAgentRecordOrigin(routine.source, routine.provenance)}`,
234
+ ` tags ${routine.tags.join(', ') || '(none)'}`,
235
+ ` triggers ${routine.triggers.join(', ') || '(manual)'}`,
236
+ ` started ${routine.startCount}${routine.lastStartedAt ? `; last ${routine.lastStartedAt}` : ''}`,
237
+ ` created ${routine.createdAt}`,
238
+ ` updated ${routine.updatedAt}`,
239
+ routine.staleReason ? ` stale reason ${routine.staleReason}` : '',
240
240
  '',
241
241
  routine.description,
242
242
  '',
@@ -267,7 +267,7 @@ async function handleRoutinePromotion(runtime: CliCommandRuntime, args: readonly
267
267
  const failure: RoutinesCommandFailure = {
268
268
  ok: false,
269
269
  kind: 'routine_not_found',
270
- error: `Unknown Agent routine: ${parsed.routineId ?? ''}`,
270
+ error: `Unknown Agent routine ${parsed.routineId ?? ''}`,
271
271
  };
272
272
  return {
273
273
  output: json ? JSON.stringify(failure, null, 2) : failure.error,
@@ -286,8 +286,8 @@ async function handleRoutinePromotion(runtime: CliCommandRuntime, args: readonly
286
286
  exitCode: 0,
287
287
  };
288
288
  }
289
- const connection = resolveAgentDaemonConnection(runtime.configManager, runtime.homeDirectory);
290
- const result = await promoteRoutineToDaemonSchedule(connection, preview);
289
+ const connection = resolveAgentConnectedHostConnection(runtime.configManager, runtime.homeDirectory);
290
+ const result = await promoteRoutineToConnectedSchedule(connection, preview);
291
291
  const receipt = routineReceiptStore(runtime).append(connection, preview, result);
292
292
  if (!result.ok) {
293
293
  return {
@@ -339,7 +339,7 @@ export async function handleRoutinesCommand(runtime: CliCommandRuntime): Promise
339
339
  : 'Agent routines',
340
340
  snapshot.path,
341
341
  routines,
342
- normalized === 'attention' || normalized === 'needs-setup' ? 'No local Agent routines need setup.' : undefined,
342
+ normalized === 'attention' || normalized === 'needs-setup' ? 'No Agent-local routines need setup.' : undefined,
343
343
  )),
344
344
  exitCode: 0,
345
345
  };
@@ -374,7 +374,7 @@ export async function handleRoutinesCommand(runtime: CliCommandRuntime): Promise
374
374
  const failure: RoutinesCommandFailure = {
375
375
  ok: false,
376
376
  kind: 'routine_discovery_not_found',
377
- error: `Unknown discovered Agent routine: ${parsed.name}\nRun goodvibes-agent routines discover to inspect available routine files.`,
377
+ error: `Unknown discovered Agent routine ${parsed.name}\nRun goodvibes-agent routines discover to inspect available routine files.`,
378
378
  };
379
379
  return {
380
380
  output: runtime.cli.flags.outputFormat === 'json' ? JSON.stringify(failure, null, 2) : failure.error,
@@ -390,12 +390,12 @@ export async function handleRoutinesCommand(runtime: CliCommandRuntime): Promise
390
390
  return {
391
391
  output: jsonOrText(runtime, value, [
392
392
  'Agent routine import preview',
393
- ` name: ${discovered.name}`,
394
- ` origin: ${discovered.origin}`,
395
- ` path: ${discovered.path}`,
396
- ` description: ${discovered.description || '(none)'}`,
397
- ` steps characters: ${discovered.steps.length}`,
398
- ' next: rerun with --yes to import into the Agent-local routine registry',
393
+ ` name ${discovered.name}`,
394
+ ` origin ${discovered.origin}`,
395
+ ` path ${discovered.path}`,
396
+ ` description ${discovered.description || '(none)'}`,
397
+ ` steps characters ${discovered.steps.length}`,
398
+ ' next rerun with --yes to import into the Agent-local routine registry',
399
399
  ].join('\n')),
400
400
  exitCode: 0,
401
401
  };
@@ -420,7 +420,11 @@ export async function handleRoutinesCommand(runtime: CliCommandRuntime): Promise
420
420
  data: routine,
421
421
  };
422
422
  return {
423
- output: jsonOrText(runtime, value, `Imported Agent routine ${routine.id}: ${routine.name}${routine.enabled ? ' (enabled)' : ''}`),
423
+ output: jsonOrText(runtime, value, [
424
+ `Imported Agent routine ${routine.id}: ${routine.name}${routine.enabled ? ' (enabled)' : ''}`,
425
+ ` name ${routine.name}`,
426
+ ` enabled ${routine.enabled ? 'yes' : 'no'}`,
427
+ ].join('\n')),
424
428
  exitCode: 0,
425
429
  };
426
430
  }
@@ -447,7 +451,11 @@ export async function handleRoutinesCommand(runtime: CliCommandRuntime): Promise
447
451
  data: routine,
448
452
  };
449
453
  return {
450
- output: jsonOrText(runtime, value, `Created Agent routine ${routine.id}: ${routine.name}${routine.enabled ? ' (enabled)' : ''}`),
454
+ output: jsonOrText(runtime, value, [
455
+ `Created Agent routine ${routine.id}: ${routine.name}${routine.enabled ? ' (enabled)' : ''}`,
456
+ ` name ${routine.name}`,
457
+ ` enabled ${routine.enabled ? 'yes' : 'no'}`,
458
+ ].join('\n')),
451
459
  exitCode: 0,
452
460
  };
453
461
  } catch (error) {
@@ -491,7 +499,7 @@ export async function handleRoutinesCommand(runtime: CliCommandRuntime): Promise
491
499
  if (normalized === 'reconcile' || normalized === 'sync' || normalized === 'status') {
492
500
  const store = routineReceiptStore(runtime);
493
501
  const result = await reconcileRoutineScheduleReceipts(
494
- resolveAgentDaemonConnection(runtime.configManager, runtime.homeDirectory),
502
+ resolveAgentConnectedHostConnection(runtime.configManager, runtime.homeDirectory),
495
503
  store.snapshot(),
496
504
  );
497
505
  return {
@@ -504,7 +512,7 @@ export async function handleRoutinesCommand(runtime: CliCommandRuntime): Promise
504
512
  if (!id) return { output: 'Usage: goodvibes-agent routines receipt <receipt-id>', exitCode: 2 };
505
513
  const receipt = routineReceiptStore(runtime).get(id);
506
514
  if (!receipt) {
507
- const failure: RoutinesCommandFailure = { ok: false, kind: 'routine_schedule_receipt_not_found', error: `Unknown routine schedule receipt: ${id}` };
515
+ const failure: RoutinesCommandFailure = { ok: false, kind: 'routine_schedule_receipt_not_found', error: `Unknown routine schedule receipt ${id}` };
508
516
  return {
509
517
  output: runtime.cli.flags.outputFormat === 'json' ? JSON.stringify(failure, null, 2) : failure.error,
510
518
  exitCode: 1,
@@ -1,7 +1,7 @@
1
1
  import { closeSync, existsSync, openSync, readSync, statSync } from 'node:fs';
2
2
  import net from 'node:net';
3
3
  import { isAbsolute, join } from 'node:path';
4
- import type { ConfigManager } from '../config/index.ts';
4
+ import type { ConfigKey, ConfigManager } from '../config/index.ts';
5
5
  import { resolveRuntimeEndpointBinding } from './endpoints.ts';
6
6
  import type { RuntimeEndpointBinding, RuntimeEndpointId } from './endpoints.ts';
7
7
  import { classifyBindPosture, isNetworkFacing } from './network-posture.ts';
@@ -60,7 +60,7 @@ export interface CliServicePosture {
60
60
  readonly issues: readonly string[];
61
61
  }
62
62
 
63
- const ENDPOINTS: readonly { readonly id: RuntimeEndpointId; readonly label: string; readonly enabledKey: string }[] = [
63
+ const ENDPOINTS: readonly { readonly id: RuntimeEndpointId; readonly label: string; readonly enabledKey: ConfigKey }[] = [
64
64
  { id: 'controlPlane', label: 'runtime connection', enabledKey: 'controlPlane.enabled' },
65
65
  { id: 'httpListener', label: 'inbound events endpoint', enabledKey: 'danger.httpListener' },
66
66
  { id: 'web', label: 'browser companion route', enabledKey: 'web.enabled' },
@@ -161,7 +161,7 @@ export async function buildCliServicePosture(
161
161
  options: CliServicePostureOptions = {},
162
162
  ): Promise<CliServicePosture> {
163
163
  const endpoints = await Promise.all(ENDPOINTS.map(async (endpoint): Promise<CliServiceEndpointPosture> => {
164
- const enabled = runtime.configManager.get(endpoint.enabledKey as never) === true;
164
+ const enabled = runtime.configManager.get(endpoint.enabledKey) === true;
165
165
  const binding = resolveRuntimeEndpointBinding(runtime.configManager, endpoint.id);
166
166
  return {
167
167
  id: endpoint.id,
@@ -232,12 +232,12 @@ export function formatCliServicePosture(posture: CliServicePosture, json = false
232
232
  ` log: ${posture.log.path ?? 'n/a'} (${posture.log.exists ? 'present' : 'missing'})`,
233
233
  ...(posture.log.readError ? [` log read error: ${posture.log.readError}`] : []),
234
234
  '',
235
- 'Connected API checks:',
235
+ 'Connected API checks',
236
236
  ...posture.endpoints.map((endpoint) =>
237
- ` ${endpoint.label}: enabled=${yesNo(endpoint.enabled)} ${endpoint.binding.hostMode} ${endpoint.binding.host}:${endpoint.binding.port} posture=${endpoint.bindPosture.label}${endpoint.reachable === undefined ? '' : ` reachable=${yesNo(endpoint.reachable)}`}`,
237
+ ` ${endpoint.label} enabled ${yesNo(endpoint.enabled)} ${endpoint.binding.hostMode} ${endpoint.binding.host}:${endpoint.binding.port} posture ${endpoint.bindPosture.label}${endpoint.reachable === undefined ? '' : ` reachable ${yesNo(endpoint.reachable)}`}`,
238
238
  ),
239
239
  '',
240
- posture.issues.length === 0 ? 'Readiness: ready' : 'Readiness: needs attention',
240
+ posture.issues.length === 0 ? 'Readiness ready' : 'Readiness needs attention',
241
241
  ...posture.issues.map((issue) => ` - ${issue}`),
242
242
  ].join('\n');
243
243
  }
package/src/cli/status.ts CHANGED
@@ -1,11 +1,12 @@
1
1
  import type { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
2
2
  import type { OnboardingCheckMarkersState } from '../runtime/onboarding/index.ts';
3
3
  import { resolveRuntimeEndpointBinding } from './endpoints.ts';
4
- import { isNetworkFacing } from './network-posture.ts';
5
4
  import type { GoodVibesCliOutputFormat } from './types.ts';
6
5
  import type { CliServicePosture } from './service-posture.ts';
7
6
  import type { CliExternalRuntimeSnapshot } from './external-runtime.ts';
8
7
  import { getProviderIdFromModel } from '../config/provider-model.ts';
8
+ import { formatAgentRecordSource } from '../agent/record-labels.ts';
9
+ import { formatAgentKnowledgeFailureKind } from './agent-knowledge-format.ts';
9
10
 
10
11
  export interface CliStatusOptions {
11
12
  readonly configManager: Pick<ConfigManager, 'get'>;
@@ -30,7 +31,7 @@ export interface CliAuthStatus {
30
31
 
31
32
  export interface CliDoctorFinding {
32
33
  readonly id: string;
33
- readonly area: 'auth' | 'network' | 'onboarding' | 'runtime' | 'security' | 'secrets';
34
+ readonly area: 'auth' | 'onboarding' | 'runtime' | 'security' | 'secrets';
34
35
  readonly severity: 'warning' | 'risk';
35
36
  readonly summary: string;
36
37
  readonly cause: string;
@@ -92,29 +93,11 @@ function secretPolicyLabel(policy: unknown): string {
92
93
  return String(policy ?? 'unknown');
93
94
  }
94
95
 
95
- function bindLine(label: string, enabled: unknown, binding: { readonly hostMode: string; readonly host: string; readonly port: number }): string {
96
- return ` ${label}: ${yesNo(enabled)} (${binding.hostMode} ${binding.host}:${binding.port})`;
97
- }
98
-
99
96
  export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliDoctorFinding[] {
100
97
  const config = options.configManager;
101
- const serviceEnabled = config.get('service.enabled') === true;
102
- const daemonEnabled = config.get('danger.daemon') === true;
103
- const listenerEnabled = config.get('danger.httpListener') === true;
104
- const webEnabled = config.get('web.enabled') === true;
105
- const controlPlaneEnabled = config.get('controlPlane.enabled') === true;
106
- const controlPlaneBinding = resolveRuntimeEndpointBinding(config, 'controlPlane');
107
- const httpListenerBinding = resolveRuntimeEndpointBinding(config, 'httpListener');
108
- const webBinding = resolveRuntimeEndpointBinding(config, 'web');
109
98
  const permissionMode = config.get('permissions.mode');
110
99
  const secretPolicy = config.get('storage.secretPolicy');
111
100
  const marker = options.onboardingMarkers?.effective;
112
- const serverBackedEnabled = daemonEnabled || controlPlaneEnabled || listenerEnabled || webEnabled;
113
- const networkFacingSurfaces = [
114
- ['runtime connection', controlPlaneEnabled, controlPlaneBinding],
115
- ['inbound events endpoint', listenerEnabled, httpListenerBinding],
116
- ['browser companion route', webEnabled, webBinding],
117
- ].filter(([, enabled, binding]) => isNetworkFacing(enabled, binding as typeof controlPlaneBinding));
118
101
 
119
102
  const findings: CliDoctorFinding[] = [];
120
103
 
@@ -127,7 +110,7 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
127
110
  summary: 'Connected GoodVibes host is not reachable.',
128
111
  cause: `Agent could not reach ${options.externalRuntime.baseUrl}${options.externalRuntime.error ? `: ${options.externalRuntime.error}` : '.'}`,
129
112
  impact: 'Companion chat, isolated Agent Knowledge, approvals, automation status, and build delegation cannot work until the connected GoodVibes host is available.',
130
- action: 'Start or repair the owning GoodVibes host, then rerun goodvibes-agent status.',
113
+ action: 'Make the owning GoodVibes host available, then rerun goodvibes-agent status.',
131
114
  });
132
115
  } else if (!options.externalRuntime.compatible) {
133
116
  findings.push({
@@ -160,24 +143,12 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
160
143
  severity: 'warning',
161
144
  summary: 'Isolated Agent Knowledge route is not ready.',
162
145
  cause: `${options.externalRuntime.agentKnowledge.route} returned ${options.externalRuntime.agentKnowledge.kind}${options.externalRuntime.agentKnowledge.statusCode === null ? '' : ` (${options.externalRuntime.agentKnowledge.statusCode})`}.`,
163
- impact: 'Agent Knowledge ask/search will not use any fallback wiki or non-Agent knowledge segment; it will fail closed until the Agent route is available.',
146
+ impact: 'Agent Knowledge ask/search will not use default or non-Agent knowledge fallback; it will fail closed until the Agent route is available.',
164
147
  action: 'Update the connected GoodVibes host to the Agent-compatible SDK and verify goodvibes-agent compat.',
165
148
  });
166
149
  }
167
150
  }
168
151
 
169
- if (serverBackedEnabled && !serviceEnabled) {
170
- findings.push({
171
- id: 'runtime-ownership-external',
172
- area: 'runtime',
173
- severity: 'warning',
174
- summary: 'Connected-host settings are present while Agent host ownership is disabled by design.',
175
- cause: 'One or more connected API, inbound events, or browser companion settings are enabled while service.enabled is false.',
176
- impact: 'The owning GoodVibes host must provide availability for those endpoints; Agent will not start or enable them.',
177
- action: 'Manage connected-host availability from GoodVibes TUI or the owning host, then use Agent for read-only diagnostics.',
178
- });
179
- }
180
-
181
152
  if (options.service) {
182
153
  for (const issue of options.service.issues) {
183
154
  if (findings.some((finding) => finding.summary === issue)) continue;
@@ -187,8 +158,8 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
187
158
  severity: 'warning',
188
159
  summary: issue,
189
160
  cause: 'The connected-host inspection found a mismatch between configured endpoint state and observed host state.',
190
- impact: 'Connected API, listener, or web availability may not match the configuration.',
191
- action: 'Use Agent status and doctor diagnostics here, then manage the connected host outside Agent.',
161
+ impact: 'Companion chat, Agent Knowledge, approvals, automation status, or build delegation may be unavailable.',
162
+ action: 'Use Agent status and doctor diagnostics here, then repair the connected GoodVibes host outside Agent.',
192
163
  });
193
164
  }
194
165
  }
@@ -200,35 +171,11 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
200
171
  severity: 'warning',
201
172
  summary: 'Agent setup has not been applied for this user.',
202
173
  cause: 'No global user setup completion marker was found.',
203
- impact: 'Important connected-host, network, provider, auth, or permission choices may still be implicit defaults.',
174
+ impact: 'Provider/model, Agent Knowledge, local behavior, channel, and permission choices may still be implicit defaults.',
204
175
  action: 'Run /setup in GoodVibes Agent or goodvibes-agent setup status to review setup state.',
205
176
  });
206
177
  }
207
178
 
208
- if (networkFacingSurfaces.length > 0 && options.auth?.userStorePresent !== true) {
209
- findings.push({
210
- id: 'network-endpoint-without-runtime-auth-signal',
211
- area: 'auth',
212
- severity: 'risk',
213
- summary: 'Network-facing connected-host endpoints are enabled without a visible auth signal.',
214
- cause: `${networkFacingSurfaces.map(([name]) => name).join(', ')} are LAN/custom-bound, but Agent cannot see connected-host auth state from its local compatibility files.`,
215
- impact: 'Remote access paths may be unusable or unsafe unless the owning host configured auth.',
216
- action: 'Review connected-host auth outside Agent; Agent will not create local service users.',
217
- });
218
- }
219
-
220
- if (networkFacingSurfaces.length > 0 && options.auth?.bootstrapCredentialPresent === true) {
221
- findings.push({
222
- id: 'network-endpoint-with-bootstrap-credential',
223
- area: 'auth',
224
- severity: 'risk',
225
- summary: 'A bootstrap credential is still present while network-facing surfaces are enabled.',
226
- cause: `${networkFacingSurfaces.map(([name]) => name).join(', ')} are LAN/custom-bound and auth-bootstrap.txt exists.`,
227
- impact: 'Bootstrap credentials should be treated as temporary setup material, not long-lived network access credentials.',
228
- action: 'Replace bootstrap auth and retire the bootstrap credential outside Agent.',
229
- });
230
- }
231
-
232
179
  if (permissionMode === 'allow-all') {
233
180
  findings.push({
234
181
  id: 'allow-all-permissions',
@@ -253,18 +200,6 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
253
200
  });
254
201
  }
255
202
 
256
- if (listenerEnabled && isNetworkFacing(listenerEnabled, httpListenerBinding)) {
257
- findings.push({
258
- id: 'network-http-listener-enabled',
259
- area: 'network',
260
- severity: 'warning',
261
- summary: 'The inbound events endpoint is reachable beyond loopback.',
262
- cause: `Inbound events endpoint is enabled on ${httpListenerBinding.host}:${httpListenerBinding.port} with ${httpListenerBinding.hostMode} binding.`,
263
- impact: 'External tools and devices may be able to reach incoming event endpoints.',
264
- action: 'Keep inbound-event secrets/signature checks configured for every enabled endpoint.',
265
- });
266
- }
267
-
268
203
  return findings;
269
204
  }
270
205
 
@@ -315,13 +250,6 @@ export function buildCliStatusSnapshot(options: CliStatusOptions): CliStatusSnap
315
250
  export function renderCliStatus(options: CliStatusOptions): string {
316
251
  const config = options.configManager;
317
252
  const snapshot = buildCliStatusSnapshot(options);
318
- const serviceEnabled = snapshot.runtimeConnection.enabled;
319
- const controlPlaneEnabled = snapshot.runtimeEndpoints.controlPlane.enabled;
320
- const listenerEnabled = snapshot.runtimeEndpoints.httpListener.enabled;
321
- const webEnabled = snapshot.runtimeEndpoints.web.enabled;
322
- const controlPlaneBinding = snapshot.runtimeEndpoints.controlPlane;
323
- const httpListenerBinding = snapshot.runtimeEndpoints.httpListener;
324
- const webBinding = snapshot.runtimeEndpoints.web;
325
253
  const marker = options.onboardingMarkers?.effective;
326
254
  const findings = snapshot.findings;
327
255
  const externalRuntime = snapshot.externalRuntime;
@@ -330,66 +258,63 @@ export function renderCliStatus(options: CliStatusOptions): string {
330
258
 
331
259
  const lines = [
332
260
  snapshot.title,
333
- ` workingDir: ${options.workingDirectory}`,
334
- ` homeDir: ${options.homeDirectory}`,
261
+ ` working directory ${options.workingDirectory}`,
262
+ ` home directory ${options.homeDirectory}`,
335
263
  '',
336
- 'Provider:',
337
- ` provider: ${getProviderIdFromModel(config.get('provider.model'))}`,
338
- ` model: ${String(config.get('provider.model'))}`,
339
- ` reasoning: ${String(config.get('provider.reasoningEffort'))}`,
264
+ 'Provider',
265
+ ` provider ${getProviderIdFromModel(config.get('provider.model'))}`,
266
+ ` model ${String(config.get('provider.model'))}`,
267
+ ` reasoning ${String(config.get('provider.reasoningEffort'))}`,
340
268
  '',
341
- 'Auth:',
269
+ 'Auth',
342
270
  ` permissions: ${permissionModeLabel(config.get('permissions.mode'))} (${String(config.get('permissions.mode'))})`,
343
271
  ` secretPolicy: ${secretPolicyLabel(config.get('storage.secretPolicy'))} (${String(config.get('storage.secretPolicy'))})`,
344
272
  options.auth
345
- ? ` runtimeAuthSignal: ${options.auth.userStorePresent ? 'present' : 'missing'} (${options.auth.userStorePath})`
346
- : ' runtimeAuthSignal: unknown',
273
+ ? ` local auth store ${options.auth.userStorePresent ? 'present' : 'missing'} (${options.auth.userStorePath})`
274
+ : ' local auth store unknown',
347
275
  options.auth
348
- ? ` bootstrapCredential: ${options.auth.bootstrapCredentialPresent ? 'present' : 'missing'} (${options.auth.bootstrapCredentialPath})`
349
- : ' bootstrapCredential: unknown',
276
+ ? ` setup credential ${options.auth.bootstrapCredentialPresent ? 'present' : 'missing'} (${options.auth.bootstrapCredentialPath})`
277
+ : ' setup credential unknown',
350
278
  options.auth
351
- ? ` operatorTokens: ${options.auth.operatorTokenPresent ? 'present' : 'missing'} (${options.auth.operatorTokenPath})`
352
- : ' operatorTokens: unknown',
279
+ ? ` connected host token ${options.auth.operatorTokenPresent ? 'present' : 'missing'} (${options.auth.operatorTokenPath})`
280
+ : ' connected host token unknown',
353
281
  '',
354
- 'Connected GoodVibes API:',
282
+ 'Connected GoodVibes host:',
355
283
  ...(externalRuntime ? [
356
284
  ` baseUrl: ${externalRuntime.baseUrl}`,
357
285
  ` reachable: ${yesNo(externalRuntime.reachable)}${externalRuntime.statusCode === null ? '' : ` (HTTP ${externalRuntime.statusCode})`}`,
358
286
  ` sdk: ${externalRuntime.version} expected ${externalRuntime.expectedVersion}`,
359
287
  ` compatible: ${yesNo(externalRuntime.compatible)}`,
360
- ` operatorToken: ${externalRuntime.operatorToken.present ? 'present' : 'missing'} (${externalRuntime.operatorToken.path})`,
361
- ` Agent Knowledge: ${externalRuntime.agentKnowledge.ready ? 'ready' : `not ready (${externalRuntime.agentKnowledge.kind})`}`,
288
+ ` access token: ${externalRuntime.operatorToken.present ? 'present' : 'missing'} (${externalRuntime.operatorToken.path})`,
289
+ ` Agent Knowledge: ${externalRuntime.agentKnowledge.ready ? 'ready' : `not ready (${formatAgentKnowledgeFailureKind(externalRuntime.agentKnowledge.kind)})`}`,
362
290
  ...(externalRuntime.error ? [` error: ${externalRuntime.error}`] : []),
363
291
  ] : [
364
- ' live check: unavailable',
292
+ ' live check unavailable',
365
293
  ]),
366
294
  '',
367
- 'Connected Host:',
368
- ' Agent role: client/operator TUI only',
369
- ' lifecycle owner: outside Agent',
370
- ' Agent starts connected host: no',
295
+ 'Agent role:',
296
+ ' product: interactive operator TUI',
297
+ ' host lifecycle: external',
298
+ ' starts or exposes host: no',
371
299
  ...(options.service ? [
372
- ` log: ${options.service.log.path ?? 'n/a'} (${options.service.log.exists ? 'present' : 'missing'})`,
300
+ ` host log signal ${options.service.log.path ?? 'n/a'} (${options.service.log.exists ? 'present' : 'missing'})`,
373
301
  ] : []),
374
- 'Onboarding:',
375
- ` checked: ${marker?.exists ? 'yes' : 'no'}`,
376
- ` scope: ${marker?.scope ?? 'none'}`,
377
- ` updatedAt: ${marker?.payload ? new Date(marker.payload.updatedAt).toISOString() : 'n/a'}`,
302
+ 'Onboarding',
303
+ ` checked ${marker?.exists ? 'yes' : 'no'}`,
304
+ ` scope ${marker?.scope ?? 'none'}`,
305
+ ` updated ${marker?.payload ? new Date(marker.payload.updatedAt).toISOString() : 'n/a'}`,
378
306
  ];
379
307
 
380
308
  if (options.doctor) {
381
309
  lines.push(
382
310
  '',
383
- 'Connected Host Config Signals:',
384
- ` host config present: ${yesNo(serviceEnabled)}`,
385
- ' lifecycle config: external to Agent',
386
- '',
387
- 'Endpoint Diagnostics:',
388
- bindLine('runtimeApi', controlPlaneEnabled, controlPlaneBinding),
389
- bindLine('incomingWebhook', listenerEnabled, httpListenerBinding),
390
- bindLine('browserCompanion', webEnabled, webBinding),
311
+ 'Readiness Checks:',
312
+ ' companion chat route',
313
+ ' isolated Agent Knowledge route',
314
+ ' approvals and automation status routes',
315
+ ' explicit build delegation route',
391
316
  );
392
- lines.push('', 'Warnings:');
317
+ lines.push('', 'Warnings');
393
318
  if (findings.length === 0) {
394
319
  lines.push(' none');
395
320
  } else {
@@ -411,11 +336,11 @@ export function renderOnboardingCliStatus(options: CliStatusOptions): string {
411
336
  const marker = options.onboardingMarkers?.effective;
412
337
  return [
413
338
  'GoodVibes Agent setup status',
414
- ` checked: ${marker?.exists ? 'yes' : 'no'}`,
415
- ` scope: ${marker?.scope ?? 'none'}`,
416
- ` source: ${marker?.payload?.source ?? 'n/a'}`,
417
- ` mode: ${marker?.payload?.mode ?? 'n/a'}`,
418
- ` updatedAt: ${marker?.payload ? new Date(marker.payload.updatedAt).toISOString() : 'n/a'}`,
419
- ` workingDir: ${options.workingDirectory}`,
339
+ ` checked ${marker?.exists ? 'yes' : 'no'}`,
340
+ ` scope ${marker?.scope ?? 'none'}`,
341
+ ` origin ${marker?.payload?.source ? formatAgentRecordSource(marker.payload.source) : 'n/a'}`,
342
+ ` mode ${marker?.payload?.mode ?? 'n/a'}`,
343
+ ` updated ${marker?.payload ? new Date(marker.payload.updatedAt).toISOString() : 'n/a'}`,
344
+ ` working directory ${options.workingDirectory}`,
420
345
  ].join('\n');
421
346
  }
@@ -0,0 +1,131 @@
1
+ import type { ConversationMessageSnapshot, TokenUsage } from './conversation.ts';
2
+ import type { ContentPart } from '@pellux/goodvibes-sdk/platform/providers';
3
+ import type { ToolCall } from '@pellux/goodvibes-sdk/platform/types';
4
+
5
+ function isRecord(value: unknown): value is Record<string, unknown> {
6
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
7
+ }
8
+
9
+ function readString(value: unknown): string | undefined {
10
+ return typeof value === 'string' ? value : undefined;
11
+ }
12
+
13
+ function readBoolean(value: unknown): boolean | undefined {
14
+ return typeof value === 'boolean' ? value : undefined;
15
+ }
16
+
17
+ function readContentPart(value: unknown): ContentPart | null {
18
+ if (!isRecord(value)) return null;
19
+ if (value.type === 'text' && typeof value.text === 'string') {
20
+ return { type: 'text', text: value.text };
21
+ }
22
+ if (value.type === 'image' && typeof value.data === 'string' && typeof value.mediaType === 'string') {
23
+ return { type: 'image', data: value.data, mediaType: value.mediaType };
24
+ }
25
+ return null;
26
+ }
27
+
28
+ function readContent(value: unknown): string | ContentPart[] | null {
29
+ if (typeof value === 'string') return value;
30
+ if (!Array.isArray(value)) return null;
31
+ const parts: ContentPart[] = [];
32
+ for (const entry of value) {
33
+ const part = readContentPart(entry);
34
+ if (!part) return null;
35
+ parts.push(part);
36
+ }
37
+ return parts;
38
+ }
39
+
40
+ function readTokenUsage(value: unknown): TokenUsage | undefined {
41
+ if (!isRecord(value) || typeof value.inputTokens !== 'number' || typeof value.outputTokens !== 'number') return undefined;
42
+ return {
43
+ inputTokens: value.inputTokens,
44
+ outputTokens: value.outputTokens,
45
+ ...(typeof value.cacheReadTokens === 'number' ? { cacheReadTokens: value.cacheReadTokens } : {}),
46
+ ...(typeof value.cacheWriteTokens === 'number' ? { cacheWriteTokens: value.cacheWriteTokens } : {}),
47
+ };
48
+ }
49
+
50
+ function readToolCalls(value: unknown): ToolCall[] | null {
51
+ if (!Array.isArray(value)) return null;
52
+ const calls: ToolCall[] = [];
53
+ for (const entry of value) {
54
+ if (!isRecord(entry) || typeof entry.id !== 'string' || typeof entry.name !== 'string' || !isRecord(entry.arguments)) return null;
55
+ calls.push({ id: entry.id, name: entry.name, arguments: entry.arguments });
56
+ }
57
+ return calls;
58
+ }
59
+
60
+ function hasOwn(record: Record<string, unknown>, key: string): boolean {
61
+ return Object.prototype.hasOwnProperty.call(record, key);
62
+ }
63
+
64
+ function readConversationMessageSnapshot(value: unknown): ConversationMessageSnapshot | null {
65
+ if (!isRecord(value)) return null;
66
+ if (value.role === 'user') {
67
+ const content = readContent(value.content);
68
+ if (content === null) return null;
69
+ const cancelled = hasOwn(value, 'cancelled') ? readBoolean(value.cancelled) : undefined;
70
+ if (hasOwn(value, 'cancelled') && cancelled === undefined) return null;
71
+ return {
72
+ role: 'user',
73
+ content,
74
+ ...(cancelled !== undefined ? { cancelled } : {}),
75
+ };
76
+ }
77
+ if (value.role === 'assistant') {
78
+ const content = readString(value.content);
79
+ if (content === undefined) return null;
80
+ const toolCalls = hasOwn(value, 'toolCalls') ? readToolCalls(value.toolCalls) : undefined;
81
+ if (toolCalls === null) return null;
82
+ const reasoningContent = hasOwn(value, 'reasoningContent') ? readString(value.reasoningContent) : undefined;
83
+ if (hasOwn(value, 'reasoningContent') && reasoningContent === undefined) return null;
84
+ const reasoningSummary = hasOwn(value, 'reasoningSummary') ? readString(value.reasoningSummary) : undefined;
85
+ if (hasOwn(value, 'reasoningSummary') && reasoningSummary === undefined) return null;
86
+ const usage = hasOwn(value, 'usage') ? readTokenUsage(value.usage) : undefined;
87
+ if (hasOwn(value, 'usage') && usage === undefined) return null;
88
+ const model = hasOwn(value, 'model') ? readString(value.model) : undefined;
89
+ if (hasOwn(value, 'model') && model === undefined) return null;
90
+ const provider = hasOwn(value, 'provider') ? readString(value.provider) : undefined;
91
+ if (hasOwn(value, 'provider') && provider === undefined) return null;
92
+ return {
93
+ role: 'assistant',
94
+ content,
95
+ ...(toolCalls !== undefined ? { toolCalls } : {}),
96
+ ...(reasoningContent !== undefined ? { reasoningContent } : {}),
97
+ ...(reasoningSummary !== undefined ? { reasoningSummary } : {}),
98
+ ...(usage !== undefined ? { usage } : {}),
99
+ ...(model !== undefined ? { model } : {}),
100
+ ...(provider !== undefined ? { provider } : {}),
101
+ };
102
+ }
103
+ if (value.role === 'system' && typeof value.content === 'string') {
104
+ return { role: 'system', content: value.content };
105
+ }
106
+ if (value.role === 'tool' && typeof value.callId === 'string' && typeof value.content === 'string') {
107
+ const toolName = hasOwn(value, 'toolName') ? readString(value.toolName) : undefined;
108
+ if (hasOwn(value, 'toolName') && toolName === undefined) return null;
109
+ return {
110
+ role: 'tool',
111
+ callId: value.callId,
112
+ content: value.content,
113
+ ...(toolName !== undefined ? { toolName } : {}),
114
+ };
115
+ }
116
+ return null;
117
+ }
118
+
119
+ export function readConversationMessageSnapshots(messages: readonly object[]): ConversationMessageSnapshot[] {
120
+ const snapshots: ConversationMessageSnapshot[] = [];
121
+ for (const [index, message] of messages.entries()) {
122
+ const snapshot = readConversationMessageSnapshot(message);
123
+ if (!snapshot) throw new Error(`Invalid saved conversation message at index ${index}.`);
124
+ snapshots.push(snapshot);
125
+ }
126
+ return snapshots;
127
+ }
128
+
129
+ export function conversationMessagesAsSessionRecords(messages: readonly ConversationMessageSnapshot[]): Array<Record<string, unknown>> {
130
+ return messages.map((message) => ({ ...message }));
131
+ }