@pellux/goodvibes-agent 1.2.0 → 1.3.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 (77) hide show
  1. package/CHANGELOG.md +159 -0
  2. package/dist/package/main.js +19016 -12684
  3. package/docs/README.md +3 -3
  4. package/docs/connected-host.md +2 -2
  5. package/docs/getting-started.md +11 -9
  6. package/docs/tools-and-commands.md +12 -10
  7. package/docs/voice-and-live-tts.md +2 -2
  8. package/package.json +1 -1
  9. package/release/release-notes.md +17 -5
  10. package/release/release-readiness.json +36 -36
  11. package/src/agent/competitive-feature-inventory.ts +42 -44
  12. package/src/agent/setup-wizard-artifact-receipts.ts +366 -0
  13. package/src/agent/setup-wizard.ts +245 -9
  14. package/src/input/agent-workspace-categories.ts +2 -2
  15. package/src/input/agent-workspace-onboarding-categories.ts +4 -4
  16. package/src/input/agent-workspace-settings.ts +52 -1
  17. package/src/input/agent-workspace-setup-snapshot.ts +20 -1
  18. package/src/input/agent-workspace-setup.ts +32 -5
  19. package/src/input/agent-workspace-snapshot.ts +23 -3
  20. package/src/input/agent-workspace.ts +5 -1
  21. package/src/input/setup-wizard-live-receipts.ts +76 -0
  22. package/src/renderer/agent-workspace-context-lines.ts +21 -6
  23. package/src/renderer/agent-workspace.ts +46 -10
  24. package/src/runtime/tool-permission-safety.ts +1 -1
  25. package/src/tools/agent-harness-agent-orchestration-policy.ts +75 -0
  26. package/src/tools/agent-harness-agent-orchestration.ts +216 -128
  27. package/src/tools/agent-harness-autonomy-live-records.ts +154 -0
  28. package/src/tools/agent-harness-autonomy-queue-types.ts +1 -1
  29. package/src/tools/agent-harness-autonomy-queue.ts +19 -8
  30. package/src/tools/agent-harness-autonomy-watcher-read-models.ts +509 -0
  31. package/src/tools/agent-harness-background-processes.ts +8 -4
  32. package/src/tools/agent-harness-browser-cockpit-route.ts +188 -34
  33. package/src/tools/agent-harness-browser-control.ts +12 -4
  34. package/src/tools/agent-harness-browser-pwa-read-models.ts +622 -0
  35. package/src/tools/agent-harness-device-live-read-models.ts +366 -0
  36. package/src/tools/agent-harness-execution-posture.ts +3 -0
  37. package/src/tools/agent-harness-interactive-runtime-records.ts +421 -0
  38. package/src/tools/agent-harness-local-model-benchmarks.ts +71 -1
  39. package/src/tools/agent-harness-local-model-endpoints.ts +469 -354
  40. package/src/tools/agent-harness-local-model-smoke.ts +277 -0
  41. package/src/tools/agent-harness-local-model-url.ts +78 -0
  42. package/src/tools/agent-harness-media-posture.ts +27 -12
  43. package/src/tools/agent-harness-memory-external-providers.ts +796 -0
  44. package/src/tools/agent-harness-memory-posture.ts +253 -137
  45. package/src/tools/agent-harness-memory-provider-certification.ts +219 -0
  46. package/src/tools/agent-harness-memory-refinement.ts +340 -0
  47. package/src/tools/agent-harness-mode-catalog.ts +4 -2
  48. package/src/tools/agent-harness-model-provider-health.ts +139 -42
  49. package/src/tools/agent-harness-model-readiness.ts +31 -5
  50. package/src/tools/agent-harness-model-routing-types.ts +61 -0
  51. package/src/tools/agent-harness-model-routing.ts +31 -6
  52. package/src/tools/agent-harness-pairing-posture.ts +30 -9
  53. package/src/tools/agent-harness-personal-ops-certification.ts +116 -0
  54. package/src/tools/agent-harness-personal-ops-lanes.ts +81 -15
  55. package/src/tools/agent-harness-personal-ops-operations.ts +225 -0
  56. package/src/tools/agent-harness-personal-ops-provider-records.ts +358 -0
  57. package/src/tools/agent-harness-personal-ops-provider-task-records.ts +321 -0
  58. package/src/tools/agent-harness-personal-ops-records.ts +176 -224
  59. package/src/tools/agent-harness-personal-ops-types.ts +19 -1
  60. package/src/tools/agent-harness-personal-ops.ts +18 -11
  61. package/src/tools/agent-harness-remote-read-models.ts +541 -0
  62. package/src/tools/agent-harness-research-briefing.ts +26 -7
  63. package/src/tools/agent-harness-research-live-read-models.ts +500 -0
  64. package/src/tools/agent-harness-research-runs.ts +15 -3
  65. package/src/tools/agent-harness-research-workflow.ts +92 -19
  66. package/src/tools/agent-harness-setup-model-helpers.ts +1 -0
  67. package/src/tools/agent-harness-setup-smoke.ts +26 -1
  68. package/src/tools/agent-harness-tool-schema.ts +23 -1
  69. package/src/tools/agent-harness-tool-types.ts +5 -0
  70. package/src/tools/agent-harness-tool.ts +9 -1
  71. package/src/tools/agent-harness-workspace-actions.ts +1 -1
  72. package/src/tools/agent-memory-tool.ts +40 -1
  73. package/src/tools/agent-model-compare-run.ts +13 -0
  74. package/src/tools/agent-research-runner.ts +367 -0
  75. package/src/tools/agent-research-tool.ts +7 -179
  76. package/src/tools/agent-route-planner-candidates-surfaces.ts +1 -1
  77. package/src/version.ts +1 -1
@@ -24,6 +24,8 @@ import type { PromptContextReceipt } from '../agent/prompt-context-receipts.ts';
24
24
  import { discoverProjectContextFiles } from '../agent/project-context-files.ts';
25
25
  import { getAgentRuntimeProfilesRoot, listAgentRuntimeProfiles, listAgentRuntimeProfileTemplates, readAgentRuntimeProfileSelection } from '../agent/runtime-profile.ts';
26
26
  import { RoutineScheduleReceiptStore } from '../agent/routine-schedule-receipts.ts';
27
+ import { buildSetupWizardDurableReceipts } from '../agent/setup-wizard-artifact-receipts.ts';
28
+ import { mergeSetupWizardDurableReceipts, setupWizardLiveDurableReceipts } from './setup-wizard-live-receipts.ts';
27
29
  import { readSetupWizardCheckpoint } from '../agent/setup-wizard-checkpoint.ts';
28
30
  import { discoverVibeFiles } from '../agent/vibe-file.ts';
29
31
  import {
@@ -35,6 +37,7 @@ import {
35
37
  buildAgentSetupWizard,
36
38
  emptyAgentSetupSmokeHistory,
37
39
  emptyAgentSetupWizardCheckpoint,
40
+ setupStepHasSatisfyingReceipt,
38
41
  type AgentSetupWizard,
39
42
  type AgentSetupWizardBlockedCheckFrequency,
40
43
  type AgentSetupWizardCheckpoint,
@@ -266,6 +269,11 @@ export function buildAgentWorkspaceRuntimeSnapshot(context: CommandContext): Age
266
269
  return { available: false, items: [] };
267
270
  }
268
271
  })();
272
+ const setupSmokeHistory = buildSetupSmokeHistory(artifactListSnapshot.items, artifactListSnapshot.available);
273
+ const durableSetupReceipts = mergeSetupWizardDurableReceipts(
274
+ buildSetupWizardDurableReceipts(artifactListSnapshot.items),
275
+ setupWizardLiveDurableReceipts(context),
276
+ );
269
277
  const recentReviewerHandoffs = (() => {
270
278
  try {
271
279
  const handoffs = artifactListSnapshot.items
@@ -452,6 +460,12 @@ export function buildAgentWorkspaceRuntimeSnapshot(context: CommandContext): Age
452
460
  voiceProviders: voiceProviderDescriptors,
453
461
  mediaProviders: mediaProviderDescriptors,
454
462
  });
463
+ const browserPwaEnabled = readConfigBoolean(context, 'web.enabled', false);
464
+ const browserPwaPublicBaseUrl = readConfigString(context, 'web.publicBaseUrl', '(not configured)');
465
+ const connectedHostAuthReceiptReady = setupStepHasSatisfyingReceipt(durableSetupReceipts, 'connected-host-auth');
466
+ const installSmokeReceiptReady = setupSmokeHistory.latestResult === 'ready-for-user-run'
467
+ || setupStepHasSatisfyingReceipt(durableSetupReceipts, 'install-smoke');
468
+ const browserPwaReceiptReady = setupStepHasSatisfyingReceipt(durableSetupReceipts, 'browser-pwa');
455
469
  const setupChecklist = buildAgentWorkspaceSetupChecklist({
456
470
  provider,
457
471
  model,
@@ -460,6 +474,7 @@ export function buildAgentWorkspaceRuntimeSnapshot(context: CommandContext): Age
460
474
  connectedHostTokenReadable: companionAccess.tokenReadable,
461
475
  connectedHostTokenPath: companionAccess.tokenPath,
462
476
  connectedHostTokenError: companionAccess.tokenError,
477
+ connectedHostAuthReceiptReady,
463
478
  activeSubscriptionCount: subscriptionSnapshot.active,
464
479
  pendingSubscriptionCount: subscriptionSnapshot.pending,
465
480
  availableSubscriptionProviderCount: subscriptionSnapshot.available,
@@ -483,14 +498,19 @@ export function buildAgentWorkspaceRuntimeSnapshot(context: CommandContext): Age
483
498
  readyChannelCount: channels.filter((channel) => channel.ready).length,
484
499
  voiceProviderCount: voiceProviders.length,
485
500
  mediaProviderCount: mediaProviders.length,
501
+ installSmokeReceiptReady,
502
+ browserPwaEnabled,
503
+ browserPwaPublicBaseUrl,
504
+ browserPwaFirstRunReceiptStatus: browserPwaReceiptReady ? 'published' : 'not-published',
486
505
  runtimeProfileCount: runtimeProfiles.length,
487
506
  runtimeStarterTemplateCount: runtimeStarterTemplates.length,
488
507
  });
489
508
  const setupWizard = buildWorkspaceSetupWizard(
490
509
  setupChecklist,
491
- buildSetupSmokeHistory(artifactListSnapshot.items, artifactListSnapshot.available),
510
+ setupSmokeHistory,
492
511
  buildSetupWizardCheckpoint(context),
493
512
  setupCompletionMarkerExists(context),
513
+ durableSetupReceipts,
494
514
  );
495
515
  const researchBrowserRunnerContract = buildResearchBrowserRunnerContract(context);
496
516
  const researchVisualReportContract = buildResearchVisualReportContract(researchSourceSnapshot);
@@ -619,8 +639,8 @@ export function buildAgentWorkspaceRuntimeSnapshot(context: CommandContext): Age
619
639
  mcpConnectedServerCount: mcpSnapshot.connectedCount,
620
640
  mcpQuarantinedServerCount: mcpSnapshot.quarantinedCount,
621
641
  mcpAllowAllServerCount: mcpSnapshot.allowAllCount,
622
- browserToolExposureEnabled: readConfigBoolean(context, 'web.enabled', false),
623
- browserToolPublicBaseUrl: readConfigString(context, 'web.publicBaseUrl', '(not configured)'),
642
+ browserToolExposureEnabled: browserPwaEnabled,
643
+ browserToolPublicBaseUrl: browserPwaPublicBaseUrl,
624
644
  activeRuntimeProfile: inferActiveRuntimeProfile(context.workspace?.shellPaths?.homeDirectory ?? ''),
625
645
  selectedRuntimeProfile: selectedRuntimeProfile?.id ?? null,
626
646
  selectedRuntimeProfileExists: selectedRuntimeProfile?.exists ?? false,
@@ -19,7 +19,7 @@ import { deleteAgentWorkspaceMemoryEditor, submitAgentWorkspaceMemoryEditor } fr
19
19
  import { jumpAgentWorkspaceSelection, moveAgentWorkspaceSelection, selectAgentWorkspaceCategory } from './agent-workspace-navigation.ts';
20
20
  import { appendAgentWorkspaceActionSearchText, backspaceAgentWorkspaceActionSearch, beginAgentWorkspaceActionSearch, clearAgentWorkspaceActionSearch, commitAgentWorkspaceActionSearchSelection, searchAgentWorkspaceActions } from './agent-workspace-search.ts';
21
21
  import { applyAgentWorkspaceSetupCheckpointAction } from './agent-workspace-setup-checkpoint-action.ts';
22
- import { agentWorkspaceSettingSchema, applyAgentWorkspaceSettingValue, buildAgentWorkspaceSettingActionEffect, importAgentWorkspaceTuiSettings, isAgentWorkspaceActionVisible } from './agent-workspace-settings.ts';
22
+ import { agentWorkspaceSettingSchema, applyAgentWorkspaceSettingValue, buildAgentWorkspaceSettingActionEffect, buildAgentWorkspaceSettingActionPreview, importAgentWorkspaceTuiSettings, isAgentWorkspaceActionVisible } from './agent-workspace-settings.ts';
23
23
  import { buildAgentWorkspaceRuntimeSnapshot } from './agent-workspace-snapshot.ts';
24
24
  import { submitAgentWorkspaceSubscriptionLoginFinishEditor, submitAgentWorkspaceSubscriptionLoginStartEditor, submitAgentWorkspaceSubscriptionLogoutEditor } from './agent-workspace-subscription-editor.ts';
25
25
  import type { AgentWorkspaceAction, AgentWorkspaceActionResult, AgentWorkspaceActionSearchResult, AgentWorkspaceCategory, AgentWorkspaceCommandDispatcher, AgentWorkspaceEditorField, AgentWorkspaceFocusPane, AgentWorkspaceLocalEditor, AgentWorkspaceLocalEditorKind, AgentWorkspaceLocalLibraryItem, AgentWorkspaceLocalOperation, AgentWorkspacePromptDispatcher, AgentWorkspaceRuntimeSnapshot } from './agent-workspace-types.ts';
@@ -142,6 +142,10 @@ export class AgentWorkspace {
142
142
  return selectedAgentWorkspaceLocalLibraryItem(this.runtimeSnapshot, this.selectedLibraryItemIndexes, kind);
143
143
  }
144
144
 
145
+ settingActionPreview(action: AgentWorkspaceAction): string | null {
146
+ return buildAgentWorkspaceSettingActionPreview(this.context, action);
147
+ }
148
+
145
149
  focusCategories(): void {
146
150
  this.focusPane = 'categories';
147
151
  }
@@ -0,0 +1,76 @@
1
+ import type { CommandContext } from './command-registry.ts';
2
+ import { buildSetupWizardDurableReceiptsFromReadModel, mergeSetupWizardDurableReceipts } from '../agent/setup-wizard-artifact-receipts.ts';
3
+ import type { AgentSetupWizardDurableReceipt } from '../agent/setup-wizard.ts';
4
+
5
+ interface SetupReceiptReadModelSource {
6
+ readonly path: string;
7
+ readonly source: unknown;
8
+ }
9
+
10
+ function readRecord(value: unknown): Record<string, unknown> {
11
+ return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {};
12
+ }
13
+
14
+ function setupReceiptReadModelSources(context: CommandContext): readonly SetupReceiptReadModelSource[] {
15
+ const contextRecord = context as unknown as Record<string, unknown>;
16
+ const platform = readRecord(contextRecord.platform);
17
+ const clients = readRecord(contextRecord.clients);
18
+ const readModels = readRecord(platform.readModels);
19
+ const setup = readRecord(readModels.setup);
20
+ const setupWizard = readRecord(readModels.setupWizard);
21
+ const firstRun = readRecord(readModels.firstRun);
22
+ const browserPwa = readRecord(readModels.browserPwa);
23
+ const operator = readRecord(clients.operator);
24
+ return [
25
+ { path: 'context.platform.readModels.setup.receipts', source: setup.receipts },
26
+ { path: 'context.platform.readModels.setup.setupReceipts', source: setup.setupReceipts },
27
+ { path: 'context.platform.readModels.setup.receiptEvents', source: setup.receiptEvents },
28
+ { path: 'context.platform.readModels.setup.eventStream', source: setup.eventStream },
29
+ { path: 'context.platform.readModels.setup', source: readModels.setup },
30
+ { path: 'context.platform.readModels.setupWizard.receipts', source: setupWizard.receipts },
31
+ { path: 'context.platform.readModels.setupWizard.durableReceipts', source: setupWizard.durableReceipts },
32
+ { path: 'context.platform.readModels.setupWizard.receiptEvents', source: setupWizard.receiptEvents },
33
+ { path: 'context.platform.readModels.setupWizard.eventStream', source: setupWizard.eventStream },
34
+ { path: 'context.platform.readModels.setupReceipts', source: readModels.setupReceipts },
35
+ { path: 'context.platform.readModels.setupReceiptEvents', source: readModels.setupReceiptEvents },
36
+ { path: 'context.platform.readModels.setupReceiptEventStream', source: readModels.setupReceiptEventStream },
37
+ { path: 'context.platform.readModels.durableSetupReceipts', source: readModels.durableSetupReceipts },
38
+ { path: 'context.platform.readModels.firstRun.receipts', source: firstRun.receipts },
39
+ { path: 'context.platform.readModels.browserPwa.firstRunReceipts', source: browserPwa.firstRunReceipts },
40
+ { path: 'context.platform.setupReceipts', source: platform.setupReceipts },
41
+ { path: 'context.platform.setupReceiptEvents', source: platform.setupReceiptEvents },
42
+ { path: 'context.clients.operator.setupReceipts', source: operator.setupReceipts },
43
+ { path: 'context.clients.operator.setupReceiptEvents', source: operator.setupReceiptEvents },
44
+ ];
45
+ }
46
+
47
+ function readSnapshot(source: unknown): unknown {
48
+ if (typeof source === 'function') {
49
+ const result = (source as () => unknown)();
50
+ return result instanceof Promise ? undefined : result;
51
+ }
52
+ const record = readRecord(source);
53
+ for (const methodName of ['getSnapshot', 'snapshot', 'list', 'listReceipts', 'listEvents', 'listReceiptEvents', 'readEvents']) {
54
+ const method = record[methodName];
55
+ if (typeof method === 'function') {
56
+ const result = (method as () => unknown)();
57
+ return result instanceof Promise ? undefined : result;
58
+ }
59
+ }
60
+ return source;
61
+ }
62
+
63
+ export function setupWizardLiveDurableReceipts(context: CommandContext): readonly AgentSetupWizardDurableReceipt[] {
64
+ const receipts: AgentSetupWizardDurableReceipt[][] = [];
65
+ for (const entry of setupReceiptReadModelSources(context)) {
66
+ if (entry.source === undefined || entry.source === null) continue;
67
+ try {
68
+ receipts.push([...buildSetupWizardDurableReceiptsFromReadModel(readSnapshot(entry.source), entry.path)]);
69
+ } catch {
70
+ // Broken host read models should not hide local setup posture.
71
+ }
72
+ }
73
+ return mergeSetupWizardDurableReceipts(...receipts);
74
+ }
75
+
76
+ export { mergeSetupWizardDurableReceipts };
@@ -83,6 +83,21 @@ function setupOverviewLines(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLin
83
83
  fg: wizard.smokeHistory.latestResult === 'blocked' ? PALETTE.warn : PALETTE.info,
84
84
  });
85
85
  }
86
+ if (wizard.stepHistory.length > 0) {
87
+ const latest = wizard.stepHistory[0]!;
88
+ lines.push({
89
+ text: `Step history: ${wizard.stepHistory.length} recorded; latest ${latest.stepLabel} ${latest.kind} at ${latest.recordedAt}.`,
90
+ fg: PALETTE.good,
91
+ });
92
+ }
93
+ if (wizard.receiptGaps.length > 0) {
94
+ const labels = wizard.receiptGaps.slice(0, 3).map((gap) => gap.stepLabel).join(', ');
95
+ const suffix = wizard.receiptGaps.length > 3 ? `, +${wizard.receiptGaps.length - 3} more` : '';
96
+ lines.push({
97
+ text: `Receipt gaps: ${labels}${suffix} still need durable setup receipt ids.`,
98
+ fg: PALETTE.warn,
99
+ });
100
+ }
86
101
  return lines;
87
102
  }
88
103
 
@@ -257,13 +272,13 @@ function promptReceiptTimelineLines(snapshot: AgentWorkspaceRuntimeSnapshot): Co
257
272
  fg: promptReceiptOutcomeColor(latest.outcomeStatus),
258
273
  bold: latest.outcomeStatus === 'error' || latest.outcomeStatus === 'cancelled',
259
274
  });
260
- lines.push({ text: `Inspect latest prompt receipt: ${latest.inspectRoute}`, fg: PALETTE.good });
275
+ lines.push({ text: `Latest prompt receipt: ${latest.receiptId}; inspect it from Prompt context.`, fg: PALETTE.good });
261
276
  if (timeline.errorCount > 0) {
262
- lines.push({ text: `Filter prompt receipt errors: ${timeline.filterRoutes.error}`, fg: PALETTE.warn });
277
+ lines.push({ text: 'Prompt receipt filter: show errors.', fg: PALETTE.warn });
263
278
  } else if (timeline.cancelledCount > 0) {
264
- lines.push({ text: `Filter cancelled prompt receipts: ${timeline.filterRoutes.cancelled}`, fg: PALETTE.warn });
279
+ lines.push({ text: 'Prompt receipt filter: show cancelled turns.', fg: PALETTE.warn });
265
280
  } else if (timeline.pendingCount > 0) {
266
- lines.push({ text: `Filter pending prompt receipts: ${timeline.filterRoutes.pending}`, fg: PALETTE.info });
281
+ lines.push({ text: 'Prompt receipt filter: show pending turns.', fg: PALETTE.info });
267
282
  }
268
283
  for (const receipt of timeline.items.slice(0, 3)) {
269
284
  lines.push({
@@ -274,7 +289,7 @@ function promptReceiptTimelineLines(snapshot: AgentWorkspaceRuntimeSnapshot): Co
274
289
  if (latest.detail) {
275
290
  lines.push({ text: `Latest outcome detail: ${compactText(latest.detail, 104)}`, fg: PALETTE.warn });
276
291
  }
277
- lines.push({ text: `Inspect: ${timeline.inspectRoute}`, fg: PALETTE.good });
292
+ lines.push({ text: 'Prompt context controls stay read-only from this setup page.', fg: PALETTE.good });
278
293
  return lines;
279
294
  }
280
295
 
@@ -478,7 +493,7 @@ export function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspac
478
493
  { text: `VIBE.md: ${vibe.applied} applied; ${vibe.blocked} blocked; ${vibe.truncated} truncated.`, fg: vibe.blocked > 0 ? PALETTE.warn : vibe.applied > 0 ? PALETTE.good : PALETTE.muted },
479
494
  { text: `Project context: ${projectContext.loaded} loaded; ${projectContext.blocked} blocked; ${projectContext.truncated} truncated.`, fg: projectContext.blocked > 0 ? PALETTE.warn : projectContext.loaded > 0 ? PALETTE.good : PALETTE.muted },
480
495
  ...promptReceiptTimelineLines(snapshot),
481
- { text: 'Context routes: context prompt/files/file/receipt, /vibe status.', fg: PALETTE.good },
496
+ { text: 'Context controls: prompt receipts, project files, one-file inspection, and VIBE.md review.', fg: PALETTE.good },
482
497
  );
483
498
  } else if (category.id === 'onboarding-automation') {
484
499
  base.push(
@@ -68,10 +68,42 @@ function actionCommand(action: AgentWorkspaceAction): string {
68
68
  return action.command ?? '(guidance)';
69
69
  }
70
70
 
71
- function actionMetaLine(action: AgentWorkspaceAction): ContextLine {
71
+ function isOnboardingCategory(category: AgentWorkspaceCategory): boolean {
72
+ return category.group === 'ONBOARDING';
73
+ }
74
+
75
+ function onboardingActionChange(workspace: AgentWorkspace, action: AgentWorkspaceAction): string {
76
+ if (action.kind === 'setting') {
77
+ return workspace.settingActionPreview(action) ?? 'Update setting';
78
+ }
79
+ if (action.kind === 'settings-import') return 'Import compatible GoodVibes preferences';
80
+ if (action.kind === 'setup-checkpoint') {
81
+ if (action.setupCheckpointOperation === 'show') return 'Show saved setup progress';
82
+ if (action.setupCheckpointOperation === 'mark-current') return 'Save current setup progress';
83
+ if (action.setupCheckpointOperation === 'clear') return 'Clear saved setup progress';
84
+ return 'Review setup progress';
85
+ }
86
+ if (action.kind === 'model-picker') return action.modelPickerFlow === 'model' ? 'Choose a model' : 'Choose provider and model';
87
+ if (action.kind === 'settings-modal') return 'Open settings';
88
+ if (action.kind === 'workspace') return 'Open setup area';
89
+ if (action.kind === 'editor') return 'Open guided form';
90
+ if (action.kind === 'local-selection') return 'Move selection';
91
+ if (action.kind === 'local-operation') return 'Apply selected library action';
92
+ if (action.kind === 'onboarding-complete') return 'Save setup completion';
93
+ if (action.safety === 'read-only') return 'Review readiness';
94
+ if (action.safety === 'blocked') return 'Unavailable in this setup step';
95
+ return 'Open option';
96
+ }
97
+
98
+ function actionChange(workspace: AgentWorkspace, category: AgentWorkspaceCategory, action: AgentWorkspaceAction): string {
99
+ return isOnboardingCategory(category) ? onboardingActionChange(workspace, action) : actionCommand(action);
100
+ }
101
+
102
+ function actionMetaLine(workspace: AgentWorkspace, category: AgentWorkspaceCategory, action: AgentWorkspaceAction): ContextLine {
103
+ const onboarding = isOnboardingCategory(category);
72
104
  return {
73
- text: `Does: ${actionCommand(action)}`,
74
- fg: action.safety === 'blocked' ? PALETTE.warn : action.kind === 'command' ? PALETTE.info : PALETTE.muted,
105
+ text: `${onboarding ? 'Change' : 'Does'}: ${actionChange(workspace, category, action)}`,
106
+ fg: action.safety === 'blocked' ? PALETTE.warn : !onboarding && action.kind === 'command' ? PALETTE.info : PALETTE.muted,
75
107
  };
76
108
  }
77
109
 
@@ -135,19 +167,20 @@ function buildContextRows(workspace: AgentWorkspace, category: AgentWorkspaceCat
135
167
  const selectedActionLines: ContextLine[] = action
136
168
  ? [
137
169
  { text: `Selected: ${action.label}`, fg: PALETTE.title, bold: true },
138
- actionMetaLine(action),
170
+ actionMetaLine(workspace, category, action),
139
171
  ]
140
172
  : [];
141
173
  const snapshotContextLines = snapshotLines(workspace, category, workspace.runtimeSnapshot);
142
174
  lines.push(...selectedActionLines, ...snapshotContextLines);
143
175
 
176
+ const onboarding = isOnboardingCategory(category);
144
177
  if (workspace.lastActionResult) {
145
178
  lines.push(
146
- { text: 'Action Result', fg: PALETTE.title, bold: true },
179
+ { text: onboarding ? 'Result' : 'Action Result', fg: PALETTE.title, bold: true },
147
180
  { text: workspace.lastActionResult.title, fg: actionResultColor(workspace.lastActionResult), bold: true },
148
181
  { text: compactText(workspace.lastActionResult.detail), fg: PALETTE.text },
149
182
  );
150
- if (workspace.lastActionResult.command) {
183
+ if (!onboarding && workspace.lastActionResult.command) {
151
184
  lines.push({ text: `Command: ${workspace.lastActionResult.command}`, fg: PALETTE.muted });
152
185
  }
153
186
  }
@@ -213,6 +246,8 @@ function buildEditorFieldRows(editor: AgentWorkspaceLocalEditor, index: number,
213
246
 
214
247
  function buildActionRows(workspace: AgentWorkspace, width: number, height: number): WorkspaceRow[] {
215
248
  if (workspace.localEditor) return buildEditorRows(workspace.localEditor, width, height);
249
+ const category = workspace.selectedActionCategory;
250
+ const onboarding = isOnboardingCategory(category);
216
251
  const rows: WorkspaceRow[] = [];
217
252
  const labelWidth = Math.min(34, Math.max(18, Math.floor(width * 0.38)));
218
253
  const commandWidth = Math.max(10, width - labelWidth - 6);
@@ -224,7 +259,7 @@ function buildActionRows(workspace: AgentWorkspace, width: number, height: numbe
224
259
  });
225
260
  }
226
261
  rows.push({
227
- text: ` ${padDisplay(workspace.actionSearchActive ? 'Result' : 'Action', labelWidth)} ${padDisplay('Does', commandWidth)}`,
262
+ text: ` ${padDisplay(workspace.actionSearchActive ? 'Result' : onboarding ? 'Option' : 'Action', labelWidth)} ${padDisplay(onboarding ? 'Change' : 'Does', commandWidth)}`,
228
263
  fg: PALETTE.muted,
229
264
  bold: true,
230
265
  });
@@ -238,10 +273,11 @@ function buildActionRows(workspace: AgentWorkspace, width: number, height: numbe
238
273
  const action = actions[index]!;
239
274
  const selected = index === workspace.selectedActionIndex;
240
275
  const searchResult = workspace.actionSearchActive ? workspace.actionSearchResults[index] : null;
276
+ const actionCategory = searchResult?.category ?? category;
241
277
  const label = searchResult ? `${searchResult.category.label} / ${action.label}` : action.label;
242
278
  const marker = selected ? GLYPHS.navigation.selected : ' ';
243
279
  rows.push({
244
- text: `${marker} ${padDisplay(label, labelWidth)} ${padDisplay(actionCommand(action), commandWidth)}`,
280
+ text: `${marker} ${padDisplay(label, labelWidth)} ${padDisplay(actionChange(workspace, actionCategory, action), commandWidth)}`,
245
281
  selected: selected && workspace.focusPane === 'actions',
246
282
  fg: action.safety === 'blocked' ? PALETTE.warn : selected ? PALETTE.text : PALETTE.info,
247
283
  bold: selected,
@@ -253,11 +289,11 @@ function buildActionRows(workspace: AgentWorkspace, width: number, height: numbe
253
289
  rows.push({ text: `Status: ${workspace.status}`, fg: PALETTE.muted });
254
290
  if (workspace.lastActionResult) {
255
291
  rows.push({ text: '' });
256
- rows.push({ text: `Action Result: ${workspace.lastActionResult.title}`, fg: actionResultColor(workspace.lastActionResult), bold: true });
292
+ rows.push({ text: `${onboarding ? 'Result' : 'Action Result'}: ${workspace.lastActionResult.title}`, fg: actionResultColor(workspace.lastActionResult), bold: true });
257
293
  for (const line of wrapText(workspace.lastActionResult.detail, Math.max(1, width - 2))) {
258
294
  rows.push({ text: ` ${line}`, fg: PALETTE.text });
259
295
  }
260
- if (workspace.lastActionResult.command) {
296
+ if (!onboarding && workspace.lastActionResult.command) {
261
297
  rows.push({ text: ` Command: ${workspace.lastActionResult.command}`, fg: PALETTE.muted });
262
298
  }
263
299
  }
@@ -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
+ }