@pellux/goodvibes-agent 0.1.117 → 1.0.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.
- package/CHANGELOG.md +13 -4
- package/README.md +35 -14
- package/bin/goodvibes-agent.ts +16 -2
- package/dist/package/main.js +176073 -170980
- package/docs/README.md +11 -5
- package/docs/channels-remote-and-api.md +50 -0
- package/docs/connected-host.md +3 -3
- package/docs/getting-started.md +29 -15
- package/docs/knowledge-artifacts-and-multimodal.md +91 -0
- package/docs/project-planning.md +79 -0
- package/docs/providers-and-routing.md +46 -0
- package/docs/release-and-publishing.md +44 -9
- package/docs/tools-and-commands.md +123 -0
- package/docs/voice-and-live-tts.md +51 -0
- package/package.json +2 -5
- package/src/agent/channel-delivery.ts +201 -0
- package/src/agent/media-generation.ts +159 -0
- package/src/agent/memory-prompt.ts +0 -1
- package/src/agent/note-registry.ts +329 -0
- package/src/agent/operator-actions.ts +343 -0
- package/src/agent/persona-registry.ts +15 -14
- package/src/agent/record-labels.ts +107 -0
- package/src/agent/reminder-schedule-format.ts +33 -24
- package/src/agent/reminder-schedule.ts +26 -25
- package/src/agent/routine-registry.ts +13 -12
- package/src/agent/routine-schedule-args.ts +2 -1
- package/src/agent/routine-schedule-format.ts +77 -53
- package/src/agent/routine-schedule-promotion.ts +34 -32
- package/src/agent/routine-schedule-receipts.ts +28 -26
- package/src/agent/runtime-profile-starters.ts +2 -2
- package/src/agent/runtime-profile.ts +18 -17
- package/src/agent/skill-registry.ts +25 -24
- package/src/cli/agent-knowledge-args.ts +5 -1
- package/src/cli/agent-knowledge-command.ts +39 -33
- package/src/cli/agent-knowledge-format.ts +80 -67
- package/src/cli/agent-knowledge-methods.ts +1 -1
- package/src/cli/agent-knowledge-runtime.ts +32 -26
- package/src/cli/bundle-command.ts +13 -8
- package/src/cli/config-overrides.ts +37 -36
- package/src/cli/external-runtime.ts +10 -4
- package/src/cli/help.ts +29 -11
- package/src/cli/local-library-command.ts +134 -68
- package/src/cli/management-commands.ts +98 -62
- package/src/cli/management.ts +52 -26
- package/src/cli/memory-command.ts +66 -32
- package/src/cli/parser.ts +37 -24
- package/src/cli/profiles-command.ts +52 -35
- package/src/cli/provider-auth-routes.ts +2 -1
- package/src/cli/routines-command.ts +44 -36
- package/src/cli/service-posture.ts +6 -6
- package/src/cli/status.ts +46 -121
- package/src/core/conversation-message-snapshot.ts +131 -0
- package/src/input/agent-workspace-activation.ts +33 -7
- package/src/input/agent-workspace-basic-command-editor-submission.ts +7 -3
- package/src/input/agent-workspace-basic-command-editors.ts +30 -10
- package/src/input/agent-workspace-categories.ts +276 -64
- package/src/input/agent-workspace-channel-command-editor-submission.ts +34 -0
- package/src/input/agent-workspace-channel-command-editors.ts +23 -5
- package/src/input/agent-workspace-channels.ts +35 -2
- package/src/input/agent-workspace-command-editor.ts +18 -2
- package/src/input/agent-workspace-config-reader.ts +16 -0
- package/src/input/agent-workspace-delegation-editor-submission.ts +1 -1
- package/src/input/agent-workspace-editors.ts +140 -2
- package/src/input/agent-workspace-knowledge-query-editor.ts +1 -1
- package/src/input/agent-workspace-learned-behavior.ts +2 -2
- package/src/input/agent-workspace-library-command-editor-submission.ts +1 -1
- package/src/input/agent-workspace-library-command-editors.ts +2 -2
- package/src/input/agent-workspace-local-operations.ts +218 -0
- package/src/input/agent-workspace-local-selection.ts +75 -0
- package/src/input/agent-workspace-media-command-editor-submission.ts +62 -0
- package/src/input/agent-workspace-media-command-editors.ts +27 -0
- package/src/input/agent-workspace-memory-command-editors.ts +1 -1
- package/src/input/agent-workspace-memory-editor.ts +2 -2
- package/src/input/agent-workspace-navigation.ts +38 -2
- package/src/input/agent-workspace-notify-editor-submission.ts +16 -2
- package/src/input/agent-workspace-operations-command-editor-submission.ts +63 -1
- package/src/input/agent-workspace-operations-command-editors.ts +80 -3
- package/src/input/agent-workspace-panel-route.ts +43 -0
- package/src/input/agent-workspace-reminder-schedule-editor.ts +1 -1
- package/src/input/agent-workspace-routine-schedule-editor.ts +2 -2
- package/src/input/agent-workspace-search.ts +169 -0
- package/src/input/agent-workspace-setup.ts +22 -11
- package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +6 -6
- package/src/input/agent-workspace-skill-bundle-command-editors.ts +10 -10
- package/src/input/agent-workspace-snapshot.ts +112 -13
- package/src/input/agent-workspace-task-command-editors.ts +4 -4
- package/src/input/agent-workspace-token.ts +18 -2
- package/src/input/agent-workspace-types.ts +92 -4
- package/src/input/agent-workspace-voice-media.ts +3 -6
- package/src/input/agent-workspace-web-research-editor.ts +104 -0
- package/src/input/agent-workspace.ts +136 -208
- package/src/input/command-registry.ts +4 -1
- package/src/input/commands/agent-runtime-profile-runtime.ts +45 -41
- package/src/input/commands/agent-skills-runtime.ts +83 -70
- package/src/input/commands/agent-workspace-runtime.ts +23 -7
- package/src/input/commands/brief-runtime.ts +25 -24
- package/src/input/commands/channels-runtime.ts +145 -31
- package/src/input/commands/delegation-runtime.ts +29 -23
- package/src/input/commands/experience-runtime.ts +6 -4
- package/src/input/commands/guidance-runtime.ts +4 -4
- package/src/input/commands/health-runtime.ts +140 -115
- package/src/input/commands/knowledge.ts +57 -56
- package/src/input/commands/local-provider-runtime.ts +36 -18
- package/src/input/commands/local-runtime.ts +138 -16
- package/src/input/commands/local-setup-review.ts +7 -7
- package/src/input/commands/mcp-runtime.ts +56 -35
- package/src/input/commands/notify-runtime.ts +38 -9
- package/src/input/commands/operator-actions-runtime.ts +138 -0
- package/src/input/commands/operator-runtime.ts +6 -17
- package/src/input/commands/personas-runtime.ts +45 -30
- package/src/input/commands/planning-runtime.ts +1 -1
- package/src/input/commands/platform-access-runtime.ts +29 -29
- package/src/input/commands/provider-accounts-runtime.ts +60 -39
- package/src/input/commands/qrcode-runtime.ts +45 -6
- package/src/input/commands/recall-bundle.ts +11 -11
- package/src/input/commands/recall-capture.ts +13 -11
- package/src/input/commands/recall-query.ts +29 -21
- package/src/input/commands/recall-review.ts +2 -1
- package/src/input/commands/routines-runtime.ts +59 -43
- package/src/input/commands/schedule-runtime.ts +33 -20
- package/src/input/commands/security-runtime.ts +4 -4
- package/src/input/commands/session-content.ts +80 -78
- package/src/input/commands/session-workflow.ts +132 -93
- package/src/input/commands/session.ts +3 -174
- package/src/input/commands/shell-core.ts +32 -17
- package/src/input/commands/subscription-runtime.ts +53 -179
- package/src/input/commands/tasks-runtime.ts +20 -20
- package/src/input/commands/work-plan-runtime.ts +33 -8
- package/src/input/commands.ts +2 -2
- package/src/input/feed-context-factory.ts +2 -1
- package/src/input/file-picker.ts +3 -2
- package/src/input/handler-command-route.ts +4 -7
- package/src/input/handler-content-actions.ts +89 -1
- package/src/input/handler-feed-routes.ts +19 -12
- package/src/input/handler-feed.ts +6 -3
- package/src/input/handler-interactions.ts +7 -5
- package/src/input/handler-modal-stack.ts +3 -3
- package/src/input/handler-onboarding.ts +24 -80
- package/src/input/handler-shortcuts.ts +15 -4
- package/src/input/handler.ts +47 -17
- package/src/input/input-history.ts +5 -6
- package/src/input/keybindings.ts +22 -11
- package/src/input/onboarding/onboarding-wizard-apply.ts +13 -0
- package/src/input/onboarding/onboarding-wizard-constants.ts +1 -0
- package/src/input/onboarding/onboarding-wizard-operator-steps.ts +147 -57
- package/src/input/onboarding/onboarding-wizard-steps.ts +46 -29
- package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
- package/src/input/onboarding/onboarding-wizard.ts +68 -0
- package/src/input/profile-picker-modal.ts +31 -12
- package/src/input/session-picker-modal.ts +21 -4
- package/src/input/settings-modal-behavior.ts +0 -3
- package/src/input/settings-modal-subscriptions.ts +3 -3
- package/src/input/settings-modal-types.ts +2 -13
- package/src/input/settings-modal.ts +6 -52
- package/src/input/submission-intent.ts +0 -1
- package/src/input/submission-router.ts +3 -3
- package/src/main.ts +18 -8
- package/src/panels/approval-panel.ts +8 -8
- package/src/panels/automation-control-panel.ts +2 -2
- package/src/panels/base-panel.ts +1 -1
- package/src/panels/builtin/agent.ts +1 -1
- package/src/panels/builtin/operations.ts +1 -10
- package/src/panels/builtin/session.ts +9 -9
- package/src/panels/builtin/shared.ts +2 -2
- package/src/panels/cost-tracker-panel.ts +1 -1
- package/src/panels/docs-panel.ts +5 -3
- package/src/panels/index.ts +0 -1
- package/src/panels/knowledge-panel.ts +6 -5
- package/src/panels/memory-panel.ts +7 -6
- package/src/panels/panel-list-panel.ts +36 -80
- package/src/panels/project-planning-panel.ts +18 -11
- package/src/panels/provider-account-snapshot.ts +51 -25
- package/src/panels/provider-accounts-panel.ts +33 -18
- package/src/panels/provider-health-domains.ts +45 -4
- package/src/panels/provider-health-panel.ts +5 -4
- package/src/panels/qr-panel.ts +1 -1
- package/src/panels/schedule-panel.ts +9 -17
- package/src/panels/security-panel.ts +8 -8
- package/src/panels/session-browser-panel.ts +10 -10
- package/src/panels/subscription-panel.ts +3 -3
- package/src/panels/system-messages-panel.ts +1 -1
- package/src/panels/tasks-panel.ts +20 -13
- package/src/panels/tool-inspector-panel.ts +19 -12
- package/src/panels/work-plan-panel.ts +5 -5
- package/src/planning/project-planning-coordinator.ts +1 -1
- package/src/provider-auth-route-display.ts +9 -0
- package/src/renderer/agent-workspace-style.ts +34 -0
- package/src/renderer/agent-workspace.ts +228 -52
- package/src/renderer/autocomplete-overlay.ts +25 -6
- package/src/renderer/bookmark-modal.ts +19 -4
- package/src/renderer/buffer.ts +4 -2
- package/src/renderer/context-inspector.ts +50 -13
- package/src/renderer/diff.ts +1 -1
- package/src/renderer/file-picker-overlay.ts +19 -6
- package/src/renderer/help-overlay.ts +106 -33
- package/src/renderer/history-search-overlay.ts +19 -4
- package/src/renderer/live-tail-modal.ts +27 -5
- package/src/renderer/mcp-workspace.ts +164 -50
- package/src/renderer/model-picker-overlay.ts +58 -2
- package/src/renderer/model-workspace.ts +104 -20
- package/src/renderer/process-modal.ts +27 -6
- package/src/renderer/profile-picker-modal.ts +20 -4
- package/src/renderer/search-overlay.ts +25 -5
- package/src/renderer/selection-modal-overlay.ts +46 -14
- package/src/renderer/session-picker-modal.ts +18 -1
- package/src/renderer/settings-modal-helpers.ts +2 -40
- package/src/renderer/settings-modal.ts +83 -50
- package/src/renderer/tool-call.ts +20 -11
- package/src/runtime/agent-runtime-events.ts +6 -6
- package/src/runtime/bootstrap-command-context.ts +7 -1
- package/src/runtime/bootstrap-command-parts.ts +9 -7
- package/src/runtime/bootstrap-core.ts +19 -1
- package/src/runtime/bootstrap-hook-bridge.ts +7 -18
- package/src/runtime/bootstrap-shell.ts +4 -4
- package/src/runtime/bootstrap.ts +31 -22
- package/src/runtime/connected-host-auth.ts +7 -2
- package/src/runtime/diagnostics/panels/index.ts +2 -2
- package/src/runtime/diagnostics/panels/policy.ts +7 -7
- package/src/runtime/index.ts +2 -1
- package/src/runtime/onboarding/apply-file-helpers.ts +66 -0
- package/src/runtime/onboarding/apply.ts +80 -79
- package/src/runtime/onboarding/derivation.ts +5 -8
- package/src/runtime/onboarding/snapshot.ts +0 -15
- package/src/runtime/onboarding/types.ts +8 -19
- package/src/runtime/onboarding/verify.ts +32 -10
- package/src/runtime/operator-token-cleanup.ts +1 -1
- package/src/runtime/services.ts +91 -22
- package/src/runtime/store/selectors/index.ts +3 -3
- package/src/runtime/store/state.ts +1 -4
- package/src/runtime/surface-feature-flags.ts +41 -6
- package/src/runtime/ui-read-models.ts +3 -4
- package/src/runtime/ui-services.ts +6 -6
- package/src/shell/blocking-input.ts +2 -1
- package/src/shell/ui-openers.ts +3 -13
- package/src/tools/agent-analysis-registry-policy.ts +0 -1
- package/src/tools/agent-channel-send-tool.ts +133 -0
- package/src/tools/agent-context-policy.ts +1 -1
- package/src/tools/agent-knowledge-ingest-tool.ts +405 -0
- package/src/tools/agent-knowledge-tool.ts +170 -0
- package/src/tools/agent-local-registry-tool.ts +269 -69
- package/src/tools/agent-media-generate-tool.ts +133 -0
- package/src/tools/agent-notify-tool.ts +143 -0
- package/src/tools/agent-operator-action-tool.ts +137 -0
- package/src/tools/agent-operator-briefing-tool.ts +217 -0
- package/src/tools/agent-reminder-schedule-tool.ts +237 -0
- package/src/tools/agent-tool-policy-guard.ts +8 -8
- package/src/tools/agent-work-plan-tool.ts +256 -0
- package/src/version.ts +1 -1
- package/src/input/commands/policy-dispatch.ts +0 -339
- package/src/input/commands/policy.ts +0 -13
- package/src/panels/panel-picker.ts +0 -105
- package/src/panels/policy-panel.ts +0 -308
- package/src/renderer/panel-picker-overlay.ts +0 -202
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { McpWorkspace, McpWorkspaceRow } from '../input/mcp-workspace.ts';
|
|
1
|
+
import type { McpWorkspace, McpWorkspaceRow, McpWorkspaceServerRow } from '../input/mcp-workspace.ts';
|
|
2
2
|
import type { Line } from '../types/grid.ts';
|
|
3
3
|
import { wrapText } from '../utils/terminal-width.ts';
|
|
4
4
|
import { GLYPHS } from './ui-primitives.ts';
|
|
@@ -11,6 +11,117 @@ import {
|
|
|
11
11
|
type WorkspaceRow,
|
|
12
12
|
} from './fullscreen-workspace.ts';
|
|
13
13
|
|
|
14
|
+
const MCP_WORKSPACE_TITLE = 'MCP Workspace / Servers';
|
|
15
|
+
const MCP_WORKSPACE_LEFT_HEADER = 'Servers';
|
|
16
|
+
const MCP_WORKSPACE_GROUP_SERVERS = 'SERVERS';
|
|
17
|
+
const MCP_WORKSPACE_GROUP_ACTIONS = 'ACTIONS';
|
|
18
|
+
const MCP_WORKSPACE_NO_CONFIGURED_SERVERS = ' No configured servers';
|
|
19
|
+
const MCP_WORKSPACE_FORM_WRITE_CONFIRMATION = 'Workspace writes require explicit confirmation. Type yes on the Confirm field, then save from here.';
|
|
20
|
+
const MCP_WORKSPACE_FORM_CONFIG_LOCATIONS = 'Project/global config locations are shown for review. The workspace dispatches confirmed MCP changes through the TUI command router.';
|
|
21
|
+
const MCP_WORKSPACE_REMOVE_HELP = 'Press Enter or y to remove through the TUI command router.';
|
|
22
|
+
const MCP_WORKSPACE_REMOVE_CANCEL_HELP = 'Press n or Esc to return without changing config.';
|
|
23
|
+
const MCP_WORKSPACE_RELOAD_TITLE = 'Reload MCP runtime';
|
|
24
|
+
const MCP_WORKSPACE_RELOAD_HELP = 'Press Enter or y to reload MCP runtime from the current config through the TUI command router.';
|
|
25
|
+
const MCP_WORKSPACE_RELOAD_CANCEL_HELP = 'Press n or Esc to return without reloading.';
|
|
26
|
+
const MCP_WORKSPACE_NO_ROWS = 'No MCP rows available.';
|
|
27
|
+
const MCP_WORKSPACE_RUNTIME_ONLY_COMMAND = '(runtime only; no launch config found)';
|
|
28
|
+
const MCP_WORKSPACE_NONE = '(none)';
|
|
29
|
+
const MCP_WORKSPACE_STATUS_PREFIX = 'Status:';
|
|
30
|
+
const MCP_WORKSPACE_FORM_HEADER_FIELD = 'Field';
|
|
31
|
+
const MCP_WORKSPACE_FORM_HEADER_VALUE = 'Value';
|
|
32
|
+
const MCP_WORKSPACE_FORM_HEADER_EDIT = 'Edit';
|
|
33
|
+
const MCP_WORKSPACE_FORM_TEXT_EDIT = 'text';
|
|
34
|
+
const MCP_WORKSPACE_FORM_ACTION_EDIT = 'cycle/action';
|
|
35
|
+
const MCP_WORKSPACE_EMPTY_VALUE = '(empty)';
|
|
36
|
+
const MCP_WORKSPACE_TOOL_HEADER_TOOL = 'Tool';
|
|
37
|
+
const MCP_WORKSPACE_TOOL_HEADER_SERVER = 'Server';
|
|
38
|
+
const MCP_WORKSPACE_TOOL_HEADER_DESCRIPTION = 'Description';
|
|
39
|
+
const MCP_WORKSPACE_TOOLS_LOADING = 'Tools: loading...';
|
|
40
|
+
const MCP_WORKSPACE_TOOLS_LOADING_DETAIL = 'Loading tool list from connected MCP servers.';
|
|
41
|
+
const MCP_WORKSPACE_TOOLS_EMPTY = 'No tools cached for the selected server. Press t to refresh.';
|
|
42
|
+
const MCP_WORKSPACE_CONFIRM_CANCEL_ROW = ' Cancel and return to MCP server browser';
|
|
43
|
+
const MCP_WORKSPACE_CONFIRM_RELOAD = 'Confirm MCP runtime reload';
|
|
44
|
+
const MCP_WORKSPACE_FOOTER_FORM = 'Focus MCP server form · Up/Down field · Left/Right cycle · Type edit · Enter save/cancel · Esc back';
|
|
45
|
+
const MCP_WORKSPACE_FOOTER_DELETE = 'Focus remove confirmation · Enter/y remove · n/Esc cancel';
|
|
46
|
+
const MCP_WORKSPACE_FOOTER_RELOAD = 'Focus reload confirmation · Enter/y reload · n/Esc cancel';
|
|
47
|
+
const MCP_WORKSPACE_FOOTER_BROWSE = 'Focus MCP workspace · Up/Down choose · Enter view/action · a add · d remove · r reload · t tools · Esc close';
|
|
48
|
+
|
|
49
|
+
function mcpWorkspaceStateLabel(mode: McpWorkspace['mode']): string {
|
|
50
|
+
return mode === 'browse' ? 'Browse' : mode === 'form' ? 'Server Form' : mode === 'delete-confirm' ? 'Remove Confirm' : 'Reload Confirm';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function mcpWorkspaceMainHeader(mode: McpWorkspace['mode'], connected: string | number, servers: string | number, tools: string | number): string {
|
|
54
|
+
if (mode === 'form') return 'MCP server form';
|
|
55
|
+
if (mode === 'delete-confirm') return 'MCP remove confirmation';
|
|
56
|
+
if (mode === 'reload-confirm') return 'MCP reload confirmation';
|
|
57
|
+
return `Servers ${connected}/${servers} connected · Tools ${tools}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function mcpWorkspaceToolsLabel(loading: boolean, server: string | undefined, count: string | number): string {
|
|
61
|
+
if (loading) return MCP_WORKSPACE_TOOLS_LOADING;
|
|
62
|
+
return server ? `Tools for ${server}: ${count}` : `Tools: ${count}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function renderMcpWorkspacePackageText(): string {
|
|
66
|
+
return [
|
|
67
|
+
MCP_WORKSPACE_TITLE,
|
|
68
|
+
MCP_WORKSPACE_LEFT_HEADER,
|
|
69
|
+
mcpWorkspaceStateLabel('browse'),
|
|
70
|
+
mcpWorkspaceStateLabel('form'),
|
|
71
|
+
mcpWorkspaceStateLabel('delete-confirm'),
|
|
72
|
+
mcpWorkspaceStateLabel('reload-confirm'),
|
|
73
|
+
mcpWorkspaceMainHeader('browse', '<connected>', '<servers>', '<tools>'),
|
|
74
|
+
mcpWorkspaceMainHeader('form', '<connected>', '<servers>', '<tools>'),
|
|
75
|
+
mcpWorkspaceMainHeader('delete-confirm', '<connected>', '<servers>', '<tools>'),
|
|
76
|
+
mcpWorkspaceMainHeader('reload-confirm', '<connected>', '<servers>', '<tools>'),
|
|
77
|
+
MCP_WORKSPACE_GROUP_SERVERS,
|
|
78
|
+
MCP_WORKSPACE_GROUP_ACTIONS,
|
|
79
|
+
MCP_WORKSPACE_NO_CONFIGURED_SERVERS.trim(),
|
|
80
|
+
'project config',
|
|
81
|
+
'global config',
|
|
82
|
+
'external config',
|
|
83
|
+
'runtime',
|
|
84
|
+
'Drafting an MCP server',
|
|
85
|
+
'Editing server <server>',
|
|
86
|
+
MCP_WORKSPACE_FORM_WRITE_CONFIRMATION,
|
|
87
|
+
MCP_WORKSPACE_FORM_CONFIG_LOCATIONS,
|
|
88
|
+
'Remove server: <server>',
|
|
89
|
+
MCP_WORKSPACE_REMOVE_HELP,
|
|
90
|
+
MCP_WORKSPACE_REMOVE_CANCEL_HELP,
|
|
91
|
+
MCP_WORKSPACE_RELOAD_TITLE,
|
|
92
|
+
MCP_WORKSPACE_RELOAD_HELP,
|
|
93
|
+
MCP_WORKSPACE_RELOAD_CANCEL_HELP,
|
|
94
|
+
MCP_WORKSPACE_NO_ROWS,
|
|
95
|
+
'Connected: <yes-no> Origin: <origin> Config: <freshness>',
|
|
96
|
+
'Role: <role> Review policy: <trust-mode>',
|
|
97
|
+
`Command: ${MCP_WORKSPACE_RUNTIME_ONLY_COMMAND}`,
|
|
98
|
+
`Allowed paths: ${MCP_WORKSPACE_NONE}`,
|
|
99
|
+
`Allowed hosts: ${MCP_WORKSPACE_NONE}`,
|
|
100
|
+
`${MCP_WORKSPACE_STATUS_PREFIX} <status>`,
|
|
101
|
+
MCP_WORKSPACE_FORM_HEADER_FIELD,
|
|
102
|
+
MCP_WORKSPACE_FORM_HEADER_VALUE,
|
|
103
|
+
MCP_WORKSPACE_FORM_HEADER_EDIT,
|
|
104
|
+
MCP_WORKSPACE_FORM_TEXT_EDIT,
|
|
105
|
+
MCP_WORKSPACE_FORM_ACTION_EDIT,
|
|
106
|
+
MCP_WORKSPACE_EMPTY_VALUE,
|
|
107
|
+
mcpWorkspaceToolsLabel(true, undefined, '<count>'),
|
|
108
|
+
mcpWorkspaceToolsLabel(false, '<server>', '<count>'),
|
|
109
|
+
mcpWorkspaceToolsLabel(false, undefined, '<count>'),
|
|
110
|
+
MCP_WORKSPACE_TOOL_HEADER_TOOL,
|
|
111
|
+
MCP_WORKSPACE_TOOL_HEADER_SERVER,
|
|
112
|
+
MCP_WORKSPACE_TOOL_HEADER_DESCRIPTION,
|
|
113
|
+
MCP_WORKSPACE_TOOLS_LOADING_DETAIL,
|
|
114
|
+
MCP_WORKSPACE_TOOLS_EMPTY,
|
|
115
|
+
'Confirm remove <server>',
|
|
116
|
+
MCP_WORKSPACE_CONFIRM_RELOAD,
|
|
117
|
+
MCP_WORKSPACE_CONFIRM_CANCEL_ROW.trim(),
|
|
118
|
+
MCP_WORKSPACE_FOOTER_FORM,
|
|
119
|
+
MCP_WORKSPACE_FOOTER_DELETE,
|
|
120
|
+
MCP_WORKSPACE_FOOTER_RELOAD,
|
|
121
|
+
MCP_WORKSPACE_FOOTER_BROWSE,
|
|
122
|
+
].join('\n');
|
|
123
|
+
}
|
|
124
|
+
|
|
14
125
|
function statusColor(text: string): string {
|
|
15
126
|
if (text.includes('failed') || text.includes('Save failed') || text.includes('Remove failed')) return PALETTE.bad;
|
|
16
127
|
if (text.includes('attention') || text.includes('quarantine')) return PALETTE.warn;
|
|
@@ -21,15 +132,28 @@ function connectedColor(connected: boolean): string {
|
|
|
21
132
|
return connected ? PALETTE.good : PALETTE.warn;
|
|
22
133
|
}
|
|
23
134
|
|
|
135
|
+
function serverOriginLabel(source: McpWorkspaceServerRow['source']): string {
|
|
136
|
+
switch (source) {
|
|
137
|
+
case 'project':
|
|
138
|
+
return 'project config';
|
|
139
|
+
case 'global':
|
|
140
|
+
return 'global config';
|
|
141
|
+
case 'external':
|
|
142
|
+
return 'external config';
|
|
143
|
+
case 'runtime':
|
|
144
|
+
return 'runtime';
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
24
148
|
function rowLabel(row: McpWorkspaceRow): string {
|
|
25
|
-
if (row.type === 'server') return `${row.server.name} (${row.server.source})`;
|
|
149
|
+
if (row.type === 'server') return `${row.server.name} (${serverOriginLabel(row.server.source)})`;
|
|
26
150
|
return row.label;
|
|
27
151
|
}
|
|
28
152
|
|
|
29
153
|
function rowDetail(row: McpWorkspaceRow): string {
|
|
30
154
|
if (row.type === 'server') {
|
|
31
155
|
const server = row.server;
|
|
32
|
-
return `${server.connected ? 'connected' : 'offline'} · ${server.role} · ${server.trustMode} ·
|
|
156
|
+
return `${server.connected ? 'connected' : 'offline'} · ${server.role} · ${server.trustMode} · config ${server.freshness}`;
|
|
33
157
|
}
|
|
34
158
|
return row.detail;
|
|
35
159
|
}
|
|
@@ -42,13 +166,13 @@ function buildLeftRows(workspace: McpWorkspace, height: number): WorkspaceRow[]
|
|
|
42
166
|
|
|
43
167
|
workspace.rows.forEach((row, rowIndex) => {
|
|
44
168
|
if (row.type === 'server' && !sawServerGroup) {
|
|
45
|
-
rendered.push({ text:
|
|
169
|
+
rendered.push({ text: MCP_WORKSPACE_GROUP_SERVERS, kind: 'group', bold: true });
|
|
46
170
|
sawServerGroup = true;
|
|
47
171
|
}
|
|
48
172
|
if (row.type === 'action' && !sawActionGroup) {
|
|
49
|
-
if (!sawServerGroup) rendered.push({ text:
|
|
50
|
-
if (workspace.servers.length === 0) rendered.push({ text:
|
|
51
|
-
rendered.push({ text:
|
|
173
|
+
if (!sawServerGroup) rendered.push({ text: MCP_WORKSPACE_GROUP_SERVERS, kind: 'group', bold: true });
|
|
174
|
+
if (workspace.servers.length === 0) rendered.push({ text: MCP_WORKSPACE_NO_CONFIGURED_SERVERS, kind: 'item', fg: PALETTE.dim, dim: true });
|
|
175
|
+
rendered.push({ text: MCP_WORKSPACE_GROUP_ACTIONS, kind: 'group', bold: true });
|
|
52
176
|
sawActionGroup = true;
|
|
53
177
|
}
|
|
54
178
|
|
|
@@ -82,47 +206,47 @@ function selectedDetailLines(workspace: McpWorkspace, width: number): WorkspaceR
|
|
|
82
206
|
if (workspace.mode === 'form') {
|
|
83
207
|
const field = workspace.formFields[workspace.formIndex];
|
|
84
208
|
lines.push(
|
|
85
|
-
workspace.editingServerName ? `Editing server
|
|
86
|
-
|
|
209
|
+
workspace.editingServerName ? `Editing server ${workspace.editingServerName}` : 'Drafting an MCP server',
|
|
210
|
+
MCP_WORKSPACE_FORM_WRITE_CONFIRMATION,
|
|
87
211
|
field ? `${field.label}: ${field.help}` : '',
|
|
88
|
-
|
|
212
|
+
MCP_WORKSPACE_FORM_CONFIG_LOCATIONS,
|
|
89
213
|
);
|
|
90
214
|
} else if (workspace.mode === 'delete-confirm') {
|
|
91
215
|
lines.push(
|
|
92
216
|
`Remove server: ${workspace.editingServerName ?? '(unknown)'}`,
|
|
93
|
-
|
|
94
|
-
|
|
217
|
+
MCP_WORKSPACE_REMOVE_HELP,
|
|
218
|
+
MCP_WORKSPACE_REMOVE_CANCEL_HELP,
|
|
95
219
|
);
|
|
96
220
|
} else if (workspace.mode === 'reload-confirm') {
|
|
97
221
|
lines.push(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
222
|
+
MCP_WORKSPACE_RELOAD_TITLE,
|
|
223
|
+
MCP_WORKSPACE_RELOAD_HELP,
|
|
224
|
+
MCP_WORKSPACE_RELOAD_CANCEL_HELP,
|
|
101
225
|
);
|
|
102
226
|
} else {
|
|
103
227
|
const selected = workspace.selectedRow;
|
|
104
|
-
if (!selected) lines.push(
|
|
228
|
+
if (!selected) lines.push(MCP_WORKSPACE_NO_ROWS);
|
|
105
229
|
else if (selected.type === 'action') lines.push(selected.label, selected.detail);
|
|
106
230
|
else {
|
|
107
231
|
const server = selected.server;
|
|
108
232
|
lines.push(
|
|
109
233
|
server.name,
|
|
110
|
-
`Connected: ${server.connected ? 'yes' : 'no'}
|
|
111
|
-
`Role: ${server.role}
|
|
112
|
-
`Command: ${server.command ? `${server.command}${server.args?.length ? ` ${server.args.join(' ')}` : ''}` :
|
|
113
|
-
`Allowed paths: ${server.allowedPaths.length > 0 ? server.allowedPaths.join(', ') :
|
|
114
|
-
`Allowed hosts: ${server.allowedHosts.length > 0 ? server.allowedHosts.join(', ') :
|
|
234
|
+
`Connected: ${server.connected ? 'yes' : 'no'} Origin: ${serverOriginLabel(server.source)} Config: ${server.freshness}`,
|
|
235
|
+
`Role: ${server.role} Review policy: ${server.trustMode}`,
|
|
236
|
+
`Command: ${server.command ? `${server.command}${server.args?.length ? ` ${server.args.join(' ')}` : ''}` : MCP_WORKSPACE_RUNTIME_ONLY_COMMAND}`,
|
|
237
|
+
`Allowed paths: ${server.allowedPaths.length > 0 ? server.allowedPaths.join(', ') : MCP_WORKSPACE_NONE}`,
|
|
238
|
+
`Allowed hosts: ${server.allowedHosts.length > 0 ? server.allowedHosts.join(', ') : MCP_WORKSPACE_NONE}`,
|
|
115
239
|
...(server.quarantineReason ? [`Quarantine: ${server.quarantineReason}${server.quarantineDetail ? ` - ${server.quarantineDetail}` : ''}`] : []),
|
|
116
240
|
);
|
|
117
241
|
}
|
|
118
242
|
}
|
|
119
243
|
|
|
120
|
-
lines.push('',
|
|
244
|
+
lines.push('', `${MCP_WORKSPACE_STATUS_PREFIX} ${workspace.status}`);
|
|
121
245
|
return lines.flatMap((text, index): WorkspaceRow[] => {
|
|
122
246
|
if (text === '') return [{ text: '', dim: true }];
|
|
123
247
|
return wrapText(text, Math.max(1, width)).map((wrapped, wrapIndex): WorkspaceRow => ({
|
|
124
248
|
text: wrapped,
|
|
125
|
-
fg: index === 0 ? PALETTE.title : text.startsWith(
|
|
249
|
+
fg: index === 0 ? PALETTE.title : text.startsWith(MCP_WORKSPACE_STATUS_PREFIX) ? statusColor(workspace.status) : PALETTE.text,
|
|
126
250
|
bold: index === 0 && wrapIndex === 0,
|
|
127
251
|
dim: text.length === 0,
|
|
128
252
|
}));
|
|
@@ -135,7 +259,7 @@ function buildFormRows(workspace: McpWorkspace, width: number, height: number):
|
|
|
135
259
|
const labelWidth = Math.min(24, Math.max(14, Math.floor(width * 0.24)));
|
|
136
260
|
const valueWidth = Math.max(12, width - labelWidth - 20);
|
|
137
261
|
rows.push({
|
|
138
|
-
text: ` ${padDisplay(
|
|
262
|
+
text: ` ${padDisplay(MCP_WORKSPACE_FORM_HEADER_FIELD, labelWidth)} ${padDisplay(MCP_WORKSPACE_FORM_HEADER_VALUE, valueWidth)} ${padDisplay(MCP_WORKSPACE_FORM_HEADER_EDIT, 10)}`,
|
|
139
263
|
fg: PALETTE.muted,
|
|
140
264
|
bold: true,
|
|
141
265
|
});
|
|
@@ -149,9 +273,9 @@ function buildFormRows(workspace: McpWorkspace, width: number, height: number):
|
|
|
149
273
|
const marker = selected ? GLYPHS.navigation.selected : ' ';
|
|
150
274
|
const value = field.id === 'save' || field.id === 'cancel'
|
|
151
275
|
? field.help
|
|
152
|
-
: field.value.length > 0 ? field.value :
|
|
276
|
+
: field.value.length > 0 ? field.value : MCP_WORKSPACE_EMPTY_VALUE;
|
|
153
277
|
rows.push({
|
|
154
|
-
text: `${marker} ${padDisplay(field.label, labelWidth)} ${padDisplay(value, valueWidth)} ${padDisplay(field.editable ?
|
|
278
|
+
text: `${marker} ${padDisplay(field.label, labelWidth)} ${padDisplay(value, valueWidth)} ${padDisplay(field.editable ? MCP_WORKSPACE_FORM_TEXT_EDIT : MCP_WORKSPACE_FORM_ACTION_EDIT, 12)}`,
|
|
155
279
|
selected,
|
|
156
280
|
fg: field.id === 'save' ? PALETTE.good : field.id === 'cancel' ? PALETTE.warn : field.editable ? PALETTE.text : PALETTE.info,
|
|
157
281
|
bold: selected,
|
|
@@ -168,19 +292,15 @@ function buildToolRows(workspace: McpWorkspace, width: number, height: number):
|
|
|
168
292
|
const toolWidth = Math.min(36, Math.max(18, Math.floor(width * 0.34)));
|
|
169
293
|
const serverWidth = Math.min(24, Math.max(12, Math.floor(width * 0.20)));
|
|
170
294
|
const descriptionWidth = Math.max(12, width - toolWidth - serverWidth - 8);
|
|
171
|
-
const label = workspace.loadingTools
|
|
172
|
-
? 'Tools: loading...'
|
|
173
|
-
: server
|
|
174
|
-
? `Tools for ${server}: ${tools.length}`
|
|
175
|
-
: `Tools: ${tools.length}`;
|
|
295
|
+
const label = mcpWorkspaceToolsLabel(workspace.loadingTools, server, tools.length);
|
|
176
296
|
const rows: WorkspaceRow[] = [
|
|
177
297
|
{ text: label, fg: PALETTE.subtitle, bold: true },
|
|
178
|
-
{ text: ` ${padDisplay(
|
|
298
|
+
{ text: ` ${padDisplay(MCP_WORKSPACE_TOOL_HEADER_TOOL, toolWidth)} ${padDisplay(MCP_WORKSPACE_TOOL_HEADER_SERVER, serverWidth)} ${padDisplay(MCP_WORKSPACE_TOOL_HEADER_DESCRIPTION, descriptionWidth)}`, fg: PALETTE.muted, bold: true },
|
|
179
299
|
];
|
|
180
300
|
|
|
181
301
|
if (tools.length === 0) {
|
|
182
302
|
rows.push({
|
|
183
|
-
text: workspace.loadingTools ?
|
|
303
|
+
text: workspace.loadingTools ? MCP_WORKSPACE_TOOLS_LOADING_DETAIL : MCP_WORKSPACE_TOOLS_EMPTY,
|
|
184
304
|
fg: PALETTE.muted,
|
|
185
305
|
dim: true,
|
|
186
306
|
});
|
|
@@ -200,7 +320,7 @@ function buildToolRows(workspace: McpWorkspace, width: number, height: number):
|
|
|
200
320
|
function buildDeleteRows(workspace: McpWorkspace, height: number): WorkspaceRow[] {
|
|
201
321
|
const rows: WorkspaceRow[] = [
|
|
202
322
|
{ text: `${GLYPHS.navigation.selected} Confirm remove ${workspace.editingServerName ?? '(unknown)'}`, selected: true, fg: PALETTE.bad, bold: true },
|
|
203
|
-
{ text:
|
|
323
|
+
{ text: MCP_WORKSPACE_CONFIRM_CANCEL_ROW, fg: PALETTE.muted },
|
|
204
324
|
];
|
|
205
325
|
while (rows.length < height) rows.push({ text: '', kind: 'empty' });
|
|
206
326
|
return rows.slice(0, height);
|
|
@@ -208,8 +328,8 @@ function buildDeleteRows(workspace: McpWorkspace, height: number): WorkspaceRow[
|
|
|
208
328
|
|
|
209
329
|
function buildReloadRows(height: number): WorkspaceRow[] {
|
|
210
330
|
const rows: WorkspaceRow[] = [
|
|
211
|
-
{ text: `${GLYPHS.navigation.selected}
|
|
212
|
-
{ text:
|
|
331
|
+
{ text: `${GLYPHS.navigation.selected} ${MCP_WORKSPACE_CONFIRM_RELOAD}`, selected: true, fg: PALETTE.warn, bold: true },
|
|
332
|
+
{ text: MCP_WORKSPACE_CONFIRM_CANCEL_ROW, fg: PALETTE.muted },
|
|
213
333
|
];
|
|
214
334
|
while (rows.length < height) rows.push({ text: '', kind: 'empty' });
|
|
215
335
|
return rows.slice(0, height);
|
|
@@ -223,30 +343,24 @@ function buildControlRows(workspace: McpWorkspace, width: number, height: number
|
|
|
223
343
|
}
|
|
224
344
|
|
|
225
345
|
function footerText(workspace: McpWorkspace): string {
|
|
226
|
-
if (workspace.mode === 'form') return
|
|
227
|
-
if (workspace.mode === 'delete-confirm') return
|
|
228
|
-
if (workspace.mode === 'reload-confirm') return
|
|
229
|
-
return
|
|
346
|
+
if (workspace.mode === 'form') return MCP_WORKSPACE_FOOTER_FORM;
|
|
347
|
+
if (workspace.mode === 'delete-confirm') return MCP_WORKSPACE_FOOTER_DELETE;
|
|
348
|
+
if (workspace.mode === 'reload-confirm') return MCP_WORKSPACE_FOOTER_RELOAD;
|
|
349
|
+
return MCP_WORKSPACE_FOOTER_BROWSE;
|
|
230
350
|
}
|
|
231
351
|
|
|
232
352
|
export function renderMcpWorkspace(workspace: McpWorkspace, width: number, height: number): Line[] {
|
|
233
353
|
const metrics = getFullscreenWorkspaceMetrics({ width, height });
|
|
234
354
|
const connected = workspace.servers.filter((server) => server.connected).length;
|
|
235
|
-
const stateLabel = workspace.mode
|
|
236
|
-
const mainHeader = workspace.mode
|
|
237
|
-
? 'MCP server form'
|
|
238
|
-
: workspace.mode === 'delete-confirm'
|
|
239
|
-
? 'MCP remove confirmation'
|
|
240
|
-
: workspace.mode === 'reload-confirm'
|
|
241
|
-
? 'MCP reload confirmation'
|
|
242
|
-
: `Servers ${connected}/${workspace.servers.length} connected · Tools ${workspace.tools.length}`;
|
|
355
|
+
const stateLabel = mcpWorkspaceStateLabel(workspace.mode);
|
|
356
|
+
const mainHeader = mcpWorkspaceMainHeader(workspace.mode, connected, workspace.servers.length, workspace.tools.length);
|
|
243
357
|
|
|
244
358
|
return renderFullscreenWorkspace({
|
|
245
359
|
width,
|
|
246
360
|
height,
|
|
247
|
-
title:
|
|
361
|
+
title: MCP_WORKSPACE_TITLE,
|
|
248
362
|
stateLabel,
|
|
249
|
-
leftHeader:
|
|
363
|
+
leftHeader: MCP_WORKSPACE_LEFT_HEADER,
|
|
250
364
|
mainHeader,
|
|
251
365
|
leftRows: buildLeftRows(workspace, metrics.bodyRows),
|
|
252
366
|
contextRows: selectedDetailLines(workspace, metrics.contextWidth),
|
|
@@ -28,6 +28,22 @@ const MODE_TITLES: Record<string, string> = {
|
|
|
28
28
|
contextCap: 'Set Context Window',
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
+
const CONTEXT_WINDOW_SOURCE_LABELS = {
|
|
32
|
+
provider_api: 'provider catalog',
|
|
33
|
+
configured_cap: 'configured limit',
|
|
34
|
+
fallback: 'default limit',
|
|
35
|
+
registry: 'model registry',
|
|
36
|
+
} as const satisfies Readonly<Record<string, string>>;
|
|
37
|
+
|
|
38
|
+
type ContextWindowSourceKey = keyof typeof CONTEXT_WINDOW_SOURCE_LABELS;
|
|
39
|
+
|
|
40
|
+
function formatContextWindowSource(value: string | undefined): string {
|
|
41
|
+
const normalized = (value ?? '').trim().toLowerCase();
|
|
42
|
+
if (!normalized) return 'configured limit';
|
|
43
|
+
if (normalized in CONTEXT_WINDOW_SOURCE_LABELS) return CONTEXT_WINDOW_SOURCE_LABELS[normalized as ContextWindowSourceKey];
|
|
44
|
+
return normalized.replace(/[_-]+/g, ' ').replace(/\s+/g, ' ');
|
|
45
|
+
}
|
|
46
|
+
|
|
31
47
|
/**
|
|
32
48
|
* Number of fixed chrome lines in the model-picker overlay (title + search + divider + detail×2 + footer).
|
|
33
49
|
* Used by callers to compute maxVisible item rows.
|
|
@@ -42,6 +58,46 @@ function putRowText(line: Line, startX: number, maxWidth: number, text: string,
|
|
|
42
58
|
putOverlayText(line, startX, maxWidth, text, { fg, bg, bold, dim });
|
|
43
59
|
}
|
|
44
60
|
|
|
61
|
+
export function renderModelPickerPackageText(): string {
|
|
62
|
+
return [
|
|
63
|
+
...Object.values(MODE_TITLES),
|
|
64
|
+
'Search',
|
|
65
|
+
'Filter: All',
|
|
66
|
+
'Filter: Free',
|
|
67
|
+
'Filter: Paid',
|
|
68
|
+
'Filter: Sub',
|
|
69
|
+
'Group: provider',
|
|
70
|
+
'Group: family',
|
|
71
|
+
'Group: pricingTier',
|
|
72
|
+
'Group: qualityTier',
|
|
73
|
+
'No models available',
|
|
74
|
+
'No models match query',
|
|
75
|
+
'No providers available',
|
|
76
|
+
'No providers match query',
|
|
77
|
+
'Provider:',
|
|
78
|
+
'Context:',
|
|
79
|
+
'Reasoning:',
|
|
80
|
+
'Vision:',
|
|
81
|
+
'Tools:',
|
|
82
|
+
'Select a provider to browse its models',
|
|
83
|
+
'Context window (tokens):',
|
|
84
|
+
'Leave blank to use default',
|
|
85
|
+
'Model:',
|
|
86
|
+
'provider catalog',
|
|
87
|
+
'configured limit',
|
|
88
|
+
'default limit',
|
|
89
|
+
'model registry',
|
|
90
|
+
'configured via environment',
|
|
91
|
+
'configured via secret reference',
|
|
92
|
+
'configured via subscription',
|
|
93
|
+
'configured anonymously',
|
|
94
|
+
'[Up/Down] [Enter] [/] Search [Space] Ctx [Esc] [Tab] Filter [G] Group',
|
|
95
|
+
'[Enter] Confirm [Esc] Cancel',
|
|
96
|
+
'[Up/Down] Nav [Enter] Select [Esc] Cancel',
|
|
97
|
+
...Object.entries(EFFORT_DESCRIPTIONS).flatMap(([level, description]) => [level, description]),
|
|
98
|
+
].join('\n');
|
|
99
|
+
}
|
|
100
|
+
|
|
45
101
|
/**
|
|
46
102
|
* Render the model picker modal as Line[] for overlay in the viewport.
|
|
47
103
|
* Handles model, provider, and effort modes.
|
|
@@ -338,7 +394,7 @@ export function renderModelPickerOverlay(
|
|
|
338
394
|
const capModel = picker.contextCapPendingModel;
|
|
339
395
|
const modelName = capModel ? capModel.displayName : 'unknown';
|
|
340
396
|
const currentCtx = capModel ? fmtContext(capModel.contextWindow) : '?';
|
|
341
|
-
const
|
|
397
|
+
const sourceLabel = formatContextWindowSource(capModel?.contextWindowProvenance);
|
|
342
398
|
|
|
343
399
|
const promptLabel = 'Context window (tokens):';
|
|
344
400
|
const cursorChar = OVERLAY_GLYPHS.cursor;
|
|
@@ -349,7 +405,7 @@ export function renderModelPickerOverlay(
|
|
|
349
405
|
|
|
350
406
|
lines.push(createOverlayContentLine(width, layout, borderFg, DEFAULT_OVERLAY_PALETTE.bodyBg));
|
|
351
407
|
|
|
352
|
-
const hintText = `Leave blank to use default (current: ${currentCtx},
|
|
408
|
+
const hintText = `Leave blank to use default (current: ${currentCtx}, from ${sourceLabel})`;
|
|
353
409
|
const hintTrunc = getDisplayWidth(hintText) > contentW
|
|
354
410
|
? hintText.slice(0, contentW - 3) + '...'
|
|
355
411
|
: hintText;
|
|
@@ -33,6 +33,31 @@ const PALETTE = {
|
|
|
33
33
|
const renderCache = new WeakMap<ModelPickerModal, { key: string; lines: Line[] }>();
|
|
34
34
|
const objectIds = new WeakMap<object, number>();
|
|
35
35
|
let nextObjectId = 1;
|
|
36
|
+
const MODEL_WORKSPACE_TITLE = ' Model Workspace / Providers And Models ';
|
|
37
|
+
const MODEL_WORKSPACE_TARGETS_TITLE = 'Targets';
|
|
38
|
+
const MODEL_WORKSPACE_MODE_LABELS = {
|
|
39
|
+
provider: 'Provider list',
|
|
40
|
+
model: 'Model list',
|
|
41
|
+
effort: 'Reasoning effort',
|
|
42
|
+
contextCap: 'Context cap',
|
|
43
|
+
} as const;
|
|
44
|
+
const MODEL_WORKSPACE_TABLE_HEADERS = {
|
|
45
|
+
provider: 'Provider Configuration Catalog',
|
|
46
|
+
effort: 'Reasoning effort Meaning',
|
|
47
|
+
contextCap: 'Context cap input',
|
|
48
|
+
modelKey: 'Model key',
|
|
49
|
+
displayName: 'Display name',
|
|
50
|
+
providerColumn: 'Provider',
|
|
51
|
+
context: 'Context',
|
|
52
|
+
tier: 'Tier',
|
|
53
|
+
caps: 'Caps',
|
|
54
|
+
} as const;
|
|
55
|
+
const MODEL_WORKSPACE_REASONING_EFFORT_DETAIL = 'Reasoning effort applies to the main chat model. Select the default effort for this model.';
|
|
56
|
+
const MODEL_WORKSPACE_CONTEXT_CAP_DETAIL = 'Context cap overrides the detected local-model context window for this selection.';
|
|
57
|
+
const MODEL_WORKSPACE_CONTEXT_CAP_INPUT_HELP = 'Type digits to set a cap. Enter confirms; Esc returns to the model list.';
|
|
58
|
+
const MODEL_WORKSPACE_FOOTER_SEARCH_ACTIVE = 'Typing filters search; Esc clears search';
|
|
59
|
+
const MODEL_WORKSPACE_FOOTER_SEARCH_INACTIVE = '/ search';
|
|
60
|
+
const MODEL_WORKSPACE_FOOTER_CONTROLS = 'Up/Down navigate • Left/Right pane • Enter select • <search> • Tab price • C caps • A available • B benchmark • G group • Esc close';
|
|
36
61
|
|
|
37
62
|
function clamp(value: number, min: number, max: number): number {
|
|
38
63
|
return Math.max(min, Math.min(max, value));
|
|
@@ -164,6 +189,65 @@ function targetLabelFor(target: string): string {
|
|
|
164
189
|
return 'Main Chat';
|
|
165
190
|
}
|
|
166
191
|
|
|
192
|
+
function providerSelectionDetail(targetLabel: string): string {
|
|
193
|
+
return `Provider selection: choose a provider, then choose a model for ${targetLabel}.`;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function modelSelectionDetail(targetLabel: string): string {
|
|
197
|
+
return `Model selection: choose the model to store for ${targetLabel}. Use filters to narrow large catalogs.`;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function modelWorkspaceFilterText(
|
|
201
|
+
query: string,
|
|
202
|
+
price: string,
|
|
203
|
+
capability: string,
|
|
204
|
+
group: string,
|
|
205
|
+
availableOnly: string,
|
|
206
|
+
): string {
|
|
207
|
+
return `Search: ${query} | Price: ${price} | Capability: ${capability} | Group: ${group} | Available only: ${availableOnly}`;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function modelWorkspaceFooterControls(searchHint: string): string {
|
|
211
|
+
return MODEL_WORKSPACE_FOOTER_CONTROLS.replace('<search>', searchHint);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function renderModelWorkspacePackageText(): string {
|
|
215
|
+
return [
|
|
216
|
+
MODEL_WORKSPACE_TITLE.trim(),
|
|
217
|
+
MODEL_WORKSPACE_TARGETS_TITLE,
|
|
218
|
+
MODEL_WORKSPACE_MODE_LABELS.provider,
|
|
219
|
+
MODEL_WORKSPACE_MODE_LABELS.model,
|
|
220
|
+
MODEL_WORKSPACE_MODE_LABELS.effort,
|
|
221
|
+
MODEL_WORKSPACE_MODE_LABELS.contextCap,
|
|
222
|
+
'Target: <target> (<state>)',
|
|
223
|
+
'Current: <route>',
|
|
224
|
+
providerSelectionDetail('<target>'),
|
|
225
|
+
'Selected provider: <provider>',
|
|
226
|
+
modelSelectionDetail('<target>'),
|
|
227
|
+
'Selected: <model> | <display> | context <context> | <capabilities>',
|
|
228
|
+
MODEL_WORKSPACE_REASONING_EFFORT_DETAIL,
|
|
229
|
+
MODEL_WORKSPACE_CONTEXT_CAP_DETAIL,
|
|
230
|
+
modelWorkspaceFilterText('<query>', '<price>', '<capability>', '<group>', '<available-only>'),
|
|
231
|
+
MODEL_WORKSPACE_TABLE_HEADERS.provider,
|
|
232
|
+
MODEL_WORKSPACE_TABLE_HEADERS.effort,
|
|
233
|
+
MODEL_WORKSPACE_TABLE_HEADERS.contextCap,
|
|
234
|
+
MODEL_WORKSPACE_TABLE_HEADERS.modelKey,
|
|
235
|
+
MODEL_WORKSPACE_TABLE_HEADERS.displayName,
|
|
236
|
+
MODEL_WORKSPACE_TABLE_HEADERS.providerColumn,
|
|
237
|
+
MODEL_WORKSPACE_TABLE_HEADERS.context,
|
|
238
|
+
MODEL_WORKSPACE_TABLE_HEADERS.tier,
|
|
239
|
+
MODEL_WORKSPACE_TABLE_HEADERS.caps,
|
|
240
|
+
'Model: <model>',
|
|
241
|
+
'Detected context: <context>',
|
|
242
|
+
'Override: <cap>',
|
|
243
|
+
MODEL_WORKSPACE_CONTEXT_CAP_INPUT_HELP,
|
|
244
|
+
'Focus targets',
|
|
245
|
+
'Focus list',
|
|
246
|
+
modelWorkspaceFooterControls(MODEL_WORKSPACE_FOOTER_SEARCH_ACTIVE),
|
|
247
|
+
modelWorkspaceFooterControls(MODEL_WORKSPACE_FOOTER_SEARCH_INACTIVE),
|
|
248
|
+
].join('\n');
|
|
249
|
+
}
|
|
250
|
+
|
|
167
251
|
function detailLines(picker: ModelPickerModal, width: number): string[] {
|
|
168
252
|
const target = picker.getSelectedTargetInfo();
|
|
169
253
|
const targetLabel = target?.label ?? targetLabelFor(picker.target);
|
|
@@ -174,10 +258,10 @@ function detailLines(picker: ModelPickerModal, width: number): string[] {
|
|
|
174
258
|
if (target) lines.push(`Current: ${targetSummary(target)}`);
|
|
175
259
|
if (picker.mode === 'provider') {
|
|
176
260
|
const provider = picker.getFilteredProviders()[picker.selectedIndex] ?? '';
|
|
177
|
-
lines.push(
|
|
261
|
+
lines.push(providerSelectionDetail(targetLabel));
|
|
178
262
|
if (provider) lines.push(`Selected provider: ${provider}`);
|
|
179
263
|
} else if (picker.mode === 'model') {
|
|
180
|
-
lines.push(
|
|
264
|
+
lines.push(modelSelectionDetail(targetLabel));
|
|
181
265
|
if (selected) {
|
|
182
266
|
const caps = selected.capabilities ?? {};
|
|
183
267
|
const capText = [
|
|
@@ -188,11 +272,11 @@ function detailLines(picker: ModelPickerModal, width: number): string[] {
|
|
|
188
272
|
lines.push(`Selected: ${modelKey(selected)} | ${selected.displayName} | context ${formatContext(selected.contextWindow)} | ${capText}`);
|
|
189
273
|
}
|
|
190
274
|
} else if (picker.mode === 'effort') {
|
|
191
|
-
lines.push(
|
|
275
|
+
lines.push(MODEL_WORKSPACE_REASONING_EFFORT_DETAIL);
|
|
192
276
|
} else {
|
|
193
|
-
lines.push(
|
|
277
|
+
lines.push(MODEL_WORKSPACE_CONTEXT_CAP_DETAIL);
|
|
194
278
|
}
|
|
195
|
-
const filterText =
|
|
279
|
+
const filterText = modelWorkspaceFilterText(picker.query || '(none)', picker.categoryFilter, picker.capabilityFilter, picker.groupBy, picker.availableOnly ? 'yes' : 'no');
|
|
196
280
|
lines.push(filterText);
|
|
197
281
|
return lines.flatMap((line) => wrapText(line, Math.max(1, width)));
|
|
198
282
|
}
|
|
@@ -293,7 +377,7 @@ function renderContextCapRows(picker: ModelPickerModal, lines: Line[], rows: num
|
|
|
293
377
|
`Model: ${model ? modelKey(model) : '(none)'}`,
|
|
294
378
|
`Detected context: ${formatContext(model?.contextWindow)}`,
|
|
295
379
|
`Override: ${input}`,
|
|
296
|
-
|
|
380
|
+
MODEL_WORKSPACE_CONTEXT_CAP_INPUT_HELP,
|
|
297
381
|
];
|
|
298
382
|
for (let row = 0; row < Math.min(rows, copy.length); row += 1) {
|
|
299
383
|
const line = lines[row]!;
|
|
@@ -306,15 +390,15 @@ function writeTableHeader(line: Line, picker: ModelPickerModal, startX: number,
|
|
|
306
390
|
fillRange(line, startX, startX + width - 1, PALETTE.footerBg);
|
|
307
391
|
const style = { fg: PALETTE.muted, bg: PALETTE.footerBg, bold: true };
|
|
308
392
|
if (picker.mode === 'provider') {
|
|
309
|
-
writeText(line, startX + 1, width - 2,
|
|
393
|
+
writeText(line, startX + 1, width - 2, MODEL_WORKSPACE_TABLE_HEADERS.provider, style);
|
|
310
394
|
return;
|
|
311
395
|
}
|
|
312
396
|
if (picker.mode === 'effort') {
|
|
313
|
-
writeText(line, startX + 1, width - 2,
|
|
397
|
+
writeText(line, startX + 1, width - 2, MODEL_WORKSPACE_TABLE_HEADERS.effort, style);
|
|
314
398
|
return;
|
|
315
399
|
}
|
|
316
400
|
if (picker.mode === 'contextCap') {
|
|
317
|
-
writeText(line, startX + 1, width - 2,
|
|
401
|
+
writeText(line, startX + 1, width - 2, MODEL_WORKSPACE_TABLE_HEADERS.contextCap, style);
|
|
318
402
|
return;
|
|
319
403
|
}
|
|
320
404
|
const providerW = clamp(Math.floor(width * 0.14), 10, 18);
|
|
@@ -324,12 +408,12 @@ function writeTableHeader(line: Line, picker: ModelPickerModal, startX: number,
|
|
|
324
408
|
const nameW = clamp(Math.floor(width * 0.28), 16, 36);
|
|
325
409
|
const keyW = Math.max(10, width - providerW - ctxW - tierW - capsW - nameW - 10);
|
|
326
410
|
let x = startX + 3;
|
|
327
|
-
writeText(line, x, keyW, padDisplay(
|
|
328
|
-
writeText(line, x, nameW, padDisplay(
|
|
329
|
-
writeText(line, x, providerW, padDisplay(
|
|
330
|
-
writeText(line, x, ctxW, padDisplay(
|
|
331
|
-
writeText(line, x, tierW, padDisplay(
|
|
332
|
-
writeText(line, x, capsW, padDisplay(
|
|
411
|
+
writeText(line, x, keyW, padDisplay(MODEL_WORKSPACE_TABLE_HEADERS.modelKey, keyW), style); x += keyW + 1;
|
|
412
|
+
writeText(line, x, nameW, padDisplay(MODEL_WORKSPACE_TABLE_HEADERS.displayName, nameW), style); x += nameW + 1;
|
|
413
|
+
writeText(line, x, providerW, padDisplay(MODEL_WORKSPACE_TABLE_HEADERS.providerColumn, providerW), style); x += providerW + 1;
|
|
414
|
+
writeText(line, x, ctxW, padDisplay(MODEL_WORKSPACE_TABLE_HEADERS.context, ctxW), style); x += ctxW + 1;
|
|
415
|
+
writeText(line, x, tierW, padDisplay(MODEL_WORKSPACE_TABLE_HEADERS.tier, tierW), style); x += tierW + 1;
|
|
416
|
+
writeText(line, x, capsW, padDisplay(MODEL_WORKSPACE_TABLE_HEADERS.caps, capsW), style);
|
|
333
417
|
}
|
|
334
418
|
|
|
335
419
|
export function renderModelWorkspace(picker: ModelPickerModal, width: number, viewportHeight: number): Line[] {
|
|
@@ -345,13 +429,13 @@ export function renderModelWorkspace(picker: ModelPickerModal, width: number, vi
|
|
|
345
429
|
const contentW = Math.max(1, safeWidth - contentX - 1);
|
|
346
430
|
|
|
347
431
|
const top = borderLine(safeWidth, GLYPHS.frame.topLeft, GLYPHS.frame.horizontal, GLYPHS.frame.topRight);
|
|
348
|
-
writeText(top, 2, safeWidth - 4,
|
|
432
|
+
writeText(top, 2, safeWidth - 4, MODEL_WORKSPACE_TITLE, { fg: PALETTE.title, bold: true });
|
|
349
433
|
lines.push(top);
|
|
350
434
|
|
|
351
435
|
const header = contentLine(safeWidth, PALETTE.footerBg);
|
|
352
|
-
writeText(header, 2, targetW - 2,
|
|
436
|
+
writeText(header, 2, targetW - 2, MODEL_WORKSPACE_TARGETS_TITLE, { fg: PALETTE.subtitle, bold: true, bg: PALETTE.footerBg });
|
|
353
437
|
drawVertical(header, targetW, PALETTE.footerBg);
|
|
354
|
-
const modeLabel = picker.mode
|
|
438
|
+
const modeLabel = MODEL_WORKSPACE_MODE_LABELS[picker.mode];
|
|
355
439
|
writeText(header, contentX + 1, contentW - 2, `${modeLabel} • ${picker.getItemCount()} item(s)`, { fg: PALETTE.subtitle, bold: true, bg: PALETTE.footerBg });
|
|
356
440
|
lines.push(header);
|
|
357
441
|
|
|
@@ -400,8 +484,8 @@ export function renderModelWorkspace(picker: ModelPickerModal, width: number, vi
|
|
|
400
484
|
|
|
401
485
|
const footer = contentLine(safeWidth, PALETTE.footerBg);
|
|
402
486
|
const targetHint = picker.focusPane === 'targets' ? 'Focus targets' : 'Focus list';
|
|
403
|
-
const searchHint = picker.searchFocused ?
|
|
404
|
-
const hints = `${targetHint} •
|
|
487
|
+
const searchHint = picker.searchFocused ? MODEL_WORKSPACE_FOOTER_SEARCH_ACTIVE : MODEL_WORKSPACE_FOOTER_SEARCH_INACTIVE;
|
|
488
|
+
const hints = `${targetHint} • ${modelWorkspaceFooterControls(searchHint)}`;
|
|
405
489
|
writeText(footer, 2, safeWidth - 4, hints, { fg: PALETTE.muted, bg: PALETTE.footerBg });
|
|
406
490
|
lines.push(footer);
|
|
407
491
|
lines.push(borderLine(safeWidth, GLYPHS.frame.bottomLeft, GLYPHS.frame.horizontal, GLYPHS.frame.bottomRight));
|