@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.
- package/CHANGELOG.md +22 -0
- package/README.md +52 -7
- package/dist/package/main.js +7491 -12253
- 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/live-verification/live-verification.json +13 -13
- package/release/live-verification/live-verification.md +15 -15
- package/release/performance-snapshot.json +2 -2
- package/release/release-notes.md +5 -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/shell-passthrough.ts +58 -0
- package/src/input/submission-router.ts +0 -5
- package/src/main.ts +129 -90
- 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/main.ts
CHANGED
|
@@ -20,25 +20,23 @@ import { renderSettingsModal } from './renderer/settings-modal.ts';
|
|
|
20
20
|
import { registerBuiltinCommands } from './input/commands.ts';
|
|
21
21
|
import { ScheduleManager } from '@pellux/goodvibes-sdk/platform/tools';
|
|
22
22
|
import { InputHistory } from './input/input-history.ts';
|
|
23
|
+
import { ShellPassthrough, SHELL_USAGE_HINT } from './input/shell-passthrough.ts';
|
|
23
24
|
import { getTierPromptSupplement, getTierForContextWindow } from '@pellux/goodvibes-sdk/platform/providers';
|
|
24
25
|
import { createShellLayout } from './renderer/layout-engine.ts';
|
|
25
26
|
import { buildShellFooter, estimateShellFooterHeight } from './renderer/shell-surface.ts';
|
|
26
27
|
import { buildConversationViewport } from './renderer/conversation-layout.ts';
|
|
27
28
|
import { applyConversationOverlays } from './renderer/conversation-overlays.ts';
|
|
28
|
-
import {
|
|
29
|
+
import { buildActivitySidebarLines, resolveActivitySidebarWidth } from './renderer/activity-sidebar.ts';
|
|
29
30
|
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
31
|
import { bootstrapRuntime } from './runtime/bootstrap.ts';
|
|
33
32
|
import type { BootstrapContext } from './runtime/bootstrap.ts';
|
|
34
33
|
import type { HITLMode } from '@pellux/goodvibes-sdk/platform/state';
|
|
35
|
-
import
|
|
34
|
+
import { wireSessionPersistenceAndRecovery, wireSetupIncompleteHint } from './shell/startup-wiring.ts';
|
|
35
|
+
import { localModelCookbook } from './tools/agent-harness-model-routing.ts';
|
|
36
|
+
import { localModelSetupStatus } from './tools/agent-harness-setup-model-helpers.ts';
|
|
36
37
|
import {
|
|
37
|
-
checkRecoveryFile,
|
|
38
38
|
deleteRecoveryFile,
|
|
39
39
|
loadRecoveryConversation,
|
|
40
|
-
persistConversation,
|
|
41
|
-
writeRecoveryFile,
|
|
42
40
|
} from '@/runtime/index.ts';
|
|
43
41
|
import type { SessionSnapshot } from '@/runtime/index.ts';
|
|
44
42
|
import { handleBlockingShellInput, type PendingPermissionState } from './shell/blocking-input.ts';
|
|
@@ -47,7 +45,7 @@ import { getTerminalSize } from './shell/terminal-size.ts';
|
|
|
47
45
|
import { buildShellSessionContinuityHints } from './shell/session-continuity-hints.ts';
|
|
48
46
|
import { wireShellUiOpeners } from './shell/ui-openers.ts';
|
|
49
47
|
import { deriveComposerState } from './core/composer-state.ts';
|
|
50
|
-
import { buildPersistedSessionContext
|
|
48
|
+
import { buildPersistedSessionContext } from '@/runtime/index.ts';
|
|
51
49
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
52
50
|
import { prepareShellCliRuntime } from './cli/entrypoint.ts';
|
|
53
51
|
import { applyInitialTuiCliState, formatFatalStartupErrorForLog, formatFatalStartupErrorForUser, getInteractiveTerminalLaunchError } from './cli/tui-startup.ts';
|
|
@@ -56,6 +54,8 @@ import { attachSpokenTurnModelRouting, createSpokenTurnInputOptions } from './au
|
|
|
56
54
|
import { allowTerminalWrite, installTuiTerminalOutputGuard } from './runtime/terminal-output-guard.ts';
|
|
57
55
|
import { buildCommandArgsHint } from './input/command-args-hint.ts';
|
|
58
56
|
import { GOODVIBES_AGENT_PAIRING_SURFACE } from './config/surface.ts';
|
|
57
|
+
import { createAutonomySurfacing, buildCalendarEventsLister, buildSkillDraftProposer } from './shell/autonomy-surfacing.ts';
|
|
58
|
+
import { startHardwareProbe } from './core/hardware-profile.ts';
|
|
59
59
|
|
|
60
60
|
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
61
|
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 +127,10 @@ async function main() {
|
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
const panelManager = ctx.services.panelManager;
|
|
131
130
|
const buildSessionContinuityHints = () => buildShellSessionContinuityHints(
|
|
132
131
|
uiServices.readModels.session.getSnapshot(),
|
|
133
132
|
uiServices.readModels.tasks.getSnapshot(),
|
|
134
133
|
uiServices.readModels.remote.getSnapshot(),
|
|
135
|
-
panelManager.getAllOpen(),
|
|
136
134
|
);
|
|
137
135
|
const buildCurrentSessionSnapshot = (): SessionSnapshot => {
|
|
138
136
|
const messages = conversation.getMessageSnapshot();
|
|
@@ -154,13 +152,39 @@ async function main() {
|
|
|
154
152
|
render();
|
|
155
153
|
});
|
|
156
154
|
|
|
157
|
-
let streamStartTime = 0;
|
|
158
|
-
let streamDeltaCount = 0;
|
|
159
155
|
let streamTokenSpeed = 0;
|
|
160
156
|
|
|
161
157
|
let scrollTop = 0;
|
|
162
158
|
let scrollLocked = true;
|
|
163
159
|
|
|
160
|
+
const shellPassthrough = new ShellPassthrough();
|
|
161
|
+
|
|
162
|
+
// Ambient autonomy surfacing: away digest at launch + sidebar Coming up.
|
|
163
|
+
const autonomy = createAutonomySurfacing({
|
|
164
|
+
shellPaths: ctx.services.shellPaths,
|
|
165
|
+
listAutomationJobs: () => ctx.services.automationManager.listJobs(),
|
|
166
|
+
listApprovals: () => ctx.services.approvalBroker.listApprovals(),
|
|
167
|
+
getTasksSnapshot: () => uiServices.readModels.tasks.getSnapshot().tasks,
|
|
168
|
+
router: {
|
|
169
|
+
high: (message) => systemMessageRouter.high(message),
|
|
170
|
+
getFeed: () => systemMessageRouter.getFeed(),
|
|
171
|
+
},
|
|
172
|
+
render: () => render(),
|
|
173
|
+
listCalendarEvents: buildCalendarEventsLister(ctx.services.shellPaths),
|
|
174
|
+
onAwayDigest: buildSkillDraftProposer(ctx.services.shellPaths, commandContext),
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Activity sidebar: shows ambient status on wide terminals. null = automatic
|
|
178
|
+
// (visible when the terminal is wide enough); the user can toggle it with
|
|
179
|
+
// Ctrl+O, which pins an explicit on/off override for the session.
|
|
180
|
+
let sidebarOverride: boolean | null = null;
|
|
181
|
+
const sidebarWidthFor = (width: number): number => {
|
|
182
|
+
const auto = resolveActivitySidebarWidth(width);
|
|
183
|
+
if (sidebarOverride === null) return auto;
|
|
184
|
+
if (!sidebarOverride) return 0;
|
|
185
|
+
return auto > 0 ? auto : Math.min(36, Math.max(28, Math.floor(width * 0.3)));
|
|
186
|
+
};
|
|
187
|
+
|
|
164
188
|
const getPromptContentWidth = () => {
|
|
165
189
|
const w = getTerminalSize(stdout).width;
|
|
166
190
|
const boxMargin = 2;
|
|
@@ -228,6 +252,8 @@ async function main() {
|
|
|
228
252
|
const exitApp = (): void => {
|
|
229
253
|
stopSpokenOutputForExit?.();
|
|
230
254
|
unsubs.forEach(fn => fn());
|
|
255
|
+
// Persist last-seen before shutdown so the next launch can compute the digest.
|
|
256
|
+
autonomy.stop();
|
|
231
257
|
const snapshot = buildCurrentSessionSnapshot();
|
|
232
258
|
ctx.shutdown(snapshot).catch((err) => {
|
|
233
259
|
logger.debug('ctx.shutdown error during exitApp (non-fatal)', { error: summarizeError(err) });
|
|
@@ -292,14 +318,28 @@ async function main() {
|
|
|
292
318
|
systemMessageRouter.high(`[Memory] Pinned: "${memoryText}" (${memId})`);
|
|
293
319
|
processedText = memoryText;
|
|
294
320
|
}
|
|
321
|
+
} else if (processedText.startsWith('!')) {
|
|
322
|
+
const command = processedText.slice(1).trim();
|
|
323
|
+
if (!command) {
|
|
324
|
+
systemMessageRouter.high(SHELL_USAGE_HINT);
|
|
325
|
+
render();
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
systemMessageRouter.high(`[Shell] $ ${command}`); render();
|
|
329
|
+
void shellPassthrough.run(command, workingDir)
|
|
330
|
+
.then((result) => systemMessageRouter.high(result.display))
|
|
331
|
+
.catch((shellErr: unknown) => systemMessageRouter.high(`[Shell] Failed to run: ${summarizeError(shellErr)}`))
|
|
332
|
+
.finally(() => render());
|
|
333
|
+
return;
|
|
295
334
|
}
|
|
296
335
|
if (processedText || content) {
|
|
297
336
|
void (async () => {
|
|
298
337
|
const inputOptions = options.spokenOutput ? createSpokenTurnInputOptions() : undefined;
|
|
338
|
+
const outgoing = shellPassthrough.consumeContext(processedText);
|
|
299
339
|
if (options.spokenOutput && processedText) {
|
|
300
340
|
spokenTurns.submitNextTurn(processedText);
|
|
301
341
|
}
|
|
302
|
-
orchestrator.handleUserInput(
|
|
342
|
+
orchestrator.handleUserInput(outgoing, content, inputOptions).catch((err: unknown) => {
|
|
303
343
|
logger.debug('handleUserInput safety catch (already handled by runTurn)', { error: summarizeError(err) });
|
|
304
344
|
});
|
|
305
345
|
})();
|
|
@@ -349,6 +389,11 @@ async function main() {
|
|
|
349
389
|
allowTerminalWrite(() => stdout.write(CLEAR_SCREEN));
|
|
350
390
|
render();
|
|
351
391
|
};
|
|
392
|
+
commandContext.toggleActivitySidebar = () => {
|
|
393
|
+
const width = getTerminalSize(stdout).width;
|
|
394
|
+
sidebarOverride = !(sidebarWidthFor(width) > 0);
|
|
395
|
+
render();
|
|
396
|
+
};
|
|
352
397
|
permissionPromptRef.requestPermission = (request) =>
|
|
353
398
|
new Promise((resolve) => {
|
|
354
399
|
pendingPermission = {
|
|
@@ -389,7 +434,6 @@ async function main() {
|
|
|
389
434
|
shell: {
|
|
390
435
|
bookmarkManager: ctx.services.bookmarkManager,
|
|
391
436
|
keybindingsManager: ctx.services.keybindingsManager,
|
|
392
|
-
panelManager,
|
|
393
437
|
processManager,
|
|
394
438
|
profileManager: ctx.services.profileManager,
|
|
395
439
|
},
|
|
@@ -430,8 +474,10 @@ async function main() {
|
|
|
430
474
|
const render = () => {
|
|
431
475
|
const { width, height } = getTerminalSize(stdout);
|
|
432
476
|
|
|
477
|
+
// Fire-and-forget refresh for the 'Coming up' sidebar section.
|
|
478
|
+
autonomy.refreshComingUp();
|
|
479
|
+
|
|
433
480
|
if (input.agentWorkspace.active) {
|
|
434
|
-
input.setPanelMouseLayout(null);
|
|
435
481
|
activeConversationWidth = width;
|
|
436
482
|
conversation.setSplashSuppressed(true);
|
|
437
483
|
if (input.modelPicker.active) {
|
|
@@ -463,7 +509,6 @@ async function main() {
|
|
|
463
509
|
const composerState = deriveComposerState({
|
|
464
510
|
text: input.prompt,
|
|
465
511
|
commandMode: input.commandMode,
|
|
466
|
-
panelFocused: input.panelFocused,
|
|
467
512
|
pendingApproval: pendingPermission !== null,
|
|
468
513
|
hasAttachments: input.getImageAttachments().size > 0,
|
|
469
514
|
turnState: sessionSnapshot.turnState,
|
|
@@ -513,31 +558,18 @@ async function main() {
|
|
|
513
558
|
|
|
514
559
|
const shellHeaderLines = headerLines;
|
|
515
560
|
const shellFooterLines = footerLines;
|
|
516
|
-
const
|
|
517
|
-
? panelManager.getRightWidth(width)
|
|
518
|
-
: 0;
|
|
561
|
+
const sidebarWidth = sidebarWidthFor(width);
|
|
519
562
|
const shellLayout = createShellLayout({
|
|
520
563
|
width,
|
|
521
564
|
height,
|
|
522
565
|
headerHeight: shellHeaderLines.length,
|
|
523
566
|
footerHeight: shellFooterLines.length,
|
|
524
|
-
panelWidth,
|
|
567
|
+
panelWidth: sidebarWidth,
|
|
525
568
|
});
|
|
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
569
|
const vHeight = shellLayout.body.height;
|
|
537
570
|
const conversationWidth = shellLayout.conversation.width;
|
|
538
571
|
activeConversationWidth = conversationWidth;
|
|
539
|
-
|
|
540
|
-
conversation.setSplashSuppressed(hasPanelWorkspace);
|
|
572
|
+
conversation.setSplashSuppressed(false);
|
|
541
573
|
|
|
542
574
|
// Flush pending renders after updating the width provider and splash posture
|
|
543
575
|
// so the transcript and splash rebuild against the current shell layout.
|
|
@@ -599,13 +631,27 @@ async function main() {
|
|
|
599
631
|
contextWindow: currentModel.contextWindow,
|
|
600
632
|
});
|
|
601
633
|
|
|
602
|
-
//
|
|
603
|
-
const
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
634
|
+
// Activity sidebar (ambient status on wide terminals)
|
|
635
|
+
const sidebar = sidebarWidth > 0
|
|
636
|
+
? {
|
|
637
|
+
lines: buildActivitySidebarLines({
|
|
638
|
+
now: {
|
|
639
|
+
busy: orchestrator.isThinking,
|
|
640
|
+
label: sessionSnapshot.streamToolPreview?.trim() || undefined,
|
|
641
|
+
agents: activeAgents.slice(0, 3).map((agent) => ({
|
|
642
|
+
label: agent.label,
|
|
643
|
+
progress: agent.latestProgress?.trim() || undefined,
|
|
644
|
+
})),
|
|
645
|
+
processes: runningProcessCount,
|
|
646
|
+
},
|
|
647
|
+
needsYou: pendingPermission
|
|
648
|
+
? ['Approval needed — answer the prompt under the conversation.']
|
|
649
|
+
: [],
|
|
650
|
+
comingUp: [...autonomy.comingUpItems()],
|
|
651
|
+
recent: systemMessageRouter.getFeed()?.latest(Math.max(4, vHeight - 8)) ?? [],
|
|
652
|
+
}, sidebarWidth, vHeight),
|
|
653
|
+
}
|
|
654
|
+
: undefined;
|
|
609
655
|
|
|
610
656
|
compositor.composite({
|
|
611
657
|
width, height,
|
|
@@ -622,8 +668,8 @@ async function main() {
|
|
|
622
668
|
scrollTop,
|
|
623
669
|
viewportStartY: shellHeaderLines.length,
|
|
624
670
|
} : undefined,
|
|
625
|
-
|
|
626
|
-
|
|
671
|
+
sidebar,
|
|
672
|
+
sidebarWidth,
|
|
627
673
|
});
|
|
628
674
|
};
|
|
629
675
|
const terminalOutputGuard = installTuiTerminalOutputGuard({ stdout, stderr: process.stderr, notify: (message) => { systemMessageRouter.low(message); render(); } });
|
|
@@ -634,7 +680,6 @@ async function main() {
|
|
|
634
680
|
wireShellUiOpeners({
|
|
635
681
|
commandContext,
|
|
636
682
|
input,
|
|
637
|
-
panelManager,
|
|
638
683
|
conversation,
|
|
639
684
|
configManager,
|
|
640
685
|
providerRegistry,
|
|
@@ -651,35 +696,6 @@ async function main() {
|
|
|
651
696
|
render,
|
|
652
697
|
});
|
|
653
698
|
|
|
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
699
|
stdin.setRawMode(true);
|
|
684
700
|
stdin.resume();
|
|
685
701
|
stdin.setEncoding('utf8');
|
|
@@ -721,25 +737,48 @@ async function main() {
|
|
|
721
737
|
conversation.rebuildHistory();
|
|
722
738
|
render();
|
|
723
739
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
for (const line of formatReturnContextForDisplay(recoveryInfo.returnContext)) {
|
|
728
|
-
systemMessageRouter.low(`[Recovery] ${line}`);
|
|
729
|
-
}
|
|
730
|
-
render();
|
|
731
|
-
recoveryPending = true;
|
|
732
|
-
}
|
|
740
|
+
// Async GPU probe runs off the render frame — nvidia-smi result will populate
|
|
741
|
+
// the module cache and appear on the next render cycle after it completes.
|
|
742
|
+
startHardwareProbe();
|
|
733
743
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
744
|
+
// Away digest runs after the first render so it lands as ambient context,
|
|
745
|
+
// never a startup blocker.
|
|
746
|
+
autonomy.announceAwayDigest();
|
|
747
|
+
|
|
748
|
+
// If setup is in-progress (user has opened /agent but not finished), show a
|
|
749
|
+
// gentle plain-language reminder and point them back to /agent.
|
|
750
|
+
wireSetupIncompleteHint({
|
|
751
|
+
shellPaths: ctx.services.shellPaths,
|
|
752
|
+
providerReady: (() => {
|
|
753
|
+
try { return Boolean(providerRegistry.getCurrentModel()?.id); } catch { return false; }
|
|
754
|
+
})(),
|
|
755
|
+
// localReady mirrors the 'local-model-readiness' plan item from buildSetupPlan:
|
|
756
|
+
// cookbook status === 'detected-local-route'. Best-effort — never blocks render.
|
|
757
|
+
localReady: (() => {
|
|
758
|
+
try { return localModelSetupStatus(localModelCookbook(commandContext, false) as Record<string, unknown>) === 'ready'; } catch { return false; }
|
|
759
|
+
})(),
|
|
760
|
+
// hostReady is intentionally omitted: at startup the external-services stub
|
|
761
|
+
// always reports mode='external', which would produce a misleading 'active'
|
|
762
|
+
// line before any real probe has run. Omitting it keeps the hint honest.
|
|
763
|
+
systemMessageRouter,
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
// Wire streaming-speed metrics, auto-save, and recovery — all run after the
|
|
767
|
+
// first render so they land as ambient context, never startup blockers.
|
|
768
|
+
({ recoveryInterval, recoveryPending } = wireSessionPersistenceAndRecovery({
|
|
769
|
+
buildCurrentSessionSnapshot,
|
|
770
|
+
runtime,
|
|
771
|
+
conversation,
|
|
772
|
+
workingDir,
|
|
773
|
+
homeDirectory,
|
|
774
|
+
systemMessageRouter,
|
|
775
|
+
render,
|
|
776
|
+
unsubs,
|
|
777
|
+
uiServicesTurns: uiServices.events.turns,
|
|
778
|
+
hookDispatcher,
|
|
779
|
+
sessionManager: ctx.services.sessionManager,
|
|
780
|
+
onStreamSpeedUpdate: (speed) => { streamTokenSpeed = speed; },
|
|
781
|
+
}));
|
|
743
782
|
}
|
|
744
783
|
main().catch((err: unknown) => {
|
|
745
784
|
const detail = formatFatalStartupErrorForLog(err);
|
|
@@ -758,4 +797,4 @@ main().catch((err: unknown) => {
|
|
|
758
797
|
// Ignore secondary stderr failures during process teardown.
|
|
759
798
|
}
|
|
760
799
|
process.exit(1);
|
|
761
|
-
});
|
|
800
|
+
});
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Activity sidebar — the ambient right-hand surface of the shell.
|
|
3
|
+
*
|
|
4
|
+
* One glanceable column with at most four sections:
|
|
5
|
+
*
|
|
6
|
+
* Now what the assistant is doing this second (only while busy)
|
|
7
|
+
* Needs you approvals waiting on the user (only when non-empty)
|
|
8
|
+
* Coming up next scheduled work, when known (only when non-empty)
|
|
9
|
+
* Recent the activity feed, newest first
|
|
10
|
+
*
|
|
11
|
+
* Deliberately display-only: no focus, no tabs, no selection. Interactions
|
|
12
|
+
* stay in the conversation and the Agent workspace.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { Line } from '../types/grid.ts';
|
|
16
|
+
import { createEmptyLine } from '../types/grid.ts';
|
|
17
|
+
import type { ActivityEntry, ActivityKind } from '../core/activity-feed.ts';
|
|
18
|
+
import {
|
|
19
|
+
DEFAULT_PANEL_PALETTE,
|
|
20
|
+
buildPanelLine,
|
|
21
|
+
buildSectionHeader,
|
|
22
|
+
buildBodyText,
|
|
23
|
+
} from './polish.ts';
|
|
24
|
+
import { getDisplayWidth, truncateDisplay } from '../utils/terminal-width.ts';
|
|
25
|
+
|
|
26
|
+
export interface ActivitySidebarNow {
|
|
27
|
+
/** True while a turn is streaming or tools are running. */
|
|
28
|
+
readonly busy: boolean;
|
|
29
|
+
/** Short human label for the current work, e.g. "Searching the web…". */
|
|
30
|
+
readonly label?: string;
|
|
31
|
+
/** Background agents with their latest progress lines. */
|
|
32
|
+
readonly agents: ReadonlyArray<{ readonly label: string; readonly progress?: string }>;
|
|
33
|
+
/** Count of running background processes. */
|
|
34
|
+
readonly processes: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ActivitySidebarView {
|
|
38
|
+
readonly now: ActivitySidebarNow;
|
|
39
|
+
/** Plain-language items waiting on the user (approvals, prompts). */
|
|
40
|
+
readonly needsYou: readonly string[];
|
|
41
|
+
/** Plain-language upcoming scheduled work, soonest first. */
|
|
42
|
+
readonly comingUp: readonly string[];
|
|
43
|
+
/** Activity feed entries, newest first. */
|
|
44
|
+
readonly recent: readonly ActivityEntry[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const C = DEFAULT_PANEL_PALETTE;
|
|
48
|
+
|
|
49
|
+
const KIND_GLYPHS: Record<ActivityKind, string> = {
|
|
50
|
+
status: '·',
|
|
51
|
+
tool: '·',
|
|
52
|
+
agent: '»',
|
|
53
|
+
schedule: '◷',
|
|
54
|
+
delivery: '↗',
|
|
55
|
+
security: '!',
|
|
56
|
+
system: '·',
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const KIND_COLORS: Record<ActivityKind, string> = {
|
|
60
|
+
status: C.dim,
|
|
61
|
+
tool: C.dim,
|
|
62
|
+
agent: C.info,
|
|
63
|
+
schedule: C.accent,
|
|
64
|
+
delivery: C.good,
|
|
65
|
+
security: C.warn,
|
|
66
|
+
system: C.dim,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
function fmtClock(at: number): string {
|
|
70
|
+
const d = new Date(at);
|
|
71
|
+
return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function entryLine(width: number, entry: ActivityEntry): Line {
|
|
75
|
+
const time = fmtClock(entry.at);
|
|
76
|
+
const glyph = KIND_GLYPHS[entry.kind];
|
|
77
|
+
const color = KIND_COLORS[entry.kind];
|
|
78
|
+
// Strip the leading "[Tag]" — the glyph and color already carry the kind,
|
|
79
|
+
// and horizontal space is the scarcest resource in the sidebar.
|
|
80
|
+
const text = entry.text.replace(/^\[[^\]]+\]\s*/, '');
|
|
81
|
+
const room = Math.max(4, width - time.length - 5);
|
|
82
|
+
return buildPanelLine(width, [
|
|
83
|
+
[` ${time} `, C.dim],
|
|
84
|
+
[`${glyph} `, color],
|
|
85
|
+
[truncateDisplay(text, room), entry.priority === 'high' ? C.value : C.dim],
|
|
86
|
+
]);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function wrappedItemLines(width: number, text: string, fg: string, bullet: string, bulletFg: string): Line[] {
|
|
90
|
+
const bodyWidth = Math.max(4, width - 3);
|
|
91
|
+
const body = buildBodyText(bodyWidth, text, C, fg);
|
|
92
|
+
return body.map((line, index) => {
|
|
93
|
+
const prefix = index === 0 ? ` ${bullet} ` : ' ';
|
|
94
|
+
const cells = buildPanelLine(width, [[prefix, bulletFg]]);
|
|
95
|
+
for (let i = 0; i < bodyWidth; i++) {
|
|
96
|
+
const cell = line[i];
|
|
97
|
+
if (cell !== undefined && 3 + i < width) cells[3 + i] = cell;
|
|
98
|
+
}
|
|
99
|
+
return cells;
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Render the sidebar to exactly `height` lines of exactly `width` cells.
|
|
105
|
+
*/
|
|
106
|
+
export function buildActivitySidebarLines(
|
|
107
|
+
view: ActivitySidebarView,
|
|
108
|
+
width: number,
|
|
109
|
+
height: number,
|
|
110
|
+
): Line[] {
|
|
111
|
+
const lines: Line[] = [];
|
|
112
|
+
const push = (line: Line) => {
|
|
113
|
+
if (lines.length < height) lines.push(line);
|
|
114
|
+
};
|
|
115
|
+
const blank = () => push(createEmptyLine(width));
|
|
116
|
+
|
|
117
|
+
// ── Now ──
|
|
118
|
+
if (view.now.busy || view.now.agents.length > 0 || view.now.processes > 0) {
|
|
119
|
+
push(buildSectionHeader(width, 'Now', C));
|
|
120
|
+
if (view.now.busy) {
|
|
121
|
+
push(buildPanelLine(width, [
|
|
122
|
+
[' ● ', C.info],
|
|
123
|
+
[truncateDisplay(view.now.label ?? 'Working…', Math.max(4, width - 4)), C.value],
|
|
124
|
+
]));
|
|
125
|
+
}
|
|
126
|
+
for (const agent of view.now.agents.slice(0, 3)) {
|
|
127
|
+
const text = agent.progress ? `${agent.label} — ${agent.progress}` : agent.label;
|
|
128
|
+
push(buildPanelLine(width, [
|
|
129
|
+
[' » ', C.info],
|
|
130
|
+
[truncateDisplay(text, Math.max(4, width - 4)), C.dim],
|
|
131
|
+
]));
|
|
132
|
+
}
|
|
133
|
+
if (view.now.processes > 0) {
|
|
134
|
+
push(buildPanelLine(width, [
|
|
135
|
+
[' ▸ ', C.dim],
|
|
136
|
+
[`${view.now.processes} background ${view.now.processes === 1 ? 'process' : 'processes'}`, C.dim],
|
|
137
|
+
]));
|
|
138
|
+
}
|
|
139
|
+
blank();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ── Needs you ──
|
|
143
|
+
if (view.needsYou.length > 0) {
|
|
144
|
+
push(buildSectionHeader(width, 'Needs you', C));
|
|
145
|
+
for (const item of view.needsYou.slice(0, 4)) {
|
|
146
|
+
for (const line of wrappedItemLines(width, item, C.value, '!', C.warn)) push(line);
|
|
147
|
+
}
|
|
148
|
+
blank();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ── Coming up ──
|
|
152
|
+
if (view.comingUp.length > 0) {
|
|
153
|
+
push(buildSectionHeader(width, 'Coming up', C));
|
|
154
|
+
for (const item of view.comingUp.slice(0, 4)) {
|
|
155
|
+
for (const line of wrappedItemLines(width, item, C.dim, '◷', C.accent)) push(line);
|
|
156
|
+
}
|
|
157
|
+
blank();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ── Recent ──
|
|
161
|
+
push(buildSectionHeader(width, 'Recent', C));
|
|
162
|
+
const remaining = Math.max(0, height - lines.length);
|
|
163
|
+
if (view.recent.length === 0) {
|
|
164
|
+
push(buildPanelLine(width, [[' Nothing yet — activity will show up here.', C.dim]]));
|
|
165
|
+
} else {
|
|
166
|
+
for (const entry of view.recent.slice(0, remaining)) {
|
|
167
|
+
push(entryLine(width, entry));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
while (lines.length < height) lines.push(createEmptyLine(width));
|
|
172
|
+
return lines.slice(0, height);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Pick the sidebar width for a terminal width, or 0 when it should not render.
|
|
177
|
+
* The sidebar only earns its space on wide terminals; the conversation always
|
|
178
|
+
* keeps at least ~80 usable columns.
|
|
179
|
+
*/
|
|
180
|
+
export function resolveActivitySidebarWidth(terminalWidth: number): number {
|
|
181
|
+
if (terminalWidth < 120) return 0;
|
|
182
|
+
return Math.min(44, Math.max(32, Math.floor(terminalWidth * 0.24)));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** True when `getDisplayWidth` would matter; exported for tests. */
|
|
186
|
+
export const __test__ = { fmtClock, entryLine, KIND_GLYPHS, getDisplayWidth };
|
|
@@ -464,12 +464,6 @@ export function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspac
|
|
|
464
464
|
...promptReceiptTimelineLines(snapshot),
|
|
465
465
|
{ text: 'Context controls: prompt receipts, project files, one-file inspection, and VIBE.md review.', fg: PALETTE.good },
|
|
466
466
|
);
|
|
467
|
-
} else if (category.id === 'onboarding-automation') {
|
|
468
|
-
base.push(
|
|
469
|
-
{ text: `Automation: ${snapshot.automationEnabled ? 'enabled' : 'disabled'}; max ${snapshot.automationMaxConcurrentRuns} concurrent; history ${snapshot.automationRunHistoryLimit}.`, fg: snapshot.automationEnabled ? PALETTE.good : PALETTE.muted },
|
|
470
|
-
{ text: `Timeout ${snapshot.automationDefaultTimeoutMs} ms; catch-up ${snapshot.automationCatchUpWindowMinutes} min; cooldown ${snapshot.automationFailureCooldownMs} ms.`, fg: PALETTE.info },
|
|
471
|
-
{ text: `Delete one-shot jobs after success: ${snapshot.automationDeleteAfterRun ? 'yes' : 'no'}.`, fg: snapshot.automationDeleteAfterRun ? PALETTE.info : PALETTE.muted },
|
|
472
|
-
);
|
|
473
467
|
} else if (category.id === 'research') {
|
|
474
468
|
const runnerContract = snapshot.researchBrowserRunnerContract;
|
|
475
469
|
const visualContract = snapshot.researchVisualReportContract;
|
|
@@ -500,31 +494,6 @@ export function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspac
|
|
|
500
494
|
{ text: 'Email/calendar: connector setup needed before inbox triage or agenda workflows are first-class.', fg: PALETTE.warn },
|
|
501
495
|
{ text: 'Model route: personal_ops action:"briefing|status|queue|intake|lane|read".', fg: PALETTE.muted },
|
|
502
496
|
);
|
|
503
|
-
} else if (category.id === 'artifacts') {
|
|
504
|
-
const mediaReady = snapshot.voiceMediaReadiness.readyMediaProviderCount;
|
|
505
|
-
base.push(
|
|
506
|
-
{ text: `Chat: ${snapshot.provider} / ${snapshot.modelDisplayName}; Knowledge: ${snapshot.knowledgeRoute}`, fg: PALETTE.info },
|
|
507
|
-
{ text: `Media: ${mediaReady}/${snapshot.mediaProviderCount} ready; generation ${snapshot.mediaGenerationProviderCount}.`, fg: mediaReady > 0 ? PALETTE.good : PALETTE.warn },
|
|
508
|
-
reviewerHandoffArtifactLine(snapshot),
|
|
509
|
-
{ text: 'Files: attach, export, inspect, ingest reviewed sources, or generate media.', fg: PALETTE.good },
|
|
510
|
-
{ text: 'Knowledge ingest and media generation require explicit actions.', fg: PALETTE.warn },
|
|
511
|
-
);
|
|
512
|
-
} else if (category.id === 'channels') {
|
|
513
|
-
const enabledCount = snapshot.channels.filter((channel) => channel.enabled).length;
|
|
514
|
-
const readyCount = snapshot.channels.filter((channel) => channel.ready).length;
|
|
515
|
-
const configuredDefaults = snapshot.channels.filter((channel) => channel.defaultTarget === 'configured').length;
|
|
516
|
-
const guide = snapshot.channelSetupGuide;
|
|
517
|
-
const currentGuideStep = guide.steps.find((step) => step.status === 'current') ?? null;
|
|
518
|
-
base.push(
|
|
519
|
-
{ text: `API: ${snapshot.runtimeBaseUrl}`, fg: PALETTE.info },
|
|
520
|
-
companionAccessLine(snapshot),
|
|
521
|
-
{ text: `Channels: ${readyCount}/${snapshot.channels.length} ready; ${enabledCount} enabled; ${configuredDefaults} target(s).`, fg: PALETTE.info },
|
|
522
|
-
{ text: `Setup guide: ${guide.progressLabel}; ${guide.currentChannelLabel ?? 'choose a channel'}.`, fg: guide.status === 'ready' ? PALETTE.good : PALETTE.warn },
|
|
523
|
-
{ text: `Next: ${currentGuideStep ? currentGuideStep.label : 'All enabled channels ready.'}`, fg: currentGuideStep ? PALETTE.warn : PALETTE.good },
|
|
524
|
-
{ text: 'Guide checks setup schema, accounts, allowlist policy, live status, and explicit test sends.', fg: PALETTE.good },
|
|
525
|
-
{ text: 'Triage: /channels triage shows blockers, delivery retries, surface messages, route bindings, and receipts.', fg: PALETTE.good },
|
|
526
|
-
{ text: 'Secrets hidden; sends require explicit action.', fg: PALETTE.warn },
|
|
527
|
-
);
|
|
528
497
|
} else if (category.id === 'knowledge') {
|
|
529
498
|
base.push(
|
|
530
499
|
{ text: `Route: ${snapshot.knowledgeRoute}; isolation ${snapshot.knowledgeIsolation}.`, fg: PALETTE.info },
|
|
@@ -554,17 +523,6 @@ export function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspac
|
|
|
554
523
|
{ text: 'Add/update/reload and trust changes require confirmation.', fg: PALETTE.good },
|
|
555
524
|
{ text: 'Start: /mcp review, /mcp tools, /mcp config, Add MCP server.', fg: PALETTE.muted },
|
|
556
525
|
);
|
|
557
|
-
} else if (category.id === 'voice-media') {
|
|
558
|
-
const readiness = snapshot.voiceMediaReadiness;
|
|
559
|
-
base.push(
|
|
560
|
-
{ text: `Voice: ${readiness.readyVoiceProviderCount}/${snapshot.voiceProviderCount} ready; TTS ${snapshot.ttsProvider}; voice ${snapshot.ttsVoice}.`, fg: readiness.readyVoiceProviderCount > 0 ? PALETTE.good : PALETTE.warn },
|
|
561
|
-
{ text: `Media: ${readiness.readyMediaProviderCount}/${snapshot.mediaProviderCount} ready; generation ${snapshot.mediaGenerationProviderCount}.`, fg: readiness.readyMediaProviderCount > 0 ? PALETTE.good : PALETTE.warn },
|
|
562
|
-
{ text: `Browser: ${readiness.browserToolState}; public URL ${snapshot.browserToolPublicBaseUrl}.`, fg: snapshot.browserToolExposureEnabled ? PALETTE.warn : PALETTE.muted },
|
|
563
|
-
{ text: 'Model route: device action:"voice|status"; computer action:"plan|browser|open_browser".', fg: PALETTE.info },
|
|
564
|
-
{ text: 'Device map: pairing, mobile/PWA, notifications, browser/desktop, camera/location via device.', fg: PALETTE.info },
|
|
565
|
-
{ text: readiness.nextSteps[0] ? `Next: ${compactText(readiness.nextSteps[0])}` : 'Next: voice/media setup is ready.', fg: readiness.nextSteps.length > 0 ? PALETTE.info : PALETTE.good },
|
|
566
|
-
{ text: 'Secrets hidden; voice, browser, and media side effects require explicit action.', fg: PALETTE.warn },
|
|
567
|
-
);
|
|
568
526
|
} else if (category.id === 'profiles') {
|
|
569
527
|
const defaultProfile = snapshot.selectedRuntimeProfile
|
|
570
528
|
? `${snapshot.selectedRuntimeProfile}${snapshot.selectedRuntimeProfileExists ? '' : ' (missing)'}`
|
|
@@ -642,6 +600,11 @@ export function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspac
|
|
|
642
600
|
{ text: 'Approval actions require id plus typed confirmation.', fg: PALETTE.warn },
|
|
643
601
|
);
|
|
644
602
|
} else if (category.id === 'automation') {
|
|
603
|
+
base.push(
|
|
604
|
+
{ text: `Automation: ${snapshot.automationEnabled ? 'enabled' : 'disabled'}; max ${snapshot.automationMaxConcurrentRuns} concurrent; history ${snapshot.automationRunHistoryLimit}.`, fg: snapshot.automationEnabled ? PALETTE.good : PALETTE.muted },
|
|
605
|
+
{ text: `Timeout ${snapshot.automationDefaultTimeoutMs} ms; catch-up ${snapshot.automationCatchUpWindowMinutes} min; cooldown ${snapshot.automationFailureCooldownMs} ms.`, fg: PALETTE.info },
|
|
606
|
+
{ text: `Delete one-shot jobs after success: ${snapshot.automationDeleteAfterRun ? 'yes' : 'no'}.`, fg: snapshot.automationDeleteAfterRun ? PALETTE.info : PALETTE.muted },
|
|
607
|
+
);
|
|
645
608
|
const ready = readyRoutineItems(snapshot);
|
|
646
609
|
base.push(
|
|
647
610
|
{ text: `Automation: ${ready.length} schedule-ready routine(s); receipts ${snapshot.routineScheduleReceiptCount}.`, fg: ready.length > 0 ? PALETTE.good : PALETTE.warn },
|
|
@@ -650,12 +613,6 @@ export function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspac
|
|
|
650
613
|
{ text: 'Autonomy queue: review visible schedules, runs, receipts, and cancel routes first.', fg: PALETTE.good },
|
|
651
614
|
{ text: 'Reminders and routine promotion require confirmation.', fg: PALETTE.warn },
|
|
652
615
|
);
|
|
653
|
-
} else if (category.id === 'delegate') {
|
|
654
|
-
base.push(
|
|
655
|
-
{ text: 'Build/fix/review work is handed to GoodVibes TUI.', fg: PALETTE.info },
|
|
656
|
-
{ text: `Delegated review policy: ${snapshot.delegatedReviewPolicy}`, fg: PALETTE.warn },
|
|
657
|
-
{ text: 'No coding-role Agent jobs are created here.', fg: PALETTE.good },
|
|
658
|
-
);
|
|
659
616
|
} else if (category.id === 'finish') {
|
|
660
617
|
base.push(
|
|
661
618
|
{ text: 'Apply & close marks onboarding finished for this user.', fg: PALETTE.good },
|
|
@@ -22,7 +22,7 @@ export function safetyColor(action: AgentWorkspaceAction): string {
|
|
|
22
22
|
export function actionResultColor(result: AgentWorkspaceActionResult): string {
|
|
23
23
|
if (result.kind === 'blocked' || result.kind === 'error') return PALETTE.bad;
|
|
24
24
|
if (result.kind === 'dispatched') return PALETTE.info;
|
|
25
|
-
if (result.kind === 'refreshed') return PALETTE.good;
|
|
25
|
+
if (result.kind === 'refreshed' || result.kind === 'recap') return PALETTE.good;
|
|
26
26
|
return PALETTE.muted;
|
|
27
27
|
}
|
|
28
28
|
|