@pellux/goodvibes-agent 1.4.3 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +7 -7
  3. package/dist/package/main.js +7450 -12285
  4. package/docs/README.md +2 -2
  5. package/docs/channels-remote-and-api.md +1 -1
  6. package/docs/getting-started.md +2 -2
  7. package/docs/release-and-publishing.md +1 -1
  8. package/docs/tools-and-commands.md +5 -5
  9. package/package.json +4 -2
  10. package/release/performance-snapshot.json +2 -2
  11. package/release/release-notes.md +11 -7
  12. package/release/release-readiness.json +6 -6
  13. package/src/agent/behavior-discovery-summary.ts +4 -18
  14. package/src/agent/calendar-registry.ts +322 -0
  15. package/src/agent/competitive-feature-inventory.ts +268 -130
  16. package/src/agent/document-registry.ts +5 -1
  17. package/src/agent/email/email-service.ts +350 -0
  18. package/src/agent/email/imap-client.ts +596 -0
  19. package/src/agent/email/smtp-client.ts +453 -0
  20. package/src/agent/ics-calendar.ts +662 -0
  21. package/src/agent/ics-timezone.ts +172 -0
  22. package/src/agent/markdown-frontmatter.ts +31 -0
  23. package/src/agent/memory-safety.ts +1 -1
  24. package/src/agent/note-registry.ts +3 -9
  25. package/src/agent/persona-discovery.ts +3 -15
  26. package/src/agent/persona-registry.ts +1 -10
  27. package/src/agent/research-source-registry.ts +5 -1
  28. package/src/agent/routine-discovery.ts +3 -15
  29. package/src/agent/runtime-profile.ts +3 -0
  30. package/src/agent/skill-discovery.ts +3 -15
  31. package/src/agent/skill-draft-proposer.ts +203 -0
  32. package/src/agent/skill-draft-runner.ts +201 -0
  33. package/src/agent/skill-registry.ts +36 -1
  34. package/src/agent/skill-standard.ts +99 -0
  35. package/src/agent/vibe-file.ts +7 -22
  36. package/src/cli/completion.ts +1 -1
  37. package/src/cli/config-overrides.ts +10 -1
  38. package/src/cli/redaction.ts +17 -5
  39. package/src/config/provider-model.ts +2 -1
  40. package/src/config/secret-config.ts +13 -6
  41. package/src/core/activity-feed.ts +97 -0
  42. package/src/core/away-digest.ts +161 -0
  43. package/src/core/conversation-rendering.ts +7 -3
  44. package/src/core/conversation.ts +22 -15
  45. package/src/core/hardware-profile.ts +362 -0
  46. package/src/core/last-seen-store.ts +78 -0
  47. package/src/core/plain-language.ts +52 -0
  48. package/src/core/setup-incomplete-hint.ts +90 -0
  49. package/src/core/system-message-router.ts +38 -87
  50. package/src/input/agent-workspace-basic-command-editor-submission.ts +60 -220
  51. package/src/input/agent-workspace-basic-command-editors.ts +39 -141
  52. package/src/input/agent-workspace-categories.ts +63 -158
  53. package/src/input/agent-workspace-command-editor.ts +4 -0
  54. package/src/input/agent-workspace-host-category.ts +0 -5
  55. package/src/input/agent-workspace-local-editor-submission.ts +3 -1
  56. package/src/input/agent-workspace-navigation.ts +10 -3
  57. package/src/input/agent-workspace-onboarding-actions.ts +132 -0
  58. package/src/input/agent-workspace-onboarding-categories.ts +29 -26
  59. package/src/input/agent-workspace-onboarding-finish.ts +11 -4
  60. package/src/input/agent-workspace-onboarding-state.ts +111 -0
  61. package/src/input/agent-workspace-profile-editor-submission.ts +260 -0
  62. package/src/input/agent-workspace-profile-editors.ts +155 -0
  63. package/src/input/agent-workspace-subscription-editor.ts +7 -0
  64. package/src/input/agent-workspace-types.ts +5 -1
  65. package/src/input/agent-workspace.ts +65 -39
  66. package/src/input/command-registry.ts +18 -5
  67. package/src/input/commands/agent-runtime-profile-runtime.ts +2 -1
  68. package/src/input/commands/agent-skills-runtime.ts +60 -3
  69. package/src/input/commands/calendar-runtime.ts +209 -0
  70. package/src/input/commands/channels-runtime.ts +1 -0
  71. package/src/input/commands/command-error.ts +9 -0
  72. package/src/input/commands/compat-runtime.ts +1 -0
  73. package/src/input/commands/config.ts +1 -0
  74. package/src/input/commands/conversation-runtime.ts +1 -1
  75. package/src/input/commands/delegation-runtime.ts +5 -6
  76. package/src/input/commands/email-runtime.ts +387 -0
  77. package/src/input/commands/experience-runtime.ts +17 -4
  78. package/src/input/commands/guidance-runtime.ts +1 -1
  79. package/src/input/commands/health-runtime.ts +6 -1
  80. package/src/input/commands/knowledge-format.ts +73 -0
  81. package/src/input/commands/knowledge.ts +9 -74
  82. package/src/input/commands/local-provider-runtime.ts +2 -1
  83. package/src/input/commands/local-runtime.ts +10 -4
  84. package/src/input/commands/mcp-runtime.ts +7 -0
  85. package/src/input/commands/notify-runtime.ts +17 -1
  86. package/src/input/commands/onboarding-runtime.ts +1 -0
  87. package/src/input/commands/operator-actions-runtime.ts +1 -0
  88. package/src/input/commands/operator-runtime.ts +3 -0
  89. package/src/input/commands/personas-runtime.ts +1 -0
  90. package/src/input/commands/platform-access-runtime.ts +40 -17
  91. package/src/input/commands/product-runtime.ts +6 -1
  92. package/src/input/commands/provider-accounts-runtime.ts +3 -2
  93. package/src/input/commands/qrcode-runtime.ts +1 -0
  94. package/src/input/commands/routines-runtime.ts +1 -0
  95. package/src/input/commands/runtime-services.ts +0 -13
  96. package/src/input/commands/security-runtime.ts +1 -0
  97. package/src/input/commands/session-content.ts +1 -0
  98. package/src/input/commands/shell-core.ts +5 -2
  99. package/src/input/commands/subscription-runtime.ts +33 -9
  100. package/src/input/commands/support-bundle-runtime.ts +8 -1
  101. package/src/input/commands/tasks-runtime.ts +1 -0
  102. package/src/input/commands/tts-runtime.ts +1 -0
  103. package/src/input/commands/vibe-runtime.ts +1 -0
  104. package/src/input/commands.ts +4 -0
  105. package/src/input/feed-context-factory.ts +3 -12
  106. package/src/input/handler-command-route.ts +7 -60
  107. package/src/input/handler-feed-routes.ts +0 -194
  108. package/src/input/handler-feed.ts +2 -54
  109. package/src/input/handler-interactions.ts +0 -2
  110. package/src/input/handler-modal-stack.ts +0 -9
  111. package/src/input/handler-picker-routes.ts +24 -1
  112. package/src/input/handler-shortcuts.ts +11 -40
  113. package/src/input/handler-ui-state.ts +13 -0
  114. package/src/input/handler.ts +8 -35
  115. package/src/input/keybindings.ts +40 -17
  116. package/src/input/model-picker-local-fit.ts +130 -0
  117. package/src/input/submission-router.ts +0 -5
  118. package/src/main.ts +111 -89
  119. package/src/renderer/activity-sidebar.ts +186 -0
  120. package/src/renderer/agent-workspace-context-lines.ts +5 -48
  121. package/src/renderer/agent-workspace-style.ts +1 -1
  122. package/src/renderer/agent-workspace.ts +14 -2
  123. package/src/renderer/compositor.ts +37 -125
  124. package/src/renderer/conversation-overlays.ts +3 -3
  125. package/src/renderer/help-overlay.ts +7 -5
  126. package/src/renderer/model-workspace.ts +101 -86
  127. package/src/{panels → renderer}/polish.ts +3 -3
  128. package/src/renderer/shell-surface.ts +4 -5
  129. package/src/renderer/status-token.ts +31 -11
  130. package/src/renderer/tab-strip.ts +1 -1
  131. package/src/renderer/tool-call.ts +7 -8
  132. package/src/renderer/tool-labels.ts +92 -0
  133. package/src/renderer/ui-factory.ts +48 -96
  134. package/src/runtime/bootstrap-command-context.ts +0 -5
  135. package/src/runtime/bootstrap-command-parts.ts +6 -15
  136. package/src/runtime/bootstrap-core.ts +34 -13
  137. package/src/runtime/bootstrap-hook-bridge.ts +3 -1
  138. package/src/runtime/bootstrap-shell.ts +3 -50
  139. package/src/runtime/bootstrap.ts +3 -4
  140. package/src/runtime/context.ts +1 -1
  141. package/src/runtime/diagnostics/panels/index.ts +0 -1
  142. package/src/runtime/diagnostics/panels/policy.ts +1 -1
  143. package/src/runtime/execution-ledger.ts +16 -1
  144. package/src/runtime/index.ts +6 -1
  145. package/src/runtime/onboarding/index.ts +1 -0
  146. package/src/runtime/onboarding/onboarding-state.ts +200 -0
  147. package/src/{panels → runtime}/provider-account-snapshot.ts +5 -2
  148. package/src/runtime/services.ts +16 -4
  149. package/src/runtime/terminal-output-guard.ts +7 -0
  150. package/src/runtime/tool-permission-safety.ts +1 -1
  151. package/src/runtime/ui/model-picker/data-provider.ts +1 -1
  152. package/src/runtime/ui/model-picker/health-enrichment.ts +2 -2
  153. package/src/runtime/ui/model-picker/types.ts +2 -2
  154. package/src/runtime/ui/provider-health/data-provider.ts +3 -3
  155. package/src/runtime/ui/provider-health/types.ts +2 -2
  156. package/src/runtime/ui-services.ts +0 -2
  157. package/src/shell/agent-workspace-fullscreen.ts +0 -1
  158. package/src/shell/autonomy-surfacing.ts +272 -0
  159. package/src/shell/session-continuity-hints.ts +0 -6
  160. package/src/shell/startup-wiring.ts +221 -0
  161. package/src/shell/ui-openers.ts +18 -29
  162. package/src/tools/agent-context-policy.ts +1 -1
  163. package/src/tools/agent-harness-background-processes.ts +4 -4
  164. package/src/tools/agent-harness-browser-cockpit-route.ts +3 -3
  165. package/src/tools/agent-harness-command-runner.ts +6 -1
  166. package/src/tools/agent-harness-document-ops.ts +26 -53
  167. package/src/tools/agent-harness-execution-history.ts +1 -13
  168. package/src/tools/agent-harness-execution-posture.ts +0 -15
  169. package/src/tools/agent-harness-keybinding-metadata.ts +28 -29
  170. package/src/tools/agent-harness-local-model-cookbook.ts +24 -1
  171. package/src/tools/agent-harness-metadata.ts +16 -0
  172. package/src/tools/agent-harness-mode-catalog.ts +2 -9
  173. package/src/tools/agent-harness-provider-account-metadata.ts +2 -2
  174. package/src/tools/agent-harness-setup-posture-utils.ts +1 -1
  175. package/src/tools/agent-harness-tool-schema.ts +13 -14
  176. package/src/tools/agent-harness-tool.ts +27 -23
  177. package/src/tools/agent-harness-ui-surface-metadata.ts +10 -20
  178. package/src/tools/agent-harness-workspace-action-runner.ts +3 -4
  179. package/src/tools/agent-workspace-tool.ts +2 -33
  180. package/src/utils/terminal-width.ts +9 -3
  181. package/src/version.ts +1 -1
  182. package/src/input/agent-workspace-panel-route.ts +0 -44
  183. package/src/input/panel-integration-actions.ts +0 -26
  184. package/src/panels/approval-panel.ts +0 -149
  185. package/src/panels/automation-control-panel.ts +0 -212
  186. package/src/panels/base-panel.ts +0 -254
  187. package/src/panels/builtin/agent.ts +0 -58
  188. package/src/panels/builtin/knowledge.ts +0 -26
  189. package/src/panels/builtin/operations.ts +0 -121
  190. package/src/panels/builtin/session.ts +0 -138
  191. package/src/panels/builtin/shared.ts +0 -275
  192. package/src/panels/builtin/usage.ts +0 -21
  193. package/src/panels/builtin-panels.ts +0 -23
  194. package/src/panels/confirm-state.ts +0 -61
  195. package/src/panels/context-visualizer-panel.ts +0 -204
  196. package/src/panels/cost-tracker-panel.ts +0 -444
  197. package/src/panels/docs-panel.ts +0 -285
  198. package/src/panels/index.ts +0 -25
  199. package/src/panels/knowledge-panel.ts +0 -417
  200. package/src/panels/memory-panel.ts +0 -226
  201. package/src/panels/panel-list-panel.ts +0 -464
  202. package/src/panels/panel-manager.ts +0 -570
  203. package/src/panels/provider-accounts-panel.ts +0 -233
  204. package/src/panels/provider-health-domains.ts +0 -208
  205. package/src/panels/provider-health-panel.ts +0 -720
  206. package/src/panels/provider-health-tracker.ts +0 -115
  207. package/src/panels/provider-stats-panel.ts +0 -366
  208. package/src/panels/qr-panel.ts +0 -207
  209. package/src/panels/schedule-panel.ts +0 -321
  210. package/src/panels/scrollable-list-panel.ts +0 -491
  211. package/src/panels/search-focus.ts +0 -32
  212. package/src/panels/security-panel.ts +0 -295
  213. package/src/panels/session-browser-panel.ts +0 -395
  214. package/src/panels/session-maintenance.ts +0 -125
  215. package/src/panels/subscription-panel.ts +0 -263
  216. package/src/panels/system-messages-panel.ts +0 -230
  217. package/src/panels/tasks-panel.ts +0 -344
  218. package/src/panels/thinking-panel.ts +0 -304
  219. package/src/panels/token-budget-panel.ts +0 -475
  220. package/src/panels/tool-inspector-panel.ts +0 -436
  221. package/src/panels/types.ts +0 -54
  222. package/src/renderer/panel-composite.ts +0 -158
  223. package/src/renderer/panel-tab-bar.ts +0 -69
  224. package/src/renderer/panel-workspace-bar.ts +0 -42
  225. package/src/runtime/diagnostics/panels/panel-resources.ts +0 -118
  226. package/src/tools/agent-harness-panel-metadata.ts +0 -211
  227. /package/src/runtime/perf/{panel-health-monitor.ts → component-health.ts} +0 -0
@@ -1,233 +0,0 @@
1
- import type { Line } from '../types/grid.ts';
2
- import { createEmptyLine } from '../types/grid.ts';
3
- import { ScrollableListPanel } from './scrollable-list-panel.ts';
4
- import {
5
- buildDetailBlock,
6
- buildEmptyState,
7
- buildGuidanceLine,
8
- buildKeyValueLine,
9
- buildPanelListRow,
10
- buildPanelLine,
11
- buildSummaryBlock,
12
- buildPanelWorkspace,
13
- DEFAULT_PANEL_PALETTE,
14
- resolvePrimaryScrollableSection,
15
- type PanelWorkspaceSection,
16
- } from './polish.ts';
17
- import {
18
- type ProviderAuthRoute,
19
- type ProviderAccountRecord,
20
- type ProviderAccountSnapshot,
21
- type ProviderAccountSnapshotQuery,
22
- } from './provider-account-snapshot.ts';
23
- import { formatProviderAuthRouteId } from '../provider-auth-route-display.ts';
24
-
25
- export interface ProviderAccountsPanelDeps {
26
- readonly providerAccounts: ProviderAccountSnapshotQuery;
27
- }
28
-
29
- const C = {
30
- ...DEFAULT_PANEL_PALETTE,
31
- selectBg: '#1e293b',
32
- } as const;
33
-
34
- function routeColor(route: ProviderAuthRoute): string {
35
- if (route === 'subscription') return C.info;
36
- if (route === 'api-key') return C.warn;
37
- if (route === 'service-oauth') return C.value;
38
- if (route === 'unconfigured') return C.bad;
39
- return C.dim;
40
- }
41
-
42
- function formatRouteList(routes: readonly ProviderAuthRoute[]): string {
43
- return routes.map((route) => formatProviderAuthRouteId(route)).join(', ');
44
- }
45
-
46
- export class ProviderAccountsPanel extends ScrollableListPanel<ProviderAccountRecord> {
47
- private records: ProviderAccountRecord[] = [];
48
- private loading = false;
49
- private readonly providerAccounts: ProviderAccountSnapshotQuery;
50
-
51
- public constructor(deps: ProviderAccountsPanelDeps) {
52
- super('accounts', 'Accounts', 'Q', 'monitoring');
53
- this.showSelectionGutter = true; // I5: non-color selection affordance
54
- this.providerAccounts = deps.providerAccounts;
55
- void this.refresh();
56
- }
57
-
58
- public override onActivate(): void {
59
- super.onActivate();
60
- if (!this.loading) void this.refresh();
61
- }
62
-
63
- protected getItems(): readonly ProviderAccountRecord[] {
64
- return this.records;
65
- }
66
-
67
- protected renderItem(item: ProviderAccountRecord, _index: number, selected: boolean, width: number): Line {
68
- const activeRoute = formatProviderAuthRouteId(item.activeRoute);
69
- return buildPanelListRow(width, [
70
- { text: item.providerId.padEnd(16), fg: item.active ? C.good : C.value },
71
- { text: ` ${activeRoute.padEnd(14)}`, fg: routeColor(item.activeRoute) },
72
- { text: ` models ${String(item.modelCount).padEnd(4)}`, fg: C.dim },
73
- { text: ` ${item.authFreshness.padEnd(10)}`, fg: item.authFreshness === 'expired' ? C.bad : item.authFreshness === 'expiring' || item.authFreshness === 'pending' ? C.warn : C.dim },
74
- { text: ` issues ${String(item.issues.length).padEnd(2)}`, fg: item.issues.length > 0 ? C.bad : C.good },
75
- ], C, { selected });
76
- }
77
-
78
- public handleInput(key: string): boolean {
79
- if (key === 'r') {
80
- void this.refresh();
81
- return true;
82
- }
83
- return super.handleInput(key);
84
- }
85
-
86
- private async refresh(): Promise<void> {
87
- this.loading = true;
88
- this.markDirty();
89
- const snapshot = await this.buildSnapshot();
90
- this.records = [...snapshot.providers];
91
- this.clampSelection();
92
- this.loading = false;
93
- this.markDirty();
94
- }
95
-
96
- private async buildSnapshot(): Promise<ProviderAccountSnapshot> {
97
- return this.providerAccounts.loadSnapshot();
98
- }
99
-
100
- public render(width: number, height: number): Line[] {
101
- this.needsRender = false;
102
- const intro = 'Provider auth routes, subscription posture, quota-window hints, and routing-safety notes.';
103
- const footerLines = [buildPanelLine(width, [[' Up/Down move r refresh /accounts routes <provider> /accounts repair <provider>', C.dim]])];
104
- if (this.loading && this.records.length === 0) {
105
- const lines = buildPanelWorkspace(width, height, {
106
- title: 'Provider Account Control Room',
107
- intro,
108
- sections: [{ lines: [buildPanelLine(width, [[' Loading provider account posture...', C.info]])] }],
109
- palette: C,
110
- });
111
- while (lines.length < height) lines.push(createEmptyLine(width));
112
- return lines;
113
- }
114
- if (this.records.length === 0) {
115
- const lines = buildPanelWorkspace(width, height, {
116
- title: 'Provider Account Control Room',
117
- intro,
118
- sections: [{
119
- lines: buildEmptyState(width, ' No provider accounts discovered.', 'Configure API keys or subscriptions to populate account routing posture.', [{ command: '/provider', summary: 'review current provider and model posture' }], C),
120
- }],
121
- palette: C,
122
- });
123
- while (lines.length < height) lines.push(createEmptyLine(width));
124
- return lines;
125
- }
126
- const issueCount = this.records.reduce((sum, record) => sum + record.issues.length + (record.fallbackRisk ? 1 : 0), 0);
127
- const expiredCount = this.records.filter((record) => record.authFreshness === 'expired').length;
128
- const pendingCount = this.records.filter((record) => record.pendingLogin).length;
129
- const fallbackCount = this.records.filter((record) => Boolean(record.fallbackRisk)).length;
130
- const selected = this.records[this.selectedIndex]!;
131
- const postureLines: Line[] = [
132
- buildKeyValueLine(width, [
133
- { label: 'providers', value: String(this.records.length), valueColor: C.value },
134
- { label: 'expired auth', value: String(expiredCount), valueColor: expiredCount > 0 ? C.bad : C.good },
135
- { label: 'pending login', value: String(pendingCount), valueColor: pendingCount > 0 ? C.warn : C.dim },
136
- { label: 'routing risk', value: String(fallbackCount), valueColor: fallbackCount > 0 ? C.warn : C.good },
137
- ], C),
138
- buildKeyValueLine(width, [
139
- { label: 'total issues', value: String(issueCount), valueColor: issueCount > 0 ? C.bad : C.good },
140
- { label: 'selected', value: selected.providerId, valueColor: C.info },
141
- { label: 'route', value: formatProviderAuthRouteId(selected.activeRoute), valueColor: routeColor(selected.activeRoute) },
142
- { label: 'freshness', value: selected.authFreshness, valueColor: selected.authFreshness === 'expired' ? C.bad : selected.authFreshness === 'expiring' || selected.authFreshness === 'pending' ? C.warn : C.good },
143
- ], C),
144
- buildGuidanceLine(width, '/accounts repair <provider>', 'review routing safety, fallback cost, and provider-specific recovery steps', C),
145
- ];
146
- const detailRows: Line[] = [
147
- buildKeyValueLine(width, [
148
- { label: 'provider', value: selected.providerId, valueColor: C.value },
149
- { label: 'active route', value: formatProviderAuthRouteId(selected.activeRoute), valueColor: routeColor(selected.activeRoute) },
150
- { label: 'preferred route', value: formatProviderAuthRouteId(selected.preferredRoute), valueColor: C.dim },
151
- { label: 'freshness', value: selected.authFreshness, valueColor: selected.authFreshness === 'expired' ? C.bad : selected.authFreshness === 'expiring' || selected.authFreshness === 'pending' ? C.warn : C.good },
152
- ], C),
153
- buildKeyValueLine(width, [
154
- { label: 'configured', value: selected.configured ? 'yes' : 'no', valueColor: selected.configured ? C.good : C.bad },
155
- { label: 'oauth ready', value: selected.oauthReady ? 'yes' : 'no', valueColor: selected.oauthReady ? C.info : C.dim },
156
- { label: 'pending login', value: selected.pendingLogin ? 'yes' : 'no', valueColor: selected.pendingLogin ? C.warn : C.dim },
157
- ], C),
158
- buildPanelLine(width, [[` Route reason ${selected.activeRouteReason}`.slice(0, width), C.dim]]),
159
- buildPanelLine(width, [[` Available routes ${formatRouteList(selected.availableRoutes) || 'unconfigured'}`.slice(0, width), C.dim]]),
160
- ];
161
- if (selected.expiresAt) {
162
- detailRows.push(buildPanelLine(width, [
163
- [' Expires ', C.label],
164
- [new Date(selected.expiresAt).toISOString(), C.dim],
165
- [' Token ', C.label],
166
- [selected.tokenType ?? 'n/a', C.value],
167
- ]));
168
- }
169
- if (selected.fallbackRisk) {
170
- detailRows.push(buildPanelLine(width, [[` routing risk ${selected.fallbackRisk}`.slice(0, width), C.warn]]));
171
- }
172
- for (const route of selected.routeRecords) {
173
- detailRows.push(buildPanelLine(width, [[
174
- ` route ${formatProviderAuthRouteId(route.route)} ${route.usable ? 'usable' : 'blocked'} • ${route.freshness} • ${route.detail}`.slice(0, width),
175
- route.usable ? C.dim : C.bad,
176
- ]]));
177
- for (const issue of route.issues) {
178
- detailRows.push(buildPanelLine(width, [[` issue ${issue}`.slice(0, width), C.bad]]));
179
- }
180
- }
181
- for (const windowHint of selected.usageWindows) {
182
- detailRows.push(buildPanelLine(width, [[` ${windowHint.label} ${windowHint.detail}`.slice(0, width), C.dim]]));
183
- }
184
- for (const issue of selected.issues) {
185
- detailRows.push(buildPanelLine(width, [[` issue ${issue}`.slice(0, width), C.bad]]));
186
- }
187
- for (const note of selected.notes) {
188
- detailRows.push(buildPanelLine(width, [[` note ${note}`.slice(0, width), C.info]]));
189
- }
190
- for (const action of selected.recommendedActions) {
191
- detailRows.push(buildPanelLine(width, [[` next ${action}`.slice(0, width), C.value]]));
192
- }
193
- if (selected.issues.length === 0 && selected.notes.length === 0 && selected.usageWindows.length === 0 && selected.recommendedActions.length === 0) {
194
- detailRows.push(buildPanelLine(width, [[' No active account warnings for this provider.', C.dim]]));
195
- }
196
- const postureSection: PanelWorkspaceSection = { lines: buildSummaryBlock(width, 'Provider posture', postureLines, C) };
197
- const detailsSection: PanelWorkspaceSection = { lines: buildDetailBlock(width, 'Selected provider', detailRows, C) };
198
- const rawProviderLines: Line[] = this.records.map((record, absolute) =>
199
- this.renderItem(record, absolute, absolute === this.selectedIndex, width),
200
- );
201
- const resolvedProvidersSection = resolvePrimaryScrollableSection(width, height, {
202
- intro,
203
- footerLines,
204
- palette: C,
205
- beforeSections: [postureSection],
206
- section: {
207
- title: 'Providers',
208
- scrollableLines: rawProviderLines,
209
- selectedIndex: this.selectedIndex,
210
- scrollOffset: this.scrollStart,
211
- guardRows: 1,
212
- minRows: 4,
213
- appendWindowSummary: { dimColor: C.dim },
214
- },
215
- afterSections: [detailsSection],
216
- });
217
- this.scrollStart = resolvedProvidersSection.scrollOffset;
218
- const sections: PanelWorkspaceSection[] = [
219
- postureSection,
220
- resolvedProvidersSection.section,
221
- detailsSection,
222
- ];
223
- const lines = buildPanelWorkspace(width, height, {
224
- title: 'Provider Account Control Room',
225
- intro,
226
- sections,
227
- footerLines,
228
- palette: C,
229
- });
230
- while (lines.length < height) lines.push(createEmptyLine(width));
231
- return lines.slice(0, height);
232
- }
233
- }
@@ -1,208 +0,0 @@
1
- import type { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
2
- import { evaluateSessionMaintenance } from '@/runtime/index.ts';
3
- import type {
4
- UiContinuitySnapshot,
5
- UiLocalAuthSnapshot,
6
- UiRemoteSnapshot,
7
- UiSecuritySnapshot,
8
- UiSessionSnapshot,
9
- UiSettingsSnapshot,
10
- } from '../runtime/ui-read-models.ts';
11
-
12
- export interface HealthDomainSummary {
13
- readonly name: string;
14
- readonly level: 'good' | 'warn' | 'bad' | 'info';
15
- readonly summary: string;
16
- readonly next: string;
17
- readonly details: readonly string[];
18
- readonly nextSteps: readonly string[];
19
- }
20
-
21
- export interface ProviderHealthDomainInputs {
22
- readonly configManager: Pick<ConfigManager, 'get'>;
23
- readonly auth: UiLocalAuthSnapshot;
24
- readonly settings: UiSettingsSnapshot;
25
- readonly remote: UiRemoteSnapshot;
26
- readonly security: UiSecuritySnapshot;
27
- readonly continuity: UiContinuitySnapshot;
28
- readonly session: UiSessionSnapshot;
29
- }
30
-
31
- type ProviderHealthMcpServer = UiSecuritySnapshot['mcpServers'][number];
32
- type ProviderHealthRemoteSession = UiRemoteSnapshot['supervisor']['sessions'][number];
33
- type ProviderHealthStatusValue =
34
- | ProviderHealthMcpServer['schemaFreshness']
35
- | ProviderHealthMcpServer['trustMode']
36
- | ProviderHealthRemoteSession['transportState']
37
- | ProviderHealthRemoteSession['heartbeat']['status'];
38
-
39
- function formatProviderHealthStatus(value: ProviderHealthStatusValue): string {
40
- if (value === 'quarantined') return 'needs review';
41
- return value.replace(/[_-]+/g, ' ');
42
- }
43
-
44
- function formatProviderHealthTrustMode(mode: ProviderHealthMcpServer['trustMode']): string {
45
- if (mode === 'ask-on-risk') return 'ask on risky actions';
46
- if (mode === 'allow-all') return 'allow all actions';
47
- return formatProviderHealthStatus(mode);
48
- }
49
-
50
- function formatMcpServerDetail(server: ProviderHealthMcpServer): string {
51
- const parts = [
52
- `${server.name}: trust ${formatProviderHealthTrustMode(server.trustMode)}`,
53
- `status ${formatProviderHealthStatus(server.schemaFreshness)}`,
54
- ];
55
- if (server.quarantineReason) {
56
- parts.push(`review reason ${server.quarantineReason}`);
57
- }
58
- return parts.join('; ');
59
- }
60
-
61
- function formatRemoteSessionDetail(session: ProviderHealthRemoteSession): string {
62
- const parts = [
63
- `${session.runnerId}: transport ${formatProviderHealthStatus(session.transportState)}`,
64
- `heartbeat ${formatProviderHealthStatus(session.heartbeat.status)}`,
65
- ];
66
- if (session.lastError) {
67
- parts.push(`error ${session.lastError}`);
68
- }
69
- return parts.join('; ');
70
- }
71
-
72
- export function buildProviderHealthDomainSummaries(
73
- input: ProviderHealthDomainInputs,
74
- ): HealthDomainSummary[] {
75
- const summaries: HealthDomainSummary[] = [];
76
- const {
77
- configManager,
78
- auth,
79
- settings,
80
- remote,
81
- security,
82
- continuity,
83
- session,
84
- } = input;
85
-
86
- summaries.push({
87
- name: 'auth',
88
- level: auth.bootstrapCredentialPresent ? 'warn' : 'info',
89
- summary: auth.bootstrapCredentialPresent
90
- ? 'connected-host bootstrap credential visible in local compatibility state'
91
- : 'connected-host auth administration belongs outside Agent',
92
- next: '/auth review',
93
- details: [
94
- 'GoodVibes Agent does not create, delete, rotate, revoke, or clear connected-host auth users or sessions.',
95
- `${auth.userCount} compatibility user record(s) and ${auth.sessionCount} session record(s) are visible for diagnostics only.`,
96
- auth.bootstrapCredentialPresent ? 'Bootstrap cleanup must be done in the owning GoodVibes host.' : '',
97
- ].filter(Boolean),
98
- nextSteps: auth.bootstrapCredentialPresent
99
- ? ['/auth review', '/provider', '/subscription providers']
100
- : ['/auth review', '/provider'],
101
- });
102
-
103
- const settingIssueCount = settings.conflictCount + settings.recentFailureCount + (settings.hasStagedManagedBundle ? 1 : 0);
104
- summaries.push({
105
- name: 'settings',
106
- level: !settings.available ? 'info' : settingIssueCount > 0 ? 'warn' : 'good',
107
- summary: !settings.available
108
- ? 'settings API unavailable'
109
- : settingIssueCount > 0
110
- ? `${settings.conflictCount} conflicts / ${settings.recentFailureCount} failures${settings.hasStagedManagedBundle ? ' / pending managed bundle' : ''}`
111
- : 'settings API clean',
112
- next: settingIssueCount > 0 ? '/settings' : '/config <key>',
113
- details: [
114
- settings.conflictCount > 0 ? `${settings.conflictCount} unresolved import conflict(s)` : '',
115
- settings.recentFailureCount > 0 ? `${settings.recentFailureCount} recent sync or managed failure(s)` : '',
116
- settings.hasStagedManagedBundle ? 'pending managed settings bundle awaits apply or rollback' : '',
117
- settings.managedLockCount > 0 ? `${settings.managedLockCount} managed lock(s) enforced` : '',
118
- ].filter(Boolean),
119
- nextSteps: settingIssueCount > 0
120
- ? ['/settings', '/config <key>', '/health settings']
121
- : ['/config <key>'],
122
- });
123
-
124
- summaries.push({
125
- name: 'remote',
126
- level: remote.supervisor.degradedConnections > 0 ? 'warn' : remote.supervisor.sessions.length > 0 ? 'good' : 'info',
127
- summary: remote.supervisor.sessions.length === 0
128
- ? 'no remote sessions tracked'
129
- : `${remote.supervisor.sessions.length} sessions / ${remote.supervisor.degradedConnections} degraded`,
130
- next: remote.supervisor.degradedConnections > 0 ? '/delegate <build/fix/review task>' : '/health remote',
131
- details: remote.supervisor.sessions.length === 0
132
- ? ['no remote sessions have been attached yet']
133
- : remote.supervisor.sessions
134
- .filter((entry) =>
135
- entry.transportState === 'degraded'
136
- || entry.transportState === 'reconnecting'
137
- || entry.transportState === 'terminal_failure'
138
- || entry.heartbeat.status !== 'fresh'
139
- || Boolean(entry.lastError))
140
- .slice(0, 3)
141
- .map(formatRemoteSessionDetail),
142
- nextSteps: remote.supervisor.degradedConnections > 0
143
- ? ['/delegate <build/fix/review task>', '/health remote']
144
- : ['/health remote'],
145
- });
146
-
147
- const degradedServers = security.mcpServers.filter((server) =>
148
- !server.connected
149
- || server.schemaFreshness !== 'fresh'
150
- || Boolean(server.quarantineReason)
151
- || server.trustMode === 'allow-all');
152
- const connectedServerCount = security.mcpServers.filter((server) => server.connected).length;
153
- summaries.push({
154
- name: 'mcp',
155
- level: degradedServers.length > 0 ? 'warn' : security.mcpServers.length > 0 ? 'good' : 'info',
156
- summary: security.mcpServers.length === 0
157
- ? 'no MCP servers configured'
158
- : `${connectedServerCount}/${security.mcpServers.length} connected, ${degradedServers.length} need review`,
159
- next: degradedServers.length > 0 ? '/mcp repair' : '/mcp review',
160
- details: degradedServers.length === 0
161
- ? (security.mcpServers.length === 0 ? ['no MCP servers registered'] : ['all MCP servers are healthy'])
162
- : degradedServers
163
- .slice(0, 3)
164
- .map(formatMcpServerDetail),
165
- nextSteps: degradedServers.length > 0
166
- ? ['/mcp review', '/mcp auth-review', '/mcp repair']
167
- : ['/mcp review'],
168
- });
169
-
170
- const maintenance = evaluateSessionMaintenance({
171
- configManager,
172
- currentTokens: session.estimatedContextTokens,
173
- contextWindow: session.contextWindow,
174
- messageCount: session.messageCount,
175
- session: session.session,
176
- });
177
- summaries.push({
178
- name: 'maintenance',
179
- level: maintenance.level === 'needs-repair'
180
- ? 'bad'
181
- : maintenance.level === 'suggest-compact' || maintenance.level === 'watch'
182
- ? 'warn'
183
- : 'good',
184
- summary: maintenance.summary,
185
- next: maintenance.nextSteps[0] ?? '/health maintenance',
186
- details: maintenance.reasons.slice(0, 3),
187
- nextSteps: maintenance.nextSteps,
188
- });
189
-
190
- summaries.push({
191
- name: 'continuity',
192
- level: continuity.recoveryFilePresent ? 'warn' : continuity.lastSessionPointer ? 'good' : 'info',
193
- summary: continuity.recoveryFilePresent
194
- ? `recovery file present for ${continuity.sessionId || '(unknown session)'}`
195
- : continuity.lastSessionPointer ? `last session pointer ${continuity.lastSessionPointer}` : 'no last-session pointer',
196
- next: continuity.recoveryFilePresent ? '/session resume <id>' : '/session list',
197
- details: [
198
- continuity.returnContext ? `last activity: ${continuity.returnContext.activityLabel}` : '',
199
- continuity.returnContext ? `resume posture: ${continuity.returnContext.statusLabel}` : '',
200
- !continuity.lastSessionPointer ? 'no persisted last-session pointer recorded' : '',
201
- ].filter(Boolean),
202
- nextSteps: continuity.recoveryFilePresent
203
- ? ['/session list', '/session resume <id>', '/health continuity']
204
- : ['/session list'],
205
- });
206
-
207
- return summaries;
208
- }