@pellux/goodvibes-agent 1.2.0 → 1.4.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 (85) hide show
  1. package/CHANGELOG.md +194 -0
  2. package/dist/package/{ast-grep-napi.linux-x64-gnu-swtppvy9.node → ast-grep-napi.linux-x64-gnu-mkk8xwww.node} +0 -0
  3. package/dist/package/{ast-grep-napi.linux-x64-musl-ttfcdtap.node → ast-grep-napi.linux-x64-musl-ryqtgdv6.node} +0 -0
  4. package/dist/package/main.js +65740 -57156
  5. package/docs/README.md +3 -3
  6. package/docs/connected-host.md +2 -2
  7. package/docs/getting-started.md +11 -9
  8. package/docs/tools-and-commands.md +12 -10
  9. package/docs/voice-and-live-tts.md +2 -2
  10. package/package.json +1 -1
  11. package/release/release-notes.md +15 -144
  12. package/release/release-readiness.json +36 -36
  13. package/src/agent/competitive-feature-inventory.ts +42 -44
  14. package/src/agent/setup-wizard-artifact-receipts.ts +366 -0
  15. package/src/agent/setup-wizard.ts +267 -24
  16. package/src/cli/tui-startup.ts +2 -2
  17. package/src/input/agent-workspace-activation.ts +2 -2
  18. package/src/input/agent-workspace-categories.ts +2 -2
  19. package/src/input/agent-workspace-onboarding-categories.ts +4 -4
  20. package/src/input/agent-workspace-onboarding-finish.ts +21 -0
  21. package/src/input/agent-workspace-settings.ts +47 -1
  22. package/src/input/agent-workspace-setup-snapshot.ts +17 -6
  23. package/src/input/agent-workspace-setup.ts +20 -26
  24. package/src/input/agent-workspace-snapshot.ts +16 -3
  25. package/src/input/agent-workspace-types.ts +5 -2
  26. package/src/input/agent-workspace.ts +21 -6
  27. package/src/input/handler.ts +67 -5
  28. package/src/input/setup-wizard-live-receipts.ts +76 -0
  29. package/src/renderer/agent-workspace-context-lines.ts +17 -33
  30. package/src/renderer/agent-workspace.ts +434 -21
  31. package/src/runtime/tool-permission-safety.ts +1 -1
  32. package/src/tools/agent-harness-agent-orchestration-policy.ts +75 -0
  33. package/src/tools/agent-harness-agent-orchestration.ts +216 -128
  34. package/src/tools/agent-harness-autonomy-live-records.ts +154 -0
  35. package/src/tools/agent-harness-autonomy-queue-types.ts +1 -1
  36. package/src/tools/agent-harness-autonomy-queue.ts +19 -8
  37. package/src/tools/agent-harness-autonomy-watcher-read-models.ts +509 -0
  38. package/src/tools/agent-harness-background-processes.ts +8 -4
  39. package/src/tools/agent-harness-browser-cockpit-route.ts +188 -34
  40. package/src/tools/agent-harness-browser-control.ts +12 -4
  41. package/src/tools/agent-harness-browser-pwa-read-models.ts +622 -0
  42. package/src/tools/agent-harness-device-live-read-models.ts +366 -0
  43. package/src/tools/agent-harness-execution-posture.ts +3 -0
  44. package/src/tools/agent-harness-interactive-runtime-records.ts +421 -0
  45. package/src/tools/agent-harness-local-model-benchmarks.ts +71 -1
  46. package/src/tools/agent-harness-local-model-endpoints.ts +469 -354
  47. package/src/tools/agent-harness-local-model-smoke.ts +277 -0
  48. package/src/tools/agent-harness-local-model-url.ts +78 -0
  49. package/src/tools/agent-harness-media-posture.ts +27 -12
  50. package/src/tools/agent-harness-memory-external-providers.ts +796 -0
  51. package/src/tools/agent-harness-memory-posture.ts +253 -137
  52. package/src/tools/agent-harness-memory-provider-certification.ts +219 -0
  53. package/src/tools/agent-harness-memory-refinement.ts +340 -0
  54. package/src/tools/agent-harness-mode-catalog.ts +4 -2
  55. package/src/tools/agent-harness-model-provider-health.ts +139 -42
  56. package/src/tools/agent-harness-model-readiness.ts +31 -5
  57. package/src/tools/agent-harness-model-routing-types.ts +61 -0
  58. package/src/tools/agent-harness-model-routing.ts +31 -6
  59. package/src/tools/agent-harness-pairing-posture.ts +30 -9
  60. package/src/tools/agent-harness-personal-ops-certification.ts +116 -0
  61. package/src/tools/agent-harness-personal-ops-lanes.ts +81 -15
  62. package/src/tools/agent-harness-personal-ops-operations.ts +225 -0
  63. package/src/tools/agent-harness-personal-ops-provider-records.ts +358 -0
  64. package/src/tools/agent-harness-personal-ops-provider-task-records.ts +321 -0
  65. package/src/tools/agent-harness-personal-ops-records.ts +176 -224
  66. package/src/tools/agent-harness-personal-ops-types.ts +19 -1
  67. package/src/tools/agent-harness-personal-ops.ts +18 -11
  68. package/src/tools/agent-harness-remote-read-models.ts +541 -0
  69. package/src/tools/agent-harness-research-briefing.ts +26 -7
  70. package/src/tools/agent-harness-research-live-read-models.ts +500 -0
  71. package/src/tools/agent-harness-research-runs.ts +15 -3
  72. package/src/tools/agent-harness-research-workflow.ts +92 -19
  73. package/src/tools/agent-harness-setup-model-helpers.ts +1 -0
  74. package/src/tools/agent-harness-setup-posture.ts +7 -7
  75. package/src/tools/agent-harness-setup-smoke.ts +26 -1
  76. package/src/tools/agent-harness-tool-schema.ts +23 -1
  77. package/src/tools/agent-harness-tool-types.ts +5 -0
  78. package/src/tools/agent-harness-tool.ts +9 -1
  79. package/src/tools/agent-harness-workspace-actions.ts +1 -1
  80. package/src/tools/agent-memory-tool.ts +40 -1
  81. package/src/tools/agent-model-compare-run.ts +13 -0
  82. package/src/tools/agent-research-runner.ts +367 -0
  83. package/src/tools/agent-research-tool.ts +7 -179
  84. package/src/tools/agent-route-planner-candidates-surfaces.ts +1 -1
  85. package/src/version.ts +1 -1
@@ -5,6 +5,8 @@ import type {
5
5
  AgentWorkspaceLocalEditor,
6
6
  AgentWorkspaceRuntimeSnapshot,
7
7
  } from '../input/agent-workspace.ts';
8
+ import type { AgentWorkspaceEditorKind } from '../input/agent-workspace-types.ts';
9
+ import type { AgentWorkspaceSetupChecklistItem } from '../input/agent-workspace-setup.ts';
8
10
  import type { Line } from '../types/grid.ts';
9
11
  import { wrapText } from '../utils/terminal-width.ts';
10
12
  import { GLYPHS } from './ui-primitives.ts';
@@ -18,11 +20,36 @@ import {
18
20
  } from './fullscreen-workspace.ts';
19
21
  import { actionResultColor, type AgentWorkspaceContextLine as ContextLine } from './agent-workspace-style.ts';
20
22
  import { compactText, reviewerReadinessContextLines, snapshotLines } from './agent-workspace-context-lines.ts';
23
+ import { ONBOARDING_COMPLETE_SYNTHETIC_ACTION, ONBOARDING_CRITICAL_STEP_IDS } from '../input/agent-workspace-onboarding-finish.ts';
24
+
25
+ function onboardingCategoryReadiness(
26
+ category: AgentWorkspaceCategory,
27
+ checklist: readonly AgentWorkspaceSetupChecklistItem[],
28
+ ): 'ready' | 'attention' | 'optional' {
29
+ const CATEGORY_CHECKLIST_MAP: Record<string, readonly string[]> = {
30
+ 'setup': ['provider-model', 'connected-host-auth', 'runtime'],
31
+ 'account-model': ['provider-model', 'subscriptions'],
32
+ 'assistant-behavior': [],
33
+ 'tools-permissions': [],
34
+ 'onboarding-display': [],
35
+ 'onboarding-channels': ['channels'],
36
+ 'onboarding-voice-media': ['voice-media'],
37
+ 'onboarding-context': ['agent-knowledge', 'persona', 'skills', 'routines', 'memory', 'notes', 'profile'],
38
+ 'onboarding-automation': [],
39
+ };
40
+ const itemIds = CATEGORY_CHECKLIST_MAP[category.id];
41
+ if (!itemIds || itemIds.length === 0) return 'optional';
42
+ const mapped = itemIds.map((id) => checklist.find((item) => item.id === id));
43
+ if (mapped.every((item) => item?.status === 'ready')) return 'ready';
44
+ if (mapped.some((item) => item?.status === 'blocked' || item?.status === 'recommended')) return 'attention';
45
+ return 'optional';
46
+ }
21
47
 
22
48
  function buildLeftRows(workspace: AgentWorkspace, height: number): WorkspaceRow[] {
23
49
  const rows: WorkspaceRow[] = [];
24
50
  let selectedRenderedIndex = 0;
25
51
  let lastGroup = '';
52
+ const checklist = workspace.runtimeSnapshot?.setupChecklist ?? [];
26
53
 
27
54
  workspace.categories.forEach((category, index) => {
28
55
  if (category.group !== lastGroup) {
@@ -32,11 +59,24 @@ function buildLeftRows(workspace: AgentWorkspace, height: number): WorkspaceRow[
32
59
  const selected = index === workspace.selectedCategoryIndex;
33
60
  if (selected) selectedRenderedIndex = rows.length;
34
61
  const marker = selected ? GLYPHS.navigation.selected : ' ';
62
+ let readinessGlyph = '';
63
+ let readinessFg: string | undefined;
64
+ if (isOnboardingCategory(category)) {
65
+ const readiness = onboardingCategoryReadiness(category, checklist);
66
+ if (readiness === 'ready') {
67
+ readinessGlyph = GLYPHS.status.success;
68
+ readinessFg = PALETTE.good;
69
+ } else if (readiness === 'attention') {
70
+ readinessGlyph = '!';
71
+ readinessFg = PALETTE.warn;
72
+ }
73
+ }
74
+ const readinessMark = readinessGlyph ? `${readinessGlyph} ` : ' ';
35
75
  rows.push({
36
- text: ` ${marker} ${category.label}`,
76
+ text: ` ${marker} ${readinessMark}${category.label}`,
37
77
  selected: selected && workspace.focusPane === 'categories',
38
78
  kind: 'item',
39
- fg: selected ? PALETTE.text : PALETTE.muted,
79
+ fg: selected ? PALETTE.text : readinessFg ?? PALETTE.muted,
40
80
  bold: selected,
41
81
  });
42
82
  });
@@ -68,13 +108,321 @@ function actionCommand(action: AgentWorkspaceAction): string {
68
108
  return action.command ?? '(guidance)';
69
109
  }
70
110
 
71
- function actionMetaLine(action: AgentWorkspaceAction): ContextLine {
111
+ function isOnboardingCategory(category: AgentWorkspaceCategory): boolean {
112
+ return category.group === 'ONBOARDING';
113
+ }
114
+
115
+ interface OnboardingActionColumns {
116
+ readonly setting: string;
117
+ readonly defaultValue: string;
118
+ readonly currentValue: string;
119
+ }
120
+
121
+ function editorPurposeLabel(editorKind: AgentWorkspaceEditorKind): string {
122
+ switch (editorKind) {
123
+ case 'mcp-server': return 'Edit MCP server';
124
+ case 'mcp-tools-server': return 'Edit MCP tools server';
125
+ case 'mcp-repair': return 'Repair MCP connection';
126
+ case 'secret-link': return 'Link secret reference';
127
+ case 'secret-set': return 'Store secret';
128
+ case 'secret-test': return 'Test secret reference';
129
+ case 'secret-delete': return 'Delete secret';
130
+ case 'subscription-login-start': return 'Sign in to provider';
131
+ case 'subscription-login-finish': return 'Finish provider sign-in';
132
+ case 'subscription-logout': return 'Sign out of provider';
133
+ case 'subscription-inspect': return 'Inspect subscription';
134
+ case 'provider-add': return 'Add custom provider';
135
+ case 'provider-remove': return 'Remove custom provider';
136
+ case 'provider-use': return 'Choose provider';
137
+ case 'provider-inspect': return 'Inspect provider';
138
+ case 'provider-routes': return 'Edit provider routes';
139
+ case 'provider-account-repair': return 'Repair provider account';
140
+ case 'memory': return 'Edit memory';
141
+ case 'memory-search': return 'Search memory';
142
+ case 'memory-get': return 'Get memory record';
143
+ case 'memory-explain': return 'Explain memory';
144
+ case 'memory-promote': return 'Promote memory';
145
+ case 'memory-link': return 'Link memory';
146
+ case 'memory-export': return 'Export memory';
147
+ case 'memory-import': return 'Import memory';
148
+ case 'memory-handoff-export': return 'Export memory handoff';
149
+ case 'memory-handoff-inspect': return 'Inspect memory handoff';
150
+ case 'memory-handoff-import': return 'Import memory handoff';
151
+ case 'memory-vector-rebuild': return 'Rebuild memory vectors';
152
+ case 'note': return 'Edit note';
153
+ case 'persona': return 'Edit persona';
154
+ case 'persona-search': return 'Search personas';
155
+ case 'persona-show': return 'Show persona';
156
+ case 'persona-discovery-import': return 'Import persona files';
157
+ case 'skill': return 'Edit skill';
158
+ case 'skill-search': return 'Search skills';
159
+ case 'skill-show': return 'Show skill';
160
+ case 'skill-discovery-import': return 'Import skill files';
161
+ case 'routine': return 'Edit routine';
162
+ case 'routine-search': return 'Search routines';
163
+ case 'routine-show': return 'Show routine';
164
+ case 'routine-discovery-import': return 'Import routine files';
165
+ case 'profile': return 'Edit profile';
166
+ case 'profile-from-discovered': return 'Create profile from files';
167
+ case 'profile-show': return 'Show profile';
168
+ case 'profile-default': return 'Set default profile';
169
+ case 'profile-default-clear': return 'Clear default profile';
170
+ case 'profile-delete': return 'Delete profile';
171
+ case 'profile-template-export': return 'Export profile template';
172
+ case 'profile-template-import': return 'Import profile template';
173
+ case 'profile-template-show': return 'Show profile template';
174
+ case 'profile-template-from-discovered': return 'Create template from files';
175
+ case 'knowledge-url': return 'Ingest URL';
176
+ case 'knowledge-urls': return 'Ingest URLs';
177
+ case 'knowledge-file': return 'Ingest file';
178
+ case 'knowledge-bookmarks': return 'Import bookmarks';
179
+ case 'knowledge-browser-history': return 'Import browser history';
180
+ case 'knowledge-connector-ingest': return 'Run connector ingest';
181
+ case 'knowledge-connector-show': return 'Show connector';
182
+ case 'knowledge-connector-doctor': return 'Check connector health';
183
+ case 'knowledge-reindex': return 'Reindex knowledge';
184
+ case 'knowledge-search': return 'Search knowledge';
185
+ case 'knowledge-ask': return 'Ask knowledge';
186
+ case 'knowledge-get': return 'Get knowledge record';
187
+ case 'knowledge-map': return 'Map knowledge';
188
+ case 'knowledge-review-issue': return 'Review knowledge issue';
189
+ case 'knowledge-consolidate': return 'Consolidate knowledge';
190
+ case 'knowledge-packet': return 'Build knowledge packet';
191
+ case 'knowledge-explain': return 'Explain knowledge';
192
+ case 'delegate-task': return 'Delegate build task';
193
+ case 'document-browse': return 'Browse documents';
194
+ case 'document-show': return 'Show document';
195
+ case 'document-create': return 'Create document';
196
+ case 'document-update': return 'Revise document';
197
+ case 'document-review': return 'Review document';
198
+ case 'document-comment': return 'Add comment';
199
+ case 'document-resolve-comment': return 'Resolve comment';
200
+ case 'document-suggest': return 'Propose suggestion';
201
+ case 'document-accept-suggestion': return 'Accept suggestion';
202
+ case 'document-reject-suggestion': return 'Reject suggestion';
203
+ case 'document-insert-artifact': return 'Insert artifact';
204
+ case 'document-attach-artifact': return 'Attach artifact';
205
+ case 'document-export': return 'Export document';
206
+ case 'document-reviewer-readiness': return 'Review readiness preflight';
207
+ case 'document-review-packet-wizard': return 'Run packet wizard';
208
+ case 'document-review-packet-preset': return 'Save packet preset';
209
+ case 'document-review-packet-preset-refresh': return 'Refresh packet preset';
210
+ case 'document-review-packet-share': return 'Share review packet';
211
+ case 'model-compare': return 'Compare models';
212
+ case 'model-compare-review': return 'Review comparison';
213
+ case 'model-compare-handoff-diff': return 'Diff reviewer handoffs';
214
+ case 'model-compare-judge': return 'Judge comparison';
215
+ case 'model-compare-apply': return 'Apply comparison winner';
216
+ case 'model-compare-route-decision': return 'Record route decision';
217
+ case 'model-compare-export': return 'Export comparison';
218
+ case 'model-compare-analytics': return 'View comparison analytics';
219
+ case 'local-model-benchmark': return 'Run local benchmark';
220
+ case 'auth-show': return 'Show auth status';
221
+ case 'auth-repair': return 'Repair auth';
222
+ case 'auth-bundle-export': return 'Export auth bundle';
223
+ case 'auth-bundle-inspect': return 'Inspect auth bundle';
224
+ case 'trust-bundle-export': return 'Export trust bundle';
225
+ case 'trust-bundle-inspect': return 'Inspect trust bundle';
226
+ case 'support-bundle-export': return 'Export support bundle';
227
+ case 'support-bundle-inspect': return 'Inspect support bundle';
228
+ case 'support-bundle-import': return 'Import support bundle';
229
+ case 'subscription-bundle-export': return 'Export subscription bundle';
230
+ case 'subscription-bundle-inspect': return 'Inspect subscription bundle';
231
+ case 'voice-enable': return 'Enable voice';
232
+ case 'voice-disable': return 'Disable voice';
233
+ case 'voice-bundle-export': return 'Export voice bundle';
234
+ case 'voice-bundle-inspect': return 'Inspect voice bundle';
235
+ case 'tts-prompt': return 'Test TTS prompt';
236
+ case 'image-input': return 'Attach image';
237
+ case 'artifact-browser': return 'Browse artifacts';
238
+ case 'artifact-show': return 'Show artifact';
239
+ case 'artifact-export-file': return 'Export artifact to file';
240
+ case 'artifact-export-package': return 'Export artifact package';
241
+ case 'artifact-promote-knowledge': return 'Promote artifact to knowledge';
242
+ case 'media-generate': return 'Generate media';
243
+ case 'conversation-export': return 'Export conversation';
244
+ case 'conversation-events': return 'Show conversation events';
245
+ case 'conversation-groups': return 'Show conversation groups';
246
+ case 'conversation-find': return 'Find conversation';
247
+ case 'effort-level': return 'Set effort level';
248
+ case 'channel-show': return 'Show channel';
249
+ case 'channel-doctor': return 'Check channel health';
250
+ case 'channel-setup': return 'Set up channel';
251
+ case 'channel-send': return 'Send to channel';
252
+ case 'session-save': return 'Save session';
253
+ case 'session-load': return 'Load session';
254
+ case 'session-rename': return 'Rename session';
255
+ case 'session-resume': return 'Resume session';
256
+ case 'session-info': return 'Inspect session';
257
+ case 'session-export-saved': return 'Export saved session';
258
+ case 'session-search': return 'Search sessions';
259
+ case 'session-delete': return 'Delete session';
260
+ case 'session-fork': return 'Fork session';
261
+ case 'session-graph': return 'Inspect session graph';
262
+ case 'task-list-filter': return 'Filter task list';
263
+ case 'task-show': return 'Show task';
264
+ case 'task-output': return 'View task output';
265
+ case 'plan-seed': return 'Seed plan';
266
+ case 'plan-show': return 'Show plan';
267
+ case 'plan-approve': return 'Approve plan';
268
+ case 'plan-override': return 'Override plan';
269
+ case 'plan-clear': return 'Clear plan';
270
+ case 'health-repair': return 'Repair health issue';
271
+ case 'approval-review': return 'Review approval';
272
+ case 'approval-approve': return 'Approve request';
273
+ case 'approval-deny': return 'Deny request';
274
+ case 'approval-cancel': return 'Cancel approval';
275
+ case 'automation-job-run': return 'Run automation job';
276
+ case 'automation-job-pause': return 'Pause automation job';
277
+ case 'automation-job-resume': return 'Resume automation job';
278
+ case 'automation-run-cancel': return 'Cancel automation run';
279
+ case 'automation-run-retry': return 'Retry automation run';
280
+ case 'schedule-run': return 'Run schedule';
281
+ case 'schedule-edit': return 'Edit schedule';
282
+ case 'routine-receipt': return 'View routine receipt';
283
+ case 'schedule-receipt': return 'View schedule receipt';
284
+ case 'mode-preset': return 'Set mode preset';
285
+ case 'mode-domain': return 'Set mode domain';
286
+ case 'setting-set': return 'Set setting';
287
+ case 'model-pin': return 'Pin model';
288
+ case 'model-unpin': return 'Unpin model';
289
+ case 'workplan-add': return 'Add work item';
290
+ case 'workplan-show': return 'Show work plan';
291
+ case 'workplan-status': return 'Check work plan status';
292
+ case 'workplan-delete': return 'Delete work item';
293
+ case 'workplan-clear-completed': return 'Clear completed items';
294
+ case 'routine-schedule': return 'Schedule routine';
295
+ case 'reminder-schedule': return 'Schedule reminder';
296
+ case 'skill-bundle': return 'Edit skill bundle';
297
+ case 'skill-bundle-search': return 'Search skill bundles';
298
+ case 'skill-bundle-show': return 'Show skill bundle';
299
+ case 'skill-bundle-update': return 'Update skill bundle';
300
+ case 'skill-bundle-enable': return 'Enable skill bundle';
301
+ case 'skill-bundle-disable': return 'Disable skill bundle';
302
+ case 'skill-bundle-review': return 'Review skill bundle';
303
+ case 'skill-bundle-stale': return 'Mark bundle stale';
304
+ case 'skill-bundle-delete': return 'Delete skill bundle';
305
+ case 'learned-behavior': return 'Review learned behavior';
306
+ case 'web-research': return 'Run web research';
307
+ case 'web-fetch': return 'Fetch from web';
308
+ case 'research-run': return 'Run research';
309
+ case 'research-source': return 'Manage research source';
310
+ case 'research-report': return 'Show research report';
311
+ case 'notify-webhook': return 'Add notification webhook';
312
+ case 'notify-webhook-remove': return 'Remove notification webhook';
313
+ case 'notify-webhook-clear': return 'Clear notification webhooks';
314
+ case 'notify-webhook-test': return 'Test notification webhook';
315
+ case 'notify-send': return 'Send notification';
316
+ default: return `Edit ${editorKind}`;
317
+ }
318
+ }
319
+
320
+ function localOperationLabel(localOperation: string): string {
321
+ switch (localOperation) {
322
+ case 'routine-start': return 'Start routine';
323
+ case 'persona-activate':
324
+ case 'persona-use': return 'Activate persona';
325
+ case 'persona-clear': return 'Deactivate persona';
326
+ case 'persona-edit': return 'Edit persona';
327
+ case 'persona-review': return 'Review persona';
328
+ case 'persona-delete': return 'Delete persona';
329
+ case 'memory-edit': return 'Edit memory';
330
+ case 'memory-review': return 'Review memory';
331
+ case 'memory-stale': return 'Mark memory stale';
332
+ case 'memory-delete': return 'Delete memory';
333
+ case 'note-edit': return 'Edit note';
334
+ case 'note-review': return 'Review note';
335
+ case 'note-stale': return 'Mark note stale';
336
+ case 'note-delete': return 'Delete note';
337
+ case 'note-promote-memory': return 'Promote note to memory';
338
+ case 'note-promote-persona': return 'Promote note to persona';
339
+ case 'note-promote-skill': return 'Promote note to skill';
340
+ case 'note-promote-routine': return 'Promote note to routine';
341
+ case 'note-promote-knowledge-url': return 'Promote note to knowledge';
342
+ case 'skill-edit': return 'Edit skill';
343
+ case 'skill-enable': return 'Enable skill';
344
+ case 'skill-disable': return 'Disable skill';
345
+ case 'skill-review': return 'Review skill';
346
+ case 'skill-delete': return 'Delete skill';
347
+ case 'routine-edit': return 'Edit routine';
348
+ case 'routine-enable': return 'Enable routine';
349
+ case 'routine-disable': return 'Disable routine';
350
+ case 'routine-review': return 'Review routine';
351
+ case 'routine-delete': return 'Delete routine';
352
+ default: return 'Apply local action';
353
+ }
354
+ }
355
+
356
+ function onboardingActionLabel(workspace: AgentWorkspace, action: AgentWorkspaceAction): string {
357
+ if (action.kind === 'settings-import') return 'Import GoodVibes preferences';
358
+ if (action.kind === 'setup-checkpoint') {
359
+ if (action.setupCheckpointOperation === 'show') return 'Review saved progress';
360
+ if (action.setupCheckpointOperation === 'mark-current') return 'Save progress';
361
+ if (action.setupCheckpointOperation === 'clear') return 'Clear saved progress';
362
+ return 'Review setup progress';
363
+ }
364
+ if (action.kind === 'model-picker') return action.modelPickerFlow === 'model' ? 'Choose model' : 'Choose provider and model';
365
+ if (action.kind === 'settings-modal') return 'Open settings';
366
+ if (action.kind === 'workspace') {
367
+ if (action.targetCategoryId) {
368
+ const target = workspace.categories.find((c) => c.id === action.targetCategoryId);
369
+ const label = target ? target.label : action.targetCategoryId;
370
+ return `Switch to ${label}`;
371
+ }
372
+ return 'Switch to setup area';
373
+ }
374
+ if (action.kind === 'editor') return action.editorKind ? editorPurposeLabel(action.editorKind) : 'Open form';
375
+ if (action.kind === 'local-selection') return 'Move selection';
376
+ if (action.kind === 'local-operation') return action.localOperation ? localOperationLabel(action.localOperation) : 'Apply local action';
377
+ if (action.kind === 'onboarding-complete') return 'Finish setup';
378
+ if (action.kind === 'command') {
379
+ const cmd = action.command ?? '';
380
+ return action.commandBehavior === 'inline' ? `Run: ${cmd}` : `Open: ${cmd}`;
381
+ }
382
+ if (action.safety === 'read-only') return 'Review';
383
+ if (action.safety === 'blocked') return 'Unavailable in this setup step';
384
+ return 'Open';
385
+ }
386
+
387
+ function onboardingActionColumns(workspace: AgentWorkspace, action: AgentWorkspaceAction): OnboardingActionColumns {
388
+ if (action.kind === 'setting') {
389
+ return workspace.settingActionDisplay(action) ?? {
390
+ setting: action.label,
391
+ defaultValue: '(unknown)',
392
+ currentValue: '(unknown)',
393
+ };
394
+ }
395
+ return {
396
+ setting: onboardingActionLabel(workspace, action),
397
+ defaultValue: '',
398
+ currentValue: '',
399
+ };
400
+ }
401
+
402
+ function actionChange(workspace: AgentWorkspace, category: AgentWorkspaceCategory, action: AgentWorkspaceAction): string {
403
+ return isOnboardingCategory(category) ? onboardingActionColumns(workspace, action).setting : actionCommand(action);
404
+ }
405
+
406
+ function actionMetaLine(workspace: AgentWorkspace, category: AgentWorkspaceCategory, action: AgentWorkspaceAction): ContextLine {
407
+ const onboarding = isOnboardingCategory(category);
408
+ if (onboarding) {
409
+ return {
410
+ text: `About: ${compactText(action.detail, 100)}`,
411
+ fg: action.safety === 'blocked' ? PALETTE.warn : PALETTE.muted,
412
+ };
413
+ }
72
414
  return {
73
- text: `Does: ${actionCommand(action)}`,
415
+ text: `Does: ${actionChange(workspace, category, action)}`,
74
416
  fg: action.safety === 'blocked' ? PALETTE.warn : action.kind === 'command' ? PALETTE.info : PALETTE.muted,
75
417
  };
76
418
  }
77
419
 
420
+ function shouldRenderOnboardingSettingsTable(actions: readonly AgentWorkspaceAction[]): boolean {
421
+ // Exclude the synthetic finish footer row — it is not a setting and must not block table mode.
422
+ const nonFinish = actions.filter((a) => a.kind !== 'onboarding-complete');
423
+ return nonFinish.length > 0 && nonFinish.every((action) => action.kind === 'setting');
424
+ }
425
+
78
426
  function editorContextLines(editor: AgentWorkspaceLocalEditor, snapshot: AgentWorkspaceRuntimeSnapshot | null): ContextLine[] {
79
427
  const selected = editor.fields[editor.selectedFieldIndex];
80
428
  const lines: ContextLine[] = [
@@ -135,19 +483,20 @@ function buildContextRows(workspace: AgentWorkspace, category: AgentWorkspaceCat
135
483
  const selectedActionLines: ContextLine[] = action
136
484
  ? [
137
485
  { text: `Selected: ${action.label}`, fg: PALETTE.title, bold: true },
138
- actionMetaLine(action),
486
+ actionMetaLine(workspace, category, action),
139
487
  ]
140
488
  : [];
141
489
  const snapshotContextLines = snapshotLines(workspace, category, workspace.runtimeSnapshot);
142
490
  lines.push(...selectedActionLines, ...snapshotContextLines);
143
491
 
492
+ const onboarding = isOnboardingCategory(category);
144
493
  if (workspace.lastActionResult) {
145
494
  lines.push(
146
- { text: 'Action Result', fg: PALETTE.title, bold: true },
495
+ { text: onboarding ? 'Result' : 'Action Result', fg: PALETTE.title, bold: true },
147
496
  { text: workspace.lastActionResult.title, fg: actionResultColor(workspace.lastActionResult), bold: true },
148
497
  { text: compactText(workspace.lastActionResult.detail), fg: PALETTE.text },
149
498
  );
150
- if (workspace.lastActionResult.command) {
499
+ if (!onboarding && workspace.lastActionResult.command) {
151
500
  lines.push({ text: `Command: ${workspace.lastActionResult.command}`, fg: PALETTE.muted });
152
501
  }
153
502
  }
@@ -211,10 +560,25 @@ function buildEditorFieldRows(editor: AgentWorkspaceLocalEditor, index: number,
211
560
  return rows;
212
561
  }
213
562
 
563
+ function onboardingFinishPrerequisitesMet(workspace: AgentWorkspace): boolean {
564
+ const checklist = workspace.runtimeSnapshot?.setupChecklist ?? [];
565
+ return ONBOARDING_CRITICAL_STEP_IDS.every((id) => checklist.find((item) => item.id === id)?.status === 'ready');
566
+ }
567
+
214
568
  function buildActionRows(workspace: AgentWorkspace, width: number, height: number): WorkspaceRow[] {
215
569
  if (workspace.localEditor) return buildEditorRows(workspace.localEditor, width, height);
570
+ const category = workspace.selectedActionCategory;
571
+ const onboarding = isOnboardingCategory(category);
572
+ const settingTable = onboarding
573
+ && !workspace.actionSearchActive
574
+ && shouldRenderOnboardingSettingsTable(workspace.actions);
216
575
  const rows: WorkspaceRow[] = [];
217
- const labelWidth = Math.min(34, Math.max(18, Math.floor(width * 0.38)));
576
+ const valueWidth = settingTable
577
+ ? Math.min(22, Math.max(10, Math.floor(width * 0.18)))
578
+ : 0;
579
+ const labelWidth = settingTable
580
+ ? Math.max(18, width - (valueWidth * 2) - 6)
581
+ : Math.min(34, Math.max(18, Math.floor(width * 0.38)));
218
582
  const commandWidth = Math.max(10, width - labelWidth - 6);
219
583
  if (workspace.actionSearchActive) {
220
584
  rows.push({
@@ -224,40 +588,89 @@ function buildActionRows(workspace: AgentWorkspace, width: number, height: numbe
224
588
  });
225
589
  }
226
590
  rows.push({
227
- text: ` ${padDisplay(workspace.actionSearchActive ? 'Result' : 'Action', labelWidth)} ${padDisplay('Does', commandWidth)}`,
591
+ text: settingTable
592
+ ? ` ${padDisplay(workspace.actionSearchActive ? 'Result' : 'Setting', labelWidth)} ${padDisplay('Default', valueWidth)} ${padDisplay('Current', valueWidth)}`
593
+ : ` ${padDisplay(workspace.actionSearchActive ? 'Result' : onboarding ? 'Option' : 'Action', labelWidth)} ${padDisplay('Does', commandWidth)}`,
228
594
  fg: PALETTE.muted,
229
595
  bold: true,
230
596
  });
231
597
 
232
- const actions = workspace.actions;
598
+ // The finish footer row is appended to workspace.actions by the input layer
599
+ // (shouldShowOnboardingFinishFooter). Detect it here by action id for special styling.
600
+ const allActions = workspace.actions;
601
+ const finishActionIndex = allActions.findIndex((a) => a.id === ONBOARDING_COMPLETE_SYNTHETIC_ACTION.id);
602
+
233
603
  const visible = Math.max(1, height - (workspace.actionSearchActive ? 3 : 2));
234
- const window = stableWindow(actions.length, workspace.selectedActionIndex, visible);
604
+ const window = stableWindow(allActions.length, workspace.selectedActionIndex, visible);
235
605
  if (window.start > 0) rows.push({ text: `${GLYPHS.navigation.moreAbove} ${window.start} more action(s) above`, kind: 'more', fg: PALETTE.dim, dim: true });
236
606
 
237
607
  for (let index = window.start; index < window.end; index += 1) {
238
- const action = actions[index]!;
608
+ const action = allActions[index]!;
609
+ const isFinishFooterRow = finishActionIndex >= 0 && index === finishActionIndex;
239
610
  const selected = index === workspace.selectedActionIndex;
240
611
  const searchResult = workspace.actionSearchActive ? workspace.actionSearchResults[index] : null;
612
+ const actionCategory = searchResult?.category ?? category;
241
613
  const label = searchResult ? `${searchResult.category.label} / ${action.label}` : action.label;
242
614
  const marker = selected ? GLYPHS.navigation.selected : ' ';
243
- rows.push({
244
- text: `${marker} ${padDisplay(label, labelWidth)} ${padDisplay(actionCommand(action), commandWidth)}`,
245
- selected: selected && workspace.focusPane === 'actions',
246
- fg: action.safety === 'blocked' ? PALETTE.warn : selected ? PALETTE.text : PALETTE.info,
247
- bold: selected,
248
- });
615
+
616
+ if (isFinishFooterRow) {
617
+ // Sticky Finish setup footer row
618
+ const prereqsMet = onboardingFinishPrerequisitesMet(workspace);
619
+ const sep = `${GLYPHS.frame.horizontal.repeat(3)} Finish setup ${GLYPHS.frame.horizontal.repeat(3)}`;
620
+ if (!selected) {
621
+ rows.push({
622
+ text: ` ${sep}`,
623
+ kind: 'item',
624
+ fg: PALETTE.dim,
625
+ dim: true,
626
+ });
627
+ }
628
+ const finishDetail = prereqsMet
629
+ ? 'Finish setup'
630
+ : (() => {
631
+ const checklist = workspace.runtimeSnapshot?.setupChecklist ?? [];
632
+ const unmet = [...ONBOARDING_CRITICAL_STEP_IDS]
633
+ .filter((id) => checklist.find((item) => item.id === id)?.status !== 'ready')
634
+ .map((id) => checklist.find((item) => item.id === id)?.label ?? id)
635
+ .join(', ');
636
+ return `Finish needs: ${unmet}`;
637
+ })();
638
+ const text = settingTable
639
+ ? `${marker} ${padDisplay('Finish setup', labelWidth)} ${padDisplay('', valueWidth)} ${padDisplay('', valueWidth)}`
640
+ : `${marker} ${padDisplay('Finish setup', labelWidth)} ${padDisplay(finishDetail, commandWidth)}`;
641
+ rows.push({
642
+ text,
643
+ selected: selected && workspace.focusPane === 'actions',
644
+ fg: prereqsMet ? PALETTE.good : PALETTE.warn,
645
+ bold: selected,
646
+ kind: 'item',
647
+ });
648
+ } else {
649
+ const text = settingTable
650
+ ? (() => {
651
+ const columns = onboardingActionColumns(workspace, action);
652
+ return `${marker} ${padDisplay(columns.setting, labelWidth)} ${padDisplay(columns.defaultValue, valueWidth)} ${padDisplay(columns.currentValue, valueWidth)}`;
653
+ })()
654
+ : `${marker} ${padDisplay(label, labelWidth)} ${padDisplay(actionChange(workspace, actionCategory, action), commandWidth)}`;
655
+ rows.push({
656
+ text,
657
+ selected: selected && workspace.focusPane === 'actions',
658
+ fg: action.safety === 'blocked' ? PALETTE.warn : selected ? PALETTE.text : PALETTE.info,
659
+ bold: selected,
660
+ });
661
+ }
249
662
  }
250
663
 
251
- if (window.end < actions.length) rows.push({ text: `${GLYPHS.navigation.moreBelow} ${actions.length - window.end} more action(s) below`, kind: 'more', fg: PALETTE.dim, dim: true });
664
+ if (window.end < allActions.length) rows.push({ text: `${GLYPHS.navigation.moreBelow} ${allActions.length - window.end} more action(s) below`, kind: 'more', fg: PALETTE.dim, dim: true });
252
665
  rows.push({ text: '' });
253
666
  rows.push({ text: `Status: ${workspace.status}`, fg: PALETTE.muted });
254
667
  if (workspace.lastActionResult) {
255
668
  rows.push({ text: '' });
256
- rows.push({ text: `Action Result: ${workspace.lastActionResult.title}`, fg: actionResultColor(workspace.lastActionResult), bold: true });
669
+ rows.push({ text: `${onboarding ? 'Result' : 'Action Result'}: ${workspace.lastActionResult.title}`, fg: actionResultColor(workspace.lastActionResult), bold: true });
257
670
  for (const line of wrapText(workspace.lastActionResult.detail, Math.max(1, width - 2))) {
258
671
  rows.push({ text: ` ${line}`, fg: PALETTE.text });
259
672
  }
260
- if (workspace.lastActionResult.command) {
673
+ if (!onboarding && workspace.lastActionResult.command) {
261
674
  rows.push({ text: ` Command: ${workspace.lastActionResult.command}`, fg: PALETTE.muted });
262
675
  }
263
676
  }
@@ -67,7 +67,7 @@ const READ_ONLY_VIBE_ACTIONS = new Set(['', 'status', 'summary', 'list', 'show',
67
67
  const READ_ONLY_PERSONAL_OPS_ACTIONS = new Set(['', 'briefing', 'brief', 'daily', 'daily_brief', 'morning', 'status', 'summary', 'overview', 'map', 'list', 'queue', 'queues', 'review_queue', 'review_queues', 'personal_queue', 'ops_queue', 'intake', 'request', 'route', 'plan', 'triage', 'draft', 'lane', 'inspect', 'show']);
68
68
  const READ_ONLY_RESEARCH_ACTIONS = new Set(['', 'briefing', 'brief', 'status', 'dashboard', 'cockpit', 'next', 'plan', 'workflow', 'research', 'runner', 'browser', 'browser_runner', 'browser_backed', 'deep_research', 'runs', 'list_runs', 'run_list', 'run', 'show_run', 'inspect_run', 'sources', 'queue', 'source_queue', 'source', 'show_source', 'inspect_source', 'bundle', 'bundle_sources', 'source_bundle', 'search', 'public_search', 'collect', 'collect_sources', 'source_candidates', 'reports', 'list_reports', 'report_list', 'visual_reports', 'report_artifact', 'show_report', 'inspect_report', 'show_visual_report', 'visual_report_artifact']);
69
69
  const READ_ONLY_CHANNELS_ACTIONS = new Set(['', 'status', 'summary', 'list', 'readiness', 'channels', 'channel', 'show', 'inspect', 'setup', 'guide', 'setup_guide', 'channel_setup_guide', 'triage', 'inbox', 'blockers', 'retries', 'channel_triage', 'deliveries', 'delivery', 'receipts', 'history', 'channel_deliveries']);
70
- const READ_ONLY_MEMORY_ACTIONS = new Set(['', 'status', 'summary', 'posture', 'memory_posture', 'recall', 'providers', 'provider', 'memory_provider', 'embedding', 'external', 'external_provider', 'curator', 'learning', 'learning_curator', 'queue', 'review_queue', 'plan', 'candidate', 'learning_candidate', 'card', 'inspect_candidate', 'list', 'records', 'memories', 'search', 'find', 'lookup', 'get', 'show', 'inspect', 'read']);
70
+ const READ_ONLY_MEMORY_ACTIONS = new Set(['', 'status', 'summary', 'posture', 'memory_posture', 'recall', 'providers', 'provider', 'memory_provider', 'embedding', 'external', 'external_provider', 'refinement', 'refinement_tasks', 'semantic_refinement', 'self_improvement', 'semantic_self_improvement', 'learning_loop', 'curator', 'learning', 'learning_curator', 'queue', 'review_queue', 'plan', 'candidate', 'learning_candidate', 'card', 'inspect_candidate', 'list', 'records', 'memories', 'search', 'find', 'lookup', 'get', 'show', 'inspect', 'read']);
71
71
  const READ_ONLY_COMPUTER_ACTIONS = new Set(['', 'status', 'summary', 'overview', 'computer', 'computer_use', 'plan', 'route', 'control_plan', 'browser_plan', 'desktop_plan', 'control', 'browser_control', 'desktop', 'desktop_control', 'screenshot', 'screen', 'screen_recording', 'observe', 'browser', 'pwa', 'cockpit', 'browser_cockpit', 'web', 'setup', 'configure', 'browser_desktop_control', 'mcp', 'tools', 'servers', 'mcp_servers']);
72
72
  const READ_ONLY_DEVICE_ACTIONS = new Set(['', 'status', 'map', 'capabilities', 'device', 'devices', 'mobile', 'phone', 'pairing', 'capability', 'route', 'pairing_route', 'show', 'inspect', 'browser', 'pwa', 'cockpit', 'browser_cockpit', 'web', 'control', 'browser_control', 'desktop', 'desktop_control', 'computer_use', 'voice', 'media', 'voice_media', 'workflows', 'provider', 'media_provider', 'voice_provider']);
73
73
  const READ_ONLY_MODELS_ACTIONS = new Set(['', 'status', 'routing', 'routes', 'models', 'model', 'readiness', 'route_readiness', 'route', 'model_route', 'inspect', 'show', 'candidate', 'endpoint', 'local', 'cookbook', 'local_cookbook', 'recipes', 'recipe', 'ollama', 'llama_cpp', 'llamacpp', 'vllm', 'local_servers', 'providers', 'provider_accounts', 'accounts', 'subscriptions', 'auth', 'logins', 'provider', 'provider_account', 'account', 'subscription', 'auth_status']);
@@ -0,0 +1,75 @@
1
+ export const AGENT_TOOL_MODES = [
2
+ 'spawn',
3
+ 'batch-spawn',
4
+ 'list',
5
+ 'templates',
6
+ 'status',
7
+ 'get',
8
+ 'budget',
9
+ 'plan',
10
+ 'wait',
11
+ 'message',
12
+ 'cancel',
13
+ 'wrfc-chains',
14
+ 'wrfc-history',
15
+ 'cohort-status',
16
+ 'cohort-report',
17
+ ] as const;
18
+
19
+ export const AGENT_TEMPLATES = ['orchestrator', 'engineer', 'reviewer', 'tester', 'researcher', 'integrator', 'general'] as const;
20
+
21
+ export function agentOrchestrationDecisionCards(agentToolAvailable: boolean): readonly Record<string, unknown>[] {
22
+ return [
23
+ {
24
+ id: 'serial-by-default',
25
+ label: 'Stay serial by default',
26
+ status: 'ready',
27
+ chooseWhen: ['Ordinary chat, planning, research, setup, local context, and short current-workspace tool work.'],
28
+ route: 'main conversation',
29
+ reason: 'Lowest-friction route for the user when parallelism does not improve outcome.',
30
+ },
31
+ {
32
+ id: 'visible-single-agent',
33
+ label: 'Spawn one visible agent',
34
+ status: agentToolAvailable ? 'ready' : 'unavailable',
35
+ chooseWhen: ['A bounded autonomous task can run independently with visible status and cancellation.'],
36
+ requiredFields: ['task', 'successCriteria or requiredEvidence when outcome quality matters'],
37
+ modelRoute: 'agent { mode: "spawn" }',
38
+ inspectRoute: 'agent_harness mode:"agent_orchestration"',
39
+ },
40
+ {
41
+ id: 'visible-batch-spawn',
42
+ label: 'Batch-spawn independent agents',
43
+ status: agentToolAvailable ? 'ready' : 'unavailable',
44
+ chooseWhen: ['Tasks are genuinely independent and parallel work materially improves time-to-result.'],
45
+ doNotUseWhen: ['Review/test/verification role fanout for one deliverable; that collapses to one owner chain.'],
46
+ requiredFields: ['tasks[]', 'authoritativeTask for the original user ask when applicable'],
47
+ modelRoute: 'agent { mode: "batch-spawn" }',
48
+ inspectRoute: 'agent_harness mode:"agent_orchestration"',
49
+ },
50
+ {
51
+ id: 'managed-multi-runner-plan',
52
+ label: 'Use a managed multi-runner plan',
53
+ status: agentToolAvailable ? 'ready' : 'unavailable',
54
+ chooseWhen: ['A large task already has approval for parallel work and needs milestones, evidence, and cancellation routes.'],
55
+ requiredFields: ['original user ask', 'lane reason', 'success criteria', 'per-runner evidence', 'cancel/recovery route'],
56
+ modelRoute: 'agent_work_plan action:"dispatch_agents" ids:["..."] confirm:true explicitUserRequest:"..."',
57
+ inspectRoute: 'agent_harness mode:"agent_orchestration"',
58
+ policy: 'Read-only plan surface first; approved work-plan dispatch, spawn, message, wait, cancel, or remote mutation stays on confirmed first-class routes.',
59
+ },
60
+ {
61
+ id: 'inspect-or-control-visible-agent',
62
+ label: 'Inspect or control a visible agent',
63
+ status: agentToolAvailable ? 'ready' : 'unavailable',
64
+ chooseWhen: ['The user asks for progress, budget, plan, message, wait, cancel, WRFC chain, or cohort status.'],
65
+ modelRoutes: ['agent { mode: "list" }', 'agent { mode: "get" }', 'agent { mode: "message" }', 'agent { mode: "wait" }', 'agent { mode: "cancel" }'],
66
+ },
67
+ {
68
+ id: 'hidden-fanout-blocked',
69
+ label: 'Block hidden fanout',
70
+ status: 'blocked',
71
+ chooseWhen: ['A request implies invisible background agents, unmanaged parallel coding workers, or orphaned jobs.'],
72
+ saferRoutes: ['visible work plan', 'research run', 'confirmed schedule', 'agent { mode: "spawn" }', 'delegation action:"status"'],
73
+ },
74
+ ];
75
+ }