@pellux/goodvibes-agent 1.4.4 → 1.5.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 (230) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +52 -7
  3. package/dist/package/main.js +7491 -12253
  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/live-verification/live-verification.json +13 -13
  11. package/release/live-verification/live-verification.md +15 -15
  12. package/release/performance-snapshot.json +2 -2
  13. package/release/release-notes.md +5 -7
  14. package/release/release-readiness.json +6 -6
  15. package/src/agent/behavior-discovery-summary.ts +4 -18
  16. package/src/agent/calendar-registry.ts +322 -0
  17. package/src/agent/competitive-feature-inventory.ts +268 -130
  18. package/src/agent/document-registry.ts +5 -1
  19. package/src/agent/email/email-service.ts +350 -0
  20. package/src/agent/email/imap-client.ts +596 -0
  21. package/src/agent/email/smtp-client.ts +453 -0
  22. package/src/agent/ics-calendar.ts +662 -0
  23. package/src/agent/ics-timezone.ts +172 -0
  24. package/src/agent/markdown-frontmatter.ts +31 -0
  25. package/src/agent/memory-safety.ts +1 -1
  26. package/src/agent/note-registry.ts +3 -9
  27. package/src/agent/persona-discovery.ts +3 -15
  28. package/src/agent/persona-registry.ts +1 -10
  29. package/src/agent/research-source-registry.ts +5 -1
  30. package/src/agent/routine-discovery.ts +3 -15
  31. package/src/agent/runtime-profile.ts +3 -0
  32. package/src/agent/skill-discovery.ts +3 -15
  33. package/src/agent/skill-draft-proposer.ts +203 -0
  34. package/src/agent/skill-draft-runner.ts +201 -0
  35. package/src/agent/skill-registry.ts +36 -1
  36. package/src/agent/skill-standard.ts +99 -0
  37. package/src/agent/vibe-file.ts +7 -22
  38. package/src/cli/completion.ts +1 -1
  39. package/src/cli/config-overrides.ts +10 -1
  40. package/src/cli/redaction.ts +17 -5
  41. package/src/config/provider-model.ts +2 -1
  42. package/src/config/secret-config.ts +13 -6
  43. package/src/core/activity-feed.ts +97 -0
  44. package/src/core/away-digest.ts +161 -0
  45. package/src/core/conversation-rendering.ts +7 -3
  46. package/src/core/conversation.ts +22 -15
  47. package/src/core/hardware-profile.ts +362 -0
  48. package/src/core/last-seen-store.ts +78 -0
  49. package/src/core/plain-language.ts +52 -0
  50. package/src/core/setup-incomplete-hint.ts +90 -0
  51. package/src/core/system-message-router.ts +38 -87
  52. package/src/input/agent-workspace-basic-command-editor-submission.ts +60 -220
  53. package/src/input/agent-workspace-basic-command-editors.ts +39 -141
  54. package/src/input/agent-workspace-categories.ts +40 -125
  55. package/src/input/agent-workspace-command-editor.ts +4 -0
  56. package/src/input/agent-workspace-host-category.ts +0 -5
  57. package/src/input/agent-workspace-local-editor-submission.ts +3 -1
  58. package/src/input/agent-workspace-navigation.ts +10 -3
  59. package/src/input/agent-workspace-onboarding-actions.ts +132 -0
  60. package/src/input/agent-workspace-onboarding-categories.ts +29 -26
  61. package/src/input/agent-workspace-onboarding-finish.ts +11 -4
  62. package/src/input/agent-workspace-onboarding-state.ts +111 -0
  63. package/src/input/agent-workspace-profile-editor-submission.ts +260 -0
  64. package/src/input/agent-workspace-profile-editors.ts +155 -0
  65. package/src/input/agent-workspace-subscription-editor.ts +7 -0
  66. package/src/input/agent-workspace-types.ts +5 -1
  67. package/src/input/agent-workspace.ts +65 -39
  68. package/src/input/command-registry.ts +18 -5
  69. package/src/input/commands/agent-runtime-profile-runtime.ts +2 -1
  70. package/src/input/commands/agent-skills-runtime.ts +60 -3
  71. package/src/input/commands/calendar-runtime.ts +209 -0
  72. package/src/input/commands/channels-runtime.ts +1 -0
  73. package/src/input/commands/command-error.ts +9 -0
  74. package/src/input/commands/compat-runtime.ts +1 -0
  75. package/src/input/commands/config.ts +1 -0
  76. package/src/input/commands/conversation-runtime.ts +1 -1
  77. package/src/input/commands/delegation-runtime.ts +5 -6
  78. package/src/input/commands/email-runtime.ts +387 -0
  79. package/src/input/commands/experience-runtime.ts +17 -4
  80. package/src/input/commands/guidance-runtime.ts +1 -1
  81. package/src/input/commands/health-runtime.ts +6 -1
  82. package/src/input/commands/knowledge-format.ts +73 -0
  83. package/src/input/commands/knowledge.ts +9 -74
  84. package/src/input/commands/local-provider-runtime.ts +2 -1
  85. package/src/input/commands/local-runtime.ts +10 -4
  86. package/src/input/commands/mcp-runtime.ts +7 -0
  87. package/src/input/commands/notify-runtime.ts +17 -1
  88. package/src/input/commands/onboarding-runtime.ts +1 -0
  89. package/src/input/commands/operator-actions-runtime.ts +1 -0
  90. package/src/input/commands/operator-runtime.ts +3 -0
  91. package/src/input/commands/personas-runtime.ts +1 -0
  92. package/src/input/commands/platform-access-runtime.ts +40 -17
  93. package/src/input/commands/product-runtime.ts +6 -1
  94. package/src/input/commands/provider-accounts-runtime.ts +3 -2
  95. package/src/input/commands/qrcode-runtime.ts +1 -0
  96. package/src/input/commands/routines-runtime.ts +1 -0
  97. package/src/input/commands/runtime-services.ts +0 -13
  98. package/src/input/commands/security-runtime.ts +1 -0
  99. package/src/input/commands/session-content.ts +1 -0
  100. package/src/input/commands/shell-core.ts +5 -2
  101. package/src/input/commands/subscription-runtime.ts +33 -9
  102. package/src/input/commands/support-bundle-runtime.ts +8 -1
  103. package/src/input/commands/tasks-runtime.ts +1 -0
  104. package/src/input/commands/tts-runtime.ts +1 -0
  105. package/src/input/commands/vibe-runtime.ts +1 -0
  106. package/src/input/commands.ts +4 -0
  107. package/src/input/feed-context-factory.ts +3 -12
  108. package/src/input/handler-command-route.ts +7 -60
  109. package/src/input/handler-feed-routes.ts +0 -194
  110. package/src/input/handler-feed.ts +2 -54
  111. package/src/input/handler-interactions.ts +0 -2
  112. package/src/input/handler-modal-stack.ts +0 -9
  113. package/src/input/handler-picker-routes.ts +24 -1
  114. package/src/input/handler-shortcuts.ts +11 -40
  115. package/src/input/handler-ui-state.ts +13 -0
  116. package/src/input/handler.ts +8 -35
  117. package/src/input/keybindings.ts +40 -17
  118. package/src/input/model-picker-local-fit.ts +130 -0
  119. package/src/input/shell-passthrough.ts +58 -0
  120. package/src/input/submission-router.ts +0 -5
  121. package/src/main.ts +129 -90
  122. package/src/renderer/activity-sidebar.ts +186 -0
  123. package/src/renderer/agent-workspace-context-lines.ts +5 -48
  124. package/src/renderer/agent-workspace-style.ts +1 -1
  125. package/src/renderer/agent-workspace.ts +14 -2
  126. package/src/renderer/compositor.ts +37 -125
  127. package/src/renderer/conversation-overlays.ts +3 -3
  128. package/src/renderer/help-overlay.ts +7 -5
  129. package/src/renderer/model-workspace.ts +101 -86
  130. package/src/{panels → renderer}/polish.ts +3 -3
  131. package/src/renderer/shell-surface.ts +4 -5
  132. package/src/renderer/status-token.ts +31 -11
  133. package/src/renderer/tab-strip.ts +1 -1
  134. package/src/renderer/tool-call.ts +7 -8
  135. package/src/renderer/tool-labels.ts +92 -0
  136. package/src/renderer/ui-factory.ts +48 -96
  137. package/src/runtime/bootstrap-command-context.ts +0 -5
  138. package/src/runtime/bootstrap-command-parts.ts +6 -15
  139. package/src/runtime/bootstrap-core.ts +34 -13
  140. package/src/runtime/bootstrap-hook-bridge.ts +3 -1
  141. package/src/runtime/bootstrap-shell.ts +3 -50
  142. package/src/runtime/bootstrap.ts +3 -4
  143. package/src/runtime/context.ts +1 -1
  144. package/src/runtime/diagnostics/panels/index.ts +0 -1
  145. package/src/runtime/diagnostics/panels/policy.ts +1 -1
  146. package/src/runtime/execution-ledger.ts +16 -1
  147. package/src/runtime/index.ts +6 -1
  148. package/src/runtime/onboarding/index.ts +1 -0
  149. package/src/runtime/onboarding/onboarding-state.ts +200 -0
  150. package/src/{panels → runtime}/provider-account-snapshot.ts +5 -2
  151. package/src/runtime/services.ts +16 -4
  152. package/src/runtime/terminal-output-guard.ts +7 -0
  153. package/src/runtime/tool-permission-safety.ts +1 -1
  154. package/src/runtime/ui/model-picker/data-provider.ts +1 -1
  155. package/src/runtime/ui/model-picker/health-enrichment.ts +2 -2
  156. package/src/runtime/ui/model-picker/types.ts +2 -2
  157. package/src/runtime/ui/provider-health/data-provider.ts +3 -3
  158. package/src/runtime/ui/provider-health/types.ts +2 -2
  159. package/src/runtime/ui-services.ts +0 -2
  160. package/src/shell/agent-workspace-fullscreen.ts +0 -1
  161. package/src/shell/autonomy-surfacing.ts +272 -0
  162. package/src/shell/session-continuity-hints.ts +0 -6
  163. package/src/shell/startup-wiring.ts +221 -0
  164. package/src/shell/ui-openers.ts +18 -29
  165. package/src/tools/agent-context-policy.ts +1 -1
  166. package/src/tools/agent-harness-background-processes.ts +4 -4
  167. package/src/tools/agent-harness-browser-cockpit-route.ts +3 -3
  168. package/src/tools/agent-harness-command-runner.ts +6 -1
  169. package/src/tools/agent-harness-document-ops.ts +26 -53
  170. package/src/tools/agent-harness-execution-history.ts +1 -13
  171. package/src/tools/agent-harness-execution-posture.ts +0 -15
  172. package/src/tools/agent-harness-keybinding-metadata.ts +28 -29
  173. package/src/tools/agent-harness-local-model-cookbook.ts +24 -1
  174. package/src/tools/agent-harness-metadata.ts +16 -0
  175. package/src/tools/agent-harness-mode-catalog.ts +2 -9
  176. package/src/tools/agent-harness-provider-account-metadata.ts +2 -2
  177. package/src/tools/agent-harness-setup-posture-utils.ts +1 -1
  178. package/src/tools/agent-harness-tool-schema.ts +13 -14
  179. package/src/tools/agent-harness-tool.ts +27 -23
  180. package/src/tools/agent-harness-ui-surface-metadata.ts +10 -20
  181. package/src/tools/agent-harness-workspace-action-runner.ts +3 -4
  182. package/src/tools/agent-workspace-tool.ts +2 -33
  183. package/src/utils/terminal-width.ts +9 -3
  184. package/src/version.ts +1 -1
  185. package/src/input/agent-workspace-panel-route.ts +0 -44
  186. package/src/input/panel-integration-actions.ts +0 -26
  187. package/src/panels/approval-panel.ts +0 -149
  188. package/src/panels/automation-control-panel.ts +0 -212
  189. package/src/panels/base-panel.ts +0 -254
  190. package/src/panels/builtin/agent.ts +0 -58
  191. package/src/panels/builtin/knowledge.ts +0 -26
  192. package/src/panels/builtin/operations.ts +0 -121
  193. package/src/panels/builtin/session.ts +0 -138
  194. package/src/panels/builtin/shared.ts +0 -275
  195. package/src/panels/builtin/usage.ts +0 -21
  196. package/src/panels/builtin-panels.ts +0 -23
  197. package/src/panels/confirm-state.ts +0 -61
  198. package/src/panels/context-visualizer-panel.ts +0 -204
  199. package/src/panels/cost-tracker-panel.ts +0 -444
  200. package/src/panels/docs-panel.ts +0 -285
  201. package/src/panels/index.ts +0 -25
  202. package/src/panels/knowledge-panel.ts +0 -417
  203. package/src/panels/memory-panel.ts +0 -226
  204. package/src/panels/panel-list-panel.ts +0 -464
  205. package/src/panels/panel-manager.ts +0 -570
  206. package/src/panels/provider-accounts-panel.ts +0 -233
  207. package/src/panels/provider-health-domains.ts +0 -208
  208. package/src/panels/provider-health-panel.ts +0 -720
  209. package/src/panels/provider-health-tracker.ts +0 -115
  210. package/src/panels/provider-stats-panel.ts +0 -366
  211. package/src/panels/qr-panel.ts +0 -207
  212. package/src/panels/schedule-panel.ts +0 -321
  213. package/src/panels/scrollable-list-panel.ts +0 -491
  214. package/src/panels/search-focus.ts +0 -32
  215. package/src/panels/security-panel.ts +0 -295
  216. package/src/panels/session-browser-panel.ts +0 -395
  217. package/src/panels/session-maintenance.ts +0 -125
  218. package/src/panels/subscription-panel.ts +0 -263
  219. package/src/panels/system-messages-panel.ts +0 -230
  220. package/src/panels/tasks-panel.ts +0 -344
  221. package/src/panels/thinking-panel.ts +0 -304
  222. package/src/panels/token-budget-panel.ts +0 -475
  223. package/src/panels/tool-inspector-panel.ts +0 -436
  224. package/src/panels/types.ts +0 -54
  225. package/src/renderer/panel-composite.ts +0 -158
  226. package/src/renderer/panel-tab-bar.ts +0 -69
  227. package/src/renderer/panel-workspace-bar.ts +0 -42
  228. package/src/runtime/diagnostics/panels/panel-resources.ts +0 -118
  229. package/src/tools/agent-harness-panel-metadata.ts +0 -211
  230. /package/src/runtime/perf/{panel-health-monitor.ts → component-health.ts} +0 -0
@@ -27,19 +27,14 @@ import {
27
27
  import {
28
28
  handleIndicatorFocusToken,
29
29
  handleMouseToken,
30
- handlePanelFocusToken,
31
30
  handlePromptKeyToken,
32
31
  handlePromptTextToken,
33
- type PanelMouseLayout,
34
32
  } from './handler-feed-routes.ts';
35
33
  import type { WrappedPromptInfo } from './handler-prompt-buffer.ts';
36
34
  import { handleModalTokenRoutes } from './handler-modal-token-routes.ts';
37
35
  import { handleCommandModeToken } from './handler-command-route.ts';
38
36
  import { handleGlobalShortcutToken } from './handler-shortcuts.ts';
39
- import type { Panel } from '../panels/types.ts';
40
- import { handlePanelIntegrationAction } from './panel-integration-actions.ts';
41
37
  import { SelectionManager } from './selection.ts';
42
- import type { PanelManager } from '../panels/panel-manager.ts';
43
38
  import type { KeybindingsManager } from './keybindings.ts';
44
39
  import type { ModelPickerTarget } from './model-picker.ts';
45
40
 
@@ -51,7 +46,7 @@ import type { ModelPickerTarget } from './model-picker.ts';
51
46
  * **Mutable per-feed** (synced from handler at the top of every feed() call, and
52
47
  * updated inside action closures via syncFeedContextMutableFields):
53
48
  * - `prompt`, `cursorPos` — current text buffer state
54
- * - `commandMode`, `panelFocused`, `indicatorFocused` — focus-mode flags
49
+ * - `commandMode`, `indicatorFocused` — focus-mode flags
55
50
  * - `helpOverlayActive`, `helpScrollOffset` — help overlay visibility and scroll
56
51
  * - `shortcutsOverlayActive`, `shortcutsScrollOffset` — shortcuts overlay state
57
52
  * - `nextPasteId`, `nextImageId` — monotonically increasing ID counters
@@ -72,7 +67,7 @@ import type { ModelPickerTarget } from './model-picker.ts';
72
67
  * - `filePicker`, `modelPicker`, `processModal`, `liveTailModal`,
73
68
  * `contextInspectorModal`, `blockActionsMenu`, `searchManager`, `historySearch` —
74
69
  * service objects constructed once
75
- * - `panelManager`, `keybindingsManager` — from uiServices, stable for app lifetime
70
+ * - `keybindingsManager` — from uiServices, stable for app lifetime
76
71
  * - `modalStack` — reference to the handler's shared array (mutated in place)
77
72
  * - `getHistory`, `getViewportHeight`, `getScrollTop`, `scroll`, `exitApp` — stable
78
73
  * callbacks bound in the InputHandler constructor
@@ -86,7 +81,6 @@ export interface InputFeedContext {
86
81
  cursorPos: number;
87
82
  inputScrollTop: number;
88
83
  commandMode: boolean;
89
- panelFocused: boolean;
90
84
  indicatorFocused: boolean;
91
85
  helpOverlayActive: boolean;
92
86
  helpScrollOffset: number;
@@ -120,8 +114,6 @@ export interface InputFeedContext {
120
114
  readonly contextInspectorModal: ContextInspectorModal;
121
115
  readonly blockActionsMenu: BlockActionsMenu;
122
116
  readonly searchManager: SearchManager;
123
- readonly panelManager: PanelManager;
124
- panelMouseLayout: PanelMouseLayout | null;
125
117
  readonly keybindingsManager: KeybindingsManager;
126
118
  readonly modalStack: string[];
127
119
  inputHistory: InputHistory | null;
@@ -147,7 +139,6 @@ export interface InputFeedContext {
147
139
  readonly executeBlockAction: (id: string) => void;
148
140
  readonly cycleAgentWorkspaceCategory: (direction: 'next' | 'prev') => void;
149
141
  readonly dismissAgentWorkspace: () => boolean;
150
- readonly onPanelInputConsumed: (activePanel: Panel | null, key: string) => void;
151
142
  readonly getWrappedPromptInfo: (contentWidth: number) => WrappedPromptInfo;
152
143
  readonly moveCursorVertical: (direction: -1 | 1) => boolean;
153
144
  readonly handlePathCompletion: () => boolean;
@@ -236,16 +227,7 @@ export function feedInputTokens(context: InputFeedContext, tokens: readonly Inpu
236
227
  }
237
228
 
238
229
  if (token.type === 'key') {
239
- if (
240
- context.panelFocused
241
- && (!context.panelManager.isVisible()
242
- || context.panelManager.getAllOpen().length === 0
243
- || context.panelManager.getActivePanel() === null)
244
- ) {
245
- context.panelFocused = false;
246
- }
247
230
  const shortcutState = {
248
- panelFocused: context.panelFocused,
249
231
  prompt: context.prompt,
250
232
  cursorPos: context.cursorPos,
251
233
  commandMode: context.commandMode,
@@ -272,36 +254,16 @@ export function feedInputTokens(context: InputFeedContext, tokens: readonly Inpu
272
254
  handleEscape: context.handleEscape,
273
255
  cycleAgentWorkspaceCategory: context.cycleAgentWorkspaceCategory,
274
256
  dismissAgentWorkspace: context.dismissAgentWorkspace,
275
- panelManager: context.panelManager,
276
257
  keybindingsManager: context.keybindingsManager,
277
258
  };
278
259
  if (handleGlobalShortcutToken(shortcutState, token, viewportHeight)) {
279
260
  context.prompt = shortcutState.prompt;
280
261
  context.cursorPos = shortcutState.cursorPos;
281
262
  context.commandMode = shortcutState.commandMode;
282
- context.panelFocused = shortcutState.panelFocused;
283
263
  continue;
284
264
  }
285
265
  }
286
266
 
287
- const panelRoute = handlePanelFocusToken({
288
- panelFocused: context.panelFocused,
289
- commandMode: context.commandMode,
290
- searchActive: context.searchManager.active,
291
- autocompleteActive: !!context.autocomplete?.isActive,
292
- requestRender: context.requestRender,
293
- handlePathCompletion: context.handlePathCompletion,
294
- cycleAgentWorkspaceCategory: context.cycleAgentWorkspaceCategory,
295
- dismissAgentWorkspace: context.dismissAgentWorkspace,
296
- panelManager: context.panelManager,
297
- keybindingsManager: context.keybindingsManager,
298
- onPanelInputConsumed: context.onPanelInputConsumed,
299
- }, token);
300
- context.panelFocused = panelRoute.panelFocused;
301
- if (panelRoute.handled) {
302
- continue;
303
- }
304
-
305
267
  const indicatorRoute = handleIndicatorFocusToken({
306
268
  indicatorFocused: context.indicatorFocused,
307
269
  modalOpened: context.modalOpened,
@@ -348,8 +310,6 @@ export function feedInputTokens(context: InputFeedContext, tokens: readonly Inpu
348
310
  modalStack: context.modalStack,
349
311
  commandRegistry: context.commandRegistry,
350
312
  commandContext: context.commandContext,
351
- panelFocused: context.panelFocused,
352
- panelManager: context.panelManager,
353
313
  conversationManager: context.conversationManager,
354
314
  requestRender: context.requestRender,
355
315
  handleEscape: context.handleEscape,
@@ -365,7 +325,6 @@ export function feedInputTokens(context: InputFeedContext, tokens: readonly Inpu
365
325
  context.commandMode = commandState.commandMode;
366
326
  context.prompt = commandState.prompt;
367
327
  context.cursorPos = commandState.cursorPos;
368
- context.panelFocused = commandState.panelFocused;
369
328
  context.nextPasteId = commandState.nextPasteId;
370
329
  context.nextImageId = commandState.nextImageId;
371
330
  continue;
@@ -411,8 +370,6 @@ export function feedInputTokens(context: InputFeedContext, tokens: readonly Inpu
411
370
  const mouseRoute = handleMouseToken({
412
371
  conversationManager: context.conversationManager,
413
372
  selection: context.selection,
414
- panelManager: context.panelManager,
415
- panelMouseLayout: context.panelMouseLayout,
416
373
  mouseDownRow: context.mouseDownRow,
417
374
  mouseDownCol: context.mouseDownCol,
418
375
  scrollTop,
@@ -433,12 +390,3 @@ export function feedInputTokens(context: InputFeedContext, tokens: readonly Inpu
433
390
 
434
391
  context.requestRender();
435
392
  }
436
-
437
- export function defaultPanelInputConsumer(
438
- panelManager: PanelManager,
439
- activePanel: Panel | null,
440
- key: string,
441
- commandContext?: CommandContext,
442
- ): void {
443
- handlePanelIntegrationAction(panelManager, activePanel, key, commandContext);
444
- }
@@ -167,7 +167,6 @@ export function handleEscapeForHandler(handler: InputHandler): void {
167
167
  commandMode: handler.commandMode,
168
168
  modalStack: handler.modalStack,
169
169
  modalReturnFocus: handler.modalReturnFocus,
170
- panelFocused: handler.panelFocused,
171
170
  indicatorFocused: handler.indicatorFocused,
172
171
  prompt: handler.prompt,
173
172
  cursorPos: handler.cursorPos,
@@ -188,7 +187,6 @@ export function handleEscapeForHandler(handler: InputHandler): void {
188
187
  handler.shortcutsOverlayActive = result.shortcutsOverlayActive;
189
188
  handler.shortcutsScrollOffset = result.shortcutsScrollOffset;
190
189
  handler.selectionCallback = result.selectionCallback;
191
- handler.panelFocused = result.panelFocused;
192
190
  handler.indicatorFocused = result.indicatorFocused;
193
191
  handler.modalReturnFocus = result.modalReturnFocus;
194
192
  }
@@ -9,7 +9,6 @@ import type { SelectionResult } from './selection-modal.ts';
9
9
  export type ModalStackState = ActiveModalState & {
10
10
  modalStack: string[];
11
11
  modalReturnFocus?: 'prompt' | 'indicator';
12
- panelFocused: boolean;
13
12
  indicatorFocused: boolean;
14
13
  };
15
14
 
@@ -61,7 +60,6 @@ export function handleEscape(state: EscapeState): {
61
60
  shortcutsOverlayActive: boolean;
62
61
  shortcutsScrollOffset: number;
63
62
  selectionCallback: ((result: SelectionResult | null) => void) | null;
64
- panelFocused: boolean;
65
63
  indicatorFocused: boolean;
66
64
  modalReturnFocus: NonNullable<ModalStackState['modalReturnFocus']>;
67
65
  } {
@@ -73,7 +71,6 @@ export function handleEscape(state: EscapeState): {
73
71
  let shortcutsOverlayActive = state.shortcutsOverlayActive;
74
72
  let shortcutsScrollOffset = state.shortcutsScrollOffset;
75
73
  let selectionCallback = state.selectionCallback;
76
- let panelFocused = state.panelFocused;
77
74
  let indicatorFocused = state.indicatorFocused;
78
75
  let modalReturnFocus: NonNullable<ModalStackState['modalReturnFocus']> = state.modalReturnFocus ?? 'prompt';
79
76
 
@@ -84,7 +81,6 @@ export function handleEscape(state: EscapeState): {
84
81
  shortcutsOverlayActive,
85
82
  commandMode,
86
83
  }) !== null) return;
87
- panelFocused = false;
88
84
  indicatorFocused = modalReturnFocus === 'indicator';
89
85
  modalReturnFocus = 'prompt';
90
86
  state.modalReturnFocus = 'prompt';
@@ -102,7 +98,6 @@ export function handleEscape(state: EscapeState): {
102
98
  shortcutsOverlayActive,
103
99
  shortcutsScrollOffset,
104
100
  selectionCallback,
105
- panelFocused,
106
101
  indicatorFocused,
107
102
  modalReturnFocus,
108
103
  };
@@ -185,7 +180,6 @@ export function handleEscape(state: EscapeState): {
185
180
  shortcutsOverlayActive,
186
181
  shortcutsScrollOffset,
187
182
  selectionCallback,
188
- panelFocused,
189
183
  indicatorFocused,
190
184
  modalReturnFocus,
191
185
  };
@@ -210,7 +204,6 @@ export function handleEscape(state: EscapeState): {
210
204
  shortcutsOverlayActive,
211
205
  shortcutsScrollOffset,
212
206
  selectionCallback,
213
- panelFocused,
214
207
  indicatorFocused,
215
208
  modalReturnFocus,
216
209
  };
@@ -229,7 +222,6 @@ export function handleEscape(state: EscapeState): {
229
222
  shortcutsOverlayActive,
230
223
  shortcutsScrollOffset,
231
224
  selectionCallback,
232
- panelFocused,
233
225
  indicatorFocused,
234
226
  modalReturnFocus,
235
227
  };
@@ -245,7 +237,6 @@ export function handleEscape(state: EscapeState): {
245
237
  shortcutsOverlayActive,
246
238
  shortcutsScrollOffset,
247
239
  selectionCallback,
248
- panelFocused,
249
240
  indicatorFocused,
250
241
  modalReturnFocus,
251
242
  };
@@ -3,6 +3,7 @@ import type { InputToken } from '@pellux/goodvibes-sdk/platform/core';
3
3
  import type { CommandContext } from './command-registry.ts';
4
4
  import type { CapabilityFilter, CategoryFilter, ModelPickerModal } from './model-picker.ts';
5
5
  import { MODEL_PICKER_CHROME_LINES } from '../renderer/model-picker-overlay.ts';
6
+ import { isLocalFitRecommendation, isProviderSignInRow } from '../input/model-picker-local-fit.ts';
6
7
  import { resolveAndValidatePath } from '@pellux/goodvibes-sdk/platform/utils';
7
8
  import { logger } from '@pellux/goodvibes-sdk/platform/utils';
8
9
  import type { ProcessEntry } from '../renderer/process-modal.ts';
@@ -59,6 +60,28 @@ export function handleModelPickerToken(state: ModelPickerRouteState, token: Inpu
59
60
  if (mode === 'model') {
60
61
  const selected = state.modelPicker.getSelected();
61
62
  if (selected) {
63
+ // Sign-in row: route to provider picker instead of committing a model.
64
+ if (isProviderSignInRow(selected)) {
65
+ state.modelPicker.close();
66
+ if (state.modalStack[state.modalStack.length - 1] === 'modelPicker') state.modalStack.pop();
67
+ state.commandContext?.openProviderPicker?.();
68
+ return true;
69
+ }
70
+ // Local fit rec: the model is not installed — do not commit it as the
71
+ // active model. Print a plain-language guide and close the picker.
72
+ if (isLocalFitRecommendation(selected)) {
73
+ state.modelPicker.close();
74
+ if (state.modalStack[state.modalStack.length - 1] === 'modelPicker') state.modalStack.pop();
75
+ state.commandContext?.print?.(
76
+ [
77
+ `${selected.displayName} is not installed yet.`,
78
+ 'To use a local model, add a custom provider:',
79
+ ' /provider add <name> <baseURL> (e.g. /provider add ollama http://localhost:11434/v1)',
80
+ 'Or sign in to a cloud provider via the provider picker.',
81
+ ].join('\n'),
82
+ );
83
+ return true;
84
+ }
62
85
  const currentEffort = state.commandContext?.session.runtime.reasoningEffort ?? 'medium';
63
86
  if (state.modelPicker.target === 'main' && selected.reasoningEffort && selected.reasoningEffort.length > 0) {
64
87
  state.modelPicker.showEffortPicker(selected, currentEffort);
@@ -162,7 +185,7 @@ export function handleModelPickerToken(state: ModelPickerRouteState, token: Inpu
162
185
  if (ch === ' ' && state.modelPicker.mode === 'model') {
163
186
  const selected = state.modelPicker.getSelected();
164
187
  if (selected && state.modelPicker.isLocalModel(selected)) state.modelPicker.enterContextCapMode(selected);
165
- else if (ch.length === 1 && ch >= ' ') state.modelPicker.appendChar(ch);
188
+ else state.modelPicker.appendChar(ch);
166
189
  } else if (ch.length === 1 && ch >= ' ') {
167
190
  state.modelPicker.appendChar(ch);
168
191
  }
@@ -4,7 +4,6 @@ import type { SearchManager } from './search.ts';
4
4
  import type { HistorySearch } from './input-history.ts';
5
5
  import type { ConversationManager } from '../core/conversation';
6
6
  import type { AutocompleteEngine } from './autocomplete.ts';
7
- import type { PanelManager } from '../panels/panel-manager.ts';
8
7
  import type { KeybindingsManager } from './keybindings.ts';
9
8
 
10
9
  type WrappedPromptInfo = {
@@ -14,8 +13,6 @@ type WrappedPromptInfo = {
14
13
  };
15
14
 
16
15
  export type GlobalShortcutRouteState = {
17
- panelFocused: boolean;
18
- panelManager: PanelManager;
19
16
  keybindingsManager: KeybindingsManager;
20
17
  prompt: string;
21
18
  cursorPos: number;
@@ -54,17 +51,15 @@ export function handleGlobalShortcutToken(
54
51
 
55
52
  // Fast-path: bare pageup/pagedown have no keybinding entry.
56
53
  if (token.logicalName === 'pageup') {
57
- if (state.panelFocused) return false;
58
54
  state.scroll(-Math.max(1, viewportHeight - 2));
59
55
  return true;
60
56
  }
61
57
  if (token.logicalName === 'pagedown') {
62
- if (state.panelFocused) return false;
63
58
  state.scroll(Math.max(1, viewportHeight - 2));
64
59
  return true;
65
60
  }
66
61
  // Bare escape is also not in the keybinding table.
67
- if (token.logicalName === 'escape' && !state.panelFocused) {
62
+ if (token.logicalName === 'escape') {
68
63
  state.handleEscape();
69
64
  return true;
70
65
  }
@@ -78,56 +73,32 @@ export function handleGlobalShortcutToken(
78
73
  state.handleCopy();
79
74
  return true;
80
75
 
81
- case 'clear-cancel':
82
- state.handleCtrlC();
83
- return true;
84
-
85
76
  case 'screen-clear':
86
77
  state.commandContext?.clearScreen?.();
87
78
  return true;
88
79
 
89
- case 'panel-close-all': {
90
- if (state.dismissAgentWorkspace()) {
91
- state.panelFocused = false;
92
- return true;
93
- }
94
- const pm = state.panelManager;
95
- for (const p of pm.getAllOpen()) pm.close(p.id);
96
- pm.hide();
97
- state.panelFocused = false;
80
+ case 'workspace-close-all':
81
+ case 'workspace-close':
82
+ state.dismissAgentWorkspace();
98
83
  state.requestRender();
99
84
  return true;
100
- }
101
85
 
102
- case 'panel-close': {
103
- if (state.dismissAgentWorkspace()) {
104
- state.panelFocused = false;
105
- return true;
106
- }
107
- const pm = state.panelManager;
108
- const active = pm.getActivePanel();
109
- if (active) {
110
- pm.close(active.id);
111
- state.requestRender();
112
- }
113
- state.panelFocused = false;
86
+ case 'workspace-picker':
87
+ state.commandContext?.openWorkspacePicker?.();
88
+ state.requestRender();
114
89
  return true;
115
- }
116
90
 
117
- case 'panel-picker':
118
- state.commandContext?.openPanelPicker?.();
119
- state.panelFocused = false;
91
+ case 'sidebar-toggle':
92
+ state.commandContext?.toggleActivitySidebar?.();
120
93
  state.requestRender();
121
94
  return true;
122
95
 
123
- case 'panel-tab-next':
96
+ case 'workspace-tab-next':
124
97
  state.cycleAgentWorkspaceCategory('next');
125
- state.panelFocused = false;
126
98
  return true;
127
99
 
128
- case 'panel-tab-prev':
100
+ case 'workspace-tab-prev':
129
101
  state.cycleAgentWorkspaceCategory('prev');
130
- state.panelFocused = false;
131
102
  return true;
132
103
 
133
104
  case 'history-search':
@@ -125,6 +125,19 @@ export type ModalOpenOps = {
125
125
  openCommandMode: () => void;
126
126
  };
127
127
 
128
+ /**
129
+ * Reopen the modal that was previously on top of the stack after it has been
130
+ * popped (so the one beneath it can resurface).
131
+ *
132
+ * INVARIANT: modals omitted from this switch are intentionally absent because
133
+ * they remain `.active` as underlays when something stacks above them — they
134
+ * never close themselves on stack push, so they need no explicit reopen.
135
+ * Only the self-closing set (help, shortcuts, bookmark, process,
136
+ * contextInspector, mcpWorkspace, agentWorkspace, command) is handled here.
137
+ *
138
+ * NOTE: any future modal that closes itself on overlay push MUST be added to
139
+ * this switch, or it will silently fail to reopen when the stack unwinds.
140
+ */
128
141
  export function reopenModalByName(name: string, ops: ModalOpenOps): void {
129
142
  switch (name) {
130
143
  case 'help':
@@ -44,10 +44,8 @@ import {
44
44
  import {
45
45
  handleIndicatorFocusToken,
46
46
  handleMouseToken,
47
- handlePanelFocusToken,
48
47
  handlePromptKeyToken,
49
48
  handlePromptTextToken,
50
- type PanelMouseLayout,
51
49
  } from './handler-feed-routes.ts';
52
50
  import {
53
51
  ensureInputCursorVisible,
@@ -67,10 +65,7 @@ import { handleCommandModeToken } from './handler-command-route.ts';
67
65
  import { handleGlobalShortcutToken } from './handler-shortcuts.ts';
68
66
  import { feedInputTokens } from './handler-feed.ts';
69
67
  import { buildInitialFeedContext, syncFeedContextMutableFields } from './feed-context-factory.ts';
70
- import { handlePanelIntegrationAction as runPanelIntegrationAction } from './panel-integration-actions.ts';
71
- import type { Panel } from '../panels/types.ts';
72
68
  import type { UiRuntimeServices } from '../runtime/ui-services.ts';
73
- export { handlePanelIntegrationAction } from './panel-integration-actions.ts';
74
69
  import type { ModelPickerTarget } from './model-picker.ts';
75
70
 
76
71
  type SelectionModalCallback = (result: SelectionResult | null) => void;
@@ -93,8 +88,6 @@ export class InputHandler {
93
88
  public commandMode = false;
94
89
  /** True when the process indicator bar has keyboard focus. */
95
90
  public indicatorFocused = false;
96
- /** True when keyboard focus is on the active panel (arrow/enter go to panel, not prompt). */
97
- public panelFocused = false;
98
91
 
99
92
  public tokenizer = new InputTokenizer();
100
93
  public pasteRegistry = new Map<string, string>();
@@ -202,12 +195,12 @@ export class InputHandler {
202
195
  this.feedContext = buildInitialFeedContext(
203
196
  {
204
197
  prompt: this.prompt, cursorPos: this.cursorPos, inputScrollTop: this.inputScrollTop, commandMode: this.commandMode,
205
- panelFocused: this.panelFocused, indicatorFocused: this.indicatorFocused,
198
+ indicatorFocused: this.indicatorFocused,
206
199
  helpOverlayActive: this.helpOverlayActive, helpScrollOffset: this.helpScrollOffset,
207
200
  shortcutsOverlayActive: this.shortcutsOverlayActive, shortcutsScrollOffset: this.shortcutsScrollOffset,
208
201
  nextPasteId: this.nextPasteId, nextImageId: this.nextImageId,
209
202
  mouseDownRow: this.mouseDownRow, mouseDownCol: this.mouseDownCol,
210
- contentWidth: this.contentWidth, panelMouseLayout: this.panelMouseLayout,
203
+ contentWidth: this.contentWidth,
211
204
  selectionCallback: this.selectionCallback,
212
205
  },
213
206
  {
@@ -236,7 +229,6 @@ export class InputHandler {
236
229
  modalStack: this.modalStack,
237
230
  inputHistory: this.inputHistory,
238
231
  conversationManager: this.conversationManager,
239
- panelManager: this.uiServices.shell.panelManager,
240
232
  keybindingsManager: this.uiServices.shell.keybindingsManager,
241
233
  getHistory: this.getHistory,
242
234
  getViewportHeight: this.getViewportHeight,
@@ -261,7 +253,6 @@ export class InputHandler {
261
253
  executeBlockAction: (id: string) => this.executeBlockAction(id),
262
254
  cycleAgentWorkspaceCategory: (direction: 'next' | 'prev') => this.cycleAgentWorkspaceCategory(direction),
263
255
  dismissAgentWorkspace: () => this.dismissAgentWorkspace(),
264
- onPanelInputConsumed: (activePanel: Panel | null, key: string) => this.handlePanelIntegrationAction(activePanel, key),
265
256
  getWrappedPromptInfo: (contentWidth: number) => this.getWrappedPromptInfo(contentWidth),
266
257
  moveCursorVertical: (direction: -1 | 1) => this.moveCursorVertical(direction),
267
258
  handlePathCompletion: () => this.handlePathCompletion(),
@@ -280,12 +271,11 @@ export class InputHandler {
280
271
  public syncFeedContextMutableFields(): void {
281
272
  const h = this;
282
273
  syncFeedContextMutableFields({ prompt: h.prompt, cursorPos: h.cursorPos, inputScrollTop: h.inputScrollTop, commandMode: h.commandMode,
283
- panelFocused: h.panelFocused, indicatorFocused: h.indicatorFocused, helpOverlayActive: h.helpOverlayActive,
274
+ indicatorFocused: h.indicatorFocused, helpOverlayActive: h.helpOverlayActive,
284
275
  helpScrollOffset: h.helpScrollOffset, shortcutsOverlayActive: h.shortcutsOverlayActive,
285
276
  shortcutsScrollOffset: h.shortcutsScrollOffset, selectionCallback: h.selectionCallback,
286
277
  nextPasteId: h.nextPasteId, nextImageId: h.nextImageId, mouseDownRow: h.mouseDownRow,
287
- mouseDownCol: h.mouseDownCol, contentWidth: h.contentWidth,
288
- panelMouseLayout: h.panelMouseLayout }, this.feedContext);
278
+ mouseDownCol: h.mouseDownCol, contentWidth: h.contentWidth }, this.feedContext);
289
279
  }
290
280
 
291
281
  /** Wire in the InputHistory instance. Optional; disables history navigation if unset. */
@@ -360,14 +350,12 @@ export class InputHandler {
360
350
  return true;
361
351
  }
362
352
  public openMcpWorkspace(context: CommandContext): void {
363
- this.panelFocused = false;
364
353
  this.indicatorFocused = false;
365
354
  this.modalOpened('mcpWorkspace');
366
355
  this.mcpWorkspace.open(context);
367
356
  this.requestRender();
368
357
  }
369
358
  public openAgentWorkspace(context: CommandContext, categoryId?: string, onlyGroup?: string): void {
370
- this.panelFocused = false;
371
359
  this.indicatorFocused = false;
372
360
  this.modalOpened('agentWorkspace');
373
361
  this.agentWorkspace.open(
@@ -390,7 +378,6 @@ export class InputHandler {
390
378
  public dismissAgentWorkspace(): boolean {
391
379
  if (!this.agentWorkspace.active) return false;
392
380
  this.closeAgentWorkspaceModal();
393
- this.panelFocused = false;
394
381
  this.indicatorFocused = false;
395
382
  this.requestRender();
396
383
  return true;
@@ -503,7 +490,6 @@ export class InputHandler {
503
490
  context.cursorPos = this.cursorPos;
504
491
  context.inputScrollTop = this.inputScrollTop;
505
492
  context.commandMode = this.commandMode;
506
- context.panelFocused = this.panelFocused;
507
493
  context.indicatorFocused = this.indicatorFocused;
508
494
  context.helpOverlayActive = this.helpOverlayActive;
509
495
  context.helpScrollOffset = this.helpScrollOffset;
@@ -515,7 +501,6 @@ export class InputHandler {
515
501
  context.mouseDownRow = this.mouseDownRow;
516
502
  context.mouseDownCol = this.mouseDownCol;
517
503
  context.contentWidth = this.contentWidth;
518
- context.panelMouseLayout = this.panelMouseLayout;
519
504
  // Sync semi-stable refs that may be wired after construction.
520
505
  context.commandRegistry = this.commandRegistry;
521
506
  context.commandContext = this.commandContext;
@@ -532,7 +517,6 @@ export class InputHandler {
532
517
  this.cursorPos = context.cursorPos;
533
518
  this.inputScrollTop = context.inputScrollTop;
534
519
  this.commandMode = context.commandMode;
535
- this.panelFocused = context.panelFocused;
536
520
  this.indicatorFocused = context.indicatorFocused;
537
521
  this.helpOverlayActive = context.helpOverlayActive;
538
522
  this.helpScrollOffset = context.helpScrollOffset;
@@ -583,17 +567,12 @@ export class InputHandler {
583
567
 
584
568
  /** Content width for wrapping — set by main.ts via setContentWidth(). */
585
569
  public contentWidth = 76;
586
- public panelMouseLayout: PanelMouseLayout | null = null;
587
570
 
588
571
  /** Set the content width used for wrapping calculations. Call from main.ts. */
589
572
  public setContentWidth(w: number): void {
590
573
  this.contentWidth = w;
591
574
  }
592
575
 
593
- public setPanelMouseLayout(layout: PanelMouseLayout | null): void {
594
- this.panelMouseLayout = layout;
595
- }
596
-
597
576
  /**
598
577
  * Move cursor up or down by one WRAPPED line.
599
578
  * Uses the segment table to navigate visual lines, not raw \n lines.
@@ -726,15 +705,10 @@ export class InputHandler {
726
705
  return true;
727
706
  }
728
707
 
729
- /**
730
- * Word-wrap a single line to fit within maxW columns.
731
- * Breaks at spaces; words wider than maxW are force-broken.
732
- */
733
708
  public cycleAgentWorkspaceCategory(direction: 'next' | 'prev'): void {
734
709
  const context = this.commandContext;
735
710
  if (!this.agentWorkspace.active) {
736
711
  if (!context) return;
737
- this.panelFocused = false;
738
712
  this.indicatorFocused = false;
739
713
  this.modalOpened('agentWorkspace');
740
714
  this.agentWorkspace.open(
@@ -745,15 +719,14 @@ export class InputHandler {
745
719
  );
746
720
  }
747
721
  this.agentWorkspace.cycleCategory(direction);
748
- this.panelFocused = false;
749
722
  this.indicatorFocused = false;
750
723
  this.requestRender();
751
724
  }
752
725
 
753
- public handlePanelIntegrationAction(activePanel: Panel | null, key: string): void {
754
- runPanelIntegrationAction(this.uiServices.shell.panelManager, activePanel, key, this.commandContext);
755
- }
756
-
726
+ /**
727
+ * Word-wrap a single line to fit within maxW columns.
728
+ * Breaks at spaces; words wider than maxW are force-broken.
729
+ */
757
730
  public wordWrapLine(line: string, maxW: number): string[] {
758
731
  return wordWrapLine(line, maxW);
759
732
  }