@pellux/goodvibes-agent 0.1.117 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. package/CHANGELOG.md +20 -4
  2. package/README.md +35 -14
  3. package/bin/goodvibes-agent.ts +16 -2
  4. package/dist/package/main.js +176093 -171065
  5. package/docs/README.md +11 -5
  6. package/docs/channels-remote-and-api.md +50 -0
  7. package/docs/connected-host.md +3 -3
  8. package/docs/getting-started.md +29 -15
  9. package/docs/knowledge-artifacts-and-multimodal.md +91 -0
  10. package/docs/project-planning.md +79 -0
  11. package/docs/providers-and-routing.md +46 -0
  12. package/docs/release-and-publishing.md +44 -9
  13. package/docs/tools-and-commands.md +123 -0
  14. package/docs/voice-and-live-tts.md +51 -0
  15. package/package.json +2 -5
  16. package/src/agent/channel-delivery.ts +201 -0
  17. package/src/agent/media-generation.ts +159 -0
  18. package/src/agent/memory-prompt.ts +0 -1
  19. package/src/agent/note-registry.ts +329 -0
  20. package/src/agent/operator-actions.ts +343 -0
  21. package/src/agent/persona-registry.ts +15 -14
  22. package/src/agent/record-labels.ts +107 -0
  23. package/src/agent/reminder-schedule-format.ts +33 -24
  24. package/src/agent/reminder-schedule.ts +26 -25
  25. package/src/agent/routine-registry.ts +13 -12
  26. package/src/agent/routine-schedule-args.ts +2 -1
  27. package/src/agent/routine-schedule-format.ts +77 -53
  28. package/src/agent/routine-schedule-promotion.ts +34 -32
  29. package/src/agent/routine-schedule-receipts.ts +28 -26
  30. package/src/agent/runtime-profile-starters.ts +2 -2
  31. package/src/agent/runtime-profile.ts +18 -17
  32. package/src/agent/skill-registry.ts +25 -24
  33. package/src/cli/agent-knowledge-args.ts +5 -1
  34. package/src/cli/agent-knowledge-command.ts +39 -33
  35. package/src/cli/agent-knowledge-format.ts +80 -67
  36. package/src/cli/agent-knowledge-methods.ts +1 -1
  37. package/src/cli/agent-knowledge-runtime.ts +32 -26
  38. package/src/cli/bundle-command.ts +13 -8
  39. package/src/cli/config-overrides.ts +37 -36
  40. package/src/cli/external-runtime.ts +10 -4
  41. package/src/cli/help.ts +29 -11
  42. package/src/cli/local-library-command.ts +159 -73
  43. package/src/cli/management-commands.ts +98 -62
  44. package/src/cli/management.ts +52 -26
  45. package/src/cli/memory-command.ts +66 -32
  46. package/src/cli/parser.ts +37 -24
  47. package/src/cli/profiles-command.ts +52 -35
  48. package/src/cli/provider-auth-routes.ts +2 -1
  49. package/src/cli/routines-command.ts +59 -39
  50. package/src/cli/service-posture.ts +6 -6
  51. package/src/cli/status.ts +46 -121
  52. package/src/core/conversation-message-snapshot.ts +131 -0
  53. package/src/input/agent-workspace-activation.ts +33 -7
  54. package/src/input/agent-workspace-basic-command-editor-submission.ts +7 -3
  55. package/src/input/agent-workspace-basic-command-editors.ts +30 -10
  56. package/src/input/agent-workspace-categories.ts +276 -64
  57. package/src/input/agent-workspace-channel-command-editor-submission.ts +34 -0
  58. package/src/input/agent-workspace-channel-command-editors.ts +23 -5
  59. package/src/input/agent-workspace-channels.ts +35 -2
  60. package/src/input/agent-workspace-command-editor.ts +18 -2
  61. package/src/input/agent-workspace-config-reader.ts +16 -0
  62. package/src/input/agent-workspace-delegation-editor-submission.ts +1 -1
  63. package/src/input/agent-workspace-editors.ts +140 -2
  64. package/src/input/agent-workspace-knowledge-query-editor.ts +1 -1
  65. package/src/input/agent-workspace-knowledge-url-editor.ts +4 -11
  66. package/src/input/agent-workspace-learned-behavior.ts +2 -2
  67. package/src/input/agent-workspace-library-command-editor-submission.ts +1 -1
  68. package/src/input/agent-workspace-library-command-editors.ts +2 -2
  69. package/src/input/agent-workspace-local-operations.ts +218 -0
  70. package/src/input/agent-workspace-local-selection.ts +75 -0
  71. package/src/input/agent-workspace-media-command-editor-submission.ts +62 -0
  72. package/src/input/agent-workspace-media-command-editors.ts +27 -0
  73. package/src/input/agent-workspace-memory-command-editors.ts +1 -1
  74. package/src/input/agent-workspace-memory-editor.ts +2 -2
  75. package/src/input/agent-workspace-navigation.ts +38 -2
  76. package/src/input/agent-workspace-notify-editor-submission.ts +16 -2
  77. package/src/input/agent-workspace-operations-command-editor-submission.ts +63 -1
  78. package/src/input/agent-workspace-operations-command-editors.ts +80 -3
  79. package/src/input/agent-workspace-panel-route.ts +43 -0
  80. package/src/input/agent-workspace-reminder-schedule-editor.ts +1 -1
  81. package/src/input/agent-workspace-routine-schedule-editor.ts +2 -2
  82. package/src/input/agent-workspace-search.ts +169 -0
  83. package/src/input/agent-workspace-setup.ts +22 -11
  84. package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +6 -6
  85. package/src/input/agent-workspace-skill-bundle-command-editors.ts +10 -10
  86. package/src/input/agent-workspace-snapshot.ts +112 -13
  87. package/src/input/agent-workspace-task-command-editors.ts +4 -4
  88. package/src/input/agent-workspace-token.ts +18 -2
  89. package/src/input/agent-workspace-types.ts +92 -4
  90. package/src/input/agent-workspace-voice-media.ts +3 -6
  91. package/src/input/agent-workspace-web-research-editor.ts +104 -0
  92. package/src/input/agent-workspace.ts +136 -208
  93. package/src/input/command-registry.ts +4 -1
  94. package/src/input/commands/agent-local-library-args.ts +52 -0
  95. package/src/input/commands/agent-runtime-profile-runtime.ts +45 -41
  96. package/src/input/commands/agent-skills-runtime.ts +87 -99
  97. package/src/input/commands/agent-workspace-runtime.ts +23 -7
  98. package/src/input/commands/brief-runtime.ts +25 -24
  99. package/src/input/commands/channels-runtime.ts +145 -31
  100. package/src/input/commands/delegation-runtime.ts +29 -23
  101. package/src/input/commands/experience-runtime.ts +6 -4
  102. package/src/input/commands/guidance-runtime.ts +4 -4
  103. package/src/input/commands/health-runtime.ts +140 -115
  104. package/src/input/commands/knowledge.ts +57 -56
  105. package/src/input/commands/local-provider-runtime.ts +36 -18
  106. package/src/input/commands/local-runtime.ts +138 -16
  107. package/src/input/commands/local-setup-review.ts +7 -7
  108. package/src/input/commands/mcp-runtime.ts +118 -50
  109. package/src/input/commands/notify-runtime.ts +38 -9
  110. package/src/input/commands/operator-actions-runtime.ts +138 -0
  111. package/src/input/commands/operator-runtime.ts +6 -17
  112. package/src/input/commands/personas-runtime.ts +49 -59
  113. package/src/input/commands/planning-runtime.ts +1 -1
  114. package/src/input/commands/platform-access-runtime.ts +29 -29
  115. package/src/input/commands/provider-accounts-runtime.ts +60 -39
  116. package/src/input/commands/qrcode-runtime.ts +45 -6
  117. package/src/input/commands/recall-bundle.ts +11 -11
  118. package/src/input/commands/recall-capture.ts +13 -11
  119. package/src/input/commands/recall-query.ts +29 -21
  120. package/src/input/commands/recall-review.ts +2 -1
  121. package/src/input/commands/routines-runtime.ts +63 -72
  122. package/src/input/commands/schedule-runtime.ts +33 -20
  123. package/src/input/commands/security-runtime.ts +4 -4
  124. package/src/input/commands/session-content.ts +80 -78
  125. package/src/input/commands/session-workflow.ts +132 -93
  126. package/src/input/commands/session.ts +3 -174
  127. package/src/input/commands/shell-core.ts +32 -17
  128. package/src/input/commands/subscription-runtime.ts +53 -179
  129. package/src/input/commands/tasks-runtime.ts +20 -20
  130. package/src/input/commands/work-plan-runtime.ts +33 -8
  131. package/src/input/commands.ts +2 -2
  132. package/src/input/feed-context-factory.ts +2 -1
  133. package/src/input/file-picker.ts +3 -2
  134. package/src/input/handler-command-route.ts +4 -7
  135. package/src/input/handler-content-actions.ts +89 -1
  136. package/src/input/handler-feed-routes.ts +19 -12
  137. package/src/input/handler-feed.ts +6 -3
  138. package/src/input/handler-interactions.ts +7 -5
  139. package/src/input/handler-modal-stack.ts +3 -3
  140. package/src/input/handler-onboarding.ts +24 -80
  141. package/src/input/handler-shortcuts.ts +15 -4
  142. package/src/input/handler.ts +47 -17
  143. package/src/input/input-history.ts +5 -6
  144. package/src/input/keybindings.ts +22 -11
  145. package/src/input/onboarding/onboarding-wizard-apply.ts +13 -0
  146. package/src/input/onboarding/onboarding-wizard-constants.ts +1 -0
  147. package/src/input/onboarding/onboarding-wizard-operator-steps.ts +147 -57
  148. package/src/input/onboarding/onboarding-wizard-steps.ts +46 -29
  149. package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
  150. package/src/input/onboarding/onboarding-wizard.ts +68 -0
  151. package/src/input/profile-picker-modal.ts +31 -12
  152. package/src/input/session-picker-modal.ts +22 -4
  153. package/src/input/settings-modal-behavior.ts +0 -3
  154. package/src/input/settings-modal-subscriptions.ts +3 -3
  155. package/src/input/settings-modal-types.ts +2 -13
  156. package/src/input/settings-modal.ts +6 -52
  157. package/src/input/submission-intent.ts +0 -1
  158. package/src/input/submission-router.ts +3 -3
  159. package/src/main.ts +18 -8
  160. package/src/panels/approval-panel.ts +8 -8
  161. package/src/panels/automation-control-panel.ts +2 -2
  162. package/src/panels/base-panel.ts +1 -1
  163. package/src/panels/builtin/agent.ts +1 -1
  164. package/src/panels/builtin/operations.ts +1 -10
  165. package/src/panels/builtin/session.ts +9 -9
  166. package/src/panels/builtin/shared.ts +2 -2
  167. package/src/panels/cost-tracker-panel.ts +1 -1
  168. package/src/panels/docs-panel.ts +5 -3
  169. package/src/panels/index.ts +0 -1
  170. package/src/panels/knowledge-panel.ts +6 -5
  171. package/src/panels/memory-panel.ts +7 -6
  172. package/src/panels/panel-list-panel.ts +36 -80
  173. package/src/panels/project-planning-panel.ts +18 -11
  174. package/src/panels/provider-account-snapshot.ts +51 -25
  175. package/src/panels/provider-accounts-panel.ts +33 -18
  176. package/src/panels/provider-health-domains.ts +45 -4
  177. package/src/panels/provider-health-panel.ts +5 -4
  178. package/src/panels/qr-panel.ts +1 -1
  179. package/src/panels/schedule-panel.ts +9 -17
  180. package/src/panels/security-panel.ts +8 -8
  181. package/src/panels/session-browser-panel.ts +10 -10
  182. package/src/panels/subscription-panel.ts +3 -3
  183. package/src/panels/system-messages-panel.ts +1 -1
  184. package/src/panels/tasks-panel.ts +20 -13
  185. package/src/panels/tool-inspector-panel.ts +19 -12
  186. package/src/panels/work-plan-panel.ts +5 -5
  187. package/src/planning/project-planning-coordinator.ts +1 -1
  188. package/src/provider-auth-route-display.ts +9 -0
  189. package/src/renderer/agent-workspace-style.ts +34 -0
  190. package/src/renderer/agent-workspace.ts +228 -52
  191. package/src/renderer/autocomplete-overlay.ts +25 -6
  192. package/src/renderer/bookmark-modal.ts +19 -4
  193. package/src/renderer/buffer.ts +4 -2
  194. package/src/renderer/context-inspector.ts +50 -13
  195. package/src/renderer/diff.ts +1 -1
  196. package/src/renderer/file-picker-overlay.ts +19 -6
  197. package/src/renderer/help-overlay.ts +106 -33
  198. package/src/renderer/history-search-overlay.ts +19 -4
  199. package/src/renderer/live-tail-modal.ts +27 -5
  200. package/src/renderer/mcp-workspace.ts +164 -50
  201. package/src/renderer/model-picker-overlay.ts +58 -2
  202. package/src/renderer/model-workspace.ts +104 -20
  203. package/src/renderer/process-modal.ts +27 -6
  204. package/src/renderer/profile-picker-modal.ts +20 -4
  205. package/src/renderer/search-overlay.ts +25 -5
  206. package/src/renderer/selection-modal-overlay.ts +46 -14
  207. package/src/renderer/session-picker-modal.ts +18 -1
  208. package/src/renderer/settings-modal-helpers.ts +2 -40
  209. package/src/renderer/settings-modal.ts +83 -50
  210. package/src/renderer/tool-call.ts +20 -11
  211. package/src/runtime/agent-runtime-events.ts +6 -6
  212. package/src/runtime/bootstrap-command-context.ts +7 -1
  213. package/src/runtime/bootstrap-command-parts.ts +9 -7
  214. package/src/runtime/bootstrap-core.ts +19 -1
  215. package/src/runtime/bootstrap-hook-bridge.ts +7 -18
  216. package/src/runtime/bootstrap-shell.ts +4 -4
  217. package/src/runtime/bootstrap.ts +31 -22
  218. package/src/runtime/connected-host-auth.ts +7 -2
  219. package/src/runtime/diagnostics/panels/index.ts +2 -2
  220. package/src/runtime/diagnostics/panels/policy.ts +7 -7
  221. package/src/runtime/index.ts +2 -1
  222. package/src/runtime/onboarding/apply-file-helpers.ts +66 -0
  223. package/src/runtime/onboarding/apply.ts +80 -79
  224. package/src/runtime/onboarding/derivation.ts +5 -8
  225. package/src/runtime/onboarding/snapshot.ts +0 -15
  226. package/src/runtime/onboarding/types.ts +8 -19
  227. package/src/runtime/onboarding/verify.ts +32 -10
  228. package/src/runtime/operator-token-cleanup.ts +1 -1
  229. package/src/runtime/services.ts +91 -22
  230. package/src/runtime/store/selectors/index.ts +3 -3
  231. package/src/runtime/store/state.ts +1 -4
  232. package/src/runtime/surface-feature-flags.ts +41 -6
  233. package/src/runtime/ui-read-models.ts +3 -4
  234. package/src/runtime/ui-services.ts +6 -6
  235. package/src/shell/blocking-input.ts +2 -1
  236. package/src/shell/ui-openers.ts +3 -13
  237. package/src/tools/agent-analysis-registry-policy.ts +0 -1
  238. package/src/tools/agent-channel-send-tool.ts +133 -0
  239. package/src/tools/agent-context-policy.ts +1 -1
  240. package/src/tools/agent-knowledge-ingest-tool.ts +405 -0
  241. package/src/tools/agent-knowledge-tool.ts +170 -0
  242. package/src/tools/agent-local-registry-tool.ts +269 -69
  243. package/src/tools/agent-media-generate-tool.ts +133 -0
  244. package/src/tools/agent-notify-tool.ts +143 -0
  245. package/src/tools/agent-operator-action-tool.ts +137 -0
  246. package/src/tools/agent-operator-briefing-tool.ts +217 -0
  247. package/src/tools/agent-reminder-schedule-tool.ts +237 -0
  248. package/src/tools/agent-tool-policy-guard.ts +8 -8
  249. package/src/tools/agent-work-plan-tool.ts +256 -0
  250. package/src/version.ts +1 -1
  251. package/src/input/commands/policy-dispatch.ts +0 -339
  252. package/src/input/commands/policy.ts +0 -13
  253. package/src/panels/panel-picker.ts +0 -105
  254. package/src/panels/policy-panel.ts +0 -308
  255. package/src/renderer/panel-picker-overlay.ts +0 -202
@@ -103,6 +103,10 @@ const PROFILE_CREATED_FILE = 'profile.json';
103
103
  const PROFILE_SELECTION_FILE = 'profile-selection.json';
104
104
  const PROFILE_ID_PATTERN = /^[a-z0-9](?:[a-z0-9._-]{0,62}[a-z0-9])?$/;
105
105
 
106
+ function isRecord(value: unknown): value is Record<string, unknown> {
107
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
108
+ }
109
+
106
110
  export function normalizeAgentRuntimeProfileId(value: string): string {
107
111
  return value
108
112
  .trim()
@@ -149,12 +153,11 @@ export function readAgentRuntimeProfileSelection(baseHomeDirectory: string): Age
149
153
  if (!existsSync(path)) return null;
150
154
  try {
151
155
  const raw: unknown = JSON.parse(readFileSync(path, 'utf-8'));
152
- if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;
153
- const record = raw as Record<string, unknown>;
154
- const profileId = record.profileId;
156
+ if (!isRecord(raw)) return null;
157
+ const profileId = raw.profileId;
155
158
  if (typeof profileId !== 'string') return null;
156
159
  const resolution = resolveAgentRuntimeProfileHome(baseHomeDirectory, profileId);
157
- const selectedAt = typeof record.selectedAt === 'string' ? record.selectedAt : null;
160
+ const selectedAt = typeof raw.selectedAt === 'string' ? raw.selectedAt : null;
158
161
  return {
159
162
  ...resolution,
160
163
  selectedAt,
@@ -192,13 +195,11 @@ function readProfileStarterTemplate(homeDirectory: string): Pick<AgentRuntimePro
192
195
  if (!existsSync(path)) return {};
193
196
  try {
194
197
  const raw: unknown = JSON.parse(readFileSync(path, 'utf-8'));
195
- if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return {};
196
- const record = raw as Record<string, unknown>;
197
- const starter = record.starterTemplate;
198
- if (!starter || typeof starter !== 'object' || Array.isArray(starter)) return {};
199
- const starterRecord = starter as Record<string, unknown>;
200
- const id = starterRecord.id;
201
- const name = starterRecord.name;
198
+ if (!isRecord(raw)) return {};
199
+ const starter = raw.starterTemplate;
200
+ if (!isRecord(starter)) return {};
201
+ const id = starter.id;
202
+ const name = starter.name;
202
203
  if (!isAgentRuntimeProfileTemplateId(id) || typeof name !== 'string') return {};
203
204
  return {
204
205
  starterTemplateId: id,
@@ -271,7 +272,7 @@ function selectDiscoveredRecord<T extends { readonly name: string; readonly path
271
272
  if (!selector?.trim()) return records[0]!;
272
273
  const lookup = selector.trim().toLowerCase();
273
274
  const match = records.find((record) => normalizedLookupValues(record.name, record.path).includes(lookup));
274
- if (!match) throw new Error(`Unknown discovered Agent ${label}: ${selector}.`);
275
+ if (!match) throw new Error(`Unknown discovered Agent ${label} ${selector}.`);
275
276
  return match;
276
277
  }
277
278
 
@@ -323,8 +324,8 @@ function readTemplateTextBlock(value: unknown, field: string): string {
323
324
  }
324
325
 
325
326
  function readTemplateObject(value: unknown, field: string): Record<string, unknown> {
326
- if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error(`Starter template ${field} must be an object.`);
327
- return value as Record<string, unknown>;
327
+ if (!isRecord(value)) throw new Error(`Starter template ${field} must be an object.`);
328
+ return value;
328
329
  }
329
330
 
330
331
  function parseTemplateSkill(value: unknown): AgentRuntimeProfileStarterTemplate['skills'][number] {
@@ -405,7 +406,7 @@ function resolveAgentRuntimeProfileTemplate(templateId: AgentRuntimeProfileTempl
405
406
  const local = baseHomeDirectory ? listLocalTemplates(baseHomeDirectory).find((template) => template.id === id) : undefined;
406
407
  if (local) return local;
407
408
  const suffix = baseHomeDirectory ? ' Use profiles templates to list starters.' : '';
408
- throw new Error(`Unknown Agent starter profile template: ${templateId}.${suffix}`);
409
+ throw new Error(`Unknown Agent starter profile template ${templateId}.${suffix}`);
409
410
  }
410
411
 
411
412
  export function listAgentRuntimeProfileTemplates(baseHomeDirectory?: string): readonly AgentRuntimeProfileTemplateSummary[] {
@@ -468,7 +469,7 @@ export async function createAgentRuntimeProfileTemplateFromDiscovered(
468
469
  const selectedRoutines = selectDiscoveredRecords(routines, options.routines, 'routine');
469
470
  const target = join(getAgentRuntimeProfileTemplatesRoot(shellPaths.homeDirectory), `${id}.json`);
470
471
  if (existsSync(target) && options.replace !== true) {
471
- throw new Error(`Agent starter template already exists: ${id}. Rerun with --replace to overwrite it.`);
472
+ throw new Error(`Agent starter template already exists ${id}. Rerun with --replace to overwrite it.`);
472
473
  }
473
474
  const persona = discoveredPersonaToTemplate(selectedPersona);
474
475
  const template: AgentRuntimeProfileStarterTemplate = {
@@ -621,7 +622,7 @@ export function createAgentRuntimeProfile(baseHomeDirectory: string, profileName
621
622
  export function setAgentRuntimeProfileSelection(baseHomeDirectory: string, profileName: string): AgentRuntimeProfileSelection {
622
623
  const resolution = resolveAgentRuntimeProfileHome(baseHomeDirectory, profileName);
623
624
  if (!existsSync(resolution.homeDirectory)) {
624
- throw new Error(`Agent profile not found: ${resolution.id}`);
625
+ throw new Error(`Agent profile not found ${resolution.id}`);
625
626
  }
626
627
  const path = getAgentRuntimeProfileSelectionPath(baseHomeDirectory);
627
628
  mkdirSync(dirname(path), { recursive: true });
@@ -3,6 +3,7 @@ import { delimiter, dirname, join } from 'node:path';
3
3
  import type { ShellPathService } from '@/runtime/index.ts';
4
4
  import { GOODVIBES_AGENT_SURFACE_ROOT } from '../config/surface.ts';
5
5
  import { assertNoSecretLikeText } from './persona-registry.ts';
6
+ import { formatAgentRecordReviewState } from './record-labels.ts';
6
7
 
7
8
  export type AgentSkillSource = 'user' | 'agent' | 'imported' | 'system';
8
9
  export type AgentSkillReviewState = 'fresh' | 'reviewed' | 'stale';
@@ -153,10 +154,10 @@ function normalizeList(values: readonly string[] | undefined): string[] {
153
154
 
154
155
  function validateRequirementName(requirement: AgentSkillRequirement): void {
155
156
  if (requirement.kind === 'env' && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(requirement.name)) {
156
- throw new Error(`Invalid skill env requirement: ${requirement.name}`);
157
+ throw new Error(`Invalid skill env requirement ${requirement.name}`);
157
158
  }
158
159
  if (requirement.kind === 'command' && !/^[A-Za-z0-9._+-]+$/.test(requirement.name)) {
159
- throw new Error(`Invalid skill command requirement: ${requirement.name}`);
160
+ throw new Error(`Invalid skill command requirement ${requirement.name}`);
160
161
  }
161
162
  }
162
163
 
@@ -375,9 +376,9 @@ export class AgentSkillRegistry {
375
376
  const procedure = input.procedure.trim();
376
377
  this.validateRequired(name, description, procedure);
377
378
  const requirements = normalizeRequirements(input.requirements);
378
- assertNoSecretLikeText([name, description, procedure, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])]);
379
+ assertNoSecretLikeText([name, description, procedure, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])], 'Skills');
379
380
  const duplicate = store.skills.find((skill) => skill.name.toLowerCase() === name.toLowerCase());
380
- if (duplicate) throw new Error(`Skill already exists: ${duplicate.id}`);
381
+ if (duplicate) throw new Error(`Skill already exists ${duplicate.id}`);
381
382
  const timestamp = nowIso();
382
383
  const skill: AgentSkillRecord = {
383
384
  id: this.nextId(name, store.skills),
@@ -404,9 +405,9 @@ export class AgentSkillRegistry {
404
405
  const description = input.description.trim();
405
406
  const skillIds = this.normalizeExistingSkillIds(store, input.skillIds);
406
407
  this.validateBundleRequired(name, description, skillIds);
407
- assertNoSecretLikeText([name, description, ...skillIds]);
408
+ assertNoSecretLikeText([name, description, ...skillIds], 'Skill bundles');
408
409
  const duplicate = store.bundles.find((bundle) => bundle.name.toLowerCase() === name.toLowerCase());
409
- if (duplicate) throw new Error(`Skill bundle already exists: ${duplicate.id}`);
410
+ if (duplicate) throw new Error(`Skill bundle already exists ${duplicate.id}`);
410
411
  const timestamp = nowIso();
411
412
  const bundle: AgentSkillBundleRecord = {
412
413
  id: this.nextBundleId(name, store.bundles),
@@ -427,15 +428,15 @@ export class AgentSkillRegistry {
427
428
  public update(idOrName: string, input: AgentSkillUpdateInput): AgentSkillRecord {
428
429
  const store = this.readStore();
429
430
  const existing = this.findInStore(store, idOrName);
430
- if (!existing) throw new Error(`Unknown skill: ${idOrName}`);
431
+ if (!existing) throw new Error(`Unknown skill ${idOrName}`);
431
432
  const name = input.name === undefined ? existing.name : normalizeName(input.name);
432
433
  const description = input.description === undefined ? existing.description : input.description.trim();
433
434
  const procedure = input.procedure === undefined ? existing.procedure : input.procedure.trim();
434
435
  this.validateRequired(name, description, procedure);
435
436
  const requirements = input.requirements === undefined ? existing.requirements : normalizeRequirements(input.requirements);
436
- assertNoSecretLikeText([name, description, procedure, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])]);
437
+ assertNoSecretLikeText([name, description, procedure, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])], 'Skills');
437
438
  const duplicate = store.skills.find((skill) => skill.id !== existing.id && skill.name.toLowerCase() === name.toLowerCase());
438
- if (duplicate) throw new Error(`Skill already exists: ${duplicate.id}`);
439
+ if (duplicate) throw new Error(`Skill already exists ${duplicate.id}`);
439
440
  const updated: AgentSkillRecord = {
440
441
  ...existing,
441
442
  name,
@@ -460,14 +461,14 @@ export class AgentSkillRegistry {
460
461
  public updateBundle(idOrName: string, input: AgentSkillBundleUpdateInput): AgentSkillBundleRecord {
461
462
  const store = this.readStore();
462
463
  const existing = this.findBundleInStore(store, idOrName);
463
- if (!existing) throw new Error(`Unknown skill bundle: ${idOrName}`);
464
+ if (!existing) throw new Error(`Unknown skill bundle ${idOrName}`);
464
465
  const name = input.name === undefined ? existing.name : normalizeName(input.name);
465
466
  const description = input.description === undefined ? existing.description : input.description.trim();
466
467
  const skillIds = input.skillIds === undefined ? existing.skillIds : this.normalizeExistingSkillIds(store, input.skillIds);
467
468
  this.validateBundleRequired(name, description, skillIds);
468
- assertNoSecretLikeText([name, description, ...skillIds]);
469
+ assertNoSecretLikeText([name, description, ...skillIds], 'Skill bundles');
469
470
  const duplicate = store.bundles.find((bundle) => bundle.id !== existing.id && bundle.name.toLowerCase() === name.toLowerCase());
470
- if (duplicate) throw new Error(`Skill bundle already exists: ${duplicate.id}`);
471
+ if (duplicate) throw new Error(`Skill bundle already exists ${duplicate.id}`);
471
472
  const updated: AgentSkillBundleRecord = {
472
473
  ...existing,
473
474
  name,
@@ -489,7 +490,7 @@ export class AgentSkillRegistry {
489
490
  public setEnabled(idOrName: string, enabled: boolean): AgentSkillRecord {
490
491
  const store = this.readStore();
491
492
  const existing = this.findInStore(store, idOrName);
492
- if (!existing) throw new Error(`Unknown skill: ${idOrName}`);
493
+ if (!existing) throw new Error(`Unknown skill ${idOrName}`);
493
494
  const updated: AgentSkillRecord = { ...existing, enabled, updatedAt: nowIso() };
494
495
  this.writeStore({
495
496
  ...store,
@@ -501,7 +502,7 @@ export class AgentSkillRegistry {
501
502
  public setBundleEnabled(idOrName: string, enabled: boolean): AgentSkillBundleRecord {
502
503
  const store = this.readStore();
503
504
  const existing = this.findBundleInStore(store, idOrName);
504
- if (!existing) throw new Error(`Unknown skill bundle: ${idOrName}`);
505
+ if (!existing) throw new Error(`Unknown skill bundle ${idOrName}`);
505
506
  const updated: AgentSkillBundleRecord = { ...existing, enabled, updatedAt: nowIso() };
506
507
  this.writeStore({
507
508
  ...store,
@@ -513,7 +514,7 @@ export class AgentSkillRegistry {
513
514
  public markReviewed(idOrName: string): AgentSkillRecord {
514
515
  const store = this.readStore();
515
516
  const existing = this.findInStore(store, idOrName);
516
- if (!existing) throw new Error(`Unknown skill: ${idOrName}`);
517
+ if (!existing) throw new Error(`Unknown skill ${idOrName}`);
517
518
  const updated: AgentSkillRecord = {
518
519
  ...existing,
519
520
  reviewState: 'reviewed',
@@ -531,7 +532,7 @@ export class AgentSkillRegistry {
531
532
  public markBundleReviewed(idOrName: string): AgentSkillBundleRecord {
532
533
  const store = this.readStore();
533
534
  const existing = this.findBundleInStore(store, idOrName);
534
- if (!existing) throw new Error(`Unknown skill bundle: ${idOrName}`);
535
+ if (!existing) throw new Error(`Unknown skill bundle ${idOrName}`);
535
536
  const updated: AgentSkillBundleRecord = {
536
537
  ...existing,
537
538
  reviewState: 'reviewed',
@@ -549,7 +550,7 @@ export class AgentSkillRegistry {
549
550
  public markStale(idOrName: string, reason: string): AgentSkillRecord {
550
551
  const store = this.readStore();
551
552
  const existing = this.findInStore(store, idOrName);
552
- if (!existing) throw new Error(`Unknown skill: ${idOrName}`);
553
+ if (!existing) throw new Error(`Unknown skill ${idOrName}`);
553
554
  const updated: AgentSkillRecord = {
554
555
  ...existing,
555
556
  reviewState: 'stale',
@@ -566,7 +567,7 @@ export class AgentSkillRegistry {
566
567
  public markBundleStale(idOrName: string, reason: string): AgentSkillBundleRecord {
567
568
  const store = this.readStore();
568
569
  const existing = this.findBundleInStore(store, idOrName);
569
- if (!existing) throw new Error(`Unknown skill bundle: ${idOrName}`);
570
+ if (!existing) throw new Error(`Unknown skill bundle ${idOrName}`);
570
571
  const updated: AgentSkillBundleRecord = {
571
572
  ...existing,
572
573
  reviewState: 'stale',
@@ -583,7 +584,7 @@ export class AgentSkillRegistry {
583
584
  public deleteSkill(idOrName: string): AgentSkillRecord {
584
585
  const store = this.readStore();
585
586
  const existing = this.findInStore(store, idOrName);
586
- if (!existing) throw new Error(`Unknown skill: ${idOrName}`);
587
+ if (!existing) throw new Error(`Unknown skill ${idOrName}`);
587
588
  this.writeStore({
588
589
  ...store,
589
590
  skills: store.skills.filter((skill) => skill.id !== existing.id),
@@ -597,7 +598,7 @@ export class AgentSkillRegistry {
597
598
  public deleteBundle(idOrName: string): AgentSkillBundleRecord {
598
599
  const store = this.readStore();
599
600
  const existing = this.findBundleInStore(store, idOrName);
600
- if (!existing) throw new Error(`Unknown skill bundle: ${idOrName}`);
601
+ if (!existing) throw new Error(`Unknown skill bundle ${idOrName}`);
601
602
  this.writeStore({
602
603
  ...store,
603
604
  bundles: store.bundles.filter((bundle) => bundle.id !== existing.id),
@@ -655,7 +656,7 @@ export class AgentSkillRegistry {
655
656
  const normalized = normalizeList(skillIds).map(slugify);
656
657
  const known = new Set(store.skills.map((skill) => skill.id));
657
658
  for (const skillId of normalized) {
658
- if (!known.has(skillId)) throw new Error(`Unknown skill for bundle: ${skillId}`);
659
+ if (!known.has(skillId)) throw new Error(`Unknown skill for bundle ${skillId}`);
659
660
  }
660
661
  return normalized;
661
662
  }
@@ -665,7 +666,7 @@ export class AgentSkillRegistry {
665
666
  try {
666
667
  return parseStore(readFileSync(this.storePath, 'utf-8'));
667
668
  } catch (error) {
668
- throw new Error(`Could not read Agent skill store: ${error instanceof Error ? error.message : String(error)}`);
669
+ throw new Error(`Could not read Agent skill store ${error instanceof Error ? error.message : String(error)}`);
669
670
  }
670
671
  }
671
672
 
@@ -768,7 +769,7 @@ export function buildEnabledSkillsPrompt(shellPaths: ShellPathService): string |
768
769
  return [
769
770
  `### Skill Bundle: ${bundle.name}`,
770
771
  `Description: ${bundle.description}`,
771
- `Review state: ${bundle.reviewState}`,
772
+ `Review: ${formatAgentRecordReviewState(bundle.reviewState)}`,
772
773
  `Readiness: ${readiness.ready ? 'ready' : `missing ${missing.join(', ')}`}`,
773
774
  `Included skills: ${bundle.skillIds.join(', ')}`,
774
775
  '',
@@ -777,7 +778,7 @@ export function buildEnabledSkillsPrompt(shellPaths: ShellPathService): string |
777
778
  ...active.slice(0, 8).flatMap((skill) => [
778
779
  `### ${skill.name}`,
779
780
  `Description: ${skill.description}`,
780
- `Review state: ${skill.reviewState}`,
781
+ `Review: ${formatAgentRecordReviewState(skill.reviewState)}`,
781
782
  `Triggers: ${skill.triggers.join(', ') || '(manual)'}`,
782
783
  `Readiness: ${evaluateAgentSkillReadiness(skill).ready ? 'ready' : `missing ${evaluateAgentSkillReadiness(skill).missing.map(formatAgentSkillRequirement).join(', ')}`}`,
783
784
  skill.procedure,
@@ -14,7 +14,7 @@ export function commandValues(args: readonly string[]): string[] {
14
14
  export function delegationTaskValues(args: readonly string[]): string[] {
15
15
  const values: string[] = [];
16
16
  for (const token of args) {
17
- if (token === '--wrfc') continue;
17
+ if (token === '--review' || token === '--wrfc') continue;
18
18
  if (!token.startsWith('--')) values.push(token);
19
19
  }
20
20
  return values;
@@ -79,6 +79,10 @@ export function hasFlag(args: readonly string[], flag: string): boolean {
79
79
  return args.includes(flag);
80
80
  }
81
81
 
82
+ export function hasAnyFlag(args: readonly string[], flags: readonly string[]): boolean {
83
+ return flags.some((flag) => args.includes(flag));
84
+ }
85
+
82
86
  export function stripCommandFlag(args: readonly string[], flag: string): { readonly rest: readonly string[]; readonly present: boolean } {
83
87
  const rest: string[] = [];
84
88
  let present = false;
@@ -3,6 +3,7 @@ import type { CliCommandRuntime } from './management.ts';
3
3
  import {
4
4
  commandValues,
5
5
  delegationTaskValues,
6
+ hasAnyFlag,
6
7
  hasFlag,
7
8
  parseConnectorInput,
8
9
  readFirstStringList,
@@ -14,6 +15,7 @@ import {
14
15
  } from './agent-knowledge-args.ts';
15
16
  import {
16
17
  formatAsk,
18
+ formatAgentKnowledgeFailureKind,
17
19
  formatBatchIngest,
18
20
  formatConnector,
19
21
  formatConnectorDoctor,
@@ -30,7 +32,7 @@ import {
30
32
  import { AGENT_KNOWLEDGE_METHODS, DELEGATION_METHOD } from './agent-knowledge-methods.ts';
31
33
  import {
32
34
  createAgentSdk,
33
- fetchDaemonStatus,
35
+ fetchConnectedHostStatus,
34
36
  findDisallowedKnowledgeScopeFlag,
35
37
  formatScopeFlagRejection,
36
38
  getAgentKnowledgeJson,
@@ -38,7 +40,7 @@ import {
38
40
  postAgentKnowledgeJson,
39
41
  readPackageMetadata,
40
42
  readString,
41
- resolveDaemonConnection,
43
+ resolveConnectedHostConnection,
42
44
  runKnowledgeCall,
43
45
  } from './agent-knowledge-runtime.ts';
44
46
  import { formatJsonOrText, yesNo } from './management.ts';
@@ -47,23 +49,23 @@ interface DelegationResult {
47
49
  readonly sessionId: string;
48
50
  readonly message: unknown;
49
51
  readonly task: string;
50
- readonly wrfcRequested: boolean;
52
+ readonly reviewRequested: boolean;
51
53
  }
52
54
 
53
- function buildDelegationBody(task: string, wrfcRequested: boolean): string {
55
+ function buildDelegationBody(task: string, reviewRequested: boolean): string {
54
56
  return [
55
57
  'GoodVibes Agent explicit build delegation.',
56
58
  '',
57
- 'Original user ask:',
59
+ 'Original user ask',
58
60
  task,
59
61
  '',
60
- 'Agent policy:',
62
+ 'Agent policy',
61
63
  '- GoodVibes Agent is not the coding TUI.',
62
64
  '- Preserve the full original ask.',
63
- '- GoodVibes TUI owns file edits, git/worktree flows, execution isolation UX, and any WRFC owner chain.',
64
- wrfcRequested
65
- ? '- WRFC was explicitly requested by the Agent user for this build/fix/review delegation.'
66
- : '- WRFC was not explicitly requested; do not turn this into WRFC solely because it came from Agent.',
65
+ '- GoodVibes TUI owns file edits, git/worktree flows, execution isolation UX, and any delegated review owner chain.',
66
+ reviewRequested
67
+ ? '- Delegated review was explicitly requested by the Agent user for this build/fix/review handoff.'
68
+ : '- Delegated review was not explicitly requested; do not add review solely because this came from Agent.',
67
69
  ].join('\n');
68
70
  }
69
71
 
@@ -479,12 +481,12 @@ export async function handleAgentKnowledgeShortcutCommand(
479
481
  }
480
482
 
481
483
  export async function handleCompatCommand(runtime: CliCommandRuntime): Promise<CliCommandOutput> {
482
- const connection = resolveDaemonConnection(runtime);
484
+ const connection = resolveConnectedHostConnection(runtime);
483
485
  const metadata = readPackageMetadata();
484
- const daemon = await fetchDaemonStatus(connection);
485
- const daemonRecord = isRecord(daemon.body) ? daemon.body : {};
486
- const daemonVersion = readString(daemonRecord, 'version') ?? 'unknown';
487
- const versionCompatible = daemon.ok && daemonVersion === metadata.sdkVersion;
486
+ const connectedHost = await fetchConnectedHostStatus(connection);
487
+ const connectedHostRecord = isRecord(connectedHost.body) ? connectedHost.body : {};
488
+ const connectedHostVersion = readString(connectedHostRecord, 'version') ?? 'unknown';
489
+ const versionCompatible = connectedHost.ok && connectedHostVersion === metadata.sdkVersion;
488
490
  const knowledgeRoute = await runKnowledgeCall(runtime, AGENT_KNOWLEDGE_METHODS.status, async (routeConnection) => (
489
491
  await createAgentSdk(routeConnection).knowledge.status()
490
492
  ));
@@ -495,9 +497,9 @@ export async function handleCompatCommand(runtime: CliCommandRuntime): Promise<C
495
497
  sdkPin: metadata.sdkVersion,
496
498
  connectedHost: {
497
499
  baseUrl: connection.baseUrl,
498
- status: daemon.status,
499
- version: daemonVersion,
500
- reachable: daemon.ok,
500
+ status: connectedHost.status,
501
+ version: connectedHostVersion,
502
+ reachable: connectedHost.ok,
501
503
  compatible: versionCompatible,
502
504
  },
503
505
  auth: {
@@ -512,13 +514,13 @@ export async function handleCompatCommand(runtime: CliCommandRuntime): Promise<C
512
514
  };
513
515
  const text = [
514
516
  'GoodVibes Agent compatibility',
515
- ` package: ${metadata.version}`,
516
- ` SDK pin: ${metadata.sdkVersion}`,
517
- ` runtime: ${daemonVersion} at ${connection.baseUrl} (${daemon.ok ? 'reachable' : 'unreachable'})`,
518
- ` version compatible: ${yesNo(versionCompatible)}`,
519
- ` operator token: ${connection.token ? 'present' : 'missing'} (${connection.tokenPath})`,
520
- ` Agent knowledge route: ${knowledgeRouteReady ? 'ready' : `not ready (${knowledgeRoute.ok ? 'unknown' : knowledgeRoute.kind})`}`,
521
- ...(versionCompatible ? [] : [' next: update the connected GoodVibes host so /status matches the Agent SDK pin.']),
517
+ ` package ${metadata.version}`,
518
+ ` SDK pin ${metadata.sdkVersion}`,
519
+ ` connected host ${connectedHostVersion} at ${connection.baseUrl} (${connectedHost.ok ? 'reachable' : 'unreachable'})`,
520
+ ` version compatible ${yesNo(versionCompatible)}`,
521
+ ` operator token ${connection.token ? 'present' : 'missing'} (${connection.tokenPath})`,
522
+ ` Agent Knowledge route ${knowledgeRouteReady ? 'ready' : `not ready (${knowledgeRoute.ok ? 'unknown' : formatAgentKnowledgeFailureKind(knowledgeRoute.kind)})`}`,
523
+ ...(versionCompatible ? [] : [' next update the connected GoodVibes host so /status matches the Agent SDK pin.']),
522
524
  ].join('\n');
523
525
  return {
524
526
  output: runtime.cli.flags.outputFormat === 'json' ? JSON.stringify(value, null, 2) : text,
@@ -527,11 +529,11 @@ export async function handleCompatCommand(runtime: CliCommandRuntime): Promise<C
527
529
  }
528
530
 
529
531
  export async function handleDelegateCommand(runtime: CliCommandRuntime): Promise<CliCommandOutput> {
530
- const wrfcRequested = hasFlag(runtime.cli.commandArgs, '--wrfc');
532
+ const reviewRequested = hasAnyFlag(runtime.cli.commandArgs, ['--review', '--wrfc']);
531
533
  const task = delegationTaskValues(runtime.cli.commandArgs).join(' ').trim();
532
534
  if (!task) {
533
535
  return {
534
- output: 'Usage: goodvibes-agent delegate [--wrfc] <build/fix/review task>',
536
+ output: 'Usage: goodvibes-agent delegate [--review] <build/fix/review task>',
535
537
  exitCode: 2,
536
538
  };
537
539
  }
@@ -548,10 +550,14 @@ export async function handleDelegateCommand(runtime: CliCommandRuntime): Promise
548
550
  if (!sessionId) throw new Error('sessions.create returned no session id.');
549
551
  const message = await sdk.operator.invoke('sessions.messages.create', {
550
552
  sessionId,
551
- body: buildDelegationBody(task, wrfcRequested),
553
+ body: buildDelegationBody(task, reviewRequested),
552
554
  surfaceKind: 'goodvibes-agent',
553
555
  surfaceId: 'goodvibes-agent-cli',
554
556
  kind: 'task',
557
+ metadata: {
558
+ reviewRequested,
559
+ wrfcRequested: reviewRequested,
560
+ },
555
561
  routing: {
556
562
  executionIntent: {
557
563
  riskClass: 'elevated',
@@ -561,15 +567,15 @@ export async function handleDelegateCommand(runtime: CliCommandRuntime): Promise
561
567
  },
562
568
  },
563
569
  });
564
- return { sessionId, message, task, wrfcRequested };
570
+ return { sessionId, message, task, reviewRequested };
565
571
  });
566
572
  if (!result.ok) return { output: formatFailure(result, runtime.cli.flags.outputFormat === 'json'), exitCode: 1 };
567
573
  const text = [
568
574
  'Delegation submitted to GoodVibes TUI/shared-session routes.',
569
- ` session: ${result.data.sessionId}`,
570
- ` mode: ${result.data.wrfcRequested ? 'WRFC requested' : 'direct build delegation'}`,
571
- ` task: ${result.data.task}`,
572
- ' next: check GoodVibes TUI shared-session/task status for the result.',
575
+ ` session ${result.data.sessionId}`,
576
+ ` mode ${result.data.reviewRequested ? 'delegated review requested' : 'direct build delegation'}`,
577
+ ` task ${result.data.task}`,
578
+ ' next check GoodVibes TUI shared-session/task status for the result.',
573
579
  ].join('\n');
574
580
  return {
575
581
  output: formatJsonOrText(runtime.cli)(result, text),