@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
|
@@ -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
|
|
|
@@ -158,6 +158,8 @@ function editorPurposeLabel(editorKind: AgentWorkspaceEditorKind): string {
|
|
|
158
158
|
case 'skill-search': return 'Search skills';
|
|
159
159
|
case 'skill-show': return 'Show skill';
|
|
160
160
|
case 'skill-discovery-import': return 'Import skill files';
|
|
161
|
+
case 'skill-standard-import': return 'Import shared skill';
|
|
162
|
+
case 'skill-standard-export': return 'Export skill to share';
|
|
161
163
|
case 'routine': return 'Edit routine';
|
|
162
164
|
case 'routine-search': return 'Search routines';
|
|
163
165
|
case 'routine-show': return 'Show routine';
|
|
@@ -669,8 +671,18 @@ function buildActionRows(workspace: AgentWorkspace, width: number, height: numbe
|
|
|
669
671
|
if (workspace.lastActionResult) {
|
|
670
672
|
rows.push({ text: '' });
|
|
671
673
|
rows.push({ text: `${onboarding ? 'Result' : 'Action Result'}: ${workspace.lastActionResult.title}`, fg: actionResultColor(workspace.lastActionResult), bold: true });
|
|
672
|
-
|
|
673
|
-
|
|
674
|
+
// For recap results, skip the detail body — the checkmarked lines below carry
|
|
675
|
+
// the full content. Rendering detail AND lines would duplicate every line.
|
|
676
|
+
if (workspace.lastActionResult.kind !== 'recap') {
|
|
677
|
+
for (const line of wrapText(workspace.lastActionResult.detail, Math.max(1, width - 2))) {
|
|
678
|
+
rows.push({ text: ` ${line}`, fg: PALETTE.text });
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
if (workspace.lastActionResult.kind === 'recap' && workspace.lastActionResult.lines?.length) {
|
|
682
|
+
rows.push({ text: '' });
|
|
683
|
+
for (const line of workspace.lastActionResult.lines) {
|
|
684
|
+
rows.push({ text: ` ✔ ${line}`, fg: PALETTE.good });
|
|
685
|
+
}
|
|
674
686
|
}
|
|
675
687
|
if (!onboarding && workspace.lastActionResult.command) {
|
|
676
688
|
rows.push({ text: ` Command: ${workspace.lastActionResult.command}`, fg: PALETTE.muted });
|
|
@@ -17,25 +17,9 @@ export interface SearchInfo {
|
|
|
17
17
|
viewportStartY: number;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
export interface
|
|
21
|
-
/**
|
|
22
|
-
|
|
23
|
-
/** Top pane: tab bar */
|
|
24
|
-
topTabBar?: Line;
|
|
25
|
-
/** Top pane: panel content lines */
|
|
26
|
-
topContent: Line[];
|
|
27
|
-
/** Whether the top pane is focused (affects separator color) */
|
|
28
|
-
topFocused: boolean;
|
|
29
|
-
/** Bottom pane tab bar. Undefined = no bottom pane. */
|
|
30
|
-
bottomTabBar?: Line;
|
|
31
|
-
/** Bottom pane content lines. Undefined = no bottom pane. */
|
|
32
|
-
bottomContent?: Line[];
|
|
33
|
-
/** Whether the bottom pane is focused */
|
|
34
|
-
bottomFocused?: boolean;
|
|
35
|
-
/** Separator between left and right panel area */
|
|
36
|
-
separator: boolean;
|
|
37
|
-
/** Ratio of panel height for the top pane (0–1). Only used when bottom pane is present. */
|
|
38
|
-
verticalSplitRatio: number;
|
|
20
|
+
export interface SidebarCompositeData {
|
|
21
|
+
/** Pre-rendered sidebar lines, one per viewport row. */
|
|
22
|
+
lines: Line[];
|
|
39
23
|
}
|
|
40
24
|
|
|
41
25
|
export interface CompositeRequest {
|
|
@@ -47,8 +31,8 @@ export interface CompositeRequest {
|
|
|
47
31
|
forceFullRedraw?: boolean;
|
|
48
32
|
selection?: SelectionInfo;
|
|
49
33
|
search?: SearchInfo;
|
|
50
|
-
|
|
51
|
-
|
|
34
|
+
sidebar?: SidebarCompositeData;
|
|
35
|
+
sidebarWidth?: number; // width of the right sidebar area (0 = no sidebar)
|
|
52
36
|
}
|
|
53
37
|
|
|
54
38
|
/**
|
|
@@ -75,7 +59,7 @@ export class Compositor {
|
|
|
75
59
|
}
|
|
76
60
|
|
|
77
61
|
public composite(params: CompositeRequest): void {
|
|
78
|
-
const { width, height, header, viewport, footer, forceFullRedraw, selection, search,
|
|
62
|
+
const { width, height, header, viewport, footer, forceFullRedraw, selection, search, sidebar, sidebarWidth } = params;
|
|
79
63
|
const previousFrontBuffer = forceFullRedraw ? null : this.frontBuffer;
|
|
80
64
|
if (forceFullRedraw) this.diffEngine.reset();
|
|
81
65
|
|
|
@@ -87,9 +71,9 @@ export class Compositor {
|
|
|
87
71
|
}
|
|
88
72
|
const newBuffer = this.backBuffer;
|
|
89
73
|
|
|
90
|
-
const
|
|
91
|
-
const leftWidth =
|
|
92
|
-
const sepX =
|
|
74
|
+
const hasSidebar = sidebar !== undefined && sidebarWidth !== undefined && sidebarWidth > 0;
|
|
75
|
+
const leftWidth = hasSidebar ? Math.max(1, width - sidebarWidth - 1) : width;
|
|
76
|
+
const sepX = hasSidebar ? leftWidth : -1;
|
|
93
77
|
|
|
94
78
|
// 1. Draw Header — always full width
|
|
95
79
|
header.forEach((line, i) => newBuffer.blitLine(i, line));
|
|
@@ -102,41 +86,17 @@ export class Compositor {
|
|
|
102
86
|
const lineCount = selection?.lineCount ?? 0;
|
|
103
87
|
const offset = Math.max(0, vHeight - lineCount);
|
|
104
88
|
|
|
105
|
-
|
|
106
|
-
// When both top and bottom panes are visible, the panel area is split:
|
|
107
|
-
// row 0: workspace tab bar
|
|
108
|
-
// row 1: top tab bar
|
|
109
|
-
// rows 2..topH+1: top content
|
|
110
|
-
// row topH+2: horizontal separator (───)
|
|
111
|
-
// row topH+3: bottom tab bar
|
|
112
|
-
// rows topH+4..end: bottom content
|
|
113
|
-
const hasBottomPane = hasPanel && panel!.bottomTabBar !== undefined;
|
|
114
|
-
let topPaneHeight = 0; // number of content rows in top pane
|
|
115
|
-
let bottomPaneHeight = 0;
|
|
116
|
-
let hSepRow = -1; // viewport row of the horizontal separator
|
|
117
|
-
if (hasPanel && hasBottomPane) {
|
|
118
|
-
const panelAreaRows = Math.max(0, vHeight - 1); // subtract workspace tab bar
|
|
119
|
-
// top: 1 (tabbar) + topContent rows; bottom: 1 (sep) + 1 (tabbar) + bottomContent
|
|
120
|
-
const contentRows = Math.max(0, panelAreaRows - 3); // subtract top-tabbar + h-sep + bottom-tabbar
|
|
121
|
-
topPaneHeight = Math.max(1, Math.floor(contentRows * panel!.verticalSplitRatio));
|
|
122
|
-
bottomPaneHeight = Math.max(1, contentRows - topPaneHeight);
|
|
123
|
-
hSepRow = 2 + topPaneHeight; // workspace bar + top tab bar + top content rows
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const sepFg = hasPanel && panel!.separator
|
|
127
|
-
? (panel!.topFocused || panel!.bottomFocused ? '244' : '238')
|
|
128
|
-
: '238';
|
|
89
|
+
const sepFg = '238';
|
|
129
90
|
|
|
130
91
|
viewport.forEach((line, i) => {
|
|
131
92
|
const screenY = viewportStartY + i;
|
|
132
93
|
if (screenY >= height) return;
|
|
133
94
|
|
|
134
|
-
if (!
|
|
135
|
-
// No
|
|
95
|
+
if (!hasSidebar) {
|
|
96
|
+
// No sidebar: existing fast path
|
|
136
97
|
newBuffer.blitLine(screenY, line);
|
|
137
98
|
} else {
|
|
138
|
-
//
|
|
139
|
-
// Left side: viewport cells 0..leftWidth-1
|
|
99
|
+
// Sidebar active: left side gets viewport cells 0..leftWidth-1
|
|
140
100
|
for (let x = 0; x < leftWidth; x++) {
|
|
141
101
|
const cell = line[x];
|
|
142
102
|
if (cell !== undefined) {
|
|
@@ -151,79 +111,21 @@ export class Compositor {
|
|
|
151
111
|
}
|
|
152
112
|
}
|
|
153
113
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
// Separator column (vertical bar between left and panel area)
|
|
157
|
-
if (p.separator) {
|
|
158
|
-
newBuffer.setCell(sepX, screenY, createStyledCell('│', { fg: sepFg }));
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const panelStartX = sepX + 1;
|
|
162
|
-
const clearPanelRemainder = (fromX = 0) => {
|
|
163
|
-
for (let x = Math.max(0, fromX); x < panelWidth; x++) {
|
|
164
|
-
newBuffer.setCell(panelStartX + x, screenY, createEmptyCell());
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
const drawPanelLine = (panelLine: Line | undefined) => {
|
|
168
|
-
if (panelLine === undefined) {
|
|
169
|
-
clearPanelRemainder();
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
const limit = Math.min(panelLine.length, panelWidth);
|
|
173
|
-
for (let x = 0; x < limit; x++) {
|
|
174
|
-
const cell = panelLine[x];
|
|
175
|
-
if (cell !== undefined) {
|
|
176
|
-
newBuffer.setCell(panelStartX + x, screenY, cell);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
clearPanelRemainder(limit);
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
if (!hasBottomPane) {
|
|
183
|
-
// --- Single pane mode ---
|
|
184
|
-
// viewport row 0 → workspace bar, viewport rows 1+ → panel content
|
|
185
|
-
const panelLine = i === 0 ? p.workspaceBar : p.topContent[i - 1];
|
|
186
|
-
drawPanelLine(panelLine);
|
|
187
|
-
} else {
|
|
188
|
-
// --- Two pane mode ---
|
|
189
|
-
// Row layout (by viewport row i):
|
|
190
|
-
// i = 0: workspace tab bar
|
|
191
|
-
// i = 1: top tab bar
|
|
192
|
-
// 2 <= i <= topPaneHeight+1: top content[i-2]
|
|
193
|
-
// i = hSepRow: horizontal separator
|
|
194
|
-
// i = hSepRow+1: bottom tab bar
|
|
195
|
-
// i >= hSepRow+2: bottom content[i - (hSepRow+2)]
|
|
196
|
-
let panelLine: Line | undefined;
|
|
197
|
-
|
|
198
|
-
if (i === 0) {
|
|
199
|
-
panelLine = p.workspaceBar;
|
|
200
|
-
} else if (i === 1) {
|
|
201
|
-
panelLine = p.topTabBar;
|
|
202
|
-
} else if (i <= topPaneHeight + 1) {
|
|
203
|
-
panelLine = p.topContent[i - 2];
|
|
204
|
-
} else if (i === hSepRow) {
|
|
205
|
-
// Horizontal separator between the two panes
|
|
206
|
-
// Render ─ chars across the panel width
|
|
207
|
-
const focusFg = p.bottomFocused ? '36' : '238'; // cyan if bottom pane focused
|
|
208
|
-
for (let x = 0; x < panelWidth; x++) {
|
|
209
|
-
newBuffer.setCell(panelStartX + x, screenY, createStyledCell('─', { fg: focusFg }));
|
|
210
|
-
}
|
|
211
|
-
// Also update the separator column char to T-junction (├):
|
|
212
|
-
// ├ connects the vertical left-separator with the horizontal pane divider,
|
|
213
|
-
// forming a clean T-shaped joint at the split point.
|
|
214
|
-
if (p.separator) {
|
|
215
|
-
newBuffer.setCell(sepX, screenY, createStyledCell('├', { fg: focusFg }));
|
|
216
|
-
}
|
|
217
|
-
} else if (i === hSepRow + 1) {
|
|
218
|
-
panelLine = p.bottomTabBar;
|
|
219
|
-
} else {
|
|
220
|
-
panelLine = p.bottomContent?.[i - (hSepRow + 2)];
|
|
221
|
-
}
|
|
114
|
+
// Separator column (vertical bar between conversation and sidebar)
|
|
115
|
+
newBuffer.setCell(sepX, screenY, createStyledCell('│', { fg: sepFg }));
|
|
222
116
|
|
|
223
|
-
|
|
224
|
-
|
|
117
|
+
const sidebarStartX = sepX + 1;
|
|
118
|
+
const sidebarLine = sidebar!.lines[i];
|
|
119
|
+
const limit = sidebarLine === undefined ? 0 : Math.min(sidebarLine.length, sidebarWidth);
|
|
120
|
+
for (let x = 0; x < limit; x++) {
|
|
121
|
+
const cell = sidebarLine![x];
|
|
122
|
+
if (cell !== undefined) {
|
|
123
|
+
newBuffer.setCell(sidebarStartX + x, screenY, cell);
|
|
225
124
|
}
|
|
226
125
|
}
|
|
126
|
+
for (let x = limit; x < sidebarWidth; x++) {
|
|
127
|
+
newBuffer.setCell(sidebarStartX + x, screenY, createEmptyCell());
|
|
128
|
+
}
|
|
227
129
|
}
|
|
228
130
|
|
|
229
131
|
// Apply Selection Highlighting Overlay (left side only)
|
|
@@ -260,12 +162,22 @@ export class Compositor {
|
|
|
260
162
|
newBuffer.blitLine(screenY, createEmptyLine(width));
|
|
261
163
|
}
|
|
262
164
|
|
|
263
|
-
// Draw separator on
|
|
264
|
-
if (
|
|
165
|
+
// Draw the separator and sidebar on viewport rows past the conversation content
|
|
166
|
+
if (hasSidebar) {
|
|
265
167
|
for (let i = viewport.length; i < vHeight; i++) {
|
|
266
168
|
const screenY = viewportStartY + i;
|
|
267
169
|
if (screenY >= height) break;
|
|
268
170
|
newBuffer.setCell(sepX, screenY, createStyledCell('│', { fg: sepFg }));
|
|
171
|
+
const sidebarStartX = sepX + 1;
|
|
172
|
+
const sidebarLine = sidebar!.lines[i];
|
|
173
|
+
const limit = sidebarLine === undefined ? 0 : Math.min(sidebarLine.length, sidebarWidth!);
|
|
174
|
+
for (let x = 0; x < limit; x++) {
|
|
175
|
+
const cell = sidebarLine![x];
|
|
176
|
+
if (cell !== undefined) newBuffer.setCell(sidebarStartX + x, screenY, cell);
|
|
177
|
+
}
|
|
178
|
+
for (let x = limit; x < sidebarWidth!; x++) {
|
|
179
|
+
newBuffer.setCell(sidebarStartX + x, screenY, createEmptyCell());
|
|
180
|
+
}
|
|
269
181
|
}
|
|
270
182
|
}
|
|
271
183
|
|
|
@@ -54,11 +54,11 @@ export function applyConversationOverlays(
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
if (input.searchManager.active) {
|
|
57
|
-
next
|
|
57
|
+
next = [...next, ...renderSearchOverlay(input.searchManager, conversationWidth)];
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
if (input.historySearch.active) {
|
|
61
|
-
next
|
|
61
|
+
next = [...next, ...renderHistorySearchOverlay(input.historySearch, conversationWidth)];
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
if (input.processModal.active) {
|
|
@@ -102,7 +102,7 @@ export function applyConversationOverlays(
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
if (input.helpOverlayActive) {
|
|
105
|
-
const lines = renderHelpOverlay(conversationWidth, keybindingsManager, commandRegistry.
|
|
105
|
+
const lines = renderHelpOverlay(conversationWidth, keybindingsManager, commandRegistry.getVisible(), input.helpScrollOffset, viewportHeight);
|
|
106
106
|
next = replaceViewportWithOverlay(lines, conversationWidth, viewportHeight);
|
|
107
107
|
}
|
|
108
108
|
|
|
@@ -15,7 +15,7 @@ import { logger } from '@pellux/goodvibes-sdk/platform/utils';
|
|
|
15
15
|
const FEATURED_HELP_COMMANDS: Array<[name: string, argHint: string, desc: string]> = [
|
|
16
16
|
['agent', '', 'Open workspace; press / there to search every action'],
|
|
17
17
|
['agent', 'knowledge', 'Open isolated Agent Knowledge workspace actions'],
|
|
18
|
-
['agent', 'voice
|
|
18
|
+
['agent', 'voice', 'Open voice, image, and media workspace actions'],
|
|
19
19
|
['setup', '', 'Open the Agent workspace'],
|
|
20
20
|
['knowledge', 'status', 'Inspect isolated Agent Knowledge readiness'],
|
|
21
21
|
['memory', '', 'Manage Agent-local memory records'],
|
|
@@ -55,6 +55,7 @@ const HELP_OVERLAY_STATIC_TEXT = [
|
|
|
55
55
|
'Quick Start',
|
|
56
56
|
'Available Slash Commands',
|
|
57
57
|
'More Commands',
|
|
58
|
+
'Hidden power commands still work — run /commands for the full catalog.',
|
|
58
59
|
'Essentials',
|
|
59
60
|
'Show this help overlay',
|
|
60
61
|
'Keyboard shortcut reference',
|
|
@@ -158,7 +159,7 @@ export function renderHelpOverlay(
|
|
|
158
159
|
' ' + '\u2500'.repeat(40),
|
|
159
160
|
` ${'?'.padEnd(20)} Toggle help`,
|
|
160
161
|
` ${'/shortcuts'.padEnd(20)} Full keyboard shortcuts`,
|
|
161
|
-
` ${kb('
|
|
162
|
+
` ${kb('workspace-picker').padEnd(20)} Open the Agent operator workspace`,
|
|
162
163
|
` ${'Workspace /'.padEnd(20)} Search all Agent workspace actions`,
|
|
163
164
|
` ${'Workspace Enter'.padEnd(20)} Open selected action or form`,
|
|
164
165
|
'',
|
|
@@ -234,6 +235,7 @@ export function renderHelpOverlay(
|
|
|
234
235
|
commandRows.push(` ${nameCol} ${cmd.description}`);
|
|
235
236
|
}
|
|
236
237
|
}
|
|
238
|
+
commandRows.push('', ' Hidden power commands still work — run /commands for the full catalog.');
|
|
237
239
|
} else if (!hasCommand('help')) {
|
|
238
240
|
commandRows.push('', ' Essentials', ' ' + '\u2500'.repeat(40));
|
|
239
241
|
commandRows.push(' /help Show this help overlay');
|
|
@@ -330,9 +332,9 @@ export function renderShortcutsOverlay(
|
|
|
330
332
|
' Workspace',
|
|
331
333
|
' ' + '\u2500'.repeat(40),
|
|
332
334
|
row('Tab', 'Swap focus between input and active Agent workspace'),
|
|
333
|
-
row(kb('
|
|
334
|
-
row(kb('
|
|
335
|
-
row(kb('
|
|
335
|
+
row(kb('workspace-picker'), 'Open the Agent operator workspace'),
|
|
336
|
+
row(kb('workspace-tab-next'), 'Cycle Agent workspace category forward'),
|
|
337
|
+
row(kb('workspace-tab-prev'), 'Cycle Agent workspace category backward'),
|
|
336
338
|
'',
|
|
337
339
|
` Config: /keybindings to list and customize`,
|
|
338
340
|
];
|