@pellux/goodvibes-agent 1.4.4 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +52 -7
  3. package/dist/package/main.js +7491 -12253
  4. package/docs/README.md +2 -2
  5. package/docs/channels-remote-and-api.md +1 -1
  6. package/docs/getting-started.md +2 -2
  7. package/docs/release-and-publishing.md +1 -1
  8. package/docs/tools-and-commands.md +5 -5
  9. package/package.json +4 -2
  10. package/release/live-verification/live-verification.json +13 -13
  11. package/release/live-verification/live-verification.md +15 -15
  12. package/release/performance-snapshot.json +2 -2
  13. package/release/release-notes.md +5 -7
  14. package/release/release-readiness.json +6 -6
  15. package/src/agent/behavior-discovery-summary.ts +4 -18
  16. package/src/agent/calendar-registry.ts +322 -0
  17. package/src/agent/competitive-feature-inventory.ts +268 -130
  18. package/src/agent/document-registry.ts +5 -1
  19. package/src/agent/email/email-service.ts +350 -0
  20. package/src/agent/email/imap-client.ts +596 -0
  21. package/src/agent/email/smtp-client.ts +453 -0
  22. package/src/agent/ics-calendar.ts +662 -0
  23. package/src/agent/ics-timezone.ts +172 -0
  24. package/src/agent/markdown-frontmatter.ts +31 -0
  25. package/src/agent/memory-safety.ts +1 -1
  26. package/src/agent/note-registry.ts +3 -9
  27. package/src/agent/persona-discovery.ts +3 -15
  28. package/src/agent/persona-registry.ts +1 -10
  29. package/src/agent/research-source-registry.ts +5 -1
  30. package/src/agent/routine-discovery.ts +3 -15
  31. package/src/agent/runtime-profile.ts +3 -0
  32. package/src/agent/skill-discovery.ts +3 -15
  33. package/src/agent/skill-draft-proposer.ts +203 -0
  34. package/src/agent/skill-draft-runner.ts +201 -0
  35. package/src/agent/skill-registry.ts +36 -1
  36. package/src/agent/skill-standard.ts +99 -0
  37. package/src/agent/vibe-file.ts +7 -22
  38. package/src/cli/completion.ts +1 -1
  39. package/src/cli/config-overrides.ts +10 -1
  40. package/src/cli/redaction.ts +17 -5
  41. package/src/config/provider-model.ts +2 -1
  42. package/src/config/secret-config.ts +13 -6
  43. package/src/core/activity-feed.ts +97 -0
  44. package/src/core/away-digest.ts +161 -0
  45. package/src/core/conversation-rendering.ts +7 -3
  46. package/src/core/conversation.ts +22 -15
  47. package/src/core/hardware-profile.ts +362 -0
  48. package/src/core/last-seen-store.ts +78 -0
  49. package/src/core/plain-language.ts +52 -0
  50. package/src/core/setup-incomplete-hint.ts +90 -0
  51. package/src/core/system-message-router.ts +38 -87
  52. package/src/input/agent-workspace-basic-command-editor-submission.ts +60 -220
  53. package/src/input/agent-workspace-basic-command-editors.ts +39 -141
  54. package/src/input/agent-workspace-categories.ts +40 -125
  55. package/src/input/agent-workspace-command-editor.ts +4 -0
  56. package/src/input/agent-workspace-host-category.ts +0 -5
  57. package/src/input/agent-workspace-local-editor-submission.ts +3 -1
  58. package/src/input/agent-workspace-navigation.ts +10 -3
  59. package/src/input/agent-workspace-onboarding-actions.ts +132 -0
  60. package/src/input/agent-workspace-onboarding-categories.ts +29 -26
  61. package/src/input/agent-workspace-onboarding-finish.ts +11 -4
  62. package/src/input/agent-workspace-onboarding-state.ts +111 -0
  63. package/src/input/agent-workspace-profile-editor-submission.ts +260 -0
  64. package/src/input/agent-workspace-profile-editors.ts +155 -0
  65. package/src/input/agent-workspace-subscription-editor.ts +7 -0
  66. package/src/input/agent-workspace-types.ts +5 -1
  67. package/src/input/agent-workspace.ts +65 -39
  68. package/src/input/command-registry.ts +18 -5
  69. package/src/input/commands/agent-runtime-profile-runtime.ts +2 -1
  70. package/src/input/commands/agent-skills-runtime.ts +60 -3
  71. package/src/input/commands/calendar-runtime.ts +209 -0
  72. package/src/input/commands/channels-runtime.ts +1 -0
  73. package/src/input/commands/command-error.ts +9 -0
  74. package/src/input/commands/compat-runtime.ts +1 -0
  75. package/src/input/commands/config.ts +1 -0
  76. package/src/input/commands/conversation-runtime.ts +1 -1
  77. package/src/input/commands/delegation-runtime.ts +5 -6
  78. package/src/input/commands/email-runtime.ts +387 -0
  79. package/src/input/commands/experience-runtime.ts +17 -4
  80. package/src/input/commands/guidance-runtime.ts +1 -1
  81. package/src/input/commands/health-runtime.ts +6 -1
  82. package/src/input/commands/knowledge-format.ts +73 -0
  83. package/src/input/commands/knowledge.ts +9 -74
  84. package/src/input/commands/local-provider-runtime.ts +2 -1
  85. package/src/input/commands/local-runtime.ts +10 -4
  86. package/src/input/commands/mcp-runtime.ts +7 -0
  87. package/src/input/commands/notify-runtime.ts +17 -1
  88. package/src/input/commands/onboarding-runtime.ts +1 -0
  89. package/src/input/commands/operator-actions-runtime.ts +1 -0
  90. package/src/input/commands/operator-runtime.ts +3 -0
  91. package/src/input/commands/personas-runtime.ts +1 -0
  92. package/src/input/commands/platform-access-runtime.ts +40 -17
  93. package/src/input/commands/product-runtime.ts +6 -1
  94. package/src/input/commands/provider-accounts-runtime.ts +3 -2
  95. package/src/input/commands/qrcode-runtime.ts +1 -0
  96. package/src/input/commands/routines-runtime.ts +1 -0
  97. package/src/input/commands/runtime-services.ts +0 -13
  98. package/src/input/commands/security-runtime.ts +1 -0
  99. package/src/input/commands/session-content.ts +1 -0
  100. package/src/input/commands/shell-core.ts +5 -2
  101. package/src/input/commands/subscription-runtime.ts +33 -9
  102. package/src/input/commands/support-bundle-runtime.ts +8 -1
  103. package/src/input/commands/tasks-runtime.ts +1 -0
  104. package/src/input/commands/tts-runtime.ts +1 -0
  105. package/src/input/commands/vibe-runtime.ts +1 -0
  106. package/src/input/commands.ts +4 -0
  107. package/src/input/feed-context-factory.ts +3 -12
  108. package/src/input/handler-command-route.ts +7 -60
  109. package/src/input/handler-feed-routes.ts +0 -194
  110. package/src/input/handler-feed.ts +2 -54
  111. package/src/input/handler-interactions.ts +0 -2
  112. package/src/input/handler-modal-stack.ts +0 -9
  113. package/src/input/handler-picker-routes.ts +24 -1
  114. package/src/input/handler-shortcuts.ts +11 -40
  115. package/src/input/handler-ui-state.ts +13 -0
  116. package/src/input/handler.ts +8 -35
  117. package/src/input/keybindings.ts +40 -17
  118. package/src/input/model-picker-local-fit.ts +130 -0
  119. package/src/input/shell-passthrough.ts +58 -0
  120. package/src/input/submission-router.ts +0 -5
  121. package/src/main.ts +129 -90
  122. package/src/renderer/activity-sidebar.ts +186 -0
  123. package/src/renderer/agent-workspace-context-lines.ts +5 -48
  124. package/src/renderer/agent-workspace-style.ts +1 -1
  125. package/src/renderer/agent-workspace.ts +14 -2
  126. package/src/renderer/compositor.ts +37 -125
  127. package/src/renderer/conversation-overlays.ts +3 -3
  128. package/src/renderer/help-overlay.ts +7 -5
  129. package/src/renderer/model-workspace.ts +101 -86
  130. package/src/{panels → renderer}/polish.ts +3 -3
  131. package/src/renderer/shell-surface.ts +4 -5
  132. package/src/renderer/status-token.ts +31 -11
  133. package/src/renderer/tab-strip.ts +1 -1
  134. package/src/renderer/tool-call.ts +7 -8
  135. package/src/renderer/tool-labels.ts +92 -0
  136. package/src/renderer/ui-factory.ts +48 -96
  137. package/src/runtime/bootstrap-command-context.ts +0 -5
  138. package/src/runtime/bootstrap-command-parts.ts +6 -15
  139. package/src/runtime/bootstrap-core.ts +34 -13
  140. package/src/runtime/bootstrap-hook-bridge.ts +3 -1
  141. package/src/runtime/bootstrap-shell.ts +3 -50
  142. package/src/runtime/bootstrap.ts +3 -4
  143. package/src/runtime/context.ts +1 -1
  144. package/src/runtime/diagnostics/panels/index.ts +0 -1
  145. package/src/runtime/diagnostics/panels/policy.ts +1 -1
  146. package/src/runtime/execution-ledger.ts +16 -1
  147. package/src/runtime/index.ts +6 -1
  148. package/src/runtime/onboarding/index.ts +1 -0
  149. package/src/runtime/onboarding/onboarding-state.ts +200 -0
  150. package/src/{panels → runtime}/provider-account-snapshot.ts +5 -2
  151. package/src/runtime/services.ts +16 -4
  152. package/src/runtime/terminal-output-guard.ts +7 -0
  153. package/src/runtime/tool-permission-safety.ts +1 -1
  154. package/src/runtime/ui/model-picker/data-provider.ts +1 -1
  155. package/src/runtime/ui/model-picker/health-enrichment.ts +2 -2
  156. package/src/runtime/ui/model-picker/types.ts +2 -2
  157. package/src/runtime/ui/provider-health/data-provider.ts +3 -3
  158. package/src/runtime/ui/provider-health/types.ts +2 -2
  159. package/src/runtime/ui-services.ts +0 -2
  160. package/src/shell/agent-workspace-fullscreen.ts +0 -1
  161. package/src/shell/autonomy-surfacing.ts +272 -0
  162. package/src/shell/session-continuity-hints.ts +0 -6
  163. package/src/shell/startup-wiring.ts +221 -0
  164. package/src/shell/ui-openers.ts +18 -29
  165. package/src/tools/agent-context-policy.ts +1 -1
  166. package/src/tools/agent-harness-background-processes.ts +4 -4
  167. package/src/tools/agent-harness-browser-cockpit-route.ts +3 -3
  168. package/src/tools/agent-harness-command-runner.ts +6 -1
  169. package/src/tools/agent-harness-document-ops.ts +26 -53
  170. package/src/tools/agent-harness-execution-history.ts +1 -13
  171. package/src/tools/agent-harness-execution-posture.ts +0 -15
  172. package/src/tools/agent-harness-keybinding-metadata.ts +28 -29
  173. package/src/tools/agent-harness-local-model-cookbook.ts +24 -1
  174. package/src/tools/agent-harness-metadata.ts +16 -0
  175. package/src/tools/agent-harness-mode-catalog.ts +2 -9
  176. package/src/tools/agent-harness-provider-account-metadata.ts +2 -2
  177. package/src/tools/agent-harness-setup-posture-utils.ts +1 -1
  178. package/src/tools/agent-harness-tool-schema.ts +13 -14
  179. package/src/tools/agent-harness-tool.ts +27 -23
  180. package/src/tools/agent-harness-ui-surface-metadata.ts +10 -20
  181. package/src/tools/agent-harness-workspace-action-runner.ts +3 -4
  182. package/src/tools/agent-workspace-tool.ts +2 -33
  183. package/src/utils/terminal-width.ts +9 -3
  184. package/src/version.ts +1 -1
  185. package/src/input/agent-workspace-panel-route.ts +0 -44
  186. package/src/input/panel-integration-actions.ts +0 -26
  187. package/src/panels/approval-panel.ts +0 -149
  188. package/src/panels/automation-control-panel.ts +0 -212
  189. package/src/panels/base-panel.ts +0 -254
  190. package/src/panels/builtin/agent.ts +0 -58
  191. package/src/panels/builtin/knowledge.ts +0 -26
  192. package/src/panels/builtin/operations.ts +0 -121
  193. package/src/panels/builtin/session.ts +0 -138
  194. package/src/panels/builtin/shared.ts +0 -275
  195. package/src/panels/builtin/usage.ts +0 -21
  196. package/src/panels/builtin-panels.ts +0 -23
  197. package/src/panels/confirm-state.ts +0 -61
  198. package/src/panels/context-visualizer-panel.ts +0 -204
  199. package/src/panels/cost-tracker-panel.ts +0 -444
  200. package/src/panels/docs-panel.ts +0 -285
  201. package/src/panels/index.ts +0 -25
  202. package/src/panels/knowledge-panel.ts +0 -417
  203. package/src/panels/memory-panel.ts +0 -226
  204. package/src/panels/panel-list-panel.ts +0 -464
  205. package/src/panels/panel-manager.ts +0 -570
  206. package/src/panels/provider-accounts-panel.ts +0 -233
  207. package/src/panels/provider-health-domains.ts +0 -208
  208. package/src/panels/provider-health-panel.ts +0 -720
  209. package/src/panels/provider-health-tracker.ts +0 -115
  210. package/src/panels/provider-stats-panel.ts +0 -366
  211. package/src/panels/qr-panel.ts +0 -207
  212. package/src/panels/schedule-panel.ts +0 -321
  213. package/src/panels/scrollable-list-panel.ts +0 -491
  214. package/src/panels/search-focus.ts +0 -32
  215. package/src/panels/security-panel.ts +0 -295
  216. package/src/panels/session-browser-panel.ts +0 -395
  217. package/src/panels/session-maintenance.ts +0 -125
  218. package/src/panels/subscription-panel.ts +0 -263
  219. package/src/panels/system-messages-panel.ts +0 -230
  220. package/src/panels/tasks-panel.ts +0 -344
  221. package/src/panels/thinking-panel.ts +0 -304
  222. package/src/panels/token-budget-panel.ts +0 -475
  223. package/src/panels/tool-inspector-panel.ts +0 -436
  224. package/src/panels/types.ts +0 -54
  225. package/src/renderer/panel-composite.ts +0 -158
  226. package/src/renderer/panel-tab-bar.ts +0 -69
  227. package/src/renderer/panel-workspace-bar.ts +0 -42
  228. package/src/runtime/diagnostics/panels/panel-resources.ts +0 -118
  229. package/src/tools/agent-harness-panel-metadata.ts +0 -211
  230. /package/src/runtime/perf/{panel-health-monitor.ts → component-health.ts} +0 -0
@@ -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
- }