@pellux/goodvibes-tui 0.18.23 → 0.19.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 (80) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/README.md +1 -1
  3. package/docs/foundation-artifacts/operator-contract.json +1 -1
  4. package/package.json +7 -3
  5. package/src/core/conversation-rendering.ts +8 -6
  6. package/src/core/orchestrator.ts +1 -1
  7. package/src/daemon/cli.ts +54 -0
  8. package/src/input/commands/diff-runtime.ts +6 -5
  9. package/src/input/commands/guidance-runtime.ts +1 -1
  10. package/src/input/commands/health-runtime.ts +2 -2
  11. package/src/input/commands/local-setup-review.ts +1 -1
  12. package/src/input/commands/session-content.ts +1 -1
  13. package/src/input/commands/shell-core.ts +3 -2
  14. package/src/input/commands/skills-runtime.ts +2 -2
  15. package/src/input/commands/subscription-runtime.ts +4 -4
  16. package/src/input/handler.ts +8 -10
  17. package/src/input/model-picker.ts +6 -2
  18. package/src/input/panel-integration-actions.ts +2 -1
  19. package/src/input/settings-modal-types.ts +60 -0
  20. package/src/input/settings-modal.ts +83 -65
  21. package/src/main.ts +52 -0
  22. package/src/panels/agent-inspector-panel.ts +10 -9
  23. package/src/panels/agent-logs-panel.ts +26 -6
  24. package/src/panels/approval-panel.ts +1 -0
  25. package/src/panels/automation-control-panel.ts +1 -0
  26. package/src/panels/base-panel.ts +108 -3
  27. package/src/panels/communication-panel.ts +1 -0
  28. package/src/panels/context-visualizer-panel.ts +2 -0
  29. package/src/panels/control-plane-panel.ts +1 -0
  30. package/src/panels/diff-panel.ts +2 -0
  31. package/src/panels/file-explorer-panel.ts +51 -31
  32. package/src/panels/file-preview-panel.ts +57 -35
  33. package/src/panels/git-panel.ts +12 -13
  34. package/src/panels/hooks-panel.ts +3 -1
  35. package/src/panels/incident-review-panel.ts +4 -2
  36. package/src/panels/knowledge-panel.ts +75 -107
  37. package/src/panels/local-auth-panel.ts +1 -0
  38. package/src/panels/marketplace-panel.ts +51 -69
  39. package/src/panels/mcp-panel.ts +3 -1
  40. package/src/panels/memory-panel.ts +90 -158
  41. package/src/panels/ops-control-panel.ts +1 -0
  42. package/src/panels/orchestration-panel.ts +70 -51
  43. package/src/panels/panel-list-panel.ts +5 -4
  44. package/src/panels/panel-manager.ts +3 -0
  45. package/src/panels/plan-dashboard-panel.ts +2 -0
  46. package/src/panels/plugins-panel.ts +1 -0
  47. package/src/panels/polish.ts +51 -2
  48. package/src/panels/provider-accounts-panel.ts +1 -0
  49. package/src/panels/provider-health-panel.ts +6 -8
  50. package/src/panels/routes-panel.ts +3 -1
  51. package/src/panels/schedule-panel.ts +7 -6
  52. package/src/panels/scrollable-list-panel.ts +19 -2
  53. package/src/panels/security-panel.ts +17 -15
  54. package/src/panels/services-panel.ts +6 -4
  55. package/src/panels/session-browser-panel.ts +19 -18
  56. package/src/panels/settings-sync-panel.ts +3 -1
  57. package/src/panels/skills-panel.ts +114 -230
  58. package/src/panels/subscription-panel.ts +1 -0
  59. package/src/panels/system-messages-panel.ts +147 -141
  60. package/src/panels/tasks-panel.ts +1 -0
  61. package/src/panels/token-budget-panel.ts +2 -0
  62. package/src/panels/watchers-panel.ts +1 -0
  63. package/src/panels/worktree-panel.ts +1 -0
  64. package/src/panels/wrfc-panel.ts +2 -0
  65. package/src/renderer/agent-detail-modal.ts +2 -2
  66. package/src/renderer/ansi-sanitize.ts +76 -0
  67. package/src/renderer/buffer.ts +12 -1
  68. package/src/renderer/help-overlay.ts +14 -3
  69. package/src/renderer/model-picker-overlay.ts +9 -2
  70. package/src/renderer/settings-modal-helpers.ts +27 -0
  71. package/src/renderer/settings-modal.ts +18 -1
  72. package/src/renderer/status-glyphs.ts +21 -0
  73. package/src/renderer/status-token.ts +4 -8
  74. package/src/renderer/tool-call.ts +4 -3
  75. package/src/runtime/bootstrap-core.ts +1 -1
  76. package/src/runtime/bootstrap-hook-bridge.ts +1 -1
  77. package/src/runtime/bootstrap.ts +7 -8
  78. package/src/runtime/diagnostics/panels/policy.ts +2 -1
  79. package/src/shell/ui-openers.ts +44 -3
  80. package/src/version.ts +1 -1
@@ -0,0 +1,21 @@
1
+ // ---------------------------------------------------------------------------
2
+ // status-glyphs.ts — canonical glyph map for status states.
3
+ //
4
+ // Extracted as a neutral module so both status-token.ts and polish.ts can
5
+ // import from here without creating a circular ESM dependency.
6
+ //
7
+ // Glyphs:
8
+ // good ✓ (CHECK MARK U+2713)
9
+ // warn ⚠ (WARNING SIGN U+26A0)
10
+ // bad ✕ (MULTIPLICATION X U+2715)
11
+ // info ○ (WHITE CIRCLE U+25CB)
12
+ // ---------------------------------------------------------------------------
13
+
14
+ export type StatusState = 'good' | 'warn' | 'bad' | 'info';
15
+
16
+ export const STATE_GLYPHS: Record<StatusState, string> = {
17
+ good: '\u2713', // ✓
18
+ warn: '\u26a0', // ⚠
19
+ bad: '\u2715', // ✕
20
+ info: '\u25cb', // ○
21
+ };
@@ -13,15 +13,11 @@
13
13
 
14
14
  import type { Cell } from '../types/grid.ts';
15
15
  import { DEFAULT_PANEL_PALETTE } from '../panels/polish.ts';
16
+ import { type StatusState, STATE_GLYPHS } from './status-glyphs.ts';
16
17
 
17
- export type StatusState = 'good' | 'warn' | 'bad' | 'info';
18
-
19
- const STATE_GLYPHS: Record<StatusState, string> = {
20
- good: '\u2713', // ✓
21
- warn: '\u26a0', // ⚠
22
- bad: '\u2715', // ✕
23
- info: '\u25cb', // ○
24
- };
18
+ // Re-export for downstream consumers that import from this module.
19
+ export type { StatusState } from './status-glyphs.ts';
20
+ export { STATE_GLYPHS } from './status-glyphs.ts';
25
21
 
26
22
  const STATE_COLORS: Record<StatusState, string> = {
27
23
  good: DEFAULT_PANEL_PALETTE.good,
@@ -2,6 +2,7 @@ import { type Line, createStyledCell, createEmptyLine } from '../types/grid.ts';
2
2
  import { LAYOUT, TOOL_STATUS } from './layout.ts';
3
3
  import { getDisplayWidth, truncateDisplay } from '../utils/terminal-width.ts';
4
4
  import type { ToolCall } from '@pellux/goodvibes-sdk/platform/types/tools';
5
+ import { stripDangerousAnsi } from './ansi-sanitize.ts';
5
6
 
6
7
  const TOOL_NAME_MIN_WIDTH = 8;
7
8
  const TOOL_NAME_MAX_WIDTH = 20;
@@ -194,11 +195,11 @@ export function renderToolCallBlock(
194
195
  const rawName = toolCall.name.includes('__')
195
196
  ? toolCall.name.split('__').pop()!
196
197
  : toolCall.name;
197
- const keyArg = extractKeyArg(toolCall);
198
+ const keyArg = stripDangerousAnsi(extractKeyArg(toolCall));
198
199
  const suffixText = status === 'error' && errorMsg
199
- ? `- ${errorMsg.slice(0, 40)}`
200
+ ? `- ${stripDangerousAnsi(errorMsg).slice(0, 40)}`
200
201
  : status === 'done' && resultSummary
201
- ? `(${resultSummary})`
202
+ ? `(${stripDangerousAnsi(resultSummary)})`
202
203
  : '';
203
204
  const leftBudget = Math.max(0, leftEndExclusive - col);
204
205
  const leftSegments = buildLeftSegments(rawName, keyArg, suffixText, leftBudget);
@@ -362,7 +362,7 @@ export async function initializeBootstrapCore(
362
362
  displayName: 'Terminal UI',
363
363
  lastSeenAt: Date.now(),
364
364
  },
365
- }).catch(() => {});
365
+ }).catch((err) => { logger.debug('session broker create session failed at bootstrap', { err }); });
366
366
 
367
367
  domainDispatch.syncSessionState({
368
368
  id: userSessionId,
@@ -51,7 +51,7 @@ export function createResumeSessionHandler(options: ResumeSessionOptions): (sess
51
51
  if (meta?.model) options.runtime.model = meta.model;
52
52
  if (meta?.provider) options.runtime.provider = meta.provider;
53
53
  options.writeLastSessionPointer(sessionId);
54
- void options.sharedSessionBroker.reopenSession(sessionId).catch(() => {});
54
+ void options.sharedSessionBroker.reopenSession(sessionId).catch((err) => { logger.debug('session broker reopen session failed', { err }); });
55
55
  options.conversation.log(`Resumed session: ${sessionId}`, { fg: '135' });
56
56
  const reopenedPanels: string[] = [];
57
57
  if (meta.returnContext?.openPanels?.length) {
@@ -177,13 +177,13 @@ export async function bootstrapRuntime(
177
177
  requestRender: (): void => { requestRender(); },
178
178
  };
179
179
 
180
- const orchestrator = new Orchestrator(
180
+ const orchestrator = new Orchestrator({
181
181
  conversation,
182
- () => orchestratorRefs.getViewportHeight(),
183
- (vHeight: number) => orchestratorRefs.scrollToEnd(vHeight),
182
+ getViewportHeight: () => orchestratorRefs.getViewportHeight(),
183
+ scrollToEnd: (vHeight: number) => orchestratorRefs.scrollToEnd(vHeight),
184
184
  toolRegistry,
185
185
  permissionManager,
186
- () => {
186
+ getSystemPrompt: () => {
187
187
  const currentModel = providerRegistry.getCurrentModel();
188
188
  const contextWindow = providerRegistry.getContextWindowForModel(currentModel);
189
189
  const tier = getTierForContextWindow(contextWindow);
@@ -191,14 +191,13 @@ export async function bootstrapRuntime(
191
191
  return supplement ? runtime.systemPrompt + '\n\n' + supplement : runtime.systemPrompt;
192
192
  },
193
193
  hookDispatcher,
194
- null,
195
- () => orchestratorRefs.requestRender(),
194
+ requestRender: () => orchestratorRefs.requestRender(),
196
195
  runtimeBus,
197
- {
196
+ services: {
198
197
  agentManager: services.agentManager,
199
198
  wrfcController: services.wrfcController,
200
199
  },
201
- );
200
+ });
202
201
  conversationFollowUpRef.value = (item) => orchestrator.enqueueConversationFollowUp(item);
203
202
  orchestrator.setCoreServices({
204
203
  configManager,
@@ -19,6 +19,7 @@ import type { PermissionAuditEntry } from '@pellux/goodvibes-sdk/platform/runtim
19
19
  import type { PolicyLintFinding } from '@pellux/goodvibes-sdk/platform/runtime/permissions/lint';
20
20
  import type { PolicySimulationSummary } from '@pellux/goodvibes-sdk/platform/runtime/permissions/simulation-scenarios';
21
21
  import type { PolicyPreflightReview } from '@pellux/goodvibes-sdk/platform/runtime/permissions/preflight';
22
+ import { logger } from '@pellux/goodvibes-sdk/platform/utils/logger';
22
23
 
23
24
  /**
24
25
  * A point-in-time snapshot of policy state for diagnostics rendering.
@@ -169,7 +170,7 @@ export class PolicyPanel {
169
170
  try {
170
171
  cb();
171
172
  } catch (err) {
172
- console.debug('[PolicyPanel] subscriber error:', err);
173
+ logger.warn(`[PolicyPanel] subscriber error: ${err}`);
173
174
  }
174
175
  }
175
176
  }
@@ -27,6 +27,42 @@ type WireShellUiOpenersOptions = {
27
27
  render: () => void;
28
28
  };
29
29
 
30
+ /**
31
+ * Derive the configuredVia tier for a provider.
32
+ * Checks env-vars first (env tier), then falls back to subscription (from configuredIds
33
+ * that aren't env-keyed). Returns undefined when not configured.
34
+ */
35
+ function deriveConfiguredVia(
36
+ providerId: string,
37
+ configuredIds: Set<string>,
38
+ subscriptionManager: SubscriptionManager,
39
+ ): 'env' | 'secrets' | 'subscription' | 'anonymous' | undefined {
40
+ if (!configuredIds.has(providerId)) return undefined;
41
+
42
+ // Check if a subscription session is active for this provider
43
+ const subs = subscriptionManager.list();
44
+ if (subs.some((s) => s.provider === providerId)) return 'subscription';
45
+
46
+ // Assume env-var backed (anonymous providers don't appear in configuredIds)
47
+ return 'env';
48
+ }
49
+
50
+ /**
51
+ * Build a configuredViaMap for the given provider list.
52
+ */
53
+ function buildConfiguredViaMap(
54
+ providers: string[],
55
+ configuredIds: Set<string>,
56
+ subscriptionManager: SubscriptionManager,
57
+ ): Map<string, 'env' | 'secrets' | 'subscription' | 'anonymous'> {
58
+ const map = new Map<string, 'env' | 'secrets' | 'subscription' | 'anonymous'>();
59
+ for (const p of providers) {
60
+ const via = deriveConfiguredVia(p, configuredIds, subscriptionManager);
61
+ if (via !== undefined) map.set(p, via);
62
+ }
63
+ return map;
64
+ }
65
+
30
66
  export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
31
67
  const {
32
68
  commandContext,
@@ -47,11 +83,14 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
47
83
 
48
84
  commandContext.openModelPicker = () => {
49
85
  const models = providerRegistry.getSelectableModels();
50
- input.modelPicker.configuredProviders = new Set(getConfiguredProviderIds());
86
+ const configuredIds = new Set(getConfiguredProviderIds());
87
+ input.modelPicker.configuredProviders = configuredIds;
88
+ const providerIds = [...new Set(models.map((m) => m.provider))];
89
+ input.modelPicker.configuredViaMap = buildConfiguredViaMap(providerIds, configuredIds, subscriptionManager);
51
90
  void getPinned().then((pinned) => {
52
91
  input.modelPicker.pinnedIds = new Set(pinned);
53
92
  });
54
- void input.modelPicker.loadRecentModels().catch(() => {});
93
+ void input.modelPicker.loadRecentModels().catch(() => {}); // best-effort: prefetch for UI, failure is non-visible
55
94
  input.modalOpened('modelPicker');
56
95
  input.modelPicker.openAllModels(models, runtime.model);
57
96
  render();
@@ -59,7 +98,9 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
59
98
 
60
99
  commandContext.openProviderPicker = () => {
61
100
  const providers = [...new Set(providerRegistry.listModels().map((model) => model.provider))];
62
- input.modelPicker.configuredProviders = new Set(getConfiguredProviderIds());
101
+ const configuredIds = new Set(getConfiguredProviderIds());
102
+ input.modelPicker.configuredProviders = configuredIds;
103
+ input.modelPicker.configuredViaMap = buildConfiguredViaMap(providers, configuredIds, subscriptionManager);
63
104
  input.modalOpened('modelPicker');
64
105
  input.modelPicker.openProviders(providers, runtime.provider);
65
106
  render();
package/src/version.ts CHANGED
@@ -6,7 +6,7 @@ import { join } from 'node:path';
6
6
  // The prebuild script updates the fallback value before compilation.
7
7
  // Uses import.meta.dir (Bun) to locate package.json relative to this file,
8
8
  // which is correct regardless of the process working directory.
9
- let _version = '0.18.23';
9
+ let _version = '0.19.1';
10
10
  try {
11
11
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8'));
12
12
  _version = pkg.version ?? _version;