@pellux/goodvibes-agent 1.1.3 → 1.1.5

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.
@@ -1,24 +1,26 @@
1
1
  import type { MemoryApi } from '@pellux/goodvibes-sdk/platform/knowledge';
2
2
  import type { MemoryRecord } from '@pellux/goodvibes-sdk/platform/state';
3
+ import type { ConfigSetting } from '@pellux/goodvibes-sdk/platform/config';
3
4
  import type { ShellPathService } from '@/runtime/index.ts';
4
5
  import type { CommandContext } from './command-registry.ts';
5
6
  import { AgentNoteRegistry } from '../agent/note-registry.ts';
6
7
  import { AgentPersonaRegistry } from '../agent/persona-registry.ts';
7
8
  import { AgentRoutineRegistry } from '../agent/routine-registry.ts';
8
- import { createAgentRuntimeProfile, type AgentRuntimeProfileInfo } from '../agent/runtime-profile.ts';
9
+ import type { AgentRuntimeProfileInfo } from '../agent/runtime-profile.ts';
9
10
  import { AgentSkillRegistry } from '../agent/skill-registry.ts';
10
11
  import { activateAgentWorkspaceSelection } from './agent-workspace-activation.ts';
11
12
  import { AGENT_WORKSPACE_CATEGORIES } from './agent-workspace-categories.ts';
12
13
  import { buildAgentWorkspaceCommandEditorSubmission, isAgentWorkspaceCommandEditorKind } from './agent-workspace-command-editor.ts';
13
- import { createDeleteEditor, editorCategoryId, isAffirmative, splitList } from './agent-workspace-editors.ts';
14
- import { createAgentWorkspaceLearnedBehavior } from './agent-workspace-learned-behavior.ts';
14
+ import { createDeleteEditor, editorCategoryId } from './agent-workspace-editors.ts';
15
+ import { submitAgentWorkspaceLocalRegistryEditor } from './agent-workspace-local-editor-submission.ts';
15
16
  import { clampAgentWorkspaceLocalLibrarySelection, moveAgentWorkspaceLocalLibraryItemSelection, selectedAgentWorkspaceLocalLibraryItem, type AgentWorkspaceLocalSelectionIndexes } from './agent-workspace-local-selection.ts';
16
17
  import { applyAgentWorkspaceLocalLibraryOperation } from './agent-workspace-local-operations.ts';
17
18
  import { deleteAgentWorkspaceMemoryEditor, submitAgentWorkspaceMemoryEditor } from './agent-workspace-memory-editor.ts';
18
19
  import { jumpAgentWorkspaceSelection, moveAgentWorkspaceSelection, selectAgentWorkspaceCategory } from './agent-workspace-navigation.ts';
19
- import { buildAgentWorkspaceRequirements } from './agent-workspace-requirements.ts';
20
20
  import { appendAgentWorkspaceActionSearchText, backspaceAgentWorkspaceActionSearch, beginAgentWorkspaceActionSearch, clearAgentWorkspaceActionSearch, commitAgentWorkspaceActionSearchSelection, searchAgentWorkspaceActions } from './agent-workspace-search.ts';
21
+ import { agentWorkspaceSettingSchema, applyAgentWorkspaceSettingValue, buildAgentWorkspaceSettingActionEffect, importAgentWorkspaceTuiSettings, isAgentWorkspaceActionVisible } from './agent-workspace-settings.ts';
21
22
  import { buildAgentWorkspaceRuntimeSnapshot } from './agent-workspace-snapshot.ts';
23
+ import { submitAgentWorkspaceSubscriptionLoginFinishEditor, submitAgentWorkspaceSubscriptionLoginStartEditor, submitAgentWorkspaceSubscriptionLogoutEditor } from './agent-workspace-subscription-editor.ts';
22
24
  import type { AgentWorkspaceAction, AgentWorkspaceActionResult, AgentWorkspaceActionSearchResult, AgentWorkspaceCategory, AgentWorkspaceCommandDispatcher, AgentWorkspaceEditorField, AgentWorkspaceFocusPane, AgentWorkspaceLocalEditor, AgentWorkspaceLocalEditorKind, AgentWorkspaceLocalLibraryItem, AgentWorkspaceLocalOperation, AgentWorkspacePromptDispatcher, AgentWorkspaceRuntimeSnapshot } from './agent-workspace-types.ts';
23
25
  import { writeOnboardingCheckMarker, writeOnboardingCompletionMarker } from '../runtime/onboarding/index.ts';
24
26
 
@@ -39,14 +41,7 @@ export class AgentWorkspace {
39
41
  public localEditor: AgentWorkspaceLocalEditor | null = null;
40
42
  public actionSearchActive = false;
41
43
  public actionSearchQuery = '';
42
- public readonly selectedLibraryItemIndexes: AgentWorkspaceLocalSelectionIndexes = {
43
- memory: 0,
44
- note: 0,
45
- persona: 0,
46
- skill: 0,
47
- routine: 0,
48
- profile: 0,
49
- };
44
+ public readonly selectedLibraryItemIndexes: AgentWorkspaceLocalSelectionIndexes = { memory: 0, note: 0, persona: 0, skill: 0, routine: 0, profile: 0 };
50
45
  private context: CommandContext | null = null;
51
46
  private dispatchCommand: AgentWorkspaceCommandDispatcher | null = null;
52
47
  private dispatchPrompt: AgentWorkspacePromptDispatcher | null = null;
@@ -63,7 +58,10 @@ export class AgentWorkspace {
63
58
  this.localEditor = null;
64
59
  this.actionSearchActive = false;
65
60
  this.actionSearchQuery = '';
66
- if (categoryId && !this.selectCategory(categoryId)) {
61
+ if (!categoryId) {
62
+ this.selectedCategoryIndex = 0;
63
+ this.selectedActionIndex = 0;
64
+ } else if (!this.selectCategory(categoryId)) {
67
65
  const normalized = categoryId.trim();
68
66
  this.status = `Unknown Agent workspace area: ${normalized}`;
69
67
  this.lastActionResult = {
@@ -98,7 +96,7 @@ export class AgentWorkspace {
98
96
 
99
97
  get actions(): readonly AgentWorkspaceAction[] {
100
98
  if (this.actionSearchActive) return this.actionSearchResults.map((result) => result.action);
101
- return this.selectedCategory.actions;
99
+ return this.selectedCategory.actions.filter((action) => isAgentWorkspaceActionVisible(this.context, action));
102
100
  }
103
101
 
104
102
  get selectedAction(): AgentWorkspaceAction | null {
@@ -111,7 +109,12 @@ export class AgentWorkspace {
111
109
  }
112
110
 
113
111
  get actionSearchResults(): readonly AgentWorkspaceActionSearchResult[] {
114
- return this.actionSearchActive ? searchAgentWorkspaceActions(this.categories, this.actionSearchQuery) : [];
112
+ if (!this.actionSearchActive) return [];
113
+ const categories = this.categories.map((category) => ({
114
+ ...category,
115
+ actions: category.actions.filter((action) => isAgentWorkspaceActionVisible(this.context, action)),
116
+ }));
117
+ return searchAgentWorkspaceActions(categories, this.actionSearchQuery);
115
118
  }
116
119
 
117
120
  get selectedActionSearchResult(): AgentWorkspaceActionSearchResult | null {
@@ -178,11 +181,7 @@ export class AgentWorkspace {
178
181
  }
179
182
  this.runtimeSnapshot = buildAgentWorkspaceRuntimeSnapshot(this.context);
180
183
  this.status = 'Runtime context refreshed.';
181
- this.lastActionResult = {
182
- kind: 'refreshed',
183
- title: 'Runtime context refreshed',
184
- detail: 'Provider, model, session, local memory, runtime endpoint, and Agent knowledge route posture were re-read from the live command context.',
185
- };
184
+ this.lastActionResult = { kind: 'refreshed', title: 'Runtime context refreshed', detail: 'Provider, model, session, local memory, runtime endpoint, and Agent knowledge route posture were re-read from the live command context.' };
186
185
  }
187
186
 
188
187
  cancelLocalEditor(): void {
@@ -321,32 +320,86 @@ export class AgentWorkspace {
321
320
  }
322
321
 
323
322
  try {
324
- const marker = {
325
- scope: 'user',
326
- source: 'wizard',
327
- mode: 'new',
328
- workspaceRoot: shellPaths.workingDirectory,
329
- } as const;
323
+ const marker = { scope: 'user', source: 'wizard', mode: 'new', workspaceRoot: shellPaths.workingDirectory } as const;
330
324
  writeOnboardingCheckMarker(shellPaths, marker);
331
325
  writeOnboardingCompletionMarker(shellPaths, marker);
332
326
  this.status = 'Onboarding applied and closed.';
333
- this.lastActionResult = {
334
- kind: 'refreshed',
335
- title: 'Onboarding complete',
336
- detail: 'Saved the user onboarding completion marker. Future normal launches start in the main conversation.',
337
- safety: 'safe',
338
- };
327
+ this.lastActionResult = { kind: 'refreshed', title: 'Onboarding complete', detail: 'Saved the user onboarding completion marker. Future normal launches start in the main conversation.', safety: 'safe' };
339
328
  if (!this.context?.dismissAgentWorkspace?.()) this.close();
340
329
  } catch (error) {
341
330
  const detail = error instanceof Error ? error.message : String(error);
342
331
  this.status = 'Onboarding completion failed.';
332
+ this.lastActionResult = { kind: 'error', title: 'Onboarding completion failed', detail, safety: 'safe' };
333
+ }
334
+ }
335
+
336
+ openModelPickerAction(action: AgentWorkspaceAction, requestRender?: () => void): void {
337
+ const target = action.modelPickerTarget ?? 'main';
338
+ const opened = action.modelPickerFlow === 'model'
339
+ ? this.context?.openModelPickerWithTarget?.(target)
340
+ : this.context?.openProviderModelPickerWithTarget?.(target);
341
+ if (!opened) {
342
+ this.status = 'Model picker is unavailable.';
343
343
  this.lastActionResult = {
344
344
  kind: 'error',
345
- title: 'Onboarding completion failed',
346
- detail,
347
- safety: 'safe',
345
+ title: 'Model picker unavailable',
346
+ detail: 'This runtime cannot open the model picker from Agent workspace.',
347
+ safety: action.safety,
348
348
  };
349
+ requestRender?.();
350
+ return;
349
351
  }
352
+ this.status = `Opening ${action.label}.`;
353
+ this.lastActionResult = {
354
+ kind: 'dispatched',
355
+ title: `Opening ${action.label}`,
356
+ detail: 'Opened the shared provider/model picker for this setup target.',
357
+ safety: action.safety,
358
+ };
359
+ requestRender?.();
360
+ }
361
+
362
+ openSettingsModalAction(action: AgentWorkspaceAction, requestRender?: () => void): void {
363
+ if (!this.context?.openSettingsModal) {
364
+ this.status = 'Settings are unavailable.';
365
+ this.lastActionResult = {
366
+ kind: 'error',
367
+ title: 'Settings unavailable',
368
+ detail: 'This runtime cannot open settings from Agent workspace.',
369
+ safety: action.safety,
370
+ };
371
+ requestRender?.();
372
+ return;
373
+ }
374
+ this.context.openSettingsModal(action.settingsTarget);
375
+ this.status = `Opening ${action.label}.`;
376
+ this.lastActionResult = {
377
+ kind: 'dispatched',
378
+ title: `Opening ${action.label}`,
379
+ detail: 'Opened the shared settings surface for this setup area.',
380
+ safety: action.safety,
381
+ };
382
+ requestRender?.();
383
+ }
384
+
385
+ applySettingAction(action: AgentWorkspaceAction, requestRender?: () => void): void {
386
+ const effect = buildAgentWorkspaceSettingActionEffect(this.context, action);
387
+ if (effect.kind === 'result') {
388
+ this.status = effect.status;
389
+ this.lastActionResult = effect.result;
390
+ return;
391
+ }
392
+ if (effect.kind === 'editor') {
393
+ this.localEditor = effect.editor;
394
+ this.status = effect.status;
395
+ this.lastActionResult = effect.result;
396
+ return;
397
+ }
398
+ void this.applySettingValue(effect.setting, effect.value, requestRender);
399
+ }
400
+
401
+ importTuiSettings(requestRender?: () => void): void {
402
+ void this.importTuiSettingsAsync(requestRender);
350
403
  }
351
404
 
352
405
  private selectedItemForOperation(operation: AgentWorkspaceLocalOperation): AgentWorkspaceLocalLibraryItem | null {
@@ -357,6 +410,24 @@ export class AgentWorkspace {
357
410
  return this.selectedLocalLibraryItem('routine');
358
411
  }
359
412
 
413
+ private async applySettingValue(setting: ConfigSetting, value: unknown, requestRender?: () => void): Promise<void> {
414
+ const outcome = await applyAgentWorkspaceSettingValue(this.context, setting, value);
415
+ this.runtimeSnapshot = this.context ? buildAgentWorkspaceRuntimeSnapshot(this.context) : this.runtimeSnapshot;
416
+ this.clampSelection();
417
+ this.status = outcome.status;
418
+ this.lastActionResult = outcome.result;
419
+ requestRender?.();
420
+ }
421
+
422
+ private async importTuiSettingsAsync(requestRender?: () => void): Promise<void> {
423
+ const outcome = await importAgentWorkspaceTuiSettings(this.context);
424
+ this.runtimeSnapshot = outcome.runtimeSnapshot ?? this.runtimeSnapshot;
425
+ this.clampSelection();
426
+ this.status = outcome.status;
427
+ this.lastActionResult = outcome.result;
428
+ requestRender?.();
429
+ }
430
+
360
431
  private memoryApi(): MemoryApi {
361
432
  const memory = this.context?.clients?.agentKnowledgeApi?.memory;
362
433
  if (!memory) throw new Error('Agent Memory API is unavailable; refusing default knowledge or non-Agent fallback.');
@@ -424,11 +495,37 @@ export class AgentWorkspace {
424
495
  this.status = `${missing.label} is required.`;
425
496
  return;
426
497
  }
498
+ if (editor.kind === 'subscription-login-start') {
499
+ void submitAgentWorkspaceSubscriptionLoginStartEditor(this, editor, this.context, (id) => this.editorField(id)).finally(() => requestRender?.());
500
+ return;
501
+ }
502
+ if (editor.kind === 'subscription-login-finish') {
503
+ void submitAgentWorkspaceSubscriptionLoginFinishEditor(this, editor, this.context, (id) => this.editorField(id)).finally(() => requestRender?.());
504
+ return;
505
+ }
506
+ if (editor.kind === 'subscription-logout') {
507
+ submitAgentWorkspaceSubscriptionLogoutEditor(this, editor, this.context, (id) => this.editorField(id));
508
+ requestRender?.();
509
+ return;
510
+ }
427
511
  if (isAgentWorkspaceCommandEditorKind(editor.kind)) {
428
512
  this.submitCommandEditor(editor);
429
513
  requestRender?.();
430
514
  return;
431
515
  }
516
+ if (editor.kind === 'setting-set') {
517
+ const setting = editor.recordId ? agentWorkspaceSettingSchema(this.context, editor.recordId) : null;
518
+ if (!setting) {
519
+ this.localEditor = { ...editor, message: 'Unknown setting; cannot save.' };
520
+ this.status = 'Unknown setting; cannot save.';
521
+ requestRender?.();
522
+ return;
523
+ }
524
+ const value = this.editorField('value');
525
+ this.localEditor = null;
526
+ void this.applySettingValue(setting, value, requestRender);
527
+ return;
528
+ }
432
529
  if (editor.kind === 'memory') {
433
530
  if (editor.mode === 'delete') {
434
531
  try {
@@ -461,135 +558,13 @@ export class AgentWorkspace {
461
558
  return;
462
559
  }
463
560
  try {
464
- if (editor.mode === 'delete') {
465
- this.submitLocalDeleteEditor(shellPaths, editor);
466
- return;
467
- }
468
- if (editor.kind === 'learned-behavior') {
469
- const created = createAgentWorkspaceLearnedBehavior(shellPaths, {
470
- target: this.learnedBehaviorTarget(),
471
- name: this.editorField('name'),
472
- description: this.editorField('description'),
473
- notes: this.editorField('notes'),
474
- tags: splitList(this.editorField('tags')),
475
- triggers: splitList(this.editorField('triggers')),
476
- enable: isAffirmative(this.editorField('enable')),
477
- });
478
- this.finishLocalEditor(created.kind, created.id, created.name, 'Created');
479
- } else if (editor.kind === 'profile') {
480
- const template = this.editorField('template');
481
- const templateId = template && template.toLowerCase() !== 'none' ? template : undefined;
482
- const profile = createAgentRuntimeProfile(shellPaths.homeDirectory, this.editorField('name'), {
483
- ...(templateId ? { templateId } : {}),
484
- });
485
- this.finishProfileEditor(profile);
486
- } else if (editor.kind === 'note') {
487
- const registry = AgentNoteRegistry.fromShellPaths(shellPaths);
488
- if (editor.mode === 'update' && editor.recordId) {
489
- const updated = registry.update(editor.recordId, {
490
- title: this.editorField('title'),
491
- body: this.editorField('body'),
492
- sourceUrl: this.editorField('sourceUrl'),
493
- tags: splitList(this.editorField('tags')),
494
- provenance: 'Workspace',
495
- });
496
- this.finishLocalEditor(editor.kind, updated.id, updated.title, 'Updated');
497
- return;
498
- }
499
- const created = registry.create({
500
- title: this.editorField('title'),
501
- body: this.editorField('body'),
502
- sourceUrl: this.editorField('sourceUrl'),
503
- tags: splitList(this.editorField('tags')),
504
- source: 'user',
505
- provenance: 'Workspace',
506
- });
507
- this.finishLocalEditor(editor.kind, created.id, created.title, 'Created');
508
- } else if (editor.kind === 'persona') {
509
- const registry = AgentPersonaRegistry.fromShellPaths(shellPaths);
510
- if (editor.mode === 'update' && editor.recordId) {
511
- const wasActive = registry.snapshot().activePersonaId === editor.recordId;
512
- const updated = registry.update(editor.recordId, {
513
- name: this.editorField('name'),
514
- description: this.editorField('description'),
515
- body: this.editorField('body'),
516
- tags: splitList(this.editorField('tags')),
517
- triggers: splitList(this.editorField('triggers')),
518
- provenance: 'Workspace',
519
- });
520
- if (isAffirmative(this.editorField('activate'))) registry.setActive(updated.id);
521
- else if (wasActive) registry.clearActive();
522
- this.finishLocalEditor(editor.kind, updated.id, updated.name, 'Updated');
523
- return;
524
- }
525
- const created = registry.create({
526
- name: this.editorField('name'),
527
- description: this.editorField('description'),
528
- body: this.editorField('body'),
529
- tags: splitList(this.editorField('tags')),
530
- triggers: splitList(this.editorField('triggers')),
531
- source: 'user',
532
- provenance: 'Workspace',
533
- });
534
- if (isAffirmative(this.editorField('activate'))) registry.setActive(created.id);
535
- this.finishLocalEditor(editor.kind, created.id, created.name, 'Created');
536
- } else if (editor.kind === 'skill') {
537
- const registry = AgentSkillRegistry.fromShellPaths(shellPaths);
538
- if (editor.mode === 'update' && editor.recordId) {
539
- const updated = registry.update(editor.recordId, {
540
- name: this.editorField('name'),
541
- description: this.editorField('description'),
542
- procedure: this.editorField('procedure'),
543
- triggers: splitList(this.editorField('triggers')),
544
- tags: splitList(this.editorField('tags')),
545
- requirements: buildAgentWorkspaceRequirements((id) => this.editorField(id)),
546
- provenance: 'Workspace',
547
- });
548
- registry.setEnabled(updated.id, isAffirmative(this.editorField('enabled')));
549
- this.finishLocalEditor(editor.kind, updated.id, updated.name, 'Updated');
550
- return;
551
- }
552
- const created = registry.create({
553
- name: this.editorField('name'),
554
- description: this.editorField('description'),
555
- procedure: this.editorField('procedure'),
556
- triggers: splitList(this.editorField('triggers')),
557
- tags: splitList(this.editorField('tags')),
558
- requirements: buildAgentWorkspaceRequirements((id) => this.editorField(id)),
559
- enabled: isAffirmative(this.editorField('enabled')),
560
- source: 'user',
561
- provenance: 'Workspace',
562
- });
563
- this.finishLocalEditor(editor.kind, created.id, created.name, 'Created');
564
- } else {
565
- const registry = AgentRoutineRegistry.fromShellPaths(shellPaths);
566
- if (editor.mode === 'update' && editor.recordId) {
567
- const updated = registry.update(editor.recordId, {
568
- name: this.editorField('name'),
569
- description: this.editorField('description'),
570
- steps: this.editorField('steps'),
571
- triggers: splitList(this.editorField('triggers')),
572
- tags: splitList(this.editorField('tags')),
573
- requirements: buildAgentWorkspaceRequirements((id) => this.editorField(id)),
574
- provenance: 'Workspace',
575
- });
576
- registry.setEnabled(updated.id, isAffirmative(this.editorField('enabled')));
577
- this.finishLocalEditor(editor.kind, updated.id, updated.name, 'Updated');
578
- return;
579
- }
580
- const created = registry.create({
581
- name: this.editorField('name'),
582
- description: this.editorField('description'),
583
- steps: this.editorField('steps'),
584
- triggers: splitList(this.editorField('triggers')),
585
- tags: splitList(this.editorField('tags')),
586
- requirements: buildAgentWorkspaceRequirements((id) => this.editorField(id)),
587
- enabled: isAffirmative(this.editorField('enabled')),
588
- source: 'user',
589
- provenance: 'Workspace',
590
- });
591
- this.finishLocalEditor(editor.kind, created.id, created.name, 'Created');
592
- }
561
+ submitAgentWorkspaceLocalRegistryEditor(shellPaths, editor, {
562
+ readField: (id) => this.editorField(id),
563
+ learnedBehaviorTarget: () => this.learnedBehaviorTarget(),
564
+ submitDeleteEditor: () => this.submitLocalDeleteEditor(shellPaths, editor),
565
+ finishLocalEditor: (kind, id, name, verb) => this.finishLocalEditor(kind, id, name, verb),
566
+ finishProfileEditor: (profile) => this.finishProfileEditor(profile),
567
+ });
593
568
  } catch (error) {
594
569
  const detail = error instanceof Error ? error.message : String(error);
595
570
  this.localEditor = { ...editor, message: detail };
@@ -131,6 +131,18 @@ export function handleModalTokenRoutes(state: ModalTokenRouteState, token: Input
131
131
  return withState(state, true);
132
132
  }
133
133
 
134
+ if (handleModelPickerToken({
135
+ modelPicker: state.modelPicker,
136
+ modalStack: state.modalStack,
137
+ commandContext: state.commandContext,
138
+ getViewportHeight: state.getViewportHeight,
139
+ requestRender: state.requestRender,
140
+ handleEscape: state.handleEscape,
141
+ onModelPickerCommit: state.onModelPickerCommit,
142
+ }, token)) {
143
+ return withState(state, true);
144
+ }
145
+
134
146
  if (handleSettingsModalToken({
135
147
  settingsModal: state.settingsModal,
136
148
  commandContext: state.commandContext,
@@ -200,18 +212,6 @@ export function handleModalTokenRoutes(state: ModalTokenRouteState, token: Input
200
212
  return withState(state, true, historyState);
201
213
  }
202
214
 
203
- if (handleModelPickerToken({
204
- modelPicker: state.modelPicker,
205
- modalStack: state.modalStack,
206
- commandContext: state.commandContext,
207
- getViewportHeight: state.getViewportHeight,
208
- requestRender: state.requestRender,
209
- handleEscape: state.handleEscape,
210
- onModelPickerCommit: state.onModelPickerCommit,
211
- }, token)) {
212
- return withState(state, true);
213
- }
214
-
215
215
  if (handleLiveTailToken({
216
216
  liveTailModal: state.liveTailModal,
217
217
  processModal: state.processModal,
@@ -336,7 +336,8 @@ export class InputHandler {
336
336
  public handleBlockToggle(): void { handleBlockToggleForHandler(this); }
337
337
  public handleCtrlC(): void { handleCtrlCForHandler(this); }
338
338
  public modalOpened(name: string): void {
339
- if (name !== 'agentWorkspace' && this.agentWorkspace.active) {
339
+ const keepAgentWorkspaceUnderlay = name === 'modelPicker' || name === 'settings';
340
+ if (name !== 'agentWorkspace' && !keepAgentWorkspaceUnderlay && this.agentWorkspace.active) {
340
341
  this.closeAgentWorkspaceModal();
341
342
  }
342
343
  modalOpenedForHandler(this, name);
package/src/main.ts CHANGED
@@ -15,6 +15,8 @@ import { PermissionPromptUI } from './permissions/prompt.ts';
15
15
  import { CommandRegistry } from './input/command-registry.ts';
16
16
  import type { CommandContext } from './input/command-registry.ts';
17
17
  import { renderProcessIndicator } from './renderer/process-indicator.ts';
18
+ import { renderModelWorkspace } from './renderer/model-workspace.ts';
19
+ import { renderSettingsModal } from './renderer/settings-modal.ts';
18
20
  import { registerBuiltinCommands } from './input/commands.ts';
19
21
  import { ScheduleManager } from '@pellux/goodvibes-sdk/platform/tools';
20
22
  import { InputHistory } from './input/input-history.ts';
@@ -40,7 +42,7 @@ import {
40
42
  } from '@/runtime/index.ts';
41
43
  import type { SessionSnapshot } from '@/runtime/index.ts';
42
44
  import { handleBlockingShellInput, type PendingPermissionState } from './shell/blocking-input.ts';
43
- import { createAgentWorkspaceFullscreenComposite } from './shell/agent-workspace-fullscreen.ts';
45
+ import { createAgentWorkspaceFullscreenComposite, createFullscreenCompositeFromLines } from './shell/agent-workspace-fullscreen.ts';
44
46
  import { getTerminalSize } from './shell/terminal-size.ts';
45
47
  import { wireShellUiOpeners } from './shell/ui-openers.ts';
46
48
  import { deriveComposerState } from './core/composer-state.ts';
@@ -477,6 +479,14 @@ async function main() {
477
479
  input.setPanelMouseLayout(null);
478
480
  activeConversationWidth = width;
479
481
  conversation.setSplashSuppressed(true);
482
+ if (input.modelPicker.active) {
483
+ compositor.composite(createFullscreenCompositeFromLines(renderModelWorkspace(input.modelPicker, width, height), width, height));
484
+ return;
485
+ }
486
+ if (input.settingsModal.active) {
487
+ compositor.composite(createFullscreenCompositeFromLines(renderSettingsModal(input.settingsModal, width, height), width, height));
488
+ return;
489
+ }
480
490
  compositor.composite(createAgentWorkspaceFullscreenComposite(input.agentWorkspace, width, height));
481
491
  return;
482
492
  }
@@ -709,7 +719,6 @@ async function main() {
709
719
  streamTokenSpeed = elapsed > 0 ? streamDeltaCount / elapsed : 0;
710
720
  }));
711
721
 
712
- // --- Terminal setup ---
713
722
  stdin.setRawMode(true);
714
723
  stdin.resume();
715
724
  stdin.setEncoding('utf8');
@@ -748,11 +757,9 @@ async function main() {
748
757
  process.on('unhandledRejection', unhandledRejectionHandler);
749
758
  stdout.on('resize', resizeHandler);
750
759
 
751
- // Initial render
752
760
  conversation.rebuildHistory();
753
761
  render();
754
762
 
755
- // --- Crash recovery check ---
756
763
  const recoveryInfo = checkRecoveryFile({ workingDirectory: workingDir, homeDirectory });
757
764
  if (recoveryInfo) {
758
765
  systemMessageRouter.high(`[Recovery] Found unsaved session from ${new Date(recoveryInfo.timestamp).toLocaleString()}. Title: "${recoveryInfo.title}". Press Ctrl+R to restore, Esc to discard, or start typing to ignore it.`);
@@ -763,7 +770,6 @@ async function main() {
763
770
  recoveryPending = true;
764
771
  }
765
772
 
766
- // --- Auto-save to recovery file every 60s ---
767
773
  recoveryInterval = setInterval(() => {
768
774
  const snapshot = buildCurrentSessionSnapshot();
769
775
  writeRecoveryFile(
@@ -773,9 +779,7 @@ async function main() {
773
779
  { workingDirectory: workingDir, homeDirectory },
774
780
  );
775
781
  }, 60_000);
776
-
777
782
  }
778
-
779
783
  main().catch((err: unknown) => {
780
784
  const detail = formatFatalStartupErrorForLog(err);
781
785
  try {