@pellux/goodvibes-agent 1.4.4 → 1.5.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 (227) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +7 -7
  3. package/dist/package/main.js +7424 -12241
  4. package/docs/README.md +2 -2
  5. package/docs/channels-remote-and-api.md +1 -1
  6. package/docs/getting-started.md +2 -2
  7. package/docs/release-and-publishing.md +1 -1
  8. package/docs/tools-and-commands.md +5 -5
  9. package/package.json +4 -2
  10. package/release/performance-snapshot.json +2 -2
  11. package/release/release-notes.md +11 -7
  12. package/release/release-readiness.json +6 -6
  13. package/src/agent/behavior-discovery-summary.ts +4 -18
  14. package/src/agent/calendar-registry.ts +322 -0
  15. package/src/agent/competitive-feature-inventory.ts +268 -130
  16. package/src/agent/document-registry.ts +5 -1
  17. package/src/agent/email/email-service.ts +350 -0
  18. package/src/agent/email/imap-client.ts +596 -0
  19. package/src/agent/email/smtp-client.ts +453 -0
  20. package/src/agent/ics-calendar.ts +662 -0
  21. package/src/agent/ics-timezone.ts +172 -0
  22. package/src/agent/markdown-frontmatter.ts +31 -0
  23. package/src/agent/memory-safety.ts +1 -1
  24. package/src/agent/note-registry.ts +3 -9
  25. package/src/agent/persona-discovery.ts +3 -15
  26. package/src/agent/persona-registry.ts +1 -10
  27. package/src/agent/research-source-registry.ts +5 -1
  28. package/src/agent/routine-discovery.ts +3 -15
  29. package/src/agent/runtime-profile.ts +3 -0
  30. package/src/agent/skill-discovery.ts +3 -15
  31. package/src/agent/skill-draft-proposer.ts +203 -0
  32. package/src/agent/skill-draft-runner.ts +201 -0
  33. package/src/agent/skill-registry.ts +36 -1
  34. package/src/agent/skill-standard.ts +99 -0
  35. package/src/agent/vibe-file.ts +7 -22
  36. package/src/cli/completion.ts +1 -1
  37. package/src/cli/config-overrides.ts +10 -1
  38. package/src/cli/redaction.ts +17 -5
  39. package/src/config/provider-model.ts +2 -1
  40. package/src/config/secret-config.ts +13 -6
  41. package/src/core/activity-feed.ts +97 -0
  42. package/src/core/away-digest.ts +161 -0
  43. package/src/core/conversation-rendering.ts +7 -3
  44. package/src/core/conversation.ts +22 -15
  45. package/src/core/hardware-profile.ts +362 -0
  46. package/src/core/last-seen-store.ts +78 -0
  47. package/src/core/plain-language.ts +52 -0
  48. package/src/core/setup-incomplete-hint.ts +90 -0
  49. package/src/core/system-message-router.ts +38 -87
  50. package/src/input/agent-workspace-basic-command-editor-submission.ts +60 -220
  51. package/src/input/agent-workspace-basic-command-editors.ts +39 -141
  52. package/src/input/agent-workspace-categories.ts +40 -125
  53. package/src/input/agent-workspace-command-editor.ts +4 -0
  54. package/src/input/agent-workspace-host-category.ts +0 -5
  55. package/src/input/agent-workspace-local-editor-submission.ts +3 -1
  56. package/src/input/agent-workspace-navigation.ts +10 -3
  57. package/src/input/agent-workspace-onboarding-actions.ts +132 -0
  58. package/src/input/agent-workspace-onboarding-categories.ts +29 -26
  59. package/src/input/agent-workspace-onboarding-finish.ts +11 -4
  60. package/src/input/agent-workspace-onboarding-state.ts +111 -0
  61. package/src/input/agent-workspace-profile-editor-submission.ts +260 -0
  62. package/src/input/agent-workspace-profile-editors.ts +155 -0
  63. package/src/input/agent-workspace-subscription-editor.ts +7 -0
  64. package/src/input/agent-workspace-types.ts +5 -1
  65. package/src/input/agent-workspace.ts +65 -39
  66. package/src/input/command-registry.ts +18 -5
  67. package/src/input/commands/agent-runtime-profile-runtime.ts +2 -1
  68. package/src/input/commands/agent-skills-runtime.ts +60 -3
  69. package/src/input/commands/calendar-runtime.ts +209 -0
  70. package/src/input/commands/channels-runtime.ts +1 -0
  71. package/src/input/commands/command-error.ts +9 -0
  72. package/src/input/commands/compat-runtime.ts +1 -0
  73. package/src/input/commands/config.ts +1 -0
  74. package/src/input/commands/conversation-runtime.ts +1 -1
  75. package/src/input/commands/delegation-runtime.ts +5 -6
  76. package/src/input/commands/email-runtime.ts +387 -0
  77. package/src/input/commands/experience-runtime.ts +17 -4
  78. package/src/input/commands/guidance-runtime.ts +1 -1
  79. package/src/input/commands/health-runtime.ts +6 -1
  80. package/src/input/commands/knowledge-format.ts +73 -0
  81. package/src/input/commands/knowledge.ts +9 -74
  82. package/src/input/commands/local-provider-runtime.ts +2 -1
  83. package/src/input/commands/local-runtime.ts +10 -4
  84. package/src/input/commands/mcp-runtime.ts +7 -0
  85. package/src/input/commands/notify-runtime.ts +17 -1
  86. package/src/input/commands/onboarding-runtime.ts +1 -0
  87. package/src/input/commands/operator-actions-runtime.ts +1 -0
  88. package/src/input/commands/operator-runtime.ts +3 -0
  89. package/src/input/commands/personas-runtime.ts +1 -0
  90. package/src/input/commands/platform-access-runtime.ts +40 -17
  91. package/src/input/commands/product-runtime.ts +6 -1
  92. package/src/input/commands/provider-accounts-runtime.ts +3 -2
  93. package/src/input/commands/qrcode-runtime.ts +1 -0
  94. package/src/input/commands/routines-runtime.ts +1 -0
  95. package/src/input/commands/runtime-services.ts +0 -13
  96. package/src/input/commands/security-runtime.ts +1 -0
  97. package/src/input/commands/session-content.ts +1 -0
  98. package/src/input/commands/shell-core.ts +5 -2
  99. package/src/input/commands/subscription-runtime.ts +33 -9
  100. package/src/input/commands/support-bundle-runtime.ts +8 -1
  101. package/src/input/commands/tasks-runtime.ts +1 -0
  102. package/src/input/commands/tts-runtime.ts +1 -0
  103. package/src/input/commands/vibe-runtime.ts +1 -0
  104. package/src/input/commands.ts +4 -0
  105. package/src/input/feed-context-factory.ts +3 -12
  106. package/src/input/handler-command-route.ts +7 -60
  107. package/src/input/handler-feed-routes.ts +0 -194
  108. package/src/input/handler-feed.ts +2 -54
  109. package/src/input/handler-interactions.ts +0 -2
  110. package/src/input/handler-modal-stack.ts +0 -9
  111. package/src/input/handler-picker-routes.ts +24 -1
  112. package/src/input/handler-shortcuts.ts +11 -40
  113. package/src/input/handler-ui-state.ts +13 -0
  114. package/src/input/handler.ts +8 -35
  115. package/src/input/keybindings.ts +40 -17
  116. package/src/input/model-picker-local-fit.ts +130 -0
  117. package/src/input/submission-router.ts +0 -5
  118. package/src/main.ts +111 -89
  119. package/src/renderer/activity-sidebar.ts +186 -0
  120. package/src/renderer/agent-workspace-context-lines.ts +5 -48
  121. package/src/renderer/agent-workspace-style.ts +1 -1
  122. package/src/renderer/agent-workspace.ts +14 -2
  123. package/src/renderer/compositor.ts +37 -125
  124. package/src/renderer/conversation-overlays.ts +3 -3
  125. package/src/renderer/help-overlay.ts +7 -5
  126. package/src/renderer/model-workspace.ts +101 -86
  127. package/src/{panels → renderer}/polish.ts +3 -3
  128. package/src/renderer/shell-surface.ts +4 -5
  129. package/src/renderer/status-token.ts +31 -11
  130. package/src/renderer/tab-strip.ts +1 -1
  131. package/src/renderer/tool-call.ts +7 -8
  132. package/src/renderer/tool-labels.ts +92 -0
  133. package/src/renderer/ui-factory.ts +48 -96
  134. package/src/runtime/bootstrap-command-context.ts +0 -5
  135. package/src/runtime/bootstrap-command-parts.ts +6 -15
  136. package/src/runtime/bootstrap-core.ts +34 -13
  137. package/src/runtime/bootstrap-hook-bridge.ts +3 -1
  138. package/src/runtime/bootstrap-shell.ts +3 -50
  139. package/src/runtime/bootstrap.ts +3 -4
  140. package/src/runtime/context.ts +1 -1
  141. package/src/runtime/diagnostics/panels/index.ts +0 -1
  142. package/src/runtime/diagnostics/panels/policy.ts +1 -1
  143. package/src/runtime/execution-ledger.ts +16 -1
  144. package/src/runtime/index.ts +6 -1
  145. package/src/runtime/onboarding/index.ts +1 -0
  146. package/src/runtime/onboarding/onboarding-state.ts +200 -0
  147. package/src/{panels → runtime}/provider-account-snapshot.ts +5 -2
  148. package/src/runtime/services.ts +16 -4
  149. package/src/runtime/terminal-output-guard.ts +7 -0
  150. package/src/runtime/tool-permission-safety.ts +1 -1
  151. package/src/runtime/ui/model-picker/data-provider.ts +1 -1
  152. package/src/runtime/ui/model-picker/health-enrichment.ts +2 -2
  153. package/src/runtime/ui/model-picker/types.ts +2 -2
  154. package/src/runtime/ui/provider-health/data-provider.ts +3 -3
  155. package/src/runtime/ui/provider-health/types.ts +2 -2
  156. package/src/runtime/ui-services.ts +0 -2
  157. package/src/shell/agent-workspace-fullscreen.ts +0 -1
  158. package/src/shell/autonomy-surfacing.ts +272 -0
  159. package/src/shell/session-continuity-hints.ts +0 -6
  160. package/src/shell/startup-wiring.ts +221 -0
  161. package/src/shell/ui-openers.ts +18 -29
  162. package/src/tools/agent-context-policy.ts +1 -1
  163. package/src/tools/agent-harness-background-processes.ts +4 -4
  164. package/src/tools/agent-harness-browser-cockpit-route.ts +3 -3
  165. package/src/tools/agent-harness-command-runner.ts +6 -1
  166. package/src/tools/agent-harness-document-ops.ts +26 -53
  167. package/src/tools/agent-harness-execution-history.ts +1 -13
  168. package/src/tools/agent-harness-execution-posture.ts +0 -15
  169. package/src/tools/agent-harness-keybinding-metadata.ts +28 -29
  170. package/src/tools/agent-harness-local-model-cookbook.ts +24 -1
  171. package/src/tools/agent-harness-metadata.ts +16 -0
  172. package/src/tools/agent-harness-mode-catalog.ts +2 -9
  173. package/src/tools/agent-harness-provider-account-metadata.ts +2 -2
  174. package/src/tools/agent-harness-setup-posture-utils.ts +1 -1
  175. package/src/tools/agent-harness-tool-schema.ts +13 -14
  176. package/src/tools/agent-harness-tool.ts +27 -23
  177. package/src/tools/agent-harness-ui-surface-metadata.ts +10 -20
  178. package/src/tools/agent-harness-workspace-action-runner.ts +3 -4
  179. package/src/tools/agent-workspace-tool.ts +2 -33
  180. package/src/utils/terminal-width.ts +9 -3
  181. package/src/version.ts +1 -1
  182. package/src/input/agent-workspace-panel-route.ts +0 -44
  183. package/src/input/panel-integration-actions.ts +0 -26
  184. package/src/panels/approval-panel.ts +0 -149
  185. package/src/panels/automation-control-panel.ts +0 -212
  186. package/src/panels/base-panel.ts +0 -254
  187. package/src/panels/builtin/agent.ts +0 -58
  188. package/src/panels/builtin/knowledge.ts +0 -26
  189. package/src/panels/builtin/operations.ts +0 -121
  190. package/src/panels/builtin/session.ts +0 -138
  191. package/src/panels/builtin/shared.ts +0 -275
  192. package/src/panels/builtin/usage.ts +0 -21
  193. package/src/panels/builtin-panels.ts +0 -23
  194. package/src/panels/confirm-state.ts +0 -61
  195. package/src/panels/context-visualizer-panel.ts +0 -204
  196. package/src/panels/cost-tracker-panel.ts +0 -444
  197. package/src/panels/docs-panel.ts +0 -285
  198. package/src/panels/index.ts +0 -25
  199. package/src/panels/knowledge-panel.ts +0 -417
  200. package/src/panels/memory-panel.ts +0 -226
  201. package/src/panels/panel-list-panel.ts +0 -464
  202. package/src/panels/panel-manager.ts +0 -570
  203. package/src/panels/provider-accounts-panel.ts +0 -233
  204. package/src/panels/provider-health-domains.ts +0 -208
  205. package/src/panels/provider-health-panel.ts +0 -720
  206. package/src/panels/provider-health-tracker.ts +0 -115
  207. package/src/panels/provider-stats-panel.ts +0 -366
  208. package/src/panels/qr-panel.ts +0 -207
  209. package/src/panels/schedule-panel.ts +0 -321
  210. package/src/panels/scrollable-list-panel.ts +0 -491
  211. package/src/panels/search-focus.ts +0 -32
  212. package/src/panels/security-panel.ts +0 -295
  213. package/src/panels/session-browser-panel.ts +0 -395
  214. package/src/panels/session-maintenance.ts +0 -125
  215. package/src/panels/subscription-panel.ts +0 -263
  216. package/src/panels/system-messages-panel.ts +0 -230
  217. package/src/panels/tasks-panel.ts +0 -344
  218. package/src/panels/thinking-panel.ts +0 -304
  219. package/src/panels/token-budget-panel.ts +0 -475
  220. package/src/panels/tool-inspector-panel.ts +0 -436
  221. package/src/panels/types.ts +0 -54
  222. package/src/renderer/panel-composite.ts +0 -158
  223. package/src/renderer/panel-tab-bar.ts +0 -69
  224. package/src/renderer/panel-workspace-bar.ts +0 -42
  225. package/src/runtime/diagnostics/panels/panel-resources.ts +0 -118
  226. package/src/tools/agent-harness-panel-metadata.ts +0 -211
  227. /package/src/runtime/perf/{panel-health-monitor.ts → component-health.ts} +0 -0
@@ -204,10 +204,6 @@ function describeCandidate(route: ExecutionRoute, context: CommandContext, toolR
204
204
  };
205
205
  }
206
206
 
207
- function toolInspectorAvailable(context: CommandContext): boolean {
208
- return Boolean(context.workspace.panelManager?.getRegisteredTypes().some((panel) => panel.id === 'tools'));
209
- }
210
-
211
207
  function executionSupervisionRoutes(context: CommandContext, route: ExecutionRoute): readonly ExecutionSupervisionRoute[] {
212
208
  const routes: ExecutionSupervisionRoute[] = [];
213
209
  if (route.id === 'local-shell-command') {
@@ -228,15 +224,6 @@ function executionSupervisionRoutes(context: CommandContext, route: ExecutionRou
228
224
  },
229
225
  );
230
226
  }
231
- if (route.id === 'local-shell-command' || route.id === 'local-edit-write') {
232
- routes.push({
233
- id: 'tool-inspector',
234
- label: 'Tool Call Inspector',
235
- available: toolInspectorAvailable(context),
236
- modelRoute: 'workspace action:"open_panel" panelId:"tools"',
237
- requiresConfirmation: true,
238
- });
239
- }
240
227
  return routes;
241
228
  }
242
229
 
@@ -244,11 +231,9 @@ function executionSupervisionSummary(context: CommandContext): Record<string, un
244
231
  return {
245
232
  processMonitorAvailable: typeof context.openProcessModal === 'function',
246
233
  liveTailAvailable: typeof context.openLiveTail === 'function',
247
- toolInspectorAvailable: toolInspectorAvailable(context),
248
234
  routes: [
249
235
  'workspace action:"open" surfaceId:"process-monitor"',
250
236
  'workspace action:"open" surfaceId:"live-tail"',
251
- 'workspace action:"open_panel" panelId:"tools"',
252
237
  ],
253
238
  };
254
239
  }
@@ -310,30 +310,37 @@ function keybindingOperationRoute(action: KeyAction): KeybindingOperationRoute {
310
310
  confirmation: 'agent_harness mode:"run_keybinding" requires confirm:true and explicitUserRequest.',
311
311
  note: 'Runs the available clear-screen route.',
312
312
  };
313
- case 'panel-picker':
313
+ case 'sidebar-toggle':
314
+ return {
315
+ supported: false,
316
+ effect: 'visible-ui-navigation',
317
+ confirmation: 'Direct user interaction only.',
318
+ note: 'Shows or hides the activity sidebar. Layout preferences stay under user control.',
319
+ };
320
+ case 'workspace-picker':
314
321
  return {
315
322
  supported: true,
316
323
  effect: 'visible-ui-navigation',
317
324
  preferredMode: 'run_keybinding',
318
325
  surfaceId: 'agent-workspace',
319
326
  confirmation: 'agent_harness mode:"run_keybinding" requires confirm:true and explicitUserRequest.',
320
- note: 'Opens the same Agent workspace home route as the panel-picker shortcut.',
327
+ note: 'Opens the Agent workspace home.',
321
328
  };
322
- case 'panel-close':
329
+ case 'workspace-close':
323
330
  return {
324
331
  supported: true,
325
332
  effect: 'visible-ui-navigation',
326
333
  preferredMode: 'run_keybinding',
327
334
  confirmation: 'agent_harness mode:"run_keybinding" requires confirm:true and explicitUserRequest.',
328
- note: 'Dismisses Agent workspace first. If it is not active, closes the active panel and focuses the prompt when focus is available.',
335
+ note: 'Dismisses the Agent workspace.',
329
336
  };
330
- case 'panel-close-all':
337
+ case 'workspace-close-all':
331
338
  return {
332
339
  supported: true,
333
340
  effect: 'visible-ui-navigation',
334
341
  preferredMode: 'run_keybinding',
335
342
  confirmation: 'agent_harness mode:"run_keybinding" requires confirm:true and explicitUserRequest.',
336
- note: 'Dismisses Agent workspace first. If it is not active, closes open panels and focuses the prompt when focus is available.',
343
+ note: 'Dismisses the Agent workspace (alias chord: Ctrl+Shift+X has the same effect as Ctrl+X).',
337
344
  };
338
345
  case 'history-search':
339
346
  return {
@@ -373,8 +380,8 @@ function keybindingOperationRoute(action: KeyAction): KeybindingOperationRoute {
373
380
  confirmation: 'agent_harness mode:"run_keybinding" requires confirm:true and explicitUserRequest.',
374
381
  note: 'Opens the visible nearest-block actions surface. The exact block action remains an interactive visible-shell selection because it depends on cursor/scroll position.',
375
382
  };
376
- case 'panel-tab-next':
377
- case 'panel-tab-prev':
383
+ case 'workspace-tab-next':
384
+ case 'workspace-tab-prev':
378
385
  return {
379
386
  supported: false,
380
387
  effect: 'visible-ui-navigation',
@@ -558,17 +565,17 @@ export function runHarnessKeybinding(context: CommandContext, args: HarnessKeybi
558
565
  if (!context.clearScreen) return runUnavailable(resolved.action, route, 'Clear-screen route is unavailable.');
559
566
  context.clearScreen();
560
567
  return { status: 'executed', action: resolved.action, effect: 'screen-clear', keybinding: descriptor };
561
- case 'panel-picker':
562
- if (context.openPanelPicker) {
563
- context.openPanelPicker();
564
- return { status: 'executed', action: resolved.action, effect: 'agent-workspace-opened', route: 'openPanelPicker', keybinding: descriptor };
568
+ case 'workspace-picker':
569
+ if (context.openWorkspacePicker) {
570
+ context.openWorkspacePicker();
571
+ return { status: 'executed', action: resolved.action, effect: 'agent-workspace-opened', route: 'openWorkspacePicker', keybinding: descriptor };
565
572
  }
566
573
  if (context.openAgentWorkspace) {
567
574
  context.openAgentWorkspace('home');
568
575
  return { status: 'executed', action: resolved.action, effect: 'agent-workspace-opened', route: 'openAgentWorkspace', categoryId: 'home', keybinding: descriptor };
569
576
  }
570
- return runUnavailable(resolved.action, route, 'No panel picker or Agent workspace route is available.');
571
- case 'panel-close': {
577
+ return runUnavailable(resolved.action, route, 'No workspace picker or Agent workspace route is available.');
578
+ case 'workspace-close': {
572
579
  const dismissedAgentWorkspace = context.dismissAgentWorkspace?.() ?? false;
573
580
  if (dismissedAgentWorkspace) {
574
581
  return {
@@ -579,20 +586,17 @@ export function runHarnessKeybinding(context: CommandContext, args: HarnessKeybi
579
586
  keybinding: descriptor,
580
587
  };
581
588
  }
582
- const active = context.workspace.panelManager?.getActivePanel() ?? null;
583
- if (active) context.workspace.panelManager?.close(active.id);
584
- if (!active && !context.focusPrompt) return runUnavailable(resolved.action, route, 'No active Agent workspace, active panel, or prompt focus route is available.');
585
- if (context.focusPrompt) context.focusPrompt();
589
+ if (!context.focusPrompt) return runUnavailable(resolved.action, route, 'No active Agent workspace or prompt focus route is available.');
590
+ context.focusPrompt();
586
591
  context.renderRequest();
587
592
  return {
588
593
  status: 'executed',
589
594
  action: resolved.action,
590
- effect: active ? 'active-panel-closed' : 'prompt-focused',
591
- ...(active ? { panelId: active.id } : {}),
595
+ effect: 'prompt-focused',
592
596
  keybinding: descriptor,
593
597
  };
594
598
  }
595
- case 'panel-close-all': {
599
+ case 'workspace-close-all': {
596
600
  const dismissedAgentWorkspace = context.dismissAgentWorkspace?.() ?? false;
597
601
  if (dismissedAgentWorkspace) {
598
602
  return {
@@ -603,18 +607,13 @@ export function runHarnessKeybinding(context: CommandContext, args: HarnessKeybi
603
607
  keybinding: descriptor,
604
608
  };
605
609
  }
606
- const managerPanel = context.workspace.panelManager;
607
- const openPanels = managerPanel?.getAllOpen() ?? [];
608
- for (const panel of openPanels) managerPanel?.close(panel.id);
609
- managerPanel?.hide();
610
- if (openPanels.length === 0 && !context.focusPrompt) return runUnavailable(resolved.action, route, 'No active Agent workspace, open panels, or prompt focus route is available.');
611
- if (context.focusPrompt) context.focusPrompt();
610
+ if (!context.focusPrompt) return runUnavailable(resolved.action, route, 'No active Agent workspace or prompt focus route is available.');
611
+ context.focusPrompt();
612
612
  context.renderRequest();
613
613
  return {
614
614
  status: 'executed',
615
615
  action: resolved.action,
616
- effect: 'all-panels-closed',
617
- closedPanels: openPanels.map((panel) => panel.id),
616
+ effect: 'prompt-focused',
618
617
  keybinding: descriptor,
619
618
  };
620
619
  }
@@ -1,4 +1,5 @@
1
1
  import type { CommandContext } from '../input/command-registry.ts';
2
+ import { estimateModelBytes, fitAssessment, fitVerdictLabel, readHardwareProfileSync, REPRESENTATIVE_7B_PARAMS } from '../core/hardware-profile.ts';
2
3
  import { previewHarnessText } from './agent-harness-text.ts';
3
4
  import { localModelDetection, localModelServerHealthMap } from './agent-harness-local-model-endpoints.ts';
4
5
  import { localHardwareProfile, localRecipeReadinessScore, localRecipeStackId, scoreLocalModelRecipe } from './agent-harness-model-readiness.ts';
@@ -83,6 +84,21 @@ export function localModelSetupPlan(
83
84
  };
84
85
  }
85
86
 
87
+ /**
88
+ * Representative Q4 parameter count for a recipe's default/starter model.
89
+ * Used to estimate memory footprint for hardware fit annotation.
90
+ * Returns null for stacks where size is too variable to estimate safely.
91
+ */
92
+ function recipeRepresentativeParamCount(recipe: LocalModelRecipe): number | null {
93
+ // Uses the shared REPRESENTATIVE_7B_PARAMS constant so the renderer and cookbook
94
+ // always reference the same basis.
95
+ // The generic openai-compatible stack is too varied to estimate.
96
+ if (recipe.id === 'ollama' || recipe.id === 'llama-cpp' || recipe.id === 'vllm') {
97
+ return REPRESENTATIVE_7B_PARAMS;
98
+ }
99
+ return null;
100
+ }
101
+
86
102
  export function localModelRecipes(): readonly LocalModelRecipe[] {
87
103
  return [
88
104
  {
@@ -175,7 +191,14 @@ export function describeLocalModelRecipe(
175
191
  hardware: previewHarnessText(recipe.hardware, includeParameters ? 180 : 96),
176
192
  hardwareMatched: fit.reasons.slice(0, includeParameters ? 6 : 3),
177
193
  detected,
178
- modelRoute: 'models action:"status" or agent_harness mode:"open_ui_surface"',
194
+ modelRoute: 'models action:"status" or agent_harness mode:"open_ui_surface"',
195
+ ...(() => {
196
+ const paramCount = recipeRepresentativeParamCount(recipe);
197
+ if (paramCount === null) return {};
198
+ const sizeBytes = estimateModelBytes(paramCount);
199
+ const label = fitVerdictLabel(fitAssessment(sizeBytes, readHardwareProfileSync()));
200
+ return label ? { hardwareFit: label } : {};
201
+ })(),
179
202
  ...(includeParameters ? {
180
203
  setup: recipe.setup,
181
204
  modelExamples: recipe.modelExamples,
@@ -204,6 +204,22 @@ export function describeCommandPolicy(commandName: string): CommandExecutionPoli
204
204
  boundary: 'Connected schedules require an explicit user request and do not create hidden Agent jobs or local schedulers.',
205
205
  };
206
206
  }
207
+ if (root === 'email') {
208
+ return {
209
+ effect: 'external-network',
210
+ confirmation,
211
+ preferredModelTool: `${agentHarnessModes('run_command')} (use /email set to configure, /email config to view settings)`,
212
+ boundary: 'Email IMAP reads are read-only (EXAMINE); sends require explicit --yes confirmation and route only to the configured account. Use /email set email.<key> <value> to configure email settings; use /email config to view current settings. The generic settings action cannot set email.* keys — /email set is the only supported path.',
213
+ };
214
+ }
215
+ if (root === 'calendar' || root === 'cal') {
216
+ return {
217
+ effect: 'local-state',
218
+ confirmation,
219
+ preferredModelTool: agentHarnessModes('run_command'),
220
+ boundary: 'Calendar commands manage local ICS events only; import/export and mutations require explicit --yes confirmation. Calendar configuration is set via /calendar subcommands, not the generic settings action.',
221
+ };
222
+ }
207
223
  if (root === 'channels' || root === 'channel' || root === 'notify') {
208
224
  return {
209
225
  effect: 'external-network',
@@ -1,4 +1,5 @@
1
1
  import { AGENT_HARNESS_MODES } from './agent-harness-tool-schema.ts';
2
+ import { readLimit } from './agent-harness-tool-utils.ts';
2
3
 
3
4
  export type AgentHarnessMode = typeof AGENT_HARNESS_MODES[number];
4
5
 
@@ -35,9 +36,6 @@ export const HARNESS_MODE_DESCRIPTORS: readonly HarnessModeDescriptor[] = [
35
36
  { id: 'route_decision', kind: 'inspect', family: 'start', summary: 'Choose the best visible Agent route for a plain user task.', next: 'Prefer route action:"plan" for first-class access.', keywords: ['where should this go', 'which tool', 'route user task', 'one assistant', 'user-first route'], parameters: ['query', 'target', 'limit', 'includeParameters'] },
36
37
  { id: 'cli_commands', kind: 'discover', family: 'cli', summary: 'List top-level package CLI mirrors for discovery only.', next: 'Prefer workspace action:"cli_commands|cli_command".', parameters: ['query', 'limit', 'includeParameters'] },
37
38
  { id: 'cli_command', kind: 'inspect', family: 'cli', summary: 'Inspect one CLI command parser result, policy, aliases, and model route.', parameters: ['cliCommand', 'command', 'commandName', 'target', 'query'] },
38
- { id: 'panels', kind: 'discover', family: 'ui', summary: 'List built-in panel catalog state and workspace routes.', next: 'Prefer workspace action:"panels|panel|open_panel".', parameters: ['query', 'category', 'limit', 'includeParameters'] },
39
- { id: 'panel', kind: 'inspect', family: 'ui', summary: 'Inspect one built-in panel, open state, workspace route, and policy.', parameters: ['panelId', 'target', 'query'] },
40
- { id: 'open_panel', kind: 'effect', family: 'ui', summary: 'Route the visible shell to one built-in panel.', requiresConfirmation: true, parameters: ['panelId', 'target', 'query', 'pane', 'confirm', 'explicitUserRequest'] },
41
39
  { id: 'ui_surfaces', kind: 'discover', family: 'ui', summary: 'List modal, picker, and visible UI surfaces the model can request.', next: 'Prefer workspace action:"surfaces|surface|open".', keywords: ['web dashboard', 'pwa', 'browser cockpit', 'connected browser', 'mobile web'], parameters: ['query', 'limit', 'includeParameters'] },
42
40
  { id: 'ui_surface', kind: 'inspect', family: 'ui', summary: 'Inspect one visible modal or picker surface and route contract.', keywords: ['web dashboard', 'pwa', 'browser cockpit', 'connected browser', 'mobile web'], parameters: ['surfaceId', 'target', 'query'] },
43
41
  { id: 'open_ui_surface', kind: 'effect', family: 'ui', summary: 'Open one visible modal, picker, or operator workspace route.', requiresConfirmation: true, keywords: ['web dashboard', 'pwa', 'browser cockpit', 'connected browser', 'mobile web'], parameters: ['surfaceId', 'target', 'query', 'confirm', 'explicitUserRequest'] },
@@ -147,18 +145,13 @@ export const HARNESS_MODE_DESCRIPTORS: readonly HarnessModeDescriptor[] = [
147
145
  { id: 'connected_host_capability', kind: 'inspect', family: 'connected-host', summary: 'Inspect one connected-host capability and blocked surfaces.', next: 'Prefer host action:"capability".', parameters: ['capabilityId', 'target', 'query'] },
148
146
  { id: 'daemon', kind: 'alias', family: 'connected-host', summary: 'GoodVibes daemon -> connected_host; mutations use confirmed methods.', next: 'Prefer host action:"capabilities".', aliases: ['connected_host'], parameters: ['includeParameters'] },
149
147
  { id: 'daemon_status', kind: 'alias', family: 'connected-host', summary: 'GoodVibes daemon status -> connected_host_status.', next: 'Prefer host action:"status".', aliases: ['connected_host_status'], parameters: ['includeParameters'] },
148
+ { id: 'propose_skill_drafts', kind: 'effect', family: 'personal-ops', summary: 'Draft up to 3 skills from recent learning candidates for review.', requiresConfirmation: true, keywords: ['skill draft', 'propose skill', 'auto-propose', 'draft skills', 'learning draft'], parameters: ['confirm', 'explicitUserRequest'] },
150
149
  ] as const;
151
150
 
152
151
  function readString(value: unknown): string {
153
152
  return typeof value === 'string' ? value.trim() : '';
154
153
  }
155
154
 
156
- function readLimit(value: unknown, fallback: number): number {
157
- const parsed = typeof value === 'string' && value.trim() ? Number(value) : value;
158
- if (typeof parsed !== 'number' || !Number.isFinite(parsed)) return fallback;
159
- return Math.max(1, Math.min(500, Math.trunc(parsed)));
160
- }
161
-
162
155
  function describeHarnessModeDescriptor(
163
156
  descriptor: HarnessModeDescriptor,
164
157
  options: { readonly includeParameters?: boolean; readonly lookup?: Record<string, unknown> } = {},
@@ -1,6 +1,6 @@
1
1
  import type { CommandContext } from '../input/command-registry.ts';
2
- import type { ProviderAccountRecord, ProviderAccountSnapshot } from '../panels/provider-account-snapshot.ts';
3
- import { buildProviderAccountSnapshot } from '../panels/provider-account-snapshot.ts';
2
+ import type { ProviderAccountRecord, ProviderAccountSnapshot } from '../runtime/provider-account-snapshot.ts';
3
+ import { buildProviderAccountSnapshot } from '../runtime/provider-account-snapshot.ts';
4
4
  import { requireProvider, requireServiceRegistry, requireSubscriptionManager } from '../input/commands/runtime-services.ts';
5
5
  import { previewHarnessText } from './agent-harness-text.ts';
6
6
 
@@ -1,5 +1,5 @@
1
1
  import { statSync } from 'node:fs';
2
- import { buildProviderAccountSnapshot } from '../panels/provider-account-snapshot.ts';
2
+ import { buildProviderAccountSnapshot } from '../runtime/provider-account-snapshot.ts';
3
3
  import { requireLocalUserAuthManager, requirePlatform, requireProvider, requireSecretsManager, requireServiceRegistry, requireShellPaths, requireSubscriptionManager } from '../input/commands/runtime-services.ts';
4
4
  import { collectOnboardingSnapshot } from '../runtime/onboarding/index.ts';
5
5
  import type { CommandContext } from '../input/command-registry.ts';
@@ -1,5 +1,5 @@
1
1
  export const AGENT_HARNESS_MODES = [
2
- 'summary', 'modes', 'mode', 'route_decision', 'cli_commands', 'cli_command', 'panels', 'panel', 'open_panel',
2
+ 'summary', 'modes', 'mode', 'route_decision', 'cli_commands', 'cli_command',
3
3
  'ui_surfaces', 'ui_surface', 'open_ui_surface',
4
4
  'shortcuts', 'keybindings', 'keybinding', 'run_keybinding', 'set_keybinding', 'reset_keybinding',
5
5
  'commands', 'command', 'run_command', 'channels', 'channel', 'channel_setup_guide', 'channel_triage', 'channel_deliveries', 'notifications', 'notification_target',
@@ -32,6 +32,12 @@ export const AGENT_HARNESS_MODES = [
32
32
  'service_posture', 'service_endpoint',
33
33
  'connected_host', 'connected_host_status', 'connected_host_capability',
34
34
  'daemon', 'daemon_status',
35
+ 'propose_skill_drafts',
36
+ ] as const;
37
+
38
+ const BACKGROUND_PROCESS_ACTION_VALUES = [
39
+ 'start', 'spawn', 'run', 'stop', 'kill', 'cancel', 'wait', 'list',
40
+ 'status', 'poll', 'log', 'output', 'write', 'capabilities', 'doctor', 'parity',
35
41
  ] as const;
36
42
 
37
43
  const KEY_COMBO_PARAMETER_SCHEMA = {
@@ -42,10 +48,12 @@ const KEY_COMBO_PARAMETER_SCHEMA = {
42
48
  } as const;
43
49
 
44
50
  export const AGENT_HARNESS_PARAMETER_PROPERTIES = {
51
+ // The full mode catalog is intentionally NOT inlined as an enum:
52
+ // it would bloat every model prompt. Dispatch still validates against
53
+ // AGENT_HARNESS_MODES and unknown modes return suggestions.
45
54
  mode: {
46
55
  type: 'string',
47
- enum: AGENT_HARNESS_MODES,
48
- description: 'Harness operation. Start with summary or a plural catalog mode.',
56
+ description: 'Operation. Start with "summary"; mode:"modes" lists the catalog.',
49
57
  },
50
58
  query: {
51
59
  type: 'string',
@@ -131,12 +139,12 @@ export const AGENT_HARNESS_PARAMETER_PROPERTIES = {
131
139
  },
132
140
  processAction: {
133
141
  type: 'string',
134
- enum: ['start', 'spawn', 'run', 'stop', 'kill', 'cancel', 'wait', 'list', 'status', 'poll', 'log', 'output', 'write', 'capabilities', 'doctor', 'parity'],
142
+ enum: BACKGROUND_PROCESS_ACTION_VALUES,
135
143
  description: 'Background process lifecycle action or process-style alias.',
136
144
  },
137
145
  action: {
138
146
  type: 'string',
139
- enum: ['start', 'spawn', 'run', 'stop', 'kill', 'cancel', 'wait', 'list', 'status', 'poll', 'log', 'output', 'write', 'capabilities', 'doctor', 'parity'],
147
+ enum: BACKGROUND_PROCESS_ACTION_VALUES,
140
148
  description: 'Process-style action alias for run_background_process.',
141
149
  },
142
150
  cwd: {
@@ -216,10 +224,6 @@ export const AGENT_HARNESS_PARAMETER_PROPERTIES = {
216
224
  type: 'string',
217
225
  description: 'UI surface id for ui_surface or open_ui_surface modes.',
218
226
  },
219
- panelId: {
220
- type: 'string',
221
- description: 'Built-in panel id for panel or open_panel modes.',
222
- },
223
227
  actionId: {
224
228
  type: 'string',
225
229
  description: 'Workspace action id or keybinding action id.',
@@ -332,11 +336,6 @@ export const AGENT_HARNESS_PARAMETER_PROPERTIES = {
332
336
  type: 'number',
333
337
  description: 'Maximum catalog entries to return.',
334
338
  },
335
- pane: {
336
- type: 'string',
337
- enum: ['top', 'bottom'],
338
- description: 'Preferred pane for open_panel.',
339
- },
340
339
  confirm: {
341
340
  type: 'boolean',
342
341
  description: 'Required true for confirmed harness effects.',
@@ -8,11 +8,12 @@ import { channelReadinessCatalogStatus, describeHarnessChannel, describeHarnessC
8
8
  import { blockedHarnessCliCommandTokens, describeHarnessCliCommand, listHarnessCliCommands, totalHarnessCliCommands } from './agent-harness-cli-metadata.ts';
9
9
  import { describeHarnessCommand, listHarnessCommands } from './agent-harness-command-catalog.ts';
10
10
  import { describeLearningCandidate, learningCuratorCatalogStatus, learningCuratorSummary } from './agent-harness-learning-curator.ts';
11
+ import { runSkillDraftProposer } from '../agent/skill-draft-runner.ts';
12
+ import { AgentSkillRegistry } from '../agent/skill-registry.ts';
11
13
  import { delegationPostureCatalogStatus, delegationPostureSummary, describeHarnessDelegationRoute } from './agent-harness-delegation-posture.ts';
12
14
  import { describeHarnessKeybinding, listHarnessKeybindings, listHarnessShortcuts, resetHarnessKeybinding, runHarnessKeybinding, setHarnessKeybinding, totalHarnessKeybindings, totalHarnessShortcuts } from './agent-harness-keybinding-metadata.ts';
13
15
  import { describeHarnessMediaProvider, mediaPostureCatalogStatus, mediaPostureSummary } from './agent-harness-media-posture.ts';
14
16
  import { describeHarnessNotificationTarget, listHarnessNotificationTargets, notificationTargetCatalogStatus } from './agent-harness-notification-metadata.ts';
15
- import { describeHarnessPanel, listHarnessPanels, openHarnessPanel, totalHarnessPanels } from './agent-harness-panel-metadata.ts';
16
17
  import { connectedHostStatusSummary } from './agent-harness-connected-host-status.ts';
17
18
  import { backgroundProcessCatalogStatus, backgroundProcessSummary, describeBackgroundProcess, runBackgroundProcessAction } from './agent-harness-background-processes.ts';
18
19
  import { describeDocumentOpsLane, documentOpsCatalogStatus, documentOpsSummary } from './agent-harness-document-ops.ts';
@@ -81,7 +82,6 @@ function compactHarnessModeGuide(): Record<string, unknown> {
81
82
  function detailedHarnessModelAccessGuide(): Record<string, string> {
82
83
  return {
83
84
  cliCommands: 'Prefer workspace action:"cli_commands|cli_command" for CLI discovery. Lower-level cli command modes remain available.',
84
- panels: 'Prefer workspace action:"panels|panel|open_panel"; visible navigation needs confirm:true and explicitUserRequest.',
85
85
  uiSurfaces: 'Prefer workspace action:"surfaces|surface|open" for visible UI and computer action:"browser|open_browser" for browser/PWA. Lower-level UI modes remain available.',
86
86
  shortcuts: 'Prefer workspace action:"shortcuts|keybindings|keybinding|run_keybinding|set_keybinding|reset_keybinding"; effects need confirmation.',
87
87
  slashCommands: 'Prefer workspace action:"commands|command|run_command"; slash-command execution needs confirmation.',
@@ -131,7 +131,7 @@ export function createAgentHarnessTool(deps: AgentHarnessToolDeps): Tool {
131
131
  return {
132
132
  definition: {
133
133
  name: 'agent_harness',
134
- description: 'Inspect or operate GoodVibes Agent harness surfaces.',
134
+ description: 'Inspect or operate Agent harness surfaces; mode:"modes" lists all.',
135
135
  parameters: {
136
136
  type: 'object',
137
137
  properties: AGENT_HARNESS_PARAMETER_PROPERTIES,
@@ -143,7 +143,13 @@ export function createAgentHarnessTool(deps: AgentHarnessToolDeps): Tool {
143
143
  },
144
144
  execute: async (rawArgs) => {
145
145
  const args = rawArgs as AgentHarnessToolArgs;
146
- if (!isMode(args.mode)) return error(`Unknown agent_harness mode: ${String(args.mode)}`);
146
+ if (!isMode(args.mode)) {
147
+ const requested = String(args.mode).toLowerCase();
148
+ const suggestions = AGENT_HARNESS_MODES
149
+ .filter((mode) => mode.includes(requested) || requested.includes(mode))
150
+ .slice(0, 5);
151
+ return error(`Unknown agent_harness mode: ${String(args.mode)}. ${suggestions.length > 0 ? `Closest modes: ${suggestions.join(', ')}. ` : ''}Use mode:"modes" to list the full catalog.`);
152
+ }
147
153
  try {
148
154
  if (args.mode === 'summary') {
149
155
  const channelReadiness = channelReadinessCatalogStatus(deps.commandContext);
@@ -214,7 +220,6 @@ export function createAgentHarnessTool(deps: AgentHarnessToolDeps): Tool {
214
220
  harnessModes: HARNESS_MODE_DESCRIPTORS.length,
215
221
  cliCommands: totalHarnessCliCommands(),
216
222
  blockedCliCommandTokens: blockedHarnessCliCommandTokens(),
217
- panels: totalHarnessPanels(deps.commandContext),
218
223
  uiSurfaces: totalHarnessUiSurfaces(),
219
224
  shortcuts: totalHarnessShortcuts(deps.commandContext),
220
225
  keybindings: totalHarnessKeybindings(deps.commandContext),
@@ -282,24 +287,6 @@ export function createAgentHarnessTool(deps: AgentHarnessToolDeps): Tool {
282
287
  if (args.mode === 'cli_command') {
283
288
  return output(describeHarnessCliCommand(args));
284
289
  }
285
- if (args.mode === 'panels') {
286
- const panels = listHarnessPanels(deps.commandContext, args);
287
- return output({
288
- panels,
289
- returned: panels.length,
290
- total: totalHarnessPanels(deps.commandContext),
291
- policy: 'Panel modes expose Agent/TUI operator view catalog and open state. open_panel is confirmation-gated and routes through the current Agent operator surface.',
292
- });
293
- }
294
- if (args.mode === 'panel') {
295
- const panel = describeHarnessPanel(deps.commandContext, args);
296
- return panel ? output(panel) : error(`Unknown panel ${readString(args.panelId || args.target || args.query) || '<missing>'}.`);
297
- }
298
- if (args.mode === 'open_panel') {
299
- const confirmationError = requireConfirmedAction(args, 'Panel routing');
300
- if (confirmationError) return error(confirmationError);
301
- return output(openHarnessPanel(deps.commandContext, args));
302
- }
303
290
  if (args.mode === 'ui_surfaces') {
304
291
  const surfaces = listHarnessUiSurfaces(deps.commandContext, args);
305
292
  return output({ surfaces, returned: surfaces.length, total: totalHarnessUiSurfaces() });
@@ -717,6 +704,23 @@ export function createAgentHarnessTool(deps: AgentHarnessToolDeps): Tool {
717
704
  includeParameters: args.includeParameters === true,
718
705
  }));
719
706
  }
707
+ if (args.mode === 'propose_skill_drafts') {
708
+ const confirmationError = requireConfirmedAction(args, 'Skill draft proposal');
709
+ if (confirmationError) return error(confirmationError);
710
+ const shellPaths = deps.commandContext.workspace?.shellPaths;
711
+ if (!shellPaths) return error('Skill draft proposal requires an active workspace.');
712
+ const registry = AgentSkillRegistry.fromShellPaths(shellPaths);
713
+ const result = runSkillDraftProposer(deps.commandContext, registry);
714
+ return output({
715
+ proposed: result.proposed,
716
+ skipped: result.skipped,
717
+ skillIds: result.skillIds,
718
+ message: result.proposed > 0
719
+ ? `Drafted ${result.proposed} skill${result.proposed !== 1 ? 's' : ''} for review. Find them under Memory > Skills with enabled:false.`
720
+ : 'No new skill drafts this pass.',
721
+ policy: 'Drafted skills are disabled and require review before use. Enable them under Memory > Skills.',
722
+ });
723
+ }
720
724
  return error(`Unhandled agent_harness mode: ${args.mode}`);
721
725
  } catch (err) {
722
726
  return error(formatHarnessError(err));
@@ -101,16 +101,6 @@ function openPanelWorkspaceSurface(
101
101
  options.opener();
102
102
  return opened(surface, { categoryId: options.categoryId, panelId: options.panelId, route: 'named-opener' });
103
103
  }
104
- if (context.showPanel) {
105
- const pane = optionalPane(args);
106
- context.showPanel(options.panelId, pane);
107
- return opened(surface, {
108
- categoryId: options.categoryId,
109
- panelId: options.panelId,
110
- pane: pane ?? 'default',
111
- route: 'panel-route',
112
- });
113
- }
114
104
  return routeUnavailable(surface);
115
105
  }
116
106
 
@@ -183,17 +173,17 @@ const UI_SURFACES: readonly UiSurfaceDefinition[] = [
183
173
  },
184
174
  {
185
175
  id: 'panel-picker',
186
- label: 'Panel Picker',
176
+ label: 'Workspace Picker',
187
177
  kind: 'picker',
188
- summary: 'Operator panel route into Agent Workspace home.',
178
+ summary: 'Operator route into the Agent Workspace home.',
189
179
  command: 'Ctrl+P',
190
- preferredModelRoute: `Use ${agentHarnessModes('panels', 'panel', 'open_panel')} for panel catalog and routing, or mode:"workspace_actions" for concrete model operation.`,
191
- available: (context) => typeof context.openPanelPicker === 'function' || typeof context.openAgentWorkspace === 'function',
180
+ preferredModelRoute: 'Use mode:"workspace_actions" for concrete model operation, or mode:"open_ui_surface" for visible routing.',
181
+ available: (context) => typeof context.openWorkspacePicker === 'function' || typeof context.openAgentWorkspace === 'function',
192
182
  open: (context) => {
193
183
  const surface = findSurfaceById('panel-picker')!;
194
- if (context.openPanelPicker) {
195
- context.openPanelPicker();
196
- return opened(surface, { categoryId: 'home', route: 'panel-picker' });
184
+ if (context.openWorkspacePicker) {
185
+ context.openWorkspacePicker();
186
+ return opened(surface, { categoryId: 'home', route: 'workspace-picker' });
197
187
  }
198
188
  return openAgentWorkspaceCategory(context, surface, 'home');
199
189
  },
@@ -206,7 +196,7 @@ const UI_SURFACES: readonly UiSurfaceDefinition[] = [
206
196
  command: '/security',
207
197
  preferredModelRoute: `Use mode:"workspace_actions" for security review actions or ${agentHarnessModes('run_command')} for confirmed /security review output.`,
208
198
  parameters: ['pane'],
209
- available: (context) => typeof context.openAgentWorkspace === 'function' || typeof context.openSecurityPanel === 'function' || typeof context.showPanel === 'function',
199
+ available: (context) => typeof context.openAgentWorkspace === 'function' || typeof context.openSecurityPanel === 'function',
210
200
  open: (context, args) => {
211
201
  const surface = findSurfaceById('security-panel')!;
212
202
  return openPanelWorkspaceSurface(context, args, surface, {
@@ -224,7 +214,7 @@ const UI_SURFACES: readonly UiSurfaceDefinition[] = [
224
214
  command: '/knowledge',
225
215
  preferredModelRoute: `Use agent_knowledge, agent_knowledge_ingest, mode:"workspace_actions", or ${agentHarnessModes('run_command')} for confirmed /knowledge operation.`,
226
216
  parameters: ['pane'],
227
- available: (context) => typeof context.openAgentWorkspace === 'function' || typeof context.openKnowledgePanel === 'function' || typeof context.showPanel === 'function',
217
+ available: (context) => typeof context.openAgentWorkspace === 'function' || typeof context.openKnowledgePanel === 'function',
228
218
  open: (context, args) => {
229
219
  const surface = findSurfaceById('knowledge-panel')!;
230
220
  return openPanelWorkspaceSurface(context, args, surface, {
@@ -242,7 +232,7 @@ const UI_SURFACES: readonly UiSurfaceDefinition[] = [
242
232
  command: '/subscription',
243
233
  preferredModelRoute: `Use mode:"workspace_actions" or ${agentHarnessModes('run_command')} for confirmed /subscription mirrors.`,
244
234
  parameters: ['pane'],
245
- available: (context) => typeof context.openAgentWorkspace === 'function' || typeof context.openSubscriptionPanel === 'function' || typeof context.showPanel === 'function',
235
+ available: (context) => typeof context.openAgentWorkspace === 'function' || typeof context.openSubscriptionPanel === 'function',
246
236
  open: (context, args) => {
247
237
  const surface = findSurfaceById('subscription-panel')!;
248
238
  return openPanelWorkspaceSurface(context, args, surface, {
@@ -49,7 +49,7 @@ export async function runWorkspaceAction(
49
49
  if (action.kind === 'settings-import') {
50
50
  const preview = previewAgentWorkspaceTuiSettingsImport(deps.commandContext);
51
51
  if (!preview) return error('GoodVibes settings import is unavailable in this runtime.');
52
- if (args.confirm !== true) {
52
+ if (args.confirm !== true && !readString(args.explicitUserRequest)) {
53
53
  return output({
54
54
  status: 'confirmation_required',
55
55
  action: describeWorkspaceAction(category, action, { lookup }),
@@ -57,10 +57,9 @@ export async function runWorkspaceAction(
57
57
  next: 'Run with confirm:true and explicitUserRequest after the user asks to import these settings.',
58
58
  });
59
59
  }
60
+ const confirmationError = requireConfirmedAction(args, 'GoodVibes settings import');
61
+ if (confirmationError) return error(confirmationError);
60
62
  const explicitUserRequest = readString(args.explicitUserRequest);
61
- if (!explicitUserRequest) {
62
- return error('GoodVibes settings import requires explicitUserRequest when confirm is true.');
63
- }
64
63
  const outcome = await importAgentWorkspaceTuiSettings(deps.commandContext);
65
64
  return output({
66
65
  status: outcome.status,