@pellux/goodvibes-agent 1.4.4 → 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.
- package/CHANGELOG.md +14 -0
- package/README.md +7 -7
- package/dist/package/main.js +7424 -12241
- package/docs/README.md +2 -2
- package/docs/channels-remote-and-api.md +1 -1
- package/docs/getting-started.md +2 -2
- package/docs/release-and-publishing.md +1 -1
- package/docs/tools-and-commands.md +5 -5
- package/package.json +4 -2
- package/release/performance-snapshot.json +2 -2
- package/release/release-notes.md +11 -7
- package/release/release-readiness.json +6 -6
- package/src/agent/behavior-discovery-summary.ts +4 -18
- package/src/agent/calendar-registry.ts +322 -0
- package/src/agent/competitive-feature-inventory.ts +268 -130
- package/src/agent/document-registry.ts +5 -1
- package/src/agent/email/email-service.ts +350 -0
- package/src/agent/email/imap-client.ts +596 -0
- package/src/agent/email/smtp-client.ts +453 -0
- package/src/agent/ics-calendar.ts +662 -0
- package/src/agent/ics-timezone.ts +172 -0
- package/src/agent/markdown-frontmatter.ts +31 -0
- package/src/agent/memory-safety.ts +1 -1
- package/src/agent/note-registry.ts +3 -9
- package/src/agent/persona-discovery.ts +3 -15
- package/src/agent/persona-registry.ts +1 -10
- package/src/agent/research-source-registry.ts +5 -1
- package/src/agent/routine-discovery.ts +3 -15
- package/src/agent/runtime-profile.ts +3 -0
- package/src/agent/skill-discovery.ts +3 -15
- package/src/agent/skill-draft-proposer.ts +203 -0
- package/src/agent/skill-draft-runner.ts +201 -0
- package/src/agent/skill-registry.ts +36 -1
- package/src/agent/skill-standard.ts +99 -0
- package/src/agent/vibe-file.ts +7 -22
- package/src/cli/completion.ts +1 -1
- package/src/cli/config-overrides.ts +10 -1
- package/src/cli/redaction.ts +17 -5
- package/src/config/provider-model.ts +2 -1
- package/src/config/secret-config.ts +13 -6
- package/src/core/activity-feed.ts +97 -0
- package/src/core/away-digest.ts +161 -0
- package/src/core/conversation-rendering.ts +7 -3
- package/src/core/conversation.ts +22 -15
- package/src/core/hardware-profile.ts +362 -0
- package/src/core/last-seen-store.ts +78 -0
- package/src/core/plain-language.ts +52 -0
- package/src/core/setup-incomplete-hint.ts +90 -0
- package/src/core/system-message-router.ts +38 -87
- package/src/input/agent-workspace-basic-command-editor-submission.ts +60 -220
- package/src/input/agent-workspace-basic-command-editors.ts +39 -141
- package/src/input/agent-workspace-categories.ts +40 -125
- package/src/input/agent-workspace-command-editor.ts +4 -0
- package/src/input/agent-workspace-host-category.ts +0 -5
- package/src/input/agent-workspace-local-editor-submission.ts +3 -1
- package/src/input/agent-workspace-navigation.ts +10 -3
- package/src/input/agent-workspace-onboarding-actions.ts +132 -0
- package/src/input/agent-workspace-onboarding-categories.ts +29 -26
- package/src/input/agent-workspace-onboarding-finish.ts +11 -4
- package/src/input/agent-workspace-onboarding-state.ts +111 -0
- package/src/input/agent-workspace-profile-editor-submission.ts +260 -0
- package/src/input/agent-workspace-profile-editors.ts +155 -0
- package/src/input/agent-workspace-subscription-editor.ts +7 -0
- package/src/input/agent-workspace-types.ts +5 -1
- package/src/input/agent-workspace.ts +65 -39
- package/src/input/command-registry.ts +18 -5
- package/src/input/commands/agent-runtime-profile-runtime.ts +2 -1
- package/src/input/commands/agent-skills-runtime.ts +60 -3
- package/src/input/commands/calendar-runtime.ts +209 -0
- package/src/input/commands/channels-runtime.ts +1 -0
- package/src/input/commands/command-error.ts +9 -0
- package/src/input/commands/compat-runtime.ts +1 -0
- package/src/input/commands/config.ts +1 -0
- package/src/input/commands/conversation-runtime.ts +1 -1
- package/src/input/commands/delegation-runtime.ts +5 -6
- package/src/input/commands/email-runtime.ts +387 -0
- package/src/input/commands/experience-runtime.ts +17 -4
- package/src/input/commands/guidance-runtime.ts +1 -1
- package/src/input/commands/health-runtime.ts +6 -1
- package/src/input/commands/knowledge-format.ts +73 -0
- package/src/input/commands/knowledge.ts +9 -74
- package/src/input/commands/local-provider-runtime.ts +2 -1
- package/src/input/commands/local-runtime.ts +10 -4
- package/src/input/commands/mcp-runtime.ts +7 -0
- package/src/input/commands/notify-runtime.ts +17 -1
- package/src/input/commands/onboarding-runtime.ts +1 -0
- package/src/input/commands/operator-actions-runtime.ts +1 -0
- package/src/input/commands/operator-runtime.ts +3 -0
- package/src/input/commands/personas-runtime.ts +1 -0
- package/src/input/commands/platform-access-runtime.ts +40 -17
- package/src/input/commands/product-runtime.ts +6 -1
- package/src/input/commands/provider-accounts-runtime.ts +3 -2
- package/src/input/commands/qrcode-runtime.ts +1 -0
- package/src/input/commands/routines-runtime.ts +1 -0
- package/src/input/commands/runtime-services.ts +0 -13
- package/src/input/commands/security-runtime.ts +1 -0
- package/src/input/commands/session-content.ts +1 -0
- package/src/input/commands/shell-core.ts +5 -2
- package/src/input/commands/subscription-runtime.ts +33 -9
- package/src/input/commands/support-bundle-runtime.ts +8 -1
- package/src/input/commands/tasks-runtime.ts +1 -0
- package/src/input/commands/tts-runtime.ts +1 -0
- package/src/input/commands/vibe-runtime.ts +1 -0
- package/src/input/commands.ts +4 -0
- package/src/input/feed-context-factory.ts +3 -12
- package/src/input/handler-command-route.ts +7 -60
- package/src/input/handler-feed-routes.ts +0 -194
- package/src/input/handler-feed.ts +2 -54
- package/src/input/handler-interactions.ts +0 -2
- package/src/input/handler-modal-stack.ts +0 -9
- package/src/input/handler-picker-routes.ts +24 -1
- package/src/input/handler-shortcuts.ts +11 -40
- package/src/input/handler-ui-state.ts +13 -0
- package/src/input/handler.ts +8 -35
- package/src/input/keybindings.ts +40 -17
- package/src/input/model-picker-local-fit.ts +130 -0
- package/src/input/submission-router.ts +0 -5
- package/src/main.ts +111 -89
- package/src/renderer/activity-sidebar.ts +186 -0
- package/src/renderer/agent-workspace-context-lines.ts +5 -48
- package/src/renderer/agent-workspace-style.ts +1 -1
- package/src/renderer/agent-workspace.ts +14 -2
- package/src/renderer/compositor.ts +37 -125
- package/src/renderer/conversation-overlays.ts +3 -3
- package/src/renderer/help-overlay.ts +7 -5
- package/src/renderer/model-workspace.ts +101 -86
- package/src/{panels → renderer}/polish.ts +3 -3
- package/src/renderer/shell-surface.ts +4 -5
- package/src/renderer/status-token.ts +31 -11
- package/src/renderer/tab-strip.ts +1 -1
- package/src/renderer/tool-call.ts +7 -8
- package/src/renderer/tool-labels.ts +92 -0
- package/src/renderer/ui-factory.ts +48 -96
- package/src/runtime/bootstrap-command-context.ts +0 -5
- package/src/runtime/bootstrap-command-parts.ts +6 -15
- package/src/runtime/bootstrap-core.ts +34 -13
- package/src/runtime/bootstrap-hook-bridge.ts +3 -1
- package/src/runtime/bootstrap-shell.ts +3 -50
- package/src/runtime/bootstrap.ts +3 -4
- package/src/runtime/context.ts +1 -1
- package/src/runtime/diagnostics/panels/index.ts +0 -1
- package/src/runtime/diagnostics/panels/policy.ts +1 -1
- package/src/runtime/execution-ledger.ts +16 -1
- package/src/runtime/index.ts +6 -1
- package/src/runtime/onboarding/index.ts +1 -0
- package/src/runtime/onboarding/onboarding-state.ts +200 -0
- package/src/{panels → runtime}/provider-account-snapshot.ts +5 -2
- package/src/runtime/services.ts +16 -4
- package/src/runtime/terminal-output-guard.ts +7 -0
- package/src/runtime/tool-permission-safety.ts +1 -1
- package/src/runtime/ui/model-picker/data-provider.ts +1 -1
- package/src/runtime/ui/model-picker/health-enrichment.ts +2 -2
- package/src/runtime/ui/model-picker/types.ts +2 -2
- package/src/runtime/ui/provider-health/data-provider.ts +3 -3
- package/src/runtime/ui/provider-health/types.ts +2 -2
- package/src/runtime/ui-services.ts +0 -2
- package/src/shell/agent-workspace-fullscreen.ts +0 -1
- package/src/shell/autonomy-surfacing.ts +272 -0
- package/src/shell/session-continuity-hints.ts +0 -6
- package/src/shell/startup-wiring.ts +221 -0
- package/src/shell/ui-openers.ts +18 -29
- package/src/tools/agent-context-policy.ts +1 -1
- package/src/tools/agent-harness-background-processes.ts +4 -4
- package/src/tools/agent-harness-browser-cockpit-route.ts +3 -3
- package/src/tools/agent-harness-command-runner.ts +6 -1
- package/src/tools/agent-harness-document-ops.ts +26 -53
- package/src/tools/agent-harness-execution-history.ts +1 -13
- package/src/tools/agent-harness-execution-posture.ts +0 -15
- package/src/tools/agent-harness-keybinding-metadata.ts +28 -29
- package/src/tools/agent-harness-local-model-cookbook.ts +24 -1
- package/src/tools/agent-harness-metadata.ts +16 -0
- package/src/tools/agent-harness-mode-catalog.ts +2 -9
- package/src/tools/agent-harness-provider-account-metadata.ts +2 -2
- package/src/tools/agent-harness-setup-posture-utils.ts +1 -1
- package/src/tools/agent-harness-tool-schema.ts +13 -14
- package/src/tools/agent-harness-tool.ts +27 -23
- package/src/tools/agent-harness-ui-surface-metadata.ts +10 -20
- package/src/tools/agent-harness-workspace-action-runner.ts +3 -4
- package/src/tools/agent-workspace-tool.ts +2 -33
- package/src/utils/terminal-width.ts +9 -3
- package/src/version.ts +1 -1
- package/src/input/agent-workspace-panel-route.ts +0 -44
- package/src/input/panel-integration-actions.ts +0 -26
- package/src/panels/approval-panel.ts +0 -149
- package/src/panels/automation-control-panel.ts +0 -212
- package/src/panels/base-panel.ts +0 -254
- package/src/panels/builtin/agent.ts +0 -58
- package/src/panels/builtin/knowledge.ts +0 -26
- package/src/panels/builtin/operations.ts +0 -121
- package/src/panels/builtin/session.ts +0 -138
- package/src/panels/builtin/shared.ts +0 -275
- package/src/panels/builtin/usage.ts +0 -21
- package/src/panels/builtin-panels.ts +0 -23
- package/src/panels/confirm-state.ts +0 -61
- package/src/panels/context-visualizer-panel.ts +0 -204
- package/src/panels/cost-tracker-panel.ts +0 -444
- package/src/panels/docs-panel.ts +0 -285
- package/src/panels/index.ts +0 -25
- package/src/panels/knowledge-panel.ts +0 -417
- package/src/panels/memory-panel.ts +0 -226
- package/src/panels/panel-list-panel.ts +0 -464
- package/src/panels/panel-manager.ts +0 -570
- package/src/panels/provider-accounts-panel.ts +0 -233
- package/src/panels/provider-health-domains.ts +0 -208
- package/src/panels/provider-health-panel.ts +0 -720
- package/src/panels/provider-health-tracker.ts +0 -115
- package/src/panels/provider-stats-panel.ts +0 -366
- package/src/panels/qr-panel.ts +0 -207
- package/src/panels/schedule-panel.ts +0 -321
- package/src/panels/scrollable-list-panel.ts +0 -491
- package/src/panels/search-focus.ts +0 -32
- package/src/panels/security-panel.ts +0 -295
- package/src/panels/session-browser-panel.ts +0 -395
- package/src/panels/session-maintenance.ts +0 -125
- package/src/panels/subscription-panel.ts +0 -263
- package/src/panels/system-messages-panel.ts +0 -230
- package/src/panels/tasks-panel.ts +0 -344
- package/src/panels/thinking-panel.ts +0 -304
- package/src/panels/token-budget-panel.ts +0 -475
- package/src/panels/tool-inspector-panel.ts +0 -436
- package/src/panels/types.ts +0 -54
- package/src/renderer/panel-composite.ts +0 -158
- package/src/renderer/panel-tab-bar.ts +0 -69
- package/src/renderer/panel-workspace-bar.ts +0 -42
- package/src/runtime/diagnostics/panels/panel-resources.ts +0 -118
- package/src/tools/agent-harness-panel-metadata.ts +0 -211
- /package/src/runtime/perf/{panel-health-monitor.ts → component-health.ts} +0 -0
package/src/input/keybindings.ts
CHANGED
|
@@ -11,6 +11,16 @@
|
|
|
11
11
|
* }
|
|
12
12
|
*
|
|
13
13
|
* Each value is a KeyCombo or an array of KeyCombos for multi-binding support.
|
|
14
|
+
*
|
|
15
|
+
* Action ID backward-compatibility aliases (accepted in keybindings.json):
|
|
16
|
+
* "panel-picker" → "workspace-picker"
|
|
17
|
+
* "panel-close" → "workspace-close"
|
|
18
|
+
* "panel-close-all" → "workspace-close-all"
|
|
19
|
+
* "panel-tab-next" → "workspace-tab-next"
|
|
20
|
+
* "panel-tab-prev" → "workspace-tab-prev"
|
|
21
|
+
*
|
|
22
|
+
* These aliases are accepted when loading user-provided keybindings.json
|
|
23
|
+
* override files so existing user configs continue to work without changes.
|
|
14
24
|
*/
|
|
15
25
|
|
|
16
26
|
import { readFileSync, existsSync } from 'node:fs';
|
|
@@ -34,11 +44,12 @@ export type KeyAction =
|
|
|
34
44
|
| 'copy-selection'
|
|
35
45
|
| 'clear-cancel'
|
|
36
46
|
| 'screen-clear'
|
|
37
|
-
| '
|
|
38
|
-
| '
|
|
39
|
-
| '
|
|
40
|
-
| '
|
|
41
|
-
| '
|
|
47
|
+
| 'workspace-picker'
|
|
48
|
+
| 'workspace-close'
|
|
49
|
+
| 'workspace-close-all'
|
|
50
|
+
| 'workspace-tab-next'
|
|
51
|
+
| 'workspace-tab-prev'
|
|
52
|
+
| 'sidebar-toggle'
|
|
42
53
|
| 'history-search'
|
|
43
54
|
| 'search'
|
|
44
55
|
| 'block-copy'
|
|
@@ -59,11 +70,12 @@ export const ACTION_DESCRIPTIONS: Record<KeyAction, string> = {
|
|
|
59
70
|
'copy-selection': 'Copy selected text to clipboard',
|
|
60
71
|
'clear-cancel': 'Clear input / cancel generation / exit (double)',
|
|
61
72
|
'screen-clear': 'Repaint the screen',
|
|
62
|
-
'
|
|
63
|
-
'
|
|
64
|
-
'
|
|
65
|
-
'
|
|
66
|
-
'
|
|
73
|
+
'workspace-picker': 'Open the Agent operator workspace',
|
|
74
|
+
'workspace-close': 'Close the Agent workspace',
|
|
75
|
+
'workspace-close-all': 'Close the Agent workspace (alias: also bound to Ctrl+Shift+X)',
|
|
76
|
+
'workspace-tab-next': 'Cycle Agent workspace category forward',
|
|
77
|
+
'workspace-tab-prev': 'Cycle Agent workspace category backward',
|
|
78
|
+
'sidebar-toggle': 'Show or hide the activity sidebar',
|
|
67
79
|
'history-search': 'Reverse input history search',
|
|
68
80
|
'search': 'Toggle conversation search',
|
|
69
81
|
'block-copy': 'Copy nearest block to clipboard',
|
|
@@ -98,11 +110,12 @@ export const DEFAULT_KEYBINDINGS: Record<KeyAction, KeyCombo[]> = {
|
|
|
98
110
|
'copy-selection': [{ key: 'c', ctrl: true, shift: true }],
|
|
99
111
|
'clear-cancel': [{ key: 'c', ctrl: true }],
|
|
100
112
|
'screen-clear': [{ key: 'l', ctrl: true }],
|
|
101
|
-
'
|
|
102
|
-
'
|
|
103
|
-
'
|
|
104
|
-
'
|
|
105
|
-
'
|
|
113
|
+
'workspace-picker': [{ key: 'p', ctrl: true }],
|
|
114
|
+
'workspace-close': [{ key: 'x', ctrl: true }],
|
|
115
|
+
'workspace-close-all': [{ key: 'x', ctrl: true, shift: true }],
|
|
116
|
+
'workspace-tab-next': [{ key: ']', ctrl: true }],
|
|
117
|
+
'workspace-tab-prev': [{ key: '[', ctrl: true }],
|
|
118
|
+
'sidebar-toggle': [{ key: 'o', ctrl: true }],
|
|
106
119
|
'history-search': [{ key: 'r', ctrl: true }],
|
|
107
120
|
'search': [{ key: 'f', ctrl: true }],
|
|
108
121
|
'block-copy': [{ key: 'y', ctrl: true }],
|
|
@@ -116,7 +129,7 @@ export const DEFAULT_KEYBINDINGS: Record<KeyAction, KeyCombo[]> = {
|
|
|
116
129
|
'undo': [{ key: 'z', ctrl: true }],
|
|
117
130
|
'redo': [{ key: 'z', ctrl: true, shift: true }],
|
|
118
131
|
'paste': [{ key: 'v', ctrl: true }],
|
|
119
|
-
'replay-panel': [
|
|
132
|
+
'replay-panel': [], // intentionally unbound until replay-panel is implemented
|
|
120
133
|
};
|
|
121
134
|
|
|
122
135
|
/** Resolved overrides type: each key can be a single combo or array. */
|
|
@@ -185,7 +198,17 @@ export class KeybindingsManager {
|
|
|
185
198
|
// Reset to defaults before applying overrides
|
|
186
199
|
this.bindings = this.cloneDefaults();
|
|
187
200
|
|
|
188
|
-
|
|
201
|
+
// Backward-compatibility aliases: map old panel-* action ids to workspace-* equivalents.
|
|
202
|
+
const ALIASES: Readonly<Record<string, KeyAction>> = {
|
|
203
|
+
'panel-picker': 'workspace-picker',
|
|
204
|
+
'panel-close': 'workspace-close',
|
|
205
|
+
'panel-close-all': 'workspace-close-all',
|
|
206
|
+
'panel-tab-next': 'workspace-tab-next',
|
|
207
|
+
'panel-tab-prev': 'workspace-tab-prev',
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
for (const [rawAction, combo] of Object.entries(parsed)) {
|
|
211
|
+
const action: string = Object.hasOwn(ALIASES, rawAction) ? ALIASES[rawAction as keyof typeof ALIASES] : rawAction;
|
|
189
212
|
if (!validActions.has(action as KeyAction)) {
|
|
190
213
|
logger.debug('keybindings: unknown action, skipping', { action });
|
|
191
214
|
continue;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local-fit recommendation builder.
|
|
3
|
+
*
|
|
4
|
+
* When no provider credentials are configured, the model picker would show an
|
|
5
|
+
* empty list. This module builds a small set of synthetic model entries that
|
|
6
|
+
* represent common local model sizes so the picker opens with useful content.
|
|
7
|
+
*
|
|
8
|
+
* Rules:
|
|
9
|
+
* - These entries are purely informational — they are NOT installed models.
|
|
10
|
+
* - They must never appear when any real provider is configured.
|
|
11
|
+
* - Selecting one routes through the local-provider install/confirm path,
|
|
12
|
+
* never a silent model switch.
|
|
13
|
+
* - The provider sentinel 'local' is distinct from 'ollama', 'lm-studio',
|
|
14
|
+
* etc. so cloud-provider fit checks are never triggered.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { ModelDefinition } from '@pellux/goodvibes-sdk/platform/providers';
|
|
18
|
+
import {
|
|
19
|
+
estimateModelBytes,
|
|
20
|
+
fitAssessment,
|
|
21
|
+
fitVerdictLabel,
|
|
22
|
+
readHardwareProfileSync,
|
|
23
|
+
} from '../core/hardware-profile.ts';
|
|
24
|
+
import type { HardwareProfile } from '../core/hardware-profile.ts';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Sentinel provider ID used on synthetic local recommendation entries.
|
|
28
|
+
* Must not collide with any real provider (ollama, lm-studio, etc.).
|
|
29
|
+
*/
|
|
30
|
+
export const LOCAL_REC_PROVIDER = 'local';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Representative local model sizes to recommend.
|
|
34
|
+
* Each entry: { id, displayName, params, sizeLabel }
|
|
35
|
+
*/
|
|
36
|
+
const LOCAL_REC_SIZES = [
|
|
37
|
+
{ id: 'local:3b', displayName: '3B model (small, fast)', params: 3_000_000_000, sizeLabel: '3B' },
|
|
38
|
+
{ id: 'local:7b', displayName: '7B model (balanced)', params: 7_000_000_000, sizeLabel: '7B' },
|
|
39
|
+
{ id: 'local:13b', displayName: '13B model (more capable)', params: 13_000_000_000, sizeLabel: '13B' },
|
|
40
|
+
] as const;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Sentinel id used on the synthetic "Sign in to a provider" row that is
|
|
44
|
+
* appended to the local-only list when no credentials are configured.
|
|
45
|
+
*/
|
|
46
|
+
export const SIGN_IN_ROW_ID = 'local:sign-in';
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Return true when a model entry is the synthetic sign-in affordance row.
|
|
50
|
+
* This row must never be committed as an active model.
|
|
51
|
+
*/
|
|
52
|
+
export function isProviderSignInRow(model: ModelDefinition): boolean {
|
|
53
|
+
return model.id === SIGN_IN_ROW_ID && model.provider === LOCAL_REC_PROVIDER;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Return true when a model entry is a synthetic local fit recommendation
|
|
58
|
+
* (i.e. not a real installed model).
|
|
59
|
+
*/
|
|
60
|
+
export function isLocalFitRecommendation(model: ModelDefinition): boolean {
|
|
61
|
+
return (
|
|
62
|
+
model.provider === LOCAL_REC_PROVIDER &&
|
|
63
|
+
(model.description ?? '').includes('not yet installed')
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Build the synthetic "Sign in to a provider" row for the local-only list.
|
|
69
|
+
* Selecting this row routes to the provider picker; it must never be committed
|
|
70
|
+
* as an active model.
|
|
71
|
+
*/
|
|
72
|
+
export function buildSignInRow(): ModelDefinition {
|
|
73
|
+
return {
|
|
74
|
+
id: SIGN_IN_ROW_ID,
|
|
75
|
+
provider: LOCAL_REC_PROVIDER,
|
|
76
|
+
registryKey: SIGN_IN_ROW_ID,
|
|
77
|
+
displayName: 'Sign in to a provider →',
|
|
78
|
+
description: 'Open the provider sign-in flow to connect a cloud or local provider.',
|
|
79
|
+
capabilities: {
|
|
80
|
+
toolCalling: false,
|
|
81
|
+
codeEditing: false,
|
|
82
|
+
reasoning: false,
|
|
83
|
+
multimodal: false,
|
|
84
|
+
},
|
|
85
|
+
contextWindow: 0,
|
|
86
|
+
selectable: true,
|
|
87
|
+
tier: 'free',
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Build synthetic local recommendation entries annotated with hardware fit
|
|
93
|
+
* labels for the current machine.
|
|
94
|
+
*
|
|
95
|
+
* Pass a `profile` override for deterministic tests; omit to use the
|
|
96
|
+
* process-level hardware probe (`readHardwareProfileSync`).
|
|
97
|
+
*/
|
|
98
|
+
export function buildLocalFitRecommendations(
|
|
99
|
+
profileOverride?: HardwareProfile,
|
|
100
|
+
): ModelDefinition[] {
|
|
101
|
+
const profile = profileOverride ?? readHardwareProfileSync();
|
|
102
|
+
|
|
103
|
+
return LOCAL_REC_SIZES.map((spec) => {
|
|
104
|
+
const sizeBytes = estimateModelBytes(spec.params);
|
|
105
|
+
const verdict = fitAssessment(sizeBytes, profile);
|
|
106
|
+
const fitHint = fitVerdictLabel(verdict, spec.sizeLabel);
|
|
107
|
+
// Plain-language detail: never imply the model is ready to use.
|
|
108
|
+
const detail = fitHint
|
|
109
|
+
? `recommended for your hardware — not yet installed (${fitHint})`
|
|
110
|
+
: 'recommended for your hardware — not yet installed';
|
|
111
|
+
|
|
112
|
+
const model: ModelDefinition = {
|
|
113
|
+
id: spec.id,
|
|
114
|
+
provider: LOCAL_REC_PROVIDER,
|
|
115
|
+
registryKey: spec.id,
|
|
116
|
+
displayName: spec.displayName,
|
|
117
|
+
description: detail,
|
|
118
|
+
capabilities: {
|
|
119
|
+
toolCalling: true,
|
|
120
|
+
codeEditing: false,
|
|
121
|
+
reasoning: false,
|
|
122
|
+
multimodal: false,
|
|
123
|
+
},
|
|
124
|
+
contextWindow: 8192,
|
|
125
|
+
selectable: true,
|
|
126
|
+
tier: 'free',
|
|
127
|
+
};
|
|
128
|
+
return model;
|
|
129
|
+
});
|
|
130
|
+
}
|
|
@@ -3,13 +3,11 @@ import type { SubmissionIntent } from './submission-intent.ts';
|
|
|
3
3
|
export interface SubmissionRouterInput {
|
|
4
4
|
readonly text: string;
|
|
5
5
|
readonly commandMode?: boolean;
|
|
6
|
-
readonly panelFocused?: boolean;
|
|
7
6
|
readonly hasAttachments?: boolean;
|
|
8
7
|
}
|
|
9
8
|
|
|
10
9
|
const PLAN_COMMANDS = new Set(['plan']);
|
|
11
10
|
const DELEGATION_COMMANDS = new Set(['delegate', 'build', 'review', 'wrfc', 'agents', 'remote']);
|
|
12
|
-
const PANEL_COMMANDS = new Set(['panel']);
|
|
13
11
|
const ORCHESTRATION_COMMANDS = new Set([
|
|
14
12
|
'orchestration',
|
|
15
13
|
'tasks',
|
|
@@ -45,9 +43,6 @@ export function routeSubmissionIntent(input: SubmissionRouterInput): SubmissionI
|
|
|
45
43
|
if (DELEGATION_COMMANDS.has(commandName)) {
|
|
46
44
|
return { kind: 'delegation', label: 'delegation', commandName, hasAttachments };
|
|
47
45
|
}
|
|
48
|
-
if (PANEL_COMMANDS.has(commandName)) {
|
|
49
|
-
return { kind: 'slash-command', label: 'Agent workspace', commandName, hasAttachments };
|
|
50
|
-
}
|
|
51
46
|
if (ORCHESTRATION_COMMANDS.has(commandName)) {
|
|
52
47
|
return { kind: 'orchestration', label: 'orchestration', commandName, hasAttachments };
|
|
53
48
|
}
|
package/src/main.ts
CHANGED
|
@@ -25,20 +25,17 @@ import { createShellLayout } from './renderer/layout-engine.ts';
|
|
|
25
25
|
import { buildShellFooter, estimateShellFooterHeight } from './renderer/shell-surface.ts';
|
|
26
26
|
import { buildConversationViewport } from './renderer/conversation-layout.ts';
|
|
27
27
|
import { applyConversationOverlays } from './renderer/conversation-overlays.ts';
|
|
28
|
-
import {
|
|
28
|
+
import { buildActivitySidebarLines, resolveActivitySidebarWidth } from './renderer/activity-sidebar.ts';
|
|
29
29
|
import { logger } from '@pellux/goodvibes-sdk/platform/utils';
|
|
30
|
-
import { registerBuiltinPanels } from './panels/builtin-panels.ts';
|
|
31
|
-
import { renderPanelTabBar } from './renderer/panel-tab-bar.ts';
|
|
32
30
|
import { bootstrapRuntime } from './runtime/bootstrap.ts';
|
|
33
31
|
import type { BootstrapContext } from './runtime/bootstrap.ts';
|
|
34
32
|
import type { HITLMode } from '@pellux/goodvibes-sdk/platform/state';
|
|
35
|
-
import
|
|
33
|
+
import { wireSessionPersistenceAndRecovery, wireSetupIncompleteHint } from './shell/startup-wiring.ts';
|
|
34
|
+
import { localModelCookbook } from './tools/agent-harness-model-routing.ts';
|
|
35
|
+
import { localModelSetupStatus } from './tools/agent-harness-setup-model-helpers.ts';
|
|
36
36
|
import {
|
|
37
|
-
checkRecoveryFile,
|
|
38
37
|
deleteRecoveryFile,
|
|
39
38
|
loadRecoveryConversation,
|
|
40
|
-
persistConversation,
|
|
41
|
-
writeRecoveryFile,
|
|
42
39
|
} from '@/runtime/index.ts';
|
|
43
40
|
import type { SessionSnapshot } from '@/runtime/index.ts';
|
|
44
41
|
import { handleBlockingShellInput, type PendingPermissionState } from './shell/blocking-input.ts';
|
|
@@ -47,7 +44,7 @@ import { getTerminalSize } from './shell/terminal-size.ts';
|
|
|
47
44
|
import { buildShellSessionContinuityHints } from './shell/session-continuity-hints.ts';
|
|
48
45
|
import { wireShellUiOpeners } from './shell/ui-openers.ts';
|
|
49
46
|
import { deriveComposerState } from './core/composer-state.ts';
|
|
50
|
-
import { buildPersistedSessionContext
|
|
47
|
+
import { buildPersistedSessionContext } from '@/runtime/index.ts';
|
|
51
48
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
52
49
|
import { prepareShellCliRuntime } from './cli/entrypoint.ts';
|
|
53
50
|
import { applyInitialTuiCliState, formatFatalStartupErrorForLog, formatFatalStartupErrorForUser, getInteractiveTerminalLaunchError } from './cli/tui-startup.ts';
|
|
@@ -56,6 +53,8 @@ import { attachSpokenTurnModelRouting, createSpokenTurnInputOptions } from './au
|
|
|
56
53
|
import { allowTerminalWrite, installTuiTerminalOutputGuard } from './runtime/terminal-output-guard.ts';
|
|
57
54
|
import { buildCommandArgsHint } from './input/command-args-hint.ts';
|
|
58
55
|
import { GOODVIBES_AGENT_PAIRING_SURFACE } from './config/surface.ts';
|
|
56
|
+
import { createAutonomySurfacing, buildCalendarEventsLister, buildSkillDraftProposer } from './shell/autonomy-surfacing.ts';
|
|
57
|
+
import { startHardwareProbe } from './core/hardware-profile.ts';
|
|
59
58
|
|
|
60
59
|
const ALT_SCREEN_ENTER = '\x1b[?1049h', ALT_SCREEN_EXIT = '\x1b[?1049l', MOUSE_ENABLE = '\x1b[?1000h\x1b[?1002h\x1b[?1006h', MOUSE_DISABLE = '\x1b[?1006l\x1b[?1002l\x1b[?1000l', CURSOR_HIDE = '\x1b[?25l', CURSOR_SHOW = '\x1b[?25h', CLEAR_SCREEN = '\x1b[2J\x1b[3J\x1b[H';
|
|
61
60
|
const KEYBOARD_EXT_ENABLE = '\x1b[>4;2m' + '\x1b[?1u', KEYBOARD_EXT_DISABLE = '\x1b[>4;0m' + '\x1b[?1l', PASTE_ENABLE = '\x1b[?2004h', PASTE_DISABLE = '\x1b[?2004l';
|
|
@@ -127,12 +126,10 @@ async function main() {
|
|
|
127
126
|
}
|
|
128
127
|
}
|
|
129
128
|
|
|
130
|
-
const panelManager = ctx.services.panelManager;
|
|
131
129
|
const buildSessionContinuityHints = () => buildShellSessionContinuityHints(
|
|
132
130
|
uiServices.readModels.session.getSnapshot(),
|
|
133
131
|
uiServices.readModels.tasks.getSnapshot(),
|
|
134
132
|
uiServices.readModels.remote.getSnapshot(),
|
|
135
|
-
panelManager.getAllOpen(),
|
|
136
133
|
);
|
|
137
134
|
const buildCurrentSessionSnapshot = (): SessionSnapshot => {
|
|
138
135
|
const messages = conversation.getMessageSnapshot();
|
|
@@ -154,13 +151,37 @@ async function main() {
|
|
|
154
151
|
render();
|
|
155
152
|
});
|
|
156
153
|
|
|
157
|
-
let streamStartTime = 0;
|
|
158
|
-
let streamDeltaCount = 0;
|
|
159
154
|
let streamTokenSpeed = 0;
|
|
160
155
|
|
|
161
156
|
let scrollTop = 0;
|
|
162
157
|
let scrollLocked = true;
|
|
163
158
|
|
|
159
|
+
// Ambient autonomy surfacing: away digest at launch + sidebar Coming up.
|
|
160
|
+
const autonomy = createAutonomySurfacing({
|
|
161
|
+
shellPaths: ctx.services.shellPaths,
|
|
162
|
+
listAutomationJobs: () => ctx.services.automationManager.listJobs(),
|
|
163
|
+
listApprovals: () => ctx.services.approvalBroker.listApprovals(),
|
|
164
|
+
getTasksSnapshot: () => uiServices.readModels.tasks.getSnapshot().tasks,
|
|
165
|
+
router: {
|
|
166
|
+
high: (message) => systemMessageRouter.high(message),
|
|
167
|
+
getFeed: () => systemMessageRouter.getFeed(),
|
|
168
|
+
},
|
|
169
|
+
render: () => render(),
|
|
170
|
+
listCalendarEvents: buildCalendarEventsLister(ctx.services.shellPaths),
|
|
171
|
+
onAwayDigest: buildSkillDraftProposer(ctx.services.shellPaths, commandContext),
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Activity sidebar: shows ambient status on wide terminals. null = automatic
|
|
175
|
+
// (visible when the terminal is wide enough); the user can toggle it with
|
|
176
|
+
// Ctrl+O, which pins an explicit on/off override for the session.
|
|
177
|
+
let sidebarOverride: boolean | null = null;
|
|
178
|
+
const sidebarWidthFor = (width: number): number => {
|
|
179
|
+
const auto = resolveActivitySidebarWidth(width);
|
|
180
|
+
if (sidebarOverride === null) return auto;
|
|
181
|
+
if (!sidebarOverride) return 0;
|
|
182
|
+
return auto > 0 ? auto : Math.min(36, Math.max(28, Math.floor(width * 0.3)));
|
|
183
|
+
};
|
|
184
|
+
|
|
164
185
|
const getPromptContentWidth = () => {
|
|
165
186
|
const w = getTerminalSize(stdout).width;
|
|
166
187
|
const boxMargin = 2;
|
|
@@ -228,6 +249,8 @@ async function main() {
|
|
|
228
249
|
const exitApp = (): void => {
|
|
229
250
|
stopSpokenOutputForExit?.();
|
|
230
251
|
unsubs.forEach(fn => fn());
|
|
252
|
+
// Persist last-seen before shutdown so the next launch can compute the digest.
|
|
253
|
+
autonomy.stop();
|
|
231
254
|
const snapshot = buildCurrentSessionSnapshot();
|
|
232
255
|
ctx.shutdown(snapshot).catch((err) => {
|
|
233
256
|
logger.debug('ctx.shutdown error during exitApp (non-fatal)', { error: summarizeError(err) });
|
|
@@ -349,6 +372,11 @@ async function main() {
|
|
|
349
372
|
allowTerminalWrite(() => stdout.write(CLEAR_SCREEN));
|
|
350
373
|
render();
|
|
351
374
|
};
|
|
375
|
+
commandContext.toggleActivitySidebar = () => {
|
|
376
|
+
const width = getTerminalSize(stdout).width;
|
|
377
|
+
sidebarOverride = !(sidebarWidthFor(width) > 0);
|
|
378
|
+
render();
|
|
379
|
+
};
|
|
352
380
|
permissionPromptRef.requestPermission = (request) =>
|
|
353
381
|
new Promise((resolve) => {
|
|
354
382
|
pendingPermission = {
|
|
@@ -389,7 +417,6 @@ async function main() {
|
|
|
389
417
|
shell: {
|
|
390
418
|
bookmarkManager: ctx.services.bookmarkManager,
|
|
391
419
|
keybindingsManager: ctx.services.keybindingsManager,
|
|
392
|
-
panelManager,
|
|
393
420
|
processManager,
|
|
394
421
|
profileManager: ctx.services.profileManager,
|
|
395
422
|
},
|
|
@@ -430,8 +457,10 @@ async function main() {
|
|
|
430
457
|
const render = () => {
|
|
431
458
|
const { width, height } = getTerminalSize(stdout);
|
|
432
459
|
|
|
460
|
+
// Fire-and-forget refresh for the 'Coming up' sidebar section.
|
|
461
|
+
autonomy.refreshComingUp();
|
|
462
|
+
|
|
433
463
|
if (input.agentWorkspace.active) {
|
|
434
|
-
input.setPanelMouseLayout(null);
|
|
435
464
|
activeConversationWidth = width;
|
|
436
465
|
conversation.setSplashSuppressed(true);
|
|
437
466
|
if (input.modelPicker.active) {
|
|
@@ -463,7 +492,6 @@ async function main() {
|
|
|
463
492
|
const composerState = deriveComposerState({
|
|
464
493
|
text: input.prompt,
|
|
465
494
|
commandMode: input.commandMode,
|
|
466
|
-
panelFocused: input.panelFocused,
|
|
467
495
|
pendingApproval: pendingPermission !== null,
|
|
468
496
|
hasAttachments: input.getImageAttachments().size > 0,
|
|
469
497
|
turnState: sessionSnapshot.turnState,
|
|
@@ -513,31 +541,18 @@ async function main() {
|
|
|
513
541
|
|
|
514
542
|
const shellHeaderLines = headerLines;
|
|
515
543
|
const shellFooterLines = footerLines;
|
|
516
|
-
const
|
|
517
|
-
? panelManager.getRightWidth(width)
|
|
518
|
-
: 0;
|
|
544
|
+
const sidebarWidth = sidebarWidthFor(width);
|
|
519
545
|
const shellLayout = createShellLayout({
|
|
520
546
|
width,
|
|
521
547
|
height,
|
|
522
548
|
headerHeight: shellHeaderLines.length,
|
|
523
549
|
footerHeight: shellFooterLines.length,
|
|
524
|
-
panelWidth,
|
|
550
|
+
panelWidth: sidebarWidth,
|
|
525
551
|
});
|
|
526
|
-
input.setPanelMouseLayout(shellLayout.panel
|
|
527
|
-
? {
|
|
528
|
-
x: shellLayout.panel.x,
|
|
529
|
-
y: shellLayout.panel.y,
|
|
530
|
-
width: shellLayout.panel.width,
|
|
531
|
-
height: shellLayout.panel.height,
|
|
532
|
-
hasBottomPane: panelManager.isBottomPaneVisible() && panelManager.getBottomPane().panels.length > 0,
|
|
533
|
-
verticalSplitRatio: panelManager.getVerticalSplitRatio(),
|
|
534
|
-
}
|
|
535
|
-
: null);
|
|
536
552
|
const vHeight = shellLayout.body.height;
|
|
537
553
|
const conversationWidth = shellLayout.conversation.width;
|
|
538
554
|
activeConversationWidth = conversationWidth;
|
|
539
|
-
|
|
540
|
-
conversation.setSplashSuppressed(hasPanelWorkspace);
|
|
555
|
+
conversation.setSplashSuppressed(false);
|
|
541
556
|
|
|
542
557
|
// Flush pending renders after updating the width provider and splash posture
|
|
543
558
|
// so the transcript and splash rebuild against the current shell layout.
|
|
@@ -599,13 +614,27 @@ async function main() {
|
|
|
599
614
|
contextWindow: currentModel.contextWindow,
|
|
600
615
|
});
|
|
601
616
|
|
|
602
|
-
//
|
|
603
|
-
const
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
617
|
+
// Activity sidebar (ambient status on wide terminals)
|
|
618
|
+
const sidebar = sidebarWidth > 0
|
|
619
|
+
? {
|
|
620
|
+
lines: buildActivitySidebarLines({
|
|
621
|
+
now: {
|
|
622
|
+
busy: orchestrator.isThinking,
|
|
623
|
+
label: sessionSnapshot.streamToolPreview?.trim() || undefined,
|
|
624
|
+
agents: activeAgents.slice(0, 3).map((agent) => ({
|
|
625
|
+
label: agent.label,
|
|
626
|
+
progress: agent.latestProgress?.trim() || undefined,
|
|
627
|
+
})),
|
|
628
|
+
processes: runningProcessCount,
|
|
629
|
+
},
|
|
630
|
+
needsYou: pendingPermission
|
|
631
|
+
? ['Approval needed — answer the prompt under the conversation.']
|
|
632
|
+
: [],
|
|
633
|
+
comingUp: [...autonomy.comingUpItems()],
|
|
634
|
+
recent: systemMessageRouter.getFeed()?.latest(Math.max(4, vHeight - 8)) ?? [],
|
|
635
|
+
}, sidebarWidth, vHeight),
|
|
636
|
+
}
|
|
637
|
+
: undefined;
|
|
609
638
|
|
|
610
639
|
compositor.composite({
|
|
611
640
|
width, height,
|
|
@@ -622,8 +651,8 @@ async function main() {
|
|
|
622
651
|
scrollTop,
|
|
623
652
|
viewportStartY: shellHeaderLines.length,
|
|
624
653
|
} : undefined,
|
|
625
|
-
|
|
626
|
-
|
|
654
|
+
sidebar,
|
|
655
|
+
sidebarWidth,
|
|
627
656
|
});
|
|
628
657
|
};
|
|
629
658
|
const terminalOutputGuard = installTuiTerminalOutputGuard({ stdout, stderr: process.stderr, notify: (message) => { systemMessageRouter.low(message); render(); } });
|
|
@@ -634,7 +663,6 @@ async function main() {
|
|
|
634
663
|
wireShellUiOpeners({
|
|
635
664
|
commandContext,
|
|
636
665
|
input,
|
|
637
|
-
panelManager,
|
|
638
666
|
conversation,
|
|
639
667
|
configManager,
|
|
640
668
|
providerRegistry,
|
|
@@ -651,35 +679,6 @@ async function main() {
|
|
|
651
679
|
render,
|
|
652
680
|
});
|
|
653
681
|
|
|
654
|
-
// --- Streaming speed + tool preview wiring ---
|
|
655
|
-
unsubs.push(uiServices.events.turns.on('TURN_COMPLETED', () => {
|
|
656
|
-
// Auto-save after every LLM turn so kills don't lose the session
|
|
657
|
-
try {
|
|
658
|
-
const snapshot = buildCurrentSessionSnapshot();
|
|
659
|
-
persistConversation(
|
|
660
|
-
runtime.sessionId,
|
|
661
|
-
snapshot,
|
|
662
|
-
runtime.model,
|
|
663
|
-
runtime.provider,
|
|
664
|
-
conversation.title || '',
|
|
665
|
-
{ workingDirectory: workingDir, homeDirectory, sessionManager: ctx.services.sessionManager },
|
|
666
|
-
);
|
|
667
|
-
hookDispatcher.fire({ path: 'Lifecycle:session:save' as HookEventPath, phase: 'Lifecycle' as HookPhase, category: 'session' as HookCategory, specific: 'save', sessionId: runtime.sessionId, timestamp: Date.now(), payload: { sessionId: runtime.sessionId } }).catch((err: unknown) => logger.debug('hook fire error', { error: summarizeError(err) }));
|
|
668
|
-
} catch (e) { logger.debug('auto-save on turn:complete failed', { error: summarizeError(e) }); }
|
|
669
|
-
}));
|
|
670
|
-
|
|
671
|
-
unsubs.push(uiServices.events.turns.on('STREAM_START', () => {
|
|
672
|
-
streamStartTime = Date.now();
|
|
673
|
-
streamDeltaCount = 0;
|
|
674
|
-
streamTokenSpeed = 0;
|
|
675
|
-
}));
|
|
676
|
-
unsubs.push(uiServices.events.turns.on('STREAM_DELTA', () => {
|
|
677
|
-
streamDeltaCount++;
|
|
678
|
-
const elapsed = (Date.now() - streamStartTime) / 1000;
|
|
679
|
-
// Note: counts stream deltas, not actual tokens. ~1 delta per token for most providers.
|
|
680
|
-
streamTokenSpeed = elapsed > 0 ? streamDeltaCount / elapsed : 0;
|
|
681
|
-
}));
|
|
682
|
-
|
|
683
682
|
stdin.setRawMode(true);
|
|
684
683
|
stdin.resume();
|
|
685
684
|
stdin.setEncoding('utf8');
|
|
@@ -721,25 +720,48 @@ async function main() {
|
|
|
721
720
|
conversation.rebuildHistory();
|
|
722
721
|
render();
|
|
723
722
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
for (const line of formatReturnContextForDisplay(recoveryInfo.returnContext)) {
|
|
728
|
-
systemMessageRouter.low(`[Recovery] ${line}`);
|
|
729
|
-
}
|
|
730
|
-
render();
|
|
731
|
-
recoveryPending = true;
|
|
732
|
-
}
|
|
723
|
+
// Async GPU probe runs off the render frame — nvidia-smi result will populate
|
|
724
|
+
// the module cache and appear on the next render cycle after it completes.
|
|
725
|
+
startHardwareProbe();
|
|
733
726
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
727
|
+
// Away digest runs after the first render so it lands as ambient context,
|
|
728
|
+
// never a startup blocker.
|
|
729
|
+
autonomy.announceAwayDigest();
|
|
730
|
+
|
|
731
|
+
// If setup is in-progress (user has opened /agent but not finished), show a
|
|
732
|
+
// gentle plain-language reminder and point them back to /agent.
|
|
733
|
+
wireSetupIncompleteHint({
|
|
734
|
+
shellPaths: ctx.services.shellPaths,
|
|
735
|
+
providerReady: (() => {
|
|
736
|
+
try { return Boolean(providerRegistry.getCurrentModel()?.id); } catch { return false; }
|
|
737
|
+
})(),
|
|
738
|
+
// localReady mirrors the 'local-model-readiness' plan item from buildSetupPlan:
|
|
739
|
+
// cookbook status === 'detected-local-route'. Best-effort — never blocks render.
|
|
740
|
+
localReady: (() => {
|
|
741
|
+
try { return localModelSetupStatus(localModelCookbook(commandContext, false) as Record<string, unknown>) === 'ready'; } catch { return false; }
|
|
742
|
+
})(),
|
|
743
|
+
// hostReady is intentionally omitted: at startup the external-services stub
|
|
744
|
+
// always reports mode='external', which would produce a misleading 'active'
|
|
745
|
+
// line before any real probe has run. Omitting it keeps the hint honest.
|
|
746
|
+
systemMessageRouter,
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
// Wire streaming-speed metrics, auto-save, and recovery — all run after the
|
|
750
|
+
// first render so they land as ambient context, never startup blockers.
|
|
751
|
+
({ recoveryInterval, recoveryPending } = wireSessionPersistenceAndRecovery({
|
|
752
|
+
buildCurrentSessionSnapshot,
|
|
753
|
+
runtime,
|
|
754
|
+
conversation,
|
|
755
|
+
workingDir,
|
|
756
|
+
homeDirectory,
|
|
757
|
+
systemMessageRouter,
|
|
758
|
+
render,
|
|
759
|
+
unsubs,
|
|
760
|
+
uiServicesTurns: uiServices.events.turns,
|
|
761
|
+
hookDispatcher,
|
|
762
|
+
sessionManager: ctx.services.sessionManager,
|
|
763
|
+
onStreamSpeedUpdate: (speed) => { streamTokenSpeed = speed; },
|
|
764
|
+
}));
|
|
743
765
|
}
|
|
744
766
|
main().catch((err: unknown) => {
|
|
745
767
|
const detail = formatFatalStartupErrorForLog(err);
|
|
@@ -758,4 +780,4 @@ main().catch((err: unknown) => {
|
|
|
758
780
|
// Ignore secondary stderr failures during process teardown.
|
|
759
781
|
}
|
|
760
782
|
process.exit(1);
|
|
761
|
-
});
|
|
783
|
+
});
|