@pellux/goodvibes-tui 0.18.12 → 0.18.13
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 +50 -0
- package/README.md +1 -1
- package/docs/foundation-artifacts/operator-contract.json +1 -1
- package/package.json +2 -2
- package/src/config/index.ts +1 -138
- package/src/config/subscription-providers.ts +1 -127
- package/src/core/conversation-rendering.ts +3 -3
- package/src/core/conversation.ts +176 -423
- package/src/core/history.ts +45 -0
- package/src/core/orchestrator.ts +3 -735
- package/src/core/system-message-router.ts +19 -58
- package/src/input/handler-content-actions.ts +2 -2
- package/src/input/handler-feed.ts +1 -1
- package/src/input/handler-modal-token-routes.ts +1 -1
- package/src/input/handler-ui-state.ts +1 -1
- package/src/input/handler.ts +1 -1
- package/src/input/search.ts +1 -1
- package/src/input/selection.ts +2 -2
- package/src/main.ts +1 -1
- package/src/panels/agent-inspector-panel.ts +3 -3
- package/src/panels/agent-logs-panel.ts +3 -3
- package/src/panels/approval-panel.ts +2 -2
- package/src/panels/automation-control-panel.ts +3 -3
- package/src/panels/base-panel.ts +14 -14
- package/src/panels/builtin/operations.ts +1 -1
- package/src/panels/builtin/session.ts +1 -1
- package/src/panels/builtin/shared.ts +3 -3
- package/src/panels/cockpit-panel.ts +2 -2
- package/src/panels/communication-panel.ts +3 -3
- package/src/panels/context-visualizer-panel.ts +2 -2
- package/src/panels/control-plane-panel.ts +3 -3
- package/src/panels/cost-tracker-panel.ts +3 -3
- package/src/panels/debug-panel.ts +2 -2
- package/src/panels/diff-panel.ts +2 -2
- package/src/panels/docs-panel.ts +1 -1
- package/src/panels/eval-panel.ts +2 -2
- package/src/panels/file-explorer-panel.ts +3 -3
- package/src/panels/file-preview-panel.ts +3 -3
- package/src/panels/forensics-panel.ts +2 -2
- package/src/panels/git-panel.ts +1 -1
- package/src/panels/hooks-panel.ts +3 -3
- package/src/panels/incident-review-panel.ts +1 -1
- package/src/panels/intelligence-panel.ts +2 -2
- package/src/panels/knowledge-panel.ts +1 -1
- package/src/panels/local-auth-panel.ts +2 -2
- package/src/panels/marketplace-panel.ts +1 -1
- package/src/panels/mcp-panel.ts +3 -3
- package/src/panels/memory-panel.ts +1 -1
- package/src/panels/ops-control-panel.ts +3 -3
- package/src/panels/ops-strategy-panel.ts +2 -2
- package/src/panels/orchestration-panel.ts +2 -2
- package/src/panels/panel-list-panel.ts +6 -6
- package/src/panels/plan-dashboard-panel.ts +1 -1
- package/src/panels/plugins-panel.ts +2 -2
- package/src/panels/policy-panel.ts +2 -2
- package/src/panels/polish.ts +3 -3
- package/src/panels/provider-accounts-panel.ts +2 -2
- package/src/panels/provider-health-panel.ts +2 -2
- package/src/panels/provider-stats-panel.ts +3 -3
- package/src/panels/remote-panel.ts +3 -3
- package/src/panels/routes-panel.ts +3 -3
- package/src/panels/sandbox-panel.ts +2 -2
- package/src/panels/schedule-panel.ts +1 -1
- package/src/panels/security-panel.ts +2 -2
- package/src/panels/services-panel.ts +2 -2
- package/src/panels/session-browser-panel.ts +2 -2
- package/src/panels/settings-sync-panel.ts +2 -2
- package/src/panels/skills-panel.ts +6 -6
- package/src/panels/subscription-panel.ts +2 -2
- package/src/panels/symbol-outline-panel.ts +3 -3
- package/src/panels/system-messages-panel.ts +4 -4
- package/src/panels/tasks-panel.ts +2 -2
- package/src/panels/thinking-panel.ts +3 -3
- package/src/panels/token-budget-panel.ts +1 -1
- package/src/panels/tool-inspector-panel.ts +3 -3
- package/src/panels/types.ts +5 -5
- package/src/panels/watchers-panel.ts +3 -3
- package/src/panels/welcome-panel.ts +1 -1
- package/src/panels/worktree-panel.ts +2 -2
- package/src/panels/wrfc-panel.ts +3 -3
- package/src/permissions/prompt.ts +3 -22
- package/src/plugins/loader.ts +15 -304
- package/src/renderer/agent-detail-modal.ts +1 -1
- package/src/renderer/autocomplete-overlay.ts +2 -2
- package/src/renderer/bookmark-modal.ts +1 -1
- package/src/renderer/bottom-bar.ts +2 -2
- package/src/renderer/buffer.ts +1 -1
- package/src/renderer/code-block.ts +2 -2
- package/src/renderer/compositor.ts +2 -2
- package/src/renderer/context-inspector.ts +1 -1
- package/src/renderer/conversation-layout.ts +2 -2
- package/src/renderer/conversation-overlays.ts +1 -1
- package/src/renderer/conversation-surface.ts +2 -2
- package/src/renderer/diff-view.ts +2 -2
- package/src/renderer/diff.ts +1 -1
- package/src/renderer/file-picker-overlay.ts +2 -2
- package/src/renderer/file-tree.ts +2 -2
- package/src/renderer/help-overlay.ts +1 -1
- package/src/renderer/history-search-overlay.ts +2 -2
- package/src/renderer/live-tail-modal.ts +1 -1
- package/src/renderer/markdown.ts +2 -2
- package/src/renderer/modal-factory.ts +3 -3
- package/src/renderer/model-picker-overlay.ts +2 -2
- package/src/renderer/overlay-box.ts +2 -2
- package/src/renderer/panel-composite.ts +1 -1
- package/src/renderer/panel-picker-overlay.ts +2 -2
- package/src/renderer/panel-tab-bar.ts +1 -1
- package/src/renderer/panel-workspace-bar.ts +1 -1
- package/src/renderer/process-indicator.ts +2 -2
- package/src/renderer/process-modal.ts +1 -1
- package/src/renderer/profile-picker-modal.ts +2 -2
- package/src/renderer/progress.ts +2 -2
- package/src/renderer/search-overlay.ts +2 -2
- package/src/renderer/selection-modal-overlay.ts +2 -2
- package/src/renderer/session-picker-modal.ts +2 -2
- package/src/renderer/settings-modal.ts +2 -2
- package/src/renderer/shell-surface.ts +1 -1
- package/src/renderer/system-message.ts +1 -1
- package/src/renderer/tab-strip.ts +2 -2
- package/src/renderer/text-layout.ts +1 -1
- package/src/renderer/thinking.ts +1 -1
- package/src/renderer/tool-call.ts +2 -2
- package/src/renderer/ui-factory.ts +2 -2
- package/src/runtime/bootstrap-command-context.ts +4 -5
- package/src/runtime/bootstrap-command-parts.ts +1 -3
- package/src/runtime/bootstrap-core.ts +3 -2
- package/src/runtime/bootstrap-hook-bridge.ts +15 -174
- package/src/runtime/bootstrap-shell.ts +4 -4
- package/src/runtime/bootstrap.ts +1 -1
- package/src/runtime/context.ts +4 -20
- package/src/runtime/diagnostics/panels/index.ts +1 -1
- package/src/runtime/diagnostics/panels/ops.ts +1 -1
- package/src/runtime/diagnostics/panels/panel-resources.ts +118 -0
- package/src/runtime/perf/panel-contracts.ts +32 -0
- package/src/runtime/perf/panel-health-monitor.ts +18 -0
- package/src/runtime/services.ts +4 -4
- package/src/runtime/store/domains/conversation.ts +1 -181
- package/src/runtime/store/domains/permissions.ts +1 -143
- package/src/runtime/store/helpers/reducers/conversation.ts +1 -228
- package/src/runtime/store/helpers/reducers/lifecycle.ts +1 -440
- package/src/runtime/store/selectors/index.ts +11 -6
- package/src/runtime/store/state.ts +12 -4
- package/src/runtime/ui-events.ts +46 -0
- package/src/runtime/ui-services.ts +1 -1
- package/src/shell/ui-openers.ts +1 -1
- package/src/tools/index.ts +1 -186
- package/src/types/grid.ts +48 -0
- package/src/utils/clipboard.ts +21 -0
- package/src/utils/splash-lines.ts +1 -1
- package/src/utils/terminal-width.ts +185 -0
- package/src/version.ts +1 -1
- package/src/daemon/facade-composition.ts +0 -398
- package/src/daemon/facade.ts +0 -638
- package/src/daemon/surface-policy.ts +0 -60
- package/src/daemon/types.ts +0 -191
- package/src/runtime/ui-read-models-core.ts +0 -95
- package/src/runtime/ui-read-models-operations.ts +0 -203
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
* - Footer hints: [Tab] Category [↑↓] Navigate [Enter] Edit/Toggle [Esc] Close
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import type { Line } from '
|
|
13
|
+
import type { Line } from '../types/grid.ts';
|
|
14
14
|
import { ModalFactory } from './modal-factory.ts';
|
|
15
15
|
import type { SettingsModal, SettingEntry, FlagEntry, McpEntry, SubscriptionEntry } from '../input/settings-modal.ts';
|
|
16
16
|
import { SETTINGS_CATEGORIES } from '../input/settings-modal.ts';
|
|
17
|
-
import { fitDisplay, truncateDisplay } from '
|
|
17
|
+
import { fitDisplay, truncateDisplay } from '../utils/terminal-width.ts';
|
|
18
18
|
import { getOverlaySurfaceMetrics, getStableOverlayContentRows } from './overlay-viewport.ts';
|
|
19
19
|
import { getVisibleWindow } from './surface-layout.ts';
|
|
20
20
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Line } from '
|
|
2
|
-
import { getDisplayWidth } from '
|
|
1
|
+
import type { Line } from '../types/grid.ts';
|
|
2
|
+
import { getDisplayWidth } from '../utils/terminal-width.ts';
|
|
3
3
|
import { buildStyledPanelLine, type StyledPanelSegment } from '../panels/polish.ts';
|
|
4
4
|
|
|
5
5
|
export interface TabStripItem {
|
package/src/renderer/thinking.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type Line, createStyledCell, createEmptyLine } from '
|
|
1
|
+
import { type Line, createStyledCell, createEmptyLine } from '../types/grid.ts';
|
|
2
2
|
import { LAYOUT, TOOL_STATUS } from './layout.ts';
|
|
3
|
-
import { getDisplayWidth, truncateDisplay } from '
|
|
3
|
+
import { getDisplayWidth, truncateDisplay } from '../utils/terminal-width.ts';
|
|
4
4
|
import type { ToolCall } from '@pellux/goodvibes-sdk/platform/types/tools';
|
|
5
5
|
|
|
6
6
|
const TOOL_NAME_MIN_WIDTH = 8;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { type Line, type Cell, createEmptyLine, createEmptyCell } from '
|
|
1
|
+
import { type Line, type Cell, createEmptyLine, createEmptyCell } from '../types/grid.ts';
|
|
2
2
|
import { LAYOUT } from './layout.ts';
|
|
3
3
|
import { VERSION } from '../version.ts';
|
|
4
|
-
import { fitDisplay, getDisplayWidth, truncateDisplay, wrapText, interpolateColor } from '
|
|
4
|
+
import { fitDisplay, getDisplayWidth, truncateDisplay, wrapText, interpolateColor } from '../utils/terminal-width.ts';
|
|
5
5
|
import type { GitHeaderInfo } from './git-status.ts';
|
|
6
6
|
import { renderConversationFragment, renderConversationStatusLine, type ConversationStatusSegment } from './conversation-surface.ts';
|
|
7
7
|
import { GLYPHS } from './ui-primitives.ts';
|
|
@@ -7,8 +7,8 @@ import type { McpApi } from '@pellux/goodvibes-sdk/platform/mcp/mcp-api';
|
|
|
7
7
|
import type { PanelManager } from '../panels/panel-manager.ts';
|
|
8
8
|
import type { ProviderApi } from '@pellux/goodvibes-sdk/platform/providers/provider-api';
|
|
9
9
|
import type { OpsApi } from '@pellux/goodvibes-sdk/platform/runtime/ops-api';
|
|
10
|
+
import type { MutableRuntimeState } from '@pellux/goodvibes-sdk/platform/runtime/mutable-runtime-state';
|
|
10
11
|
import type { ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers/registry';
|
|
11
|
-
import type { MutableRuntimeState } from './context.ts';
|
|
12
12
|
import type { CommandContext } from '../input/command-registry.ts';
|
|
13
13
|
import type { KeybindingsManager } from '../input/keybindings.ts';
|
|
14
14
|
import type { PermissionRequestHandler } from '@pellux/goodvibes-sdk/platform/permissions/prompt';
|
|
@@ -22,7 +22,6 @@ import type { IntegrationHelperService } from '@pellux/goodvibes-sdk/platform/ru
|
|
|
22
22
|
import type { KnowledgeService } from '@pellux/goodvibes-sdk/platform/knowledge/index';
|
|
23
23
|
import type { PluginManager } from '@pellux/goodvibes-sdk/platform/plugins/manager';
|
|
24
24
|
import type { HookWorkbench } from '@pellux/goodvibes-sdk/platform/hooks/workbench';
|
|
25
|
-
import type { PanelHealthMonitor } from '@pellux/goodvibes-sdk/platform/runtime/perf/panel-health-monitor';
|
|
26
25
|
import type { WorktreeRegistry } from '@pellux/goodvibes-sdk/platform/runtime/worktree/registry';
|
|
27
26
|
import type { SandboxSessionRegistry } from '@pellux/goodvibes-sdk/platform/runtime/sandbox/session-registry';
|
|
28
27
|
import type { UiReadModels } from './ui-read-models.ts';
|
|
@@ -101,13 +100,13 @@ export type CreateBootstrapCommandContextOptions = {
|
|
|
101
100
|
opsApi?: OpsApi;
|
|
102
101
|
directTransport?: DirectTransport;
|
|
103
102
|
panelManager: PanelManager;
|
|
104
|
-
panelHealthMonitor: PanelHealthMonitor;
|
|
105
103
|
worktreeRegistry: WorktreeRegistry;
|
|
106
104
|
sandboxSessionRegistry: SandboxSessionRegistry;
|
|
107
105
|
loadSystemPrompt: () => string;
|
|
108
106
|
activatePlan: (planId: string, task: string) => void;
|
|
109
107
|
completeModelSelectionSideEffect?: () => void;
|
|
110
108
|
sessionLineageTracker?: import('@pellux/goodvibes-sdk/platform/core/session-lineage').SessionLineageTracker;
|
|
109
|
+
componentHealthMonitor: import('@pellux/goodvibes-sdk/platform/runtime/perf/component-health-monitor').ComponentHealthMonitor;
|
|
111
110
|
};
|
|
112
111
|
|
|
113
112
|
export function createBootstrapCommandContext(
|
|
@@ -166,12 +165,12 @@ export function createBootstrapCommandContext(
|
|
|
166
165
|
opsApi,
|
|
167
166
|
directTransport,
|
|
168
167
|
panelManager,
|
|
169
|
-
panelHealthMonitor,
|
|
170
168
|
worktreeRegistry,
|
|
171
169
|
sandboxSessionRegistry,
|
|
172
170
|
loadSystemPrompt,
|
|
173
171
|
activatePlan,
|
|
174
172
|
completeModelSelectionSideEffect,
|
|
173
|
+
componentHealthMonitor,
|
|
175
174
|
} = options;
|
|
176
175
|
|
|
177
176
|
const shellServices = createBootstrapCommandShellServices({
|
|
@@ -182,7 +181,7 @@ export function createBootstrapCommandContext(
|
|
|
182
181
|
adaptivePlanner,
|
|
183
182
|
sessionOrchestration,
|
|
184
183
|
shellPaths,
|
|
185
|
-
|
|
184
|
+
componentHealthMonitor,
|
|
186
185
|
worktreeRegistry,
|
|
187
186
|
sandboxSessionRegistry,
|
|
188
187
|
readModels,
|
|
@@ -8,8 +8,8 @@ import type { McpApi } from '@pellux/goodvibes-sdk/platform/mcp/mcp-api';
|
|
|
8
8
|
import type { PanelManager } from '../panels/panel-manager.ts';
|
|
9
9
|
import type { ProviderApi } from '@pellux/goodvibes-sdk/platform/providers/provider-api';
|
|
10
10
|
import type { OpsApi } from '@pellux/goodvibes-sdk/platform/runtime/ops-api';
|
|
11
|
+
import type { MutableRuntimeState } from '@pellux/goodvibes-sdk/platform/runtime/mutable-runtime-state';
|
|
11
12
|
import type { ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers/registry';
|
|
12
|
-
import type { MutableRuntimeState } from './context.ts';
|
|
13
13
|
import type { CommandContext } from '../input/command-registry.ts';
|
|
14
14
|
import type { KeybindingsManager } from '../input/keybindings.ts';
|
|
15
15
|
import type { PermissionRequestHandler } from '@pellux/goodvibes-sdk/platform/permissions/prompt';
|
|
@@ -23,7 +23,6 @@ import type { IntegrationHelperService } from '@pellux/goodvibes-sdk/platform/ru
|
|
|
23
23
|
import type { KnowledgeService } from '@pellux/goodvibes-sdk/platform/knowledge/index';
|
|
24
24
|
import type { PluginManager } from '@pellux/goodvibes-sdk/platform/plugins/manager';
|
|
25
25
|
import type { HookWorkbench } from '@pellux/goodvibes-sdk/platform/hooks/workbench';
|
|
26
|
-
import type { PanelHealthMonitor } from '@pellux/goodvibes-sdk/platform/runtime/perf/panel-health-monitor';
|
|
27
26
|
import type { WorktreeRegistry } from '@pellux/goodvibes-sdk/platform/runtime/worktree/registry';
|
|
28
27
|
import type { SandboxSessionRegistry } from '@pellux/goodvibes-sdk/platform/runtime/sandbox/session-registry';
|
|
29
28
|
import type { UiReadModels } from './ui-read-models.ts';
|
|
@@ -117,7 +116,6 @@ export interface BootstrapCommandSectionOptions {
|
|
|
117
116
|
readonly mcpApi?: McpApi;
|
|
118
117
|
readonly opsApi?: OpsApi;
|
|
119
118
|
readonly directTransport?: DirectTransport;
|
|
120
|
-
readonly panelHealthMonitor: PanelHealthMonitor;
|
|
121
119
|
readonly worktreeRegistry: WorktreeRegistry;
|
|
122
120
|
readonly sandboxSessionRegistry: SandboxSessionRegistry;
|
|
123
121
|
}
|
|
@@ -11,7 +11,8 @@ import type { PermissionRequestHandler } from '@pellux/goodvibes-sdk/platform/pe
|
|
|
11
11
|
import type { SystemMessageRouter } from '../core/system-message-router.ts';
|
|
12
12
|
import type { ConversationFollowUpItem } from '@pellux/goodvibes-sdk/platform/core/conversation-follow-ups';
|
|
13
13
|
import type { ControlPlaneRecentEvent } from '@pellux/goodvibes-sdk/platform/control-plane/gateway';
|
|
14
|
-
import type {
|
|
14
|
+
import type { MutableRuntimeState } from '@pellux/goodvibes-sdk/platform/runtime/mutable-runtime-state';
|
|
15
|
+
import type { BootstrapOptions } from './context.ts';
|
|
15
16
|
import { createFeatureFlagManager } from '@pellux/goodvibes-sdk/platform/runtime/feature-flags/index';
|
|
16
17
|
import { RuntimeEventBus } from '@pellux/goodvibes-sdk/platform/runtime/events/index';
|
|
17
18
|
import { createRuntimeStore, createDomainDispatch, type RuntimeStore } from './store/index.ts';
|
|
@@ -20,7 +21,7 @@ import {
|
|
|
20
21
|
generateUserSessionId,
|
|
21
22
|
} from '@pellux/goodvibes-sdk/platform/runtime/session-persistence';
|
|
22
23
|
import { loadBootstrapSystemPrompt, syncConfiguredServices } from '@pellux/goodvibes-sdk/platform/runtime/bootstrap-helpers';
|
|
23
|
-
import { registerBootstrapHookBridge } from '
|
|
24
|
+
import { registerBootstrapHookBridge } from '@pellux/goodvibes-sdk/platform/runtime/bootstrap-hook-bridge';
|
|
24
25
|
import { registerBootstrapRuntimeEvents } from '@pellux/goodvibes-sdk/platform/runtime/bootstrap-runtime-events';
|
|
25
26
|
import { createRuntimeServices, type RuntimeServices } from './services.ts';
|
|
26
27
|
import { createUiRuntimeServices, type UiRuntimeServices } from './ui-services.ts';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ConversationManager } from '../core/conversation';
|
|
2
2
|
import type { HookDispatcher } from '@pellux/goodvibes-sdk/platform/hooks/index';
|
|
3
|
-
import type {
|
|
4
|
-
import
|
|
5
|
-
import type {
|
|
3
|
+
import type { MutableRuntimeState } from '@pellux/goodvibes-sdk/platform/runtime/mutable-runtime-state';
|
|
4
|
+
import { registerBootstrapHookBridge } from '@pellux/goodvibes-sdk/platform/runtime/bootstrap-hook-bridge';
|
|
5
|
+
import type { RuntimeEventBus } from '@pellux/goodvibes-sdk/platform/runtime/events/index';
|
|
6
6
|
import { logger } from '@pellux/goodvibes-sdk/platform/utils/logger';
|
|
7
7
|
import { emitSessionResumed } from '@pellux/goodvibes-sdk/platform/runtime/emitters/index';
|
|
8
8
|
import { HelperModel } from '@pellux/goodvibes-sdk/platform/config/helper-model';
|
|
@@ -14,30 +14,6 @@ import type { PanelManager } from '../panels/panel-manager.ts';
|
|
|
14
14
|
import type { ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers/registry';
|
|
15
15
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils/error-display';
|
|
16
16
|
|
|
17
|
-
interface FireHookOptions {
|
|
18
|
-
readonly hookDispatcher: HookDispatcher;
|
|
19
|
-
readonly runtime: MutableRuntimeState;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function fireHook(
|
|
23
|
-
options: FireHookOptions,
|
|
24
|
-
path: HookEventPath,
|
|
25
|
-
phase: HookPhase,
|
|
26
|
-
category: HookCategory,
|
|
27
|
-
specific: string,
|
|
28
|
-
payload: Record<string, unknown>,
|
|
29
|
-
): void {
|
|
30
|
-
options.hookDispatcher.fire({
|
|
31
|
-
path,
|
|
32
|
-
phase,
|
|
33
|
-
category,
|
|
34
|
-
specific,
|
|
35
|
-
sessionId: options.runtime.sessionId,
|
|
36
|
-
timestamp: Date.now(),
|
|
37
|
-
payload,
|
|
38
|
-
}).catch((err: unknown) => logger.debug('Hook bridge fire error', { path, error: summarizeError(err) }));
|
|
39
|
-
}
|
|
40
|
-
|
|
41
17
|
export interface ResumeSessionOptions {
|
|
42
18
|
readonly runtimeBus: RuntimeEventBus;
|
|
43
19
|
readonly runtime: MutableRuntimeState;
|
|
@@ -54,10 +30,6 @@ export interface ResumeSessionOptions {
|
|
|
54
30
|
}
|
|
55
31
|
|
|
56
32
|
export function createResumeSessionHandler(options: ResumeSessionOptions): (sessionId: string) => void {
|
|
57
|
-
const fireOptions: FireHookOptions = {
|
|
58
|
-
hookDispatcher: options.hookDispatcher,
|
|
59
|
-
runtime: options.runtime,
|
|
60
|
-
};
|
|
61
33
|
return (sessionId: string): void => {
|
|
62
34
|
try {
|
|
63
35
|
const { messages, meta } = options.sessionManager.load(sessionId);
|
|
@@ -119,7 +91,18 @@ export function createResumeSessionHandler(options: ResumeSessionOptions): (sess
|
|
|
119
91
|
});
|
|
120
92
|
}
|
|
121
93
|
}
|
|
122
|
-
|
|
94
|
+
options.hookDispatcher.fire({
|
|
95
|
+
path: 'Lifecycle:session:load',
|
|
96
|
+
phase: 'Lifecycle',
|
|
97
|
+
category: 'session',
|
|
98
|
+
specific: 'load',
|
|
99
|
+
sessionId: options.runtime.sessionId,
|
|
100
|
+
timestamp: Date.now(),
|
|
101
|
+
payload: { sessionId },
|
|
102
|
+
}).catch((err: unknown) => logger.debug('Hook bridge fire error', {
|
|
103
|
+
path: 'Lifecycle:session:load',
|
|
104
|
+
error: summarizeError(err),
|
|
105
|
+
}));
|
|
123
106
|
} catch (error) {
|
|
124
107
|
logger.debug('resumeSession failed', { error: summarizeError(error) });
|
|
125
108
|
options.conversation.log('Failed to resume session.', { fg: '#ef4444' });
|
|
@@ -127,145 +110,3 @@ export function createResumeSessionHandler(options: ResumeSessionOptions): (sess
|
|
|
127
110
|
options.requestRender();
|
|
128
111
|
};
|
|
129
112
|
}
|
|
130
|
-
|
|
131
|
-
export interface HookBridgeRegistrationOptions {
|
|
132
|
-
readonly runtimeBus: RuntimeEventBus;
|
|
133
|
-
readonly hookDispatcher: HookDispatcher;
|
|
134
|
-
readonly runtime: MutableRuntimeState;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export function registerBootstrapHookBridge(
|
|
138
|
-
options: HookBridgeRegistrationOptions,
|
|
139
|
-
): Array<() => void> {
|
|
140
|
-
const fireOptions: FireHookOptions = {
|
|
141
|
-
hookDispatcher: options.hookDispatcher,
|
|
142
|
-
runtime: options.runtime,
|
|
143
|
-
};
|
|
144
|
-
const unsubs: Array<() => void> = [];
|
|
145
|
-
const { runtimeBus } = options;
|
|
146
|
-
|
|
147
|
-
unsubs.push(runtimeBus.on<Extract<AgentEvent, { type: 'AGENT_SPAWNING' }>>('AGENT_SPAWNING', ({ payload }) => {
|
|
148
|
-
fireHook(fireOptions, 'Lifecycle:agent:spawned', 'Lifecycle', 'agent', 'spawned', { agentId: payload.agentId, task: payload.task });
|
|
149
|
-
}));
|
|
150
|
-
unsubs.push(runtimeBus.on<Extract<AgentEvent, { type: 'AGENT_COMPLETED' }>>('AGENT_COMPLETED', ({ payload }) => {
|
|
151
|
-
fireHook(fireOptions, 'Lifecycle:agent:completed', 'Lifecycle', 'agent', 'completed', {
|
|
152
|
-
agentId: payload.agentId,
|
|
153
|
-
result: {
|
|
154
|
-
durationMs: payload.durationMs,
|
|
155
|
-
...(payload.output !== undefined ? { output: payload.output } : {}),
|
|
156
|
-
...(payload.toolCallsMade !== undefined ? { toolCallsMade: payload.toolCallsMade } : {}),
|
|
157
|
-
},
|
|
158
|
-
});
|
|
159
|
-
}));
|
|
160
|
-
unsubs.push(runtimeBus.on<Extract<AgentEvent, { type: 'AGENT_FAILED' }>>('AGENT_FAILED', ({ payload }) => {
|
|
161
|
-
const specific = payload.error === 'Agent cancelled' || payload.error.includes('cancelled') ? 'cancelled' : 'failed';
|
|
162
|
-
fireHook(fireOptions, `Lifecycle:agent:${specific}` as HookEventPath, 'Lifecycle', 'agent', specific, { agentId: payload.agentId, error: payload.error });
|
|
163
|
-
}));
|
|
164
|
-
|
|
165
|
-
unsubs.push(runtimeBus.on<Extract<WorkflowEvent, { type: 'WORKFLOW_CHAIN_CREATED' }>>('WORKFLOW_CHAIN_CREATED', ({ payload }) => {
|
|
166
|
-
fireHook(fireOptions, 'Lifecycle:workflow:started', 'Lifecycle', 'workflow', 'started', { chainId: payload.chainId, task: payload.task });
|
|
167
|
-
}));
|
|
168
|
-
unsubs.push(runtimeBus.on<Extract<WorkflowEvent, { type: 'WORKFLOW_CHAIN_PASSED' }>>('WORKFLOW_CHAIN_PASSED', ({ payload }) => {
|
|
169
|
-
fireHook(fireOptions, 'Lifecycle:workflow:completed', 'Lifecycle', 'workflow', 'completed', { chainId: payload.chainId });
|
|
170
|
-
}));
|
|
171
|
-
unsubs.push(runtimeBus.on<Extract<WorkflowEvent, { type: 'WORKFLOW_CHAIN_FAILED' }>>('WORKFLOW_CHAIN_FAILED', ({ payload }) => {
|
|
172
|
-
fireHook(fireOptions, 'Lifecycle:workflow:failed', 'Lifecycle', 'workflow', 'failed', { chainId: payload.chainId, reason: payload.reason });
|
|
173
|
-
}));
|
|
174
|
-
unsubs.push(runtimeBus.on<Extract<WorkflowEvent, { type: 'WORKFLOW_REVIEW_COMPLETED' }>>('WORKFLOW_REVIEW_COMPLETED', ({ payload }) => {
|
|
175
|
-
fireHook(fireOptions, 'Lifecycle:workflow:reviewed', 'Lifecycle', 'workflow', 'reviewed', {
|
|
176
|
-
chainId: payload.chainId,
|
|
177
|
-
score: payload.score,
|
|
178
|
-
passed: payload.passed,
|
|
179
|
-
});
|
|
180
|
-
}));
|
|
181
|
-
unsubs.push(runtimeBus.on<Extract<WorkflowEvent, { type: 'WORKFLOW_FIX_ATTEMPTED' }>>('WORKFLOW_FIX_ATTEMPTED', ({ payload }) => {
|
|
182
|
-
fireHook(fireOptions, 'Lifecycle:workflow:fix-attempted', 'Lifecycle', 'workflow', 'fix-attempted', {
|
|
183
|
-
chainId: payload.chainId,
|
|
184
|
-
attempt: payload.attempt,
|
|
185
|
-
maxAttempts: payload.maxAttempts,
|
|
186
|
-
});
|
|
187
|
-
}));
|
|
188
|
-
unsubs.push(runtimeBus.on<Extract<WorkflowEvent, { type: 'WORKFLOW_GATE_RESULT' }>>('WORKFLOW_GATE_RESULT', ({ payload }) => {
|
|
189
|
-
fireHook(fireOptions, 'Lifecycle:workflow:gate-result', 'Lifecycle', 'workflow', 'gate-result', {
|
|
190
|
-
chainId: payload.chainId,
|
|
191
|
-
gate: payload.gate,
|
|
192
|
-
passed: payload.passed,
|
|
193
|
-
});
|
|
194
|
-
}));
|
|
195
|
-
unsubs.push(runtimeBus.on<Extract<import('@pellux/goodvibes-sdk/platform/runtime/events/orchestration').OrchestrationEvent, { type: 'ORCHESTRATION_GRAPH_CREATED' }>>('ORCHESTRATION_GRAPH_CREATED', ({ payload }) => {
|
|
196
|
-
fireHook(fireOptions, 'Lifecycle:orchestration:graph-created', 'Lifecycle', 'orchestration', 'graph-created', {
|
|
197
|
-
graphId: payload.graphId,
|
|
198
|
-
title: payload.title,
|
|
199
|
-
mode: payload.mode,
|
|
200
|
-
});
|
|
201
|
-
}));
|
|
202
|
-
unsubs.push(runtimeBus.on<Extract<import('@pellux/goodvibes-sdk/platform/runtime/events/orchestration').OrchestrationEvent, { type: 'ORCHESTRATION_NODE_STARTED' }>>('ORCHESTRATION_NODE_STARTED', ({ payload }) => {
|
|
203
|
-
fireHook(fireOptions, 'Lifecycle:orchestration:node-started', 'Lifecycle', 'orchestration', 'node-started', {
|
|
204
|
-
graphId: payload.graphId,
|
|
205
|
-
nodeId: payload.nodeId,
|
|
206
|
-
...(payload.taskId !== undefined ? { taskId: payload.taskId } : {}),
|
|
207
|
-
...(payload.agentId !== undefined ? { agentId: payload.agentId } : {}),
|
|
208
|
-
});
|
|
209
|
-
}));
|
|
210
|
-
unsubs.push(runtimeBus.on<Extract<import('@pellux/goodvibes-sdk/platform/runtime/events/orchestration').OrchestrationEvent, { type: 'ORCHESTRATION_NODE_COMPLETED' }>>('ORCHESTRATION_NODE_COMPLETED', ({ payload }) => {
|
|
211
|
-
fireHook(fireOptions, 'Lifecycle:orchestration:node-completed', 'Lifecycle', 'orchestration', 'node-completed', {
|
|
212
|
-
graphId: payload.graphId,
|
|
213
|
-
nodeId: payload.nodeId,
|
|
214
|
-
...(payload.summary !== undefined ? { summary: payload.summary } : {}),
|
|
215
|
-
});
|
|
216
|
-
}));
|
|
217
|
-
unsubs.push(runtimeBus.on<Extract<import('@pellux/goodvibes-sdk/platform/runtime/events/orchestration').OrchestrationEvent, { type: 'ORCHESTRATION_NODE_FAILED' }>>('ORCHESTRATION_NODE_FAILED', ({ payload }) => {
|
|
218
|
-
fireHook(fireOptions, 'Lifecycle:orchestration:node-failed', 'Lifecycle', 'orchestration', 'node-failed', {
|
|
219
|
-
graphId: payload.graphId,
|
|
220
|
-
nodeId: payload.nodeId,
|
|
221
|
-
error: payload.error,
|
|
222
|
-
});
|
|
223
|
-
}));
|
|
224
|
-
unsubs.push(runtimeBus.on<Extract<import('@pellux/goodvibes-sdk/platform/runtime/events/orchestration').OrchestrationEvent, { type: 'ORCHESTRATION_RECURSION_GUARD_TRIGGERED' }>>('ORCHESTRATION_RECURSION_GUARD_TRIGGERED', ({ payload }) => {
|
|
225
|
-
fireHook(fireOptions, 'Change:orchestration:recursion-guard', 'Change', 'orchestration', 'recursion-guard', {
|
|
226
|
-
graphId: payload.graphId,
|
|
227
|
-
...(payload.nodeId !== undefined ? { nodeId: payload.nodeId } : {}),
|
|
228
|
-
depth: payload.depth,
|
|
229
|
-
activeAgents: payload.activeAgents,
|
|
230
|
-
reason: payload.reason,
|
|
231
|
-
});
|
|
232
|
-
}));
|
|
233
|
-
unsubs.push(runtimeBus.on<Extract<import('@pellux/goodvibes-sdk/platform/runtime/events/communication').CommunicationEvent, { type: 'COMMUNICATION_SENT' }>>('COMMUNICATION_SENT', ({ payload }) => {
|
|
234
|
-
fireHook(fireOptions, 'Lifecycle:communication:sent', 'Lifecycle', 'communication', 'sent', {
|
|
235
|
-
messageId: payload.messageId,
|
|
236
|
-
fromId: payload.fromId,
|
|
237
|
-
toId: payload.toId,
|
|
238
|
-
scope: payload.scope,
|
|
239
|
-
kind: payload.kind,
|
|
240
|
-
...(payload.fromRole !== undefined ? { fromRole: payload.fromRole } : {}),
|
|
241
|
-
...(payload.toRole !== undefined ? { toRole: payload.toRole } : {}),
|
|
242
|
-
});
|
|
243
|
-
}));
|
|
244
|
-
unsubs.push(runtimeBus.on<Extract<import('@pellux/goodvibes-sdk/platform/runtime/events/communication').CommunicationEvent, { type: 'COMMUNICATION_DELIVERED' }>>('COMMUNICATION_DELIVERED', ({ payload }) => {
|
|
245
|
-
fireHook(fireOptions, 'Lifecycle:communication:delivered', 'Lifecycle', 'communication', 'delivered', {
|
|
246
|
-
messageId: payload.messageId,
|
|
247
|
-
fromId: payload.fromId,
|
|
248
|
-
toId: payload.toId,
|
|
249
|
-
scope: payload.scope,
|
|
250
|
-
kind: payload.kind,
|
|
251
|
-
});
|
|
252
|
-
}));
|
|
253
|
-
unsubs.push(runtimeBus.on<Extract<import('@pellux/goodvibes-sdk/platform/runtime/events/communication').CommunicationEvent, { type: 'COMMUNICATION_BLOCKED' }>>('COMMUNICATION_BLOCKED', ({ payload }) => {
|
|
254
|
-
fireHook(fireOptions, 'Change:communication:blocked', 'Change', 'communication', 'blocked', {
|
|
255
|
-
messageId: payload.messageId,
|
|
256
|
-
fromId: payload.fromId,
|
|
257
|
-
toId: payload.toId,
|
|
258
|
-
scope: payload.scope,
|
|
259
|
-
kind: payload.kind,
|
|
260
|
-
reason: payload.reason,
|
|
261
|
-
...(payload.fromRole !== undefined ? { fromRole: payload.fromRole } : {}),
|
|
262
|
-
...(payload.toRole !== undefined ? { toRole: payload.toRole } : {}),
|
|
263
|
-
});
|
|
264
|
-
}));
|
|
265
|
-
unsubs.push(runtimeBus.on<Extract<OpsEvent, { type: 'OPS_CONTEXT_WARNING' }>>('OPS_CONTEXT_WARNING', ({ payload: { usage, threshold } }) => {
|
|
266
|
-
const specific = usage >= threshold ? 'exceeded' : 'warning';
|
|
267
|
-
fireHook(fireOptions, `Change:budget:${specific}` as HookEventPath, 'Change', 'budget', specific, { usage, threshold });
|
|
268
|
-
}));
|
|
269
|
-
|
|
270
|
-
return unsubs;
|
|
271
|
-
}
|
|
@@ -2,9 +2,9 @@ import type { ConversationManager } from '../core/conversation';
|
|
|
2
2
|
import type { Orchestrator } from '../core/orchestrator';
|
|
3
3
|
import type { ConfigManager } from '@pellux/goodvibes-sdk/platform/config/manager';
|
|
4
4
|
import type { RuntimeEventBus } from '@pellux/goodvibes-sdk/platform/runtime/events/index';
|
|
5
|
+
import type { MutableRuntimeState } from '@pellux/goodvibes-sdk/platform/runtime/mutable-runtime-state';
|
|
5
6
|
import type { RuntimeStore } from './store/index.ts';
|
|
6
7
|
import type { RuntimeServices } from './services.ts';
|
|
7
|
-
import type { MutableRuntimeState } from './context.ts';
|
|
8
8
|
import type { CommandContext } from '../input/command-registry.ts';
|
|
9
9
|
import type { OpsControlPlane } from '@pellux/goodvibes-sdk/platform/runtime/ops/control-plane';
|
|
10
10
|
import { CommandRegistry } from '../input/command-registry.ts';
|
|
@@ -86,7 +86,7 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
|
|
|
86
86
|
completeModelSelectionSideEffect,
|
|
87
87
|
} = options;
|
|
88
88
|
|
|
89
|
-
const systemMessagesPanel = new SystemMessagesPanel(configManager, services.
|
|
89
|
+
const systemMessagesPanel = new SystemMessagesPanel(configManager, services.componentHealthMonitor);
|
|
90
90
|
const resumeSession = createResumeSessionHandler({
|
|
91
91
|
runtimeBus,
|
|
92
92
|
runtime,
|
|
@@ -119,7 +119,7 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
|
|
|
119
119
|
automationManager: services.automationManager,
|
|
120
120
|
getControlPlaneRecentEvents,
|
|
121
121
|
tokenAuditor: services.tokenAuditor,
|
|
122
|
-
|
|
122
|
+
componentHealthMonitor: services.componentHealthMonitor,
|
|
123
123
|
worktreeRegistry: services.worktreeRegistry,
|
|
124
124
|
sandboxSessionRegistry: services.sandboxSessionRegistry,
|
|
125
125
|
systemMessagesPanel,
|
|
@@ -224,7 +224,6 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
|
|
|
224
224
|
opsApi,
|
|
225
225
|
directTransport,
|
|
226
226
|
panelManager: services.panelManager,
|
|
227
|
-
panelHealthMonitor: services.panelHealthMonitor,
|
|
228
227
|
worktreeRegistry: services.worktreeRegistry,
|
|
229
228
|
sandboxSessionRegistry: services.sandboxSessionRegistry,
|
|
230
229
|
loadSystemPrompt: () => loadBootstrapSystemPrompt(configManager),
|
|
@@ -236,6 +235,7 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
|
|
|
236
235
|
}, 50);
|
|
237
236
|
},
|
|
238
237
|
completeModelSelectionSideEffect,
|
|
238
|
+
componentHealthMonitor: services.componentHealthMonitor,
|
|
239
239
|
});
|
|
240
240
|
|
|
241
241
|
const gitStatusProvider = new GitStatusProvider(services.workingDirectory);
|
package/src/runtime/bootstrap.ts
CHANGED
|
@@ -407,7 +407,7 @@ export async function bootstrapRuntime(
|
|
|
407
407
|
permissions: permissionManager,
|
|
408
408
|
toolRegistry,
|
|
409
409
|
providerRegistry,
|
|
410
|
-
|
|
410
|
+
componentHealthMonitor: services.componentHealthMonitor,
|
|
411
411
|
worktreeRegistry: services.worktreeRegistry,
|
|
412
412
|
sandboxSessionRegistry: services.sandboxSessionRegistry,
|
|
413
413
|
hookDispatcher,
|
package/src/runtime/context.ts
CHANGED
|
@@ -16,29 +16,13 @@ import type { ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers/
|
|
|
16
16
|
import type { RuntimeStore } from './store/index.ts';
|
|
17
17
|
import type { RuntimeEventBus } from '@pellux/goodvibes-sdk/platform/runtime/events/index';
|
|
18
18
|
import type { FeatureFlagManager } from '@pellux/goodvibes-sdk/platform/runtime/feature-flags/index';
|
|
19
|
+
import type { MutableRuntimeState } from '@pellux/goodvibes-sdk/platform/runtime/mutable-runtime-state';
|
|
19
20
|
import type { SessionSnapshot } from '@pellux/goodvibes-sdk/platform/runtime/session-persistence';
|
|
20
21
|
import type { RuntimeServices } from './services.ts';
|
|
21
|
-
import type {
|
|
22
|
+
import type { ComponentHealthMonitor } from './perf/panel-health-monitor.ts';
|
|
22
23
|
import type { WorktreeRegistry } from '@pellux/goodvibes-sdk/platform/runtime/worktree/registry';
|
|
23
24
|
import type { SandboxSessionRegistry } from '@pellux/goodvibes-sdk/platform/runtime/sandbox/session-registry';
|
|
24
25
|
|
|
25
|
-
/**
|
|
26
|
-
* Mutable runtime state that may be changed by slash commands or model-picker events.
|
|
27
|
-
* Kept as a plain object so event handlers can close over it by reference.
|
|
28
|
-
*
|
|
29
|
-
* Named `MutableRuntimeState` to avoid a name collision with `RuntimeState` in
|
|
30
|
-
* `src/runtime/store/state.ts`.
|
|
31
|
-
*/
|
|
32
|
-
export interface MutableRuntimeState {
|
|
33
|
-
model: string;
|
|
34
|
-
provider: string;
|
|
35
|
-
debugMode: boolean;
|
|
36
|
-
systemPrompt: string;
|
|
37
|
-
/** Empty string if not configured. */
|
|
38
|
-
reasoningEffort: string;
|
|
39
|
-
sessionId: string;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
26
|
/**
|
|
43
27
|
* Options accepted by bootstrapRuntime().
|
|
44
28
|
*/
|
|
@@ -103,8 +87,8 @@ export interface RuntimeContext {
|
|
|
103
87
|
/** Shared provider registry owned by the runtime services graph. */
|
|
104
88
|
providerRegistry: ProviderRegistry;
|
|
105
89
|
|
|
106
|
-
/** Shared
|
|
107
|
-
|
|
90
|
+
/** Shared component-health monitor owned by the runtime services graph. */
|
|
91
|
+
componentHealthMonitor: ComponentHealthMonitor;
|
|
108
92
|
|
|
109
93
|
/** Shared worktree registry owned by the runtime services graph. */
|
|
110
94
|
worktreeRegistry: WorktreeRegistry;
|
|
@@ -19,6 +19,6 @@ export { TransportPanel } from '@pellux/goodvibes-sdk/platform/runtime/diagnosti
|
|
|
19
19
|
export type { TransportPanelSnapshot } from '@pellux/goodvibes-sdk/platform/runtime/diagnostics/panels/transport';
|
|
20
20
|
export { OpsPanel } from './ops.ts';
|
|
21
21
|
export type { OpsAuditEntry } from './ops.ts';
|
|
22
|
-
export { PanelResourcesPanel } from '
|
|
22
|
+
export { PanelResourcesPanel } from './panel-resources.ts';
|
|
23
23
|
export { SecurityPanel } from '@pellux/goodvibes-sdk/platform/runtime/diagnostics/panels/security';
|
|
24
24
|
export type { SecurityPanelSnapshot } from '@pellux/goodvibes-sdk/platform/runtime/diagnostics/panels/security';
|
|
@@ -13,7 +13,7 @@ import type { PanelConfig } from '@pellux/goodvibes-sdk/platform/runtime/diagnos
|
|
|
13
13
|
import { DEFAULT_PANEL_CONFIG, appendBounded, applyFilter } from '@pellux/goodvibes-sdk/platform/runtime/diagnostics/types';
|
|
14
14
|
import type { DiagnosticFilter } from '@pellux/goodvibes-sdk/platform/runtime/diagnostics/types';
|
|
15
15
|
import type { OpsInterventionReason, OpsEvent } from '@pellux/goodvibes-sdk/platform/runtime/events/ops';
|
|
16
|
-
import type { UiEventFeed } from '
|
|
16
|
+
import type { UiEventFeed } from '../../ui-events.ts';
|
|
17
17
|
|
|
18
18
|
// ---------------------------------------------------------------------------
|
|
19
19
|
// Audit entry
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Panel resource diagnostics panel data provider.
|
|
3
|
+
*
|
|
4
|
+
* Polls the shared TUI-owned ComponentHealthMonitor and produces PanelResourceSnapshot
|
|
5
|
+
* values for the diagnostics panel to render.
|
|
6
|
+
*/
|
|
7
|
+
import type { ComponentHealthMonitor } from '../../perf/panel-health-monitor.ts';
|
|
8
|
+
import type {
|
|
9
|
+
ComponentResourceEntry,
|
|
10
|
+
ComponentResourceSnapshot,
|
|
11
|
+
} from '@pellux/goodvibes-sdk/platform/runtime/diagnostics/types';
|
|
12
|
+
|
|
13
|
+
const DEFAULT_POLL_INTERVAL_MS = 500;
|
|
14
|
+
|
|
15
|
+
const HEALTH_ORDER: Record<string, number> = {
|
|
16
|
+
overloaded: 0,
|
|
17
|
+
warning: 1,
|
|
18
|
+
healthy: 2,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export class PanelResourcesPanel {
|
|
22
|
+
private readonly _pollIntervalMs: number;
|
|
23
|
+
private readonly _monitor: ComponentHealthMonitor;
|
|
24
|
+
private _current: ComponentResourceSnapshot;
|
|
25
|
+
private _timerId: ReturnType<typeof setInterval> | null = null;
|
|
26
|
+
private readonly _subscribers = new Set<() => void>();
|
|
27
|
+
|
|
28
|
+
constructor(monitor: ComponentHealthMonitor, pollIntervalMs: number = DEFAULT_POLL_INTERVAL_MS) {
|
|
29
|
+
this._monitor = monitor;
|
|
30
|
+
this._pollIntervalMs = pollIntervalMs;
|
|
31
|
+
this._current = this._buildSnapshot(Date.now());
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
start(): void {
|
|
35
|
+
if (this._timerId !== null) return;
|
|
36
|
+
this._timerId = setInterval(() => {
|
|
37
|
+
this._current = this._buildSnapshot(Date.now());
|
|
38
|
+
this._notify();
|
|
39
|
+
}, this._pollIntervalMs);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
stop(): void {
|
|
43
|
+
if (this._timerId !== null) {
|
|
44
|
+
clearInterval(this._timerId);
|
|
45
|
+
this._timerId = null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
getSnapshot(): ComponentResourceSnapshot {
|
|
50
|
+
return this._current;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
refresh(now: number = Date.now()): ComponentResourceSnapshot {
|
|
54
|
+
this._current = this._buildSnapshot(now);
|
|
55
|
+
return this._current;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
subscribe(callback: () => void): () => void {
|
|
59
|
+
this._subscribers.add(callback);
|
|
60
|
+
return () => this._subscribers.delete(callback);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
dispose(): void {
|
|
64
|
+
this.stop();
|
|
65
|
+
this._subscribers.clear();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private _buildSnapshot(capturedAt: number): ComponentResourceSnapshot {
|
|
69
|
+
const healthStates = this._monitor.getAllHealth();
|
|
70
|
+
|
|
71
|
+
const panels: ComponentResourceEntry[] = healthStates.map((health) => {
|
|
72
|
+
const contract = this._monitor.getContract(health.componentId);
|
|
73
|
+
return {
|
|
74
|
+
componentId: health.componentId,
|
|
75
|
+
throttleStatus: health.throttleStatus,
|
|
76
|
+
healthStatus: health.healthStatus,
|
|
77
|
+
renderP95Ms: health.renderP95Ms,
|
|
78
|
+
maxRenderMs: contract?.maxRenderMs ?? 0,
|
|
79
|
+
rendersInWindow: health.rendersInWindow,
|
|
80
|
+
maxUpdatesPerSecond: contract?.maxUpdatesPerSecond ?? 0,
|
|
81
|
+
consecutiveViolations: health.consecutiveViolations,
|
|
82
|
+
totalSuppressed: health.totalSuppressed,
|
|
83
|
+
totalPermitted: health.totalPermitted,
|
|
84
|
+
lastRenderAt: health.lastRenderAt,
|
|
85
|
+
nextAllowedAt: health.nextAllowedAt,
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
panels.sort((left, right) => {
|
|
90
|
+
const diff = (HEALTH_ORDER[left.healthStatus] ?? 2) - (HEALTH_ORDER[right.healthStatus] ?? 2);
|
|
91
|
+
return diff !== 0 ? diff : left.componentId.localeCompare(right.componentId);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const overloadedCount = panels.filter((panel) => panel.healthStatus === 'overloaded').length;
|
|
95
|
+
const warningCount = panels.filter((panel) => panel.healthStatus === 'warning').length;
|
|
96
|
+
const healthyCount = panels.filter((panel) => panel.healthStatus === 'healthy').length;
|
|
97
|
+
const totalSuppressed = panels.reduce((sum, panel) => sum + panel.totalSuppressed, 0);
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
panels,
|
|
101
|
+
overloadedCount,
|
|
102
|
+
warningCount,
|
|
103
|
+
healthyCount,
|
|
104
|
+
totalSuppressed,
|
|
105
|
+
capturedAt,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private _notify(): void {
|
|
110
|
+
for (const callback of this._subscribers) {
|
|
111
|
+
try {
|
|
112
|
+
callback();
|
|
113
|
+
} catch {
|
|
114
|
+
// Subscriber failures must not take down diagnostics polling.
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|