@pellux/goodvibes-agent 0.1.117 → 1.0.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 +13 -4
- package/README.md +35 -14
- package/bin/goodvibes-agent.ts +16 -2
- package/dist/package/main.js +176073 -170980
- package/docs/README.md +11 -5
- package/docs/channels-remote-and-api.md +50 -0
- package/docs/connected-host.md +3 -3
- package/docs/getting-started.md +29 -15
- package/docs/knowledge-artifacts-and-multimodal.md +91 -0
- package/docs/project-planning.md +79 -0
- package/docs/providers-and-routing.md +46 -0
- package/docs/release-and-publishing.md +44 -9
- package/docs/tools-and-commands.md +123 -0
- package/docs/voice-and-live-tts.md +51 -0
- package/package.json +2 -5
- package/src/agent/channel-delivery.ts +201 -0
- package/src/agent/media-generation.ts +159 -0
- package/src/agent/memory-prompt.ts +0 -1
- package/src/agent/note-registry.ts +329 -0
- package/src/agent/operator-actions.ts +343 -0
- package/src/agent/persona-registry.ts +15 -14
- package/src/agent/record-labels.ts +107 -0
- package/src/agent/reminder-schedule-format.ts +33 -24
- package/src/agent/reminder-schedule.ts +26 -25
- package/src/agent/routine-registry.ts +13 -12
- package/src/agent/routine-schedule-args.ts +2 -1
- package/src/agent/routine-schedule-format.ts +77 -53
- package/src/agent/routine-schedule-promotion.ts +34 -32
- package/src/agent/routine-schedule-receipts.ts +28 -26
- package/src/agent/runtime-profile-starters.ts +2 -2
- package/src/agent/runtime-profile.ts +18 -17
- package/src/agent/skill-registry.ts +25 -24
- package/src/cli/agent-knowledge-args.ts +5 -1
- package/src/cli/agent-knowledge-command.ts +39 -33
- package/src/cli/agent-knowledge-format.ts +80 -67
- package/src/cli/agent-knowledge-methods.ts +1 -1
- package/src/cli/agent-knowledge-runtime.ts +32 -26
- package/src/cli/bundle-command.ts +13 -8
- package/src/cli/config-overrides.ts +37 -36
- package/src/cli/external-runtime.ts +10 -4
- package/src/cli/help.ts +29 -11
- package/src/cli/local-library-command.ts +134 -68
- package/src/cli/management-commands.ts +98 -62
- package/src/cli/management.ts +52 -26
- package/src/cli/memory-command.ts +66 -32
- package/src/cli/parser.ts +37 -24
- package/src/cli/profiles-command.ts +52 -35
- package/src/cli/provider-auth-routes.ts +2 -1
- package/src/cli/routines-command.ts +44 -36
- package/src/cli/service-posture.ts +6 -6
- package/src/cli/status.ts +46 -121
- package/src/core/conversation-message-snapshot.ts +131 -0
- package/src/input/agent-workspace-activation.ts +33 -7
- package/src/input/agent-workspace-basic-command-editor-submission.ts +7 -3
- package/src/input/agent-workspace-basic-command-editors.ts +30 -10
- package/src/input/agent-workspace-categories.ts +276 -64
- package/src/input/agent-workspace-channel-command-editor-submission.ts +34 -0
- package/src/input/agent-workspace-channel-command-editors.ts +23 -5
- package/src/input/agent-workspace-channels.ts +35 -2
- package/src/input/agent-workspace-command-editor.ts +18 -2
- package/src/input/agent-workspace-config-reader.ts +16 -0
- package/src/input/agent-workspace-delegation-editor-submission.ts +1 -1
- package/src/input/agent-workspace-editors.ts +140 -2
- package/src/input/agent-workspace-knowledge-query-editor.ts +1 -1
- package/src/input/agent-workspace-learned-behavior.ts +2 -2
- package/src/input/agent-workspace-library-command-editor-submission.ts +1 -1
- package/src/input/agent-workspace-library-command-editors.ts +2 -2
- package/src/input/agent-workspace-local-operations.ts +218 -0
- package/src/input/agent-workspace-local-selection.ts +75 -0
- package/src/input/agent-workspace-media-command-editor-submission.ts +62 -0
- package/src/input/agent-workspace-media-command-editors.ts +27 -0
- package/src/input/agent-workspace-memory-command-editors.ts +1 -1
- package/src/input/agent-workspace-memory-editor.ts +2 -2
- package/src/input/agent-workspace-navigation.ts +38 -2
- package/src/input/agent-workspace-notify-editor-submission.ts +16 -2
- package/src/input/agent-workspace-operations-command-editor-submission.ts +63 -1
- package/src/input/agent-workspace-operations-command-editors.ts +80 -3
- package/src/input/agent-workspace-panel-route.ts +43 -0
- package/src/input/agent-workspace-reminder-schedule-editor.ts +1 -1
- package/src/input/agent-workspace-routine-schedule-editor.ts +2 -2
- package/src/input/agent-workspace-search.ts +169 -0
- package/src/input/agent-workspace-setup.ts +22 -11
- package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +6 -6
- package/src/input/agent-workspace-skill-bundle-command-editors.ts +10 -10
- package/src/input/agent-workspace-snapshot.ts +112 -13
- package/src/input/agent-workspace-task-command-editors.ts +4 -4
- package/src/input/agent-workspace-token.ts +18 -2
- package/src/input/agent-workspace-types.ts +92 -4
- package/src/input/agent-workspace-voice-media.ts +3 -6
- package/src/input/agent-workspace-web-research-editor.ts +104 -0
- package/src/input/agent-workspace.ts +136 -208
- package/src/input/command-registry.ts +4 -1
- package/src/input/commands/agent-runtime-profile-runtime.ts +45 -41
- package/src/input/commands/agent-skills-runtime.ts +83 -70
- package/src/input/commands/agent-workspace-runtime.ts +23 -7
- package/src/input/commands/brief-runtime.ts +25 -24
- package/src/input/commands/channels-runtime.ts +145 -31
- package/src/input/commands/delegation-runtime.ts +29 -23
- package/src/input/commands/experience-runtime.ts +6 -4
- package/src/input/commands/guidance-runtime.ts +4 -4
- package/src/input/commands/health-runtime.ts +140 -115
- package/src/input/commands/knowledge.ts +57 -56
- package/src/input/commands/local-provider-runtime.ts +36 -18
- package/src/input/commands/local-runtime.ts +138 -16
- package/src/input/commands/local-setup-review.ts +7 -7
- package/src/input/commands/mcp-runtime.ts +56 -35
- package/src/input/commands/notify-runtime.ts +38 -9
- package/src/input/commands/operator-actions-runtime.ts +138 -0
- package/src/input/commands/operator-runtime.ts +6 -17
- package/src/input/commands/personas-runtime.ts +45 -30
- package/src/input/commands/planning-runtime.ts +1 -1
- package/src/input/commands/platform-access-runtime.ts +29 -29
- package/src/input/commands/provider-accounts-runtime.ts +60 -39
- package/src/input/commands/qrcode-runtime.ts +45 -6
- package/src/input/commands/recall-bundle.ts +11 -11
- package/src/input/commands/recall-capture.ts +13 -11
- package/src/input/commands/recall-query.ts +29 -21
- package/src/input/commands/recall-review.ts +2 -1
- package/src/input/commands/routines-runtime.ts +59 -43
- package/src/input/commands/schedule-runtime.ts +33 -20
- package/src/input/commands/security-runtime.ts +4 -4
- package/src/input/commands/session-content.ts +80 -78
- package/src/input/commands/session-workflow.ts +132 -93
- package/src/input/commands/session.ts +3 -174
- package/src/input/commands/shell-core.ts +32 -17
- package/src/input/commands/subscription-runtime.ts +53 -179
- package/src/input/commands/tasks-runtime.ts +20 -20
- package/src/input/commands/work-plan-runtime.ts +33 -8
- package/src/input/commands.ts +2 -2
- package/src/input/feed-context-factory.ts +2 -1
- package/src/input/file-picker.ts +3 -2
- package/src/input/handler-command-route.ts +4 -7
- package/src/input/handler-content-actions.ts +89 -1
- package/src/input/handler-feed-routes.ts +19 -12
- package/src/input/handler-feed.ts +6 -3
- package/src/input/handler-interactions.ts +7 -5
- package/src/input/handler-modal-stack.ts +3 -3
- package/src/input/handler-onboarding.ts +24 -80
- package/src/input/handler-shortcuts.ts +15 -4
- package/src/input/handler.ts +47 -17
- package/src/input/input-history.ts +5 -6
- package/src/input/keybindings.ts +22 -11
- package/src/input/onboarding/onboarding-wizard-apply.ts +13 -0
- package/src/input/onboarding/onboarding-wizard-constants.ts +1 -0
- package/src/input/onboarding/onboarding-wizard-operator-steps.ts +147 -57
- package/src/input/onboarding/onboarding-wizard-steps.ts +46 -29
- package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
- package/src/input/onboarding/onboarding-wizard.ts +68 -0
- package/src/input/profile-picker-modal.ts +31 -12
- package/src/input/session-picker-modal.ts +21 -4
- package/src/input/settings-modal-behavior.ts +0 -3
- package/src/input/settings-modal-subscriptions.ts +3 -3
- package/src/input/settings-modal-types.ts +2 -13
- package/src/input/settings-modal.ts +6 -52
- package/src/input/submission-intent.ts +0 -1
- package/src/input/submission-router.ts +3 -3
- package/src/main.ts +18 -8
- package/src/panels/approval-panel.ts +8 -8
- package/src/panels/automation-control-panel.ts +2 -2
- package/src/panels/base-panel.ts +1 -1
- package/src/panels/builtin/agent.ts +1 -1
- package/src/panels/builtin/operations.ts +1 -10
- package/src/panels/builtin/session.ts +9 -9
- package/src/panels/builtin/shared.ts +2 -2
- package/src/panels/cost-tracker-panel.ts +1 -1
- package/src/panels/docs-panel.ts +5 -3
- package/src/panels/index.ts +0 -1
- package/src/panels/knowledge-panel.ts +6 -5
- package/src/panels/memory-panel.ts +7 -6
- package/src/panels/panel-list-panel.ts +36 -80
- package/src/panels/project-planning-panel.ts +18 -11
- package/src/panels/provider-account-snapshot.ts +51 -25
- package/src/panels/provider-accounts-panel.ts +33 -18
- package/src/panels/provider-health-domains.ts +45 -4
- package/src/panels/provider-health-panel.ts +5 -4
- package/src/panels/qr-panel.ts +1 -1
- package/src/panels/schedule-panel.ts +9 -17
- package/src/panels/security-panel.ts +8 -8
- package/src/panels/session-browser-panel.ts +10 -10
- package/src/panels/subscription-panel.ts +3 -3
- package/src/panels/system-messages-panel.ts +1 -1
- package/src/panels/tasks-panel.ts +20 -13
- package/src/panels/tool-inspector-panel.ts +19 -12
- package/src/panels/work-plan-panel.ts +5 -5
- package/src/planning/project-planning-coordinator.ts +1 -1
- package/src/provider-auth-route-display.ts +9 -0
- package/src/renderer/agent-workspace-style.ts +34 -0
- package/src/renderer/agent-workspace.ts +228 -52
- package/src/renderer/autocomplete-overlay.ts +25 -6
- package/src/renderer/bookmark-modal.ts +19 -4
- package/src/renderer/buffer.ts +4 -2
- package/src/renderer/context-inspector.ts +50 -13
- package/src/renderer/diff.ts +1 -1
- package/src/renderer/file-picker-overlay.ts +19 -6
- package/src/renderer/help-overlay.ts +106 -33
- package/src/renderer/history-search-overlay.ts +19 -4
- package/src/renderer/live-tail-modal.ts +27 -5
- package/src/renderer/mcp-workspace.ts +164 -50
- package/src/renderer/model-picker-overlay.ts +58 -2
- package/src/renderer/model-workspace.ts +104 -20
- package/src/renderer/process-modal.ts +27 -6
- package/src/renderer/profile-picker-modal.ts +20 -4
- package/src/renderer/search-overlay.ts +25 -5
- package/src/renderer/selection-modal-overlay.ts +46 -14
- package/src/renderer/session-picker-modal.ts +18 -1
- package/src/renderer/settings-modal-helpers.ts +2 -40
- package/src/renderer/settings-modal.ts +83 -50
- package/src/renderer/tool-call.ts +20 -11
- package/src/runtime/agent-runtime-events.ts +6 -6
- package/src/runtime/bootstrap-command-context.ts +7 -1
- package/src/runtime/bootstrap-command-parts.ts +9 -7
- package/src/runtime/bootstrap-core.ts +19 -1
- package/src/runtime/bootstrap-hook-bridge.ts +7 -18
- package/src/runtime/bootstrap-shell.ts +4 -4
- package/src/runtime/bootstrap.ts +31 -22
- package/src/runtime/connected-host-auth.ts +7 -2
- package/src/runtime/diagnostics/panels/index.ts +2 -2
- package/src/runtime/diagnostics/panels/policy.ts +7 -7
- package/src/runtime/index.ts +2 -1
- package/src/runtime/onboarding/apply-file-helpers.ts +66 -0
- package/src/runtime/onboarding/apply.ts +80 -79
- package/src/runtime/onboarding/derivation.ts +5 -8
- package/src/runtime/onboarding/snapshot.ts +0 -15
- package/src/runtime/onboarding/types.ts +8 -19
- package/src/runtime/onboarding/verify.ts +32 -10
- package/src/runtime/operator-token-cleanup.ts +1 -1
- package/src/runtime/services.ts +91 -22
- package/src/runtime/store/selectors/index.ts +3 -3
- package/src/runtime/store/state.ts +1 -4
- package/src/runtime/surface-feature-flags.ts +41 -6
- package/src/runtime/ui-read-models.ts +3 -4
- package/src/runtime/ui-services.ts +6 -6
- package/src/shell/blocking-input.ts +2 -1
- package/src/shell/ui-openers.ts +3 -13
- package/src/tools/agent-analysis-registry-policy.ts +0 -1
- package/src/tools/agent-channel-send-tool.ts +133 -0
- package/src/tools/agent-context-policy.ts +1 -1
- package/src/tools/agent-knowledge-ingest-tool.ts +405 -0
- package/src/tools/agent-knowledge-tool.ts +170 -0
- package/src/tools/agent-local-registry-tool.ts +269 -69
- package/src/tools/agent-media-generate-tool.ts +133 -0
- package/src/tools/agent-notify-tool.ts +143 -0
- package/src/tools/agent-operator-action-tool.ts +137 -0
- package/src/tools/agent-operator-briefing-tool.ts +217 -0
- package/src/tools/agent-reminder-schedule-tool.ts +237 -0
- package/src/tools/agent-tool-policy-guard.ts +8 -8
- package/src/tools/agent-work-plan-tool.ts +256 -0
- package/src/version.ts +1 -1
- package/src/input/commands/policy-dispatch.ts +0 -339
- package/src/input/commands/policy.ts +0 -13
- package/src/panels/panel-picker.ts +0 -105
- package/src/panels/policy-panel.ts +0 -308
- package/src/renderer/panel-picker-overlay.ts +0 -202
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
AgentWorkspace,
|
|
3
3
|
AgentWorkspaceAction,
|
|
4
|
-
AgentWorkspaceActionResult,
|
|
5
4
|
AgentWorkspaceCategory,
|
|
6
5
|
AgentWorkspaceLocalEditor,
|
|
7
6
|
AgentWorkspaceRuntimeSnapshot,
|
|
8
7
|
} from '../input/agent-workspace.ts';
|
|
8
|
+
import { formatAgentRecordReviewState, formatAgentRecordSource } from '../agent/record-labels.ts';
|
|
9
9
|
import type { Line } from '../types/grid.ts';
|
|
10
10
|
import { wrapText } from '../utils/terminal-width.ts';
|
|
11
11
|
import { GLYPHS } from './ui-primitives.ts';
|
|
@@ -17,20 +17,7 @@ import {
|
|
|
17
17
|
WORKSPACE_PALETTE as PALETTE,
|
|
18
18
|
type WorkspaceRow,
|
|
19
19
|
} from './fullscreen-workspace.ts';
|
|
20
|
-
|
|
21
|
-
function safetyColor(action: AgentWorkspaceAction): string {
|
|
22
|
-
if (action.safety === 'safe') return PALETTE.good;
|
|
23
|
-
if (action.safety === 'read-only') return PALETTE.info;
|
|
24
|
-
if (action.safety === 'delegates') return PALETTE.warn;
|
|
25
|
-
return PALETTE.bad;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function actionResultColor(result: AgentWorkspaceActionResult): string {
|
|
29
|
-
if (result.kind === 'blocked' || result.kind === 'error') return PALETTE.bad;
|
|
30
|
-
if (result.kind === 'dispatched') return PALETTE.info;
|
|
31
|
-
if (result.kind === 'refreshed') return PALETTE.good;
|
|
32
|
-
return PALETTE.muted;
|
|
33
|
-
}
|
|
20
|
+
import { actionResultColor, safetyColor, setupStatusColor, type AgentWorkspaceContextLine as ContextLine } from './agent-workspace-style.ts';
|
|
34
21
|
|
|
35
22
|
function buildLeftRows(workspace: AgentWorkspace, height: number): WorkspaceRow[] {
|
|
36
23
|
const rows: WorkspaceRow[] = [];
|
|
@@ -75,15 +62,6 @@ function actionCommand(action: AgentWorkspaceAction): string {
|
|
|
75
62
|
return action.command ?? '(guidance)';
|
|
76
63
|
}
|
|
77
64
|
|
|
78
|
-
type ContextLine = { readonly text: string; readonly fg?: string; readonly bold?: boolean; readonly dim?: boolean };
|
|
79
|
-
|
|
80
|
-
function setupStatusColor(status: AgentWorkspaceRuntimeSnapshot['setupChecklist'][number]['status']): string {
|
|
81
|
-
if (status === 'ready') return PALETTE.good;
|
|
82
|
-
if (status === 'recommended') return PALETTE.warn;
|
|
83
|
-
if (status === 'blocked') return PALETTE.bad;
|
|
84
|
-
return PALETTE.muted;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
65
|
function setupChecklistLines(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLine[] {
|
|
88
66
|
const readyCount = snapshot.setupChecklist.filter((item) => item.status === 'ready').length;
|
|
89
67
|
const recommendedCount = snapshot.setupChecklist.filter((item) => item.status === 'recommended').length;
|
|
@@ -104,22 +82,60 @@ function setupChecklistLines(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLi
|
|
|
104
82
|
return lines;
|
|
105
83
|
}
|
|
106
84
|
|
|
107
|
-
function
|
|
85
|
+
function homeNextActionLines(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLine[] {
|
|
86
|
+
const blocked = snapshot.setupChecklist.filter((item) => item.status === 'blocked');
|
|
87
|
+
const recommended = snapshot.setupChecklist.filter((item) => item.status === 'recommended');
|
|
88
|
+
const optional = snapshot.setupChecklist.filter((item) => item.status === 'optional');
|
|
89
|
+
const candidates = [...blocked, ...recommended, ...optional].slice(0, 5);
|
|
90
|
+
if (candidates.length === 0) {
|
|
91
|
+
return [
|
|
92
|
+
{ text: 'Next Actions', fg: PALETTE.title, bold: true },
|
|
93
|
+
{ text: 'Core setup is ready. Continue normal assistant work, review Knowledge sources, or tune local skills/routines as needed.', fg: PALETTE.good },
|
|
94
|
+
];
|
|
95
|
+
}
|
|
96
|
+
const lines: ContextLine[] = [{ text: 'Next Actions', fg: PALETTE.title, bold: true }];
|
|
97
|
+
for (const item of candidates) {
|
|
98
|
+
const command = item.command ? ` -> ${item.command}` : '';
|
|
99
|
+
lines.push({
|
|
100
|
+
text: `${item.status.toUpperCase()} ${item.label}${command}`,
|
|
101
|
+
fg: setupStatusColor(item.status),
|
|
102
|
+
bold: item.status === 'blocked',
|
|
103
|
+
});
|
|
104
|
+
lines.push({ text: ` ${item.detail}`, fg: PALETTE.muted });
|
|
105
|
+
}
|
|
106
|
+
return lines;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function companionAccessLine(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLine {
|
|
110
|
+
const access = snapshot.companionAccess;
|
|
111
|
+
const tokenState = access.tokenReadable
|
|
112
|
+
? `ready sha256:${access.tokenFingerprint ?? 'unknown'}`
|
|
113
|
+
: access.tokenPresent
|
|
114
|
+
? 'present but unreadable'
|
|
115
|
+
: 'missing';
|
|
116
|
+
const error = access.tokenError ? `; token read error ${access.tokenError}` : '';
|
|
117
|
+
return {
|
|
118
|
+
text: `Companion: ${access.surface}; token ${tokenState}; QR ${access.qrCommand}; manual token text hidden${error}.`,
|
|
119
|
+
fg: access.pairingReady ? PALETTE.good : PALETTE.warn,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function discoverySummaryLine(label: string, summary: AgentWorkspaceRuntimeSnapshot['discoveredBehavior']['personas'], actionLabel: string): ContextLine[] {
|
|
108
124
|
if (summary.count === 0) return [];
|
|
109
125
|
const names = summary.names.length > 0
|
|
110
126
|
? ` ${summary.names.join(', ')}${summary.count > summary.names.length ? `, +${summary.count - summary.names.length} more` : ''}.`
|
|
111
127
|
: '';
|
|
112
128
|
return [
|
|
113
129
|
{ text: `${label}: ${summary.count} discovered; project ${summary.projectLocalCount}; global ${summary.globalCount}.`, fg: PALETTE.info, bold: true },
|
|
114
|
-
{ text: ` ${
|
|
130
|
+
{ text: ` Choose ${actionLabel} to preview, then use the import form after review.${names}`, fg: PALETTE.muted },
|
|
115
131
|
];
|
|
116
132
|
}
|
|
117
133
|
|
|
118
134
|
function behaviorDiscoveryLines(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLine[] {
|
|
119
135
|
const lines: ContextLine[] = [
|
|
120
|
-
...discoverySummaryLine('Discovered personas', snapshot.discoveredBehavior.personas, '
|
|
121
|
-
...discoverySummaryLine('Discovered skills', snapshot.discoveredBehavior.skills, '
|
|
122
|
-
...discoverySummaryLine('Discovered routines', snapshot.discoveredBehavior.routines, '
|
|
136
|
+
...discoverySummaryLine('Discovered personas', snapshot.discoveredBehavior.personas, 'Personas -> Discover persona files'),
|
|
137
|
+
...discoverySummaryLine('Discovered skills', snapshot.discoveredBehavior.skills, 'Skills -> Discover skill files'),
|
|
138
|
+
...discoverySummaryLine('Discovered routines', snapshot.discoveredBehavior.routines, 'Routines -> Discover routine files'),
|
|
123
139
|
];
|
|
124
140
|
if (lines.length === 0) return [];
|
|
125
141
|
return [
|
|
@@ -153,11 +169,12 @@ function localLibraryLines(
|
|
|
153
169
|
item.requirementCount !== undefined && item.requirementCount > 0
|
|
154
170
|
? (item.missingRequirementCount && item.missingRequirementCount > 0 ? `needs ${item.missingRequirementCount}/${item.requirementCount}` : `ready ${item.requirementCount}/${item.requirementCount}`)
|
|
155
171
|
: '',
|
|
156
|
-
item.reviewState,
|
|
172
|
+
formatAgentRecordReviewState(item.reviewState),
|
|
173
|
+
item.source ? `origin ${item.source}` : '',
|
|
157
174
|
item.startCount !== undefined ? `starts ${item.startCount}` : '',
|
|
158
175
|
].filter(Boolean).join(' / ');
|
|
159
|
-
const tags = item.tags.length > 0 ? ` tags
|
|
160
|
-
const triggers = item.triggers.length > 0 ? ` triggers
|
|
176
|
+
const tags = item.tags.length > 0 ? ` tags ${item.tags.join(',')}` : '';
|
|
177
|
+
const triggers = item.triggers.length > 0 ? ` triggers ${item.triggers.join(',')}` : '';
|
|
161
178
|
const marker = selected ? `${GLYPHS.navigation.selected} ` : '';
|
|
162
179
|
lines.push({
|
|
163
180
|
text: `${marker}${item.id}: ${item.name} (${status})`,
|
|
@@ -175,6 +192,86 @@ function localLibraryLines(
|
|
|
175
192
|
return lines;
|
|
176
193
|
}
|
|
177
194
|
|
|
195
|
+
type LocalRoutineItem = AgentWorkspaceRuntimeSnapshot['localRoutines'][number];
|
|
196
|
+
|
|
197
|
+
function readyRoutineItems(snapshot: AgentWorkspaceRuntimeSnapshot): readonly LocalRoutineItem[] {
|
|
198
|
+
return snapshot.localRoutines.filter((routine) =>
|
|
199
|
+
routine.enabled === true
|
|
200
|
+
&& routine.reviewState === 'reviewed'
|
|
201
|
+
&& (routine.missingRequirementCount ?? 0) === 0
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function routinesNeedingSetup(snapshot: AgentWorkspaceRuntimeSnapshot): readonly LocalRoutineItem[] {
|
|
206
|
+
return snapshot.localRoutines.filter((routine) => (routine.missingRequirementCount ?? 0) > 0);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function routinesNeedingReview(snapshot: AgentWorkspaceRuntimeSnapshot): readonly LocalRoutineItem[] {
|
|
210
|
+
return snapshot.localRoutines.filter((routine) => routine.reviewState !== 'reviewed');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function routineNextActionLine(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLine {
|
|
214
|
+
const discovered = snapshot.discoveredBehavior.routines.count;
|
|
215
|
+
const ready = readyRoutineItems(snapshot);
|
|
216
|
+
const needsSetup = routinesNeedingSetup(snapshot);
|
|
217
|
+
const needsReview = routinesNeedingReview(snapshot);
|
|
218
|
+
|
|
219
|
+
if (snapshot.localRoutineCount === 0 && discovered > 0) {
|
|
220
|
+
return { text: 'Next routine action: Discover routine files, preview one, then import it from this workspace.', fg: PALETTE.warn, bold: true };
|
|
221
|
+
}
|
|
222
|
+
if (snapshot.localRoutineCount === 0) {
|
|
223
|
+
return { text: 'Next routine action: Create routine for a repeatable main-conversation workflow.', fg: PALETTE.warn, bold: true };
|
|
224
|
+
}
|
|
225
|
+
if (needsSetup.length > 0) {
|
|
226
|
+
return { text: `Next routine action: Needs setup for ${needsSetup[0]?.id ?? 'a routine'} before it can be trusted for schedule promotion.`, fg: PALETTE.warn, bold: true };
|
|
227
|
+
}
|
|
228
|
+
if (needsReview.length > 0) {
|
|
229
|
+
return { text: `Next routine action: Review selected or inspect ${needsReview[0]?.id ?? 'a routine'} before schedule promotion.`, fg: PALETTE.warn, bold: true };
|
|
230
|
+
}
|
|
231
|
+
if (ready.length > 0 && snapshot.routineScheduleReceiptCount === 0) {
|
|
232
|
+
return { text: `Next routine action: Promote ${ready[0]?.id ?? 'a reviewed routine'} to a connected schedule when you have a real cadence.`, fg: PALETTE.good, bold: true };
|
|
233
|
+
}
|
|
234
|
+
return { text: 'Next routine action: Start selected in the main conversation, inspect receipts, or reconcile connected schedules.', fg: PALETTE.info, bold: true };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function routineScheduleReceiptLines(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLine[] {
|
|
238
|
+
const latest = snapshot.latestRoutineScheduleReceipt;
|
|
239
|
+
const lines: ContextLine[] = [
|
|
240
|
+
{
|
|
241
|
+
text: `Promotion receipts: ${snapshot.routineScheduleReceiptCount}; created: ${snapshot.successfulRoutineScheduleReceiptCount}; failed: ${snapshot.failedRoutineScheduleReceiptCount}`,
|
|
242
|
+
fg: snapshot.failedRoutineScheduleReceiptCount > 0 ? PALETTE.warn : PALETTE.info,
|
|
243
|
+
},
|
|
244
|
+
];
|
|
245
|
+
if (latest) {
|
|
246
|
+
lines.push({
|
|
247
|
+
text: `Latest receipt: ${latest.id} ${latest.status} routine=${latest.routineId} schedule="${latest.scheduleName}" ${latest.scheduleKind} ${latest.scheduleValue}`,
|
|
248
|
+
fg: latest.status === 'failed' ? PALETTE.warn : PALETTE.good,
|
|
249
|
+
});
|
|
250
|
+
} else {
|
|
251
|
+
lines.push({ text: 'No schedule promotion receipts yet. Confirm a routine promotion from Automation when ready.', fg: PALETTE.muted });
|
|
252
|
+
}
|
|
253
|
+
return lines;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function automationNextActionLine(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLine {
|
|
257
|
+
const ready = readyRoutineItems(snapshot);
|
|
258
|
+
const needsSetup = routinesNeedingSetup(snapshot);
|
|
259
|
+
const needsReview = routinesNeedingReview(snapshot);
|
|
260
|
+
if (snapshot.routineScheduleReceiptCount > 0) {
|
|
261
|
+
return { text: 'Next automation action: Reconcile schedules to compare local receipts with the connected host.', fg: PALETTE.info, bold: true };
|
|
262
|
+
}
|
|
263
|
+
if (ready.length > 0) {
|
|
264
|
+
return { text: `Next automation action: Promote ${ready[0]?.id ?? 'a reviewed routine'} or create a one-off reminder.`, fg: PALETTE.good, bold: true };
|
|
265
|
+
}
|
|
266
|
+
if (needsSetup.length > 0) {
|
|
267
|
+
return { text: `Next automation action: Resolve routine setup gaps in ${needsSetup[0]?.id ?? 'Routines'} before promotion, or create a reminder.`, fg: PALETTE.warn, bold: true };
|
|
268
|
+
}
|
|
269
|
+
if (needsReview.length > 0) {
|
|
270
|
+
return { text: `Next automation action: Review ${needsReview[0]?.id ?? 'a routine'} in Routines before schedule promotion, or create a reminder.`, fg: PALETTE.warn, bold: true };
|
|
271
|
+
}
|
|
272
|
+
return { text: 'Next automation action: Create a reminder, or create/import a routine before recurring workflow promotion.', fg: PALETTE.warn, bold: true };
|
|
273
|
+
}
|
|
274
|
+
|
|
178
275
|
function profileLines(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLine[] {
|
|
179
276
|
const lines: ContextLine[] = [
|
|
180
277
|
{ text: 'Agent Profiles', fg: PALETTE.title, bold: true },
|
|
@@ -185,7 +282,7 @@ function profileLines(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLine[] {
|
|
|
185
282
|
}
|
|
186
283
|
for (const profile of snapshot.runtimeProfiles.slice(0, 6)) {
|
|
187
284
|
const starter = profile.starterTemplateId ? ` starter=${profile.starterTemplateId}` : ' starter=none';
|
|
188
|
-
const created = profile.createdAt ? ` created
|
|
285
|
+
const created = profile.createdAt ? ` created ${profile.createdAt.slice(0, 10)}` : '';
|
|
189
286
|
const states = [
|
|
190
287
|
profile.id === snapshot.activeRuntimeProfile ? 'active' : '',
|
|
191
288
|
profile.id === snapshot.selectedRuntimeProfile ? 'default' : '',
|
|
@@ -209,8 +306,9 @@ function starterTemplateLines(snapshot: AgentWorkspaceRuntimeSnapshot): ContextL
|
|
|
209
306
|
{ text: 'Starter Templates', fg: PALETTE.title, bold: true },
|
|
210
307
|
];
|
|
211
308
|
for (const template of snapshot.runtimeStarterTemplates.slice(0, 6)) {
|
|
309
|
+
const origin = template.source === 'local' ? 'Local' : formatAgentRecordSource(template.source);
|
|
212
310
|
lines.push({
|
|
213
|
-
text: `${template.id}: ${template.name} [${
|
|
311
|
+
text: `${template.id}: ${template.name} [${origin}]`,
|
|
214
312
|
fg: template.source === 'local' ? PALETTE.good : PALETTE.info,
|
|
215
313
|
bold: template.id === 'research',
|
|
216
314
|
});
|
|
@@ -232,7 +330,12 @@ function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspaceCatego
|
|
|
232
330
|
base.push(
|
|
233
331
|
{ text: `Chat route: ${snapshot.provider} / ${snapshot.modelDisplayName}`, fg: PALETTE.info },
|
|
234
332
|
{ text: `Session: ${snapshot.sessionId}`, fg: PALETTE.muted },
|
|
235
|
-
{ text: `Policy: ${snapshot.executionPolicy};
|
|
333
|
+
{ text: `Policy: ${snapshot.executionPolicy}; delegated review ${snapshot.delegatedReviewPolicy}`, fg: PALETTE.good },
|
|
334
|
+
{ text: `Knowledge: ${snapshot.knowledgeRoute}; ${snapshot.knowledgeIsolation}; no fallback`, fg: PALETTE.good },
|
|
335
|
+
{ text: `Local: ${snapshot.localMemoryCount} memory, ${snapshot.localNoteCount} notes, ${snapshot.localPersonaCount} personas, ${snapshot.localSkillCount} skills, ${snapshot.localRoutineCount} routines.`, fg: PALETTE.info },
|
|
336
|
+
{ text: `Channels: ${snapshot.channels.filter((channel) => channel.ready).length}/${snapshot.channels.length} ready; MCP ${snapshot.mcpConnectedServerCount}/${snapshot.mcpServerCount} connected; voice/media ${snapshot.voiceProviderCount}/${snapshot.mediaProviderCount}.`, fg: PALETTE.info },
|
|
337
|
+
{ text: '' },
|
|
338
|
+
...homeNextActionLines(snapshot),
|
|
236
339
|
);
|
|
237
340
|
} else if (category.id === 'setup') {
|
|
238
341
|
base.push(
|
|
@@ -244,6 +347,17 @@ function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspaceCatego
|
|
|
244
347
|
{ text: `Workspace: ${snapshot.workingDirectory}`, fg: PALETTE.muted },
|
|
245
348
|
{ text: `Home: ${snapshot.homeDirectory}`, fg: PALETTE.muted },
|
|
246
349
|
);
|
|
350
|
+
} else if (category.id === 'artifacts') {
|
|
351
|
+
const mediaReady = snapshot.voiceMediaReadiness.readyMediaProviderCount;
|
|
352
|
+
base.push(
|
|
353
|
+
{ text: `Chat route: ${snapshot.provider} / ${snapshot.modelDisplayName}`, fg: PALETTE.info },
|
|
354
|
+
{ text: `Agent Knowledge route: ${snapshot.knowledgeRoute}`, fg: PALETTE.info },
|
|
355
|
+
{ text: `Media providers: ${mediaReady}/${snapshot.mediaProviderCount} ready; generation-capable ${snapshot.mediaGenerationProviderCount}.`, fg: mediaReady > 0 ? PALETTE.good : PALETTE.warn },
|
|
356
|
+
{ text: 'Use this area for concrete files and generated output: attach images, export transcripts, ingest reviewed sources, inspect source records, and generate media.', fg: PALETTE.good },
|
|
357
|
+
{ text: 'Agent Knowledge ingest writes only to the isolated Agent segment. Default knowledge and non-Agent routes are not fallback storage.', fg: PALETTE.warn },
|
|
358
|
+
{ text: 'Generated media is stored as artifacts and referenced by id; the TUI should not print inline base64.', fg: PALETTE.muted },
|
|
359
|
+
{ text: `Workspace path: ${snapshot.workingDirectory}`, fg: PALETTE.muted },
|
|
360
|
+
);
|
|
247
361
|
} else if (category.id === 'channels') {
|
|
248
362
|
const enabledCount = snapshot.channels.filter((channel) => channel.enabled).length;
|
|
249
363
|
const readyCount = snapshot.channels.filter((channel) => channel.ready).length;
|
|
@@ -251,16 +365,20 @@ function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspaceCatego
|
|
|
251
365
|
const readyChannels = snapshot.channels.filter((channel) => channel.ready).map((channel) => channel.label);
|
|
252
366
|
const needsTarget = snapshot.channels.filter((channel) => channel.setupState === 'needs-target');
|
|
253
367
|
const needsConfig = snapshot.channels.filter((channel) => channel.setupState === 'needs-config');
|
|
368
|
+
const nextAttentionChannel = needsConfig[0] ?? needsTarget[0] ?? snapshot.channels.find((channel) => !channel.enabled);
|
|
254
369
|
const disabledChannels = snapshot.channels.filter((channel) => !channel.enabled).map((channel) => channel.label);
|
|
255
370
|
const disabledPreview = disabledChannels.slice(0, 6).join(', ');
|
|
256
371
|
const disabledSuffix = disabledChannels.length > 6 ? `, +${disabledChannels.length - 6} more` : '';
|
|
257
372
|
const orderedChannels = [
|
|
258
373
|
...snapshot.channels.filter((channel) => channel.enabled),
|
|
259
374
|
...snapshot.channels.filter((channel) => !channel.enabled),
|
|
260
|
-
].slice(0,
|
|
375
|
+
].slice(0, 3);
|
|
261
376
|
base.push(
|
|
262
377
|
{ text: `GoodVibes API: ${snapshot.runtimeBaseUrl}`, fg: PALETTE.info },
|
|
378
|
+
companionAccessLine(snapshot),
|
|
263
379
|
{ text: `Readiness: ${readyCount}/${snapshot.channels.length} ready; ${enabledCount} enabled; ${configuredDefaults} default target(s) configured.`, fg: PALETTE.info },
|
|
380
|
+
{ text: 'Setup path: pair companion -> inspect readiness -> review accounts/policies/status -> fix one channel -> add explicit notification target if needed.', fg: PALETTE.good },
|
|
381
|
+
{ text: `Next channel action: ${nextAttentionChannel ? `${nextAttentionChannel.label} - ${nextAttentionChannel.nextStep}` : 'All enabled channels are ready; keep delivery explicit and review policies before sending.'}`, fg: nextAttentionChannel ? PALETTE.warn : PALETTE.good },
|
|
264
382
|
{ text: `Ready channels: ${readyChannels.join(', ') || 'none'}.`, fg: readyChannels.length > 0 ? PALETTE.good : PALETTE.warn },
|
|
265
383
|
{ text: `Needs default target: ${needsTarget.map((channel) => `${channel.label} -> ${channel.defaultTargetKeys.join('|')}`).join(', ') || 'none'}.`, fg: needsTarget.length > 0 ? PALETTE.warn : PALETTE.muted },
|
|
266
384
|
{ text: `Needs config: ${needsConfig.map((channel) => `${channel.label} -> ${channel.missingRequiredKeys.join('|')}`).join(', ') || 'none'}.`, fg: needsConfig.length > 0 ? PALETTE.warn : PALETTE.muted },
|
|
@@ -274,16 +392,25 @@ function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspaceCatego
|
|
|
274
392
|
fg: channel.ready ? PALETTE.good : channel.enabled ? PALETTE.warn : PALETTE.dim,
|
|
275
393
|
});
|
|
276
394
|
}
|
|
277
|
-
base.push({ text: 'Only config key names and readiness state are rendered here.', fg: PALETTE.muted });
|
|
278
|
-
base.push({ text: 'Pairing: use /pair or /qrcode; inspect notification targets with /notify list and health with /health review.', fg: PALETTE.info });
|
|
279
395
|
} else if (category.id === 'knowledge') {
|
|
280
396
|
base.push(
|
|
281
397
|
{ text: `Route family: ${snapshot.knowledgeRoute}/{status,ask,search}`, fg: PALETTE.info },
|
|
282
|
-
{ text: `Isolation: ${snapshot.knowledgeIsolation}; no default
|
|
398
|
+
{ text: `Isolation: ${snapshot.knowledgeIsolation}; no default knowledge or non-Agent fallback`, fg: PALETTE.good },
|
|
283
399
|
{ text: 'Ingest: URL, URL-list, and bookmark imports require explicit --yes and write only to Agent Knowledge.', fg: PALETTE.info },
|
|
284
400
|
{ text: 'Review: queue, issues, candidates, reports, reindex, and consolidation stay inside the Agent segment.', fg: PALETTE.muted },
|
|
285
401
|
{ text: 'Agent-owned content appears here only after explicit Agent knowledge ingestion.', fg: PALETTE.muted },
|
|
286
402
|
);
|
|
403
|
+
} else if (category.id === 'research') {
|
|
404
|
+
base.push(
|
|
405
|
+
{ text: `Chat route: ${snapshot.provider} / ${snapshot.modelDisplayName}`, fg: PALETTE.info },
|
|
406
|
+
{ text: `Agent Knowledge route: ${snapshot.knowledgeRoute}`, fg: PALETTE.info },
|
|
407
|
+
{ text: `Browser tools: ${snapshot.voiceMediaReadiness.browserToolState}; public base URL ${snapshot.browserToolPublicBaseUrl}.`, fg: snapshot.browserToolExposureEnabled ? PALETTE.warn : PALETTE.muted },
|
|
408
|
+
{ text: snapshot.voiceMediaReadiness.browserToolNextStep, fg: PALETTE.muted },
|
|
409
|
+
{ text: 'Research requests are submitted to the main conversation. Agent may use connected read-only web tools when the user asks.', fg: PALETTE.good },
|
|
410
|
+
{ text: 'URL references and web research do not ingest into Agent Knowledge. Ingestion is a separate confirmed Agent Knowledge action.', fg: PALETTE.warn },
|
|
411
|
+
{ text: 'Use Agent Knowledge ask/search first for known source-backed context; use web research for current or external sources.', fg: PALETTE.info },
|
|
412
|
+
{ text: 'External sends, browser-side effects, package installs, and connected-host mutations are outside this read-only research lane.', fg: PALETTE.warn },
|
|
413
|
+
);
|
|
287
414
|
} else if (category.id === 'tools') {
|
|
288
415
|
base.push(
|
|
289
416
|
{ text: `MCP servers: ${snapshot.mcpConnectedServerCount}/${snapshot.mcpServerCount} connected; quarantined ${snapshot.mcpQuarantinedServerCount}; allow-all ${snapshot.mcpAllowAllServerCount}.`, fg: snapshot.mcpQuarantinedServerCount > 0 || snapshot.mcpAllowAllServerCount > 0 ? PALETTE.warn : PALETTE.info },
|
|
@@ -357,18 +484,28 @@ function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspaceCatego
|
|
|
357
484
|
base.push(
|
|
358
485
|
{ text: `Session memories: ${snapshot.sessionMemoryCount}`, fg: PALETTE.info },
|
|
359
486
|
{ text: `Agent memory: ${snapshot.localMemoryCount}; prompt-active: ${snapshot.localMemoryPromptActiveCount}; review queue: ${snapshot.localMemoryReviewQueueCount}`, fg: PALETTE.info },
|
|
487
|
+
{ text: `Scratchpad notes: ${snapshot.localNoteCount}; review queue: ${snapshot.localNoteReviewQueueCount}`, fg: PALETTE.info },
|
|
360
488
|
{ text: `Local routines: ${snapshot.localRoutineCount}; enabled: ${snapshot.enabledRoutineCount}`, fg: PALETTE.info },
|
|
361
489
|
{ text: `Local skills: ${snapshot.localSkillCount}; enabled: ${snapshot.enabledSkillCount}; bundles: ${snapshot.localSkillBundleCount}; active skills: ${snapshot.activeSkillCount}`, fg: PALETTE.info },
|
|
362
490
|
{ text: `Local personas: ${snapshot.localPersonaCount}; active: ${snapshot.activePersonaName}`, fg: PALETTE.info },
|
|
363
|
-
{ text: 'Durable memory, routines, skills, and personas remain Agent-local until shared registry contracts exist.', fg: PALETTE.good },
|
|
491
|
+
{ text: 'Durable memory, scratchpad notes, routines, skills, and personas remain Agent-local until shared registry contracts exist.', fg: PALETTE.good },
|
|
364
492
|
{ text: 'Secrets are rejected/redacted; store secret references instead of secret values.', fg: PALETTE.warn },
|
|
365
493
|
{ text: '' },
|
|
366
494
|
...localLibraryLines('Agent Memory', snapshot.localMemories, 'No Agent memory yet. Create one here with Create memory.', workspace.selectedLocalLibraryItem('memory')?.id ?? null),
|
|
367
495
|
);
|
|
496
|
+
} else if (category.id === 'notes') {
|
|
497
|
+
base.push(
|
|
498
|
+
{ text: `Scratchpad notes: ${snapshot.localNoteCount}; review queue: ${snapshot.localNoteReviewQueueCount}`, fg: PALETTE.info },
|
|
499
|
+
{ text: 'Notes are Agent-local working context for source triage, temporary decisions, and operator handoff.', fg: PALETTE.good },
|
|
500
|
+
{ text: 'Notes do not become memory and are not ingested into Agent Knowledge unless the user takes a separate explicit action.', fg: PALETTE.warn },
|
|
501
|
+
{ text: 'Use reviewed notes to prefill durable memory, skills, routines, or personas, or to decide that a reviewed source deserves Agent Knowledge ingest.', fg: PALETTE.muted },
|
|
502
|
+
{ text: '' },
|
|
503
|
+
...localLibraryLines('Scratchpad Notes', snapshot.localNotes, 'No local notes yet. Create one here with Create note.', workspace.selectedLocalLibraryItem('note')?.id ?? null),
|
|
504
|
+
);
|
|
368
505
|
} else if (category.id === 'personas') {
|
|
369
506
|
base.push(
|
|
370
507
|
{ text: `Personas: ${snapshot.localPersonaCount}; active: ${snapshot.activePersonaName}`, fg: PALETTE.info },
|
|
371
|
-
{ text: 'Personas are local behavior profiles for the serial main-conversation assistant, not
|
|
508
|
+
{ text: 'Personas are local behavior profiles for the serial main-conversation assistant, not separate Agent jobs.', fg: PALETTE.good },
|
|
372
509
|
{ text: 'Use them for tone, role, domain constraints, tool posture, and repeatable operating preferences.', fg: PALETTE.muted },
|
|
373
510
|
{ text: '' },
|
|
374
511
|
...localLibraryLines('Persona Library', snapshot.localPersonas, 'No local personas yet. Create one here with Create persona.', workspace.selectedLocalLibraryItem('persona')?.id ?? null),
|
|
@@ -384,10 +521,16 @@ function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspaceCatego
|
|
|
384
521
|
...localLibraryLines('Skill Bundles', snapshot.localSkillBundles, 'No local skill bundles yet. Use Skill bundles and Create bundle after creating skills.', null),
|
|
385
522
|
);
|
|
386
523
|
} else if (category.id === 'routines') {
|
|
524
|
+
const ready = readyRoutineItems(snapshot);
|
|
525
|
+
const needsSetup = routinesNeedingSetup(snapshot);
|
|
526
|
+
const needsReview = routinesNeedingReview(snapshot);
|
|
387
527
|
base.push(
|
|
388
528
|
{ text: `Routines: ${snapshot.localRoutineCount}; enabled: ${snapshot.enabledRoutineCount}`, fg: PALETTE.info },
|
|
529
|
+
{ text: `Schedule-ready routines: ${ready.length}; setup gaps: ${needsSetup.length}; review needed: ${needsReview.length}`, fg: needsSetup.length > 0 || needsReview.length > 0 ? PALETTE.warn : PALETTE.good },
|
|
389
530
|
{ text: 'Routines are repeatable main-conversation workflows. Starting one does not create hidden jobs.', fg: PALETTE.good },
|
|
390
531
|
{ text: 'Scheduling a reviewed routine is explicit and requires a confirmed schedule command.', fg: PALETTE.warn },
|
|
532
|
+
routineNextActionLine(snapshot),
|
|
533
|
+
...routineScheduleReceiptLines(snapshot),
|
|
391
534
|
{ text: '' },
|
|
392
535
|
...localLibraryLines('Routine Library', snapshot.localRoutines, 'No local routines yet. Create one here with Create routine.', workspace.selectedLocalLibraryItem('routine')?.id ?? null),
|
|
393
536
|
);
|
|
@@ -395,18 +538,26 @@ function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspaceCatego
|
|
|
395
538
|
base.push(
|
|
396
539
|
{ text: 'Work plan and approvals are read or explicitly confirmed through public operator routes.', fg: PALETTE.info },
|
|
397
540
|
{ text: 'This workspace does not approve, deny, cancel, or mutate requests by selection alone.', fg: PALETTE.good },
|
|
541
|
+
{ text: 'Approve, deny, and cancel forms require an approval id and typed confirmation.', fg: PALETTE.warn },
|
|
398
542
|
);
|
|
399
543
|
} else if (category.id === 'automation') {
|
|
544
|
+
const ready = readyRoutineItems(snapshot);
|
|
400
545
|
base.push(
|
|
401
|
-
{ text: 'Automation and schedules default to read-only observability.', fg: PALETTE.info },
|
|
546
|
+
{ text: 'Automation and schedules default to read-only observability; side effects require confirmed forms.', fg: PALETTE.info },
|
|
402
547
|
{ text: 'Confirmed reminders and routine promotion use connected schedules only.', fg: PALETTE.info },
|
|
403
|
-
{ text: 'Local scheduler mutation/run controls
|
|
548
|
+
{ text: 'Local scheduler mutation controls remain blocked; job/run/schedule controls go through the connected host.', fg: PALETTE.warn },
|
|
549
|
+
{ text: 'Reminder path: Create reminder -> choose at/every/cron -> optional delivery target -> confirm yes.', fg: PALETTE.good },
|
|
550
|
+
{ text: 'Operator path: choose job/run/schedule action -> enter id -> type yes -> dispatch once.', fg: PALETTE.good },
|
|
551
|
+
{ text: 'Routine path: Routines -> resolve setup -> review selected -> Promote routine -> Reconcile schedules.', fg: PALETTE.good },
|
|
552
|
+
{ text: `Schedule-ready routines: ${ready.length}; local promotion receipts: ${snapshot.routineScheduleReceiptCount}`, fg: ready.length > 0 ? PALETTE.good : PALETTE.warn },
|
|
553
|
+
automationNextActionLine(snapshot),
|
|
554
|
+
...routineScheduleReceiptLines(snapshot),
|
|
404
555
|
);
|
|
405
556
|
} else if (category.id === 'delegate') {
|
|
406
557
|
base.push(
|
|
407
558
|
{ text: 'Build/fix/review work is handed to GoodVibes TUI/shared-session contracts.', fg: PALETTE.info },
|
|
408
|
-
{ text: `
|
|
409
|
-
{ text: 'Agent does not
|
|
559
|
+
{ text: `Delegated review policy: ${snapshot.delegatedReviewPolicy}`, fg: PALETTE.warn },
|
|
560
|
+
{ text: 'Agent does not create coding-role Agent jobs.', fg: PALETTE.good },
|
|
410
561
|
);
|
|
411
562
|
}
|
|
412
563
|
if (snapshot.warnings.length > 0) {
|
|
@@ -440,6 +591,17 @@ function buildContextRows(workspace: AgentWorkspace, category: AgentWorkspaceCat
|
|
|
440
591
|
{ text: '' },
|
|
441
592
|
{ text: category.detail, fg: PALETTE.text },
|
|
442
593
|
{ text: '' },
|
|
594
|
+
...(workspace.actionSearchActive ? [
|
|
595
|
+
{ text: 'Action Search', fg: PALETTE.title, bold: true },
|
|
596
|
+
{
|
|
597
|
+
text: workspace.actionSearchQuery.length > 0
|
|
598
|
+
? `Query: ${workspace.actionSearchQuery} (${workspace.actionSearchResults.length} result${workspace.actionSearchResults.length === 1 ? '' : 's'})`
|
|
599
|
+
: 'Type to search every Agent workspace action.',
|
|
600
|
+
fg: workspace.actionSearchQuery.length > 0 && workspace.actionSearchResults.length === 0 ? PALETTE.warn : PALETTE.info,
|
|
601
|
+
},
|
|
602
|
+
{ text: 'Enter opens the selected result. Esc clears search and returns to normal workspace navigation.', fg: PALETTE.muted },
|
|
603
|
+
{ text: '' },
|
|
604
|
+
] satisfies ContextLine[] : []),
|
|
443
605
|
...(workspace.localEditor ? editorContextLines(workspace.localEditor) : []),
|
|
444
606
|
...(workspace.localEditor ? [{ text: '' }] : []),
|
|
445
607
|
...snapshotLines(workspace, category, workspace.runtimeSnapshot),
|
|
@@ -532,23 +694,32 @@ function buildActionRows(workspace: AgentWorkspace, width: number, height: numbe
|
|
|
532
694
|
const labelWidth = Math.min(28, Math.max(16, Math.floor(width * 0.30)));
|
|
533
695
|
const safetyWidth = 10;
|
|
534
696
|
const commandWidth = Math.max(10, width - labelWidth - safetyWidth - 9);
|
|
697
|
+
if (workspace.actionSearchActive) {
|
|
698
|
+
rows.push({
|
|
699
|
+
text: ` Search: ${workspace.actionSearchQuery || '(type to filter actions)'}`,
|
|
700
|
+
fg: workspace.actionSearchQuery.length > 0 && workspace.actions.length === 0 ? PALETTE.warn : PALETTE.info,
|
|
701
|
+
bold: true,
|
|
702
|
+
});
|
|
703
|
+
}
|
|
535
704
|
rows.push({
|
|
536
|
-
text: ` ${padDisplay('Action', labelWidth)} ${padDisplay('Safety', safetyWidth)} ${padDisplay('Command', commandWidth)}`,
|
|
705
|
+
text: ` ${padDisplay(workspace.actionSearchActive ? 'Result' : 'Action', labelWidth)} ${padDisplay('Safety', safetyWidth)} ${padDisplay('Command', commandWidth)}`,
|
|
537
706
|
fg: PALETTE.muted,
|
|
538
707
|
bold: true,
|
|
539
708
|
});
|
|
540
709
|
|
|
541
710
|
const actions = workspace.actions;
|
|
542
|
-
const visible = Math.max(1, height - 2);
|
|
711
|
+
const visible = Math.max(1, height - (workspace.actionSearchActive ? 3 : 2));
|
|
543
712
|
const window = stableWindow(actions.length, workspace.selectedActionIndex, visible);
|
|
544
713
|
if (window.start > 0) rows.push({ text: `${GLYPHS.navigation.moreAbove} ${window.start} more action(s) above`, kind: 'more', fg: PALETTE.dim, dim: true });
|
|
545
714
|
|
|
546
715
|
for (let index = window.start; index < window.end; index += 1) {
|
|
547
716
|
const action = actions[index]!;
|
|
548
717
|
const selected = index === workspace.selectedActionIndex;
|
|
718
|
+
const searchResult = workspace.actionSearchActive ? workspace.actionSearchResults[index] : null;
|
|
719
|
+
const label = searchResult ? `${searchResult.category.label} / ${action.label}` : action.label;
|
|
549
720
|
const marker = selected ? GLYPHS.navigation.selected : ' ';
|
|
550
721
|
rows.push({
|
|
551
|
-
text: `${marker} ${padDisplay(
|
|
722
|
+
text: `${marker} ${padDisplay(label, labelWidth)} ${padDisplay(action.safety, safetyWidth)} ${padDisplay(actionCommand(action), commandWidth)}`,
|
|
552
723
|
selected: selected && workspace.focusPane === 'actions',
|
|
553
724
|
fg: safetyColor(action),
|
|
554
725
|
bold: selected,
|
|
@@ -577,12 +748,15 @@ function footerText(workspace: AgentWorkspace): string {
|
|
|
577
748
|
if (workspace.localEditor) {
|
|
578
749
|
return `Agent workspace · editing ${workspace.localEditor.kind} · Enter next/save · Ctrl-J newline · Esc cancel`;
|
|
579
750
|
}
|
|
751
|
+
if (workspace.actionSearchActive) {
|
|
752
|
+
return 'Agent workspace · action search · type filter · Up/Down results · Enter open · Esc clear';
|
|
753
|
+
}
|
|
580
754
|
const focus = workspace.focusPane === 'categories' ? 'categories' : 'actions';
|
|
581
|
-
return `Agent workspace ·
|
|
755
|
+
return `Agent workspace · ${focus} · / search · Up/Down · Left/Right · Ctrl+[/] area · Enter open/action · R refresh · Esc close`;
|
|
582
756
|
}
|
|
583
757
|
|
|
584
758
|
export function renderAgentWorkspace(workspace: AgentWorkspace, width: number, height: number): Line[] {
|
|
585
|
-
const category = workspace.
|
|
759
|
+
const category = workspace.selectedActionCategory;
|
|
586
760
|
const action = workspace.selectedAction;
|
|
587
761
|
const setupCategory = category.id === 'setup';
|
|
588
762
|
const layoutOptions = {
|
|
@@ -598,9 +772,11 @@ export function renderAgentWorkspace(workspace: AgentWorkspace, width: number, h
|
|
|
598
772
|
width,
|
|
599
773
|
height,
|
|
600
774
|
title: 'GoodVibes Agent / Operator Workspace',
|
|
601
|
-
stateLabel: workspace.localEditor ? 'Editor' : workspace.focusPane === 'categories' ? 'Categories' : 'Actions',
|
|
775
|
+
stateLabel: workspace.localEditor ? 'Editor' : workspace.actionSearchActive ? 'Search' : workspace.focusPane === 'categories' ? 'Categories' : 'Actions',
|
|
602
776
|
leftHeader: 'Operator Areas',
|
|
603
|
-
mainHeader:
|
|
777
|
+
mainHeader: workspace.actionSearchActive
|
|
778
|
+
? `Search actions · ${workspace.actions.length} result(s)`
|
|
779
|
+
: `${category.label} · ${category.actions.length} action(s)`,
|
|
604
780
|
leftRows: buildLeftRows(workspace, metrics.bodyRows),
|
|
605
781
|
contextRows: buildContextRows(workspace, category, action, metrics.contextWidth),
|
|
606
782
|
controlRows: buildActionRows(workspace, metrics.contextWidth, metrics.controlRows),
|
|
@@ -15,6 +15,9 @@ const TITLE_FG = DEFAULT_OVERLAY_PALETTE.titleFg;
|
|
|
15
15
|
const BODY_FG = DEFAULT_OVERLAY_PALETTE.bodyFg;
|
|
16
16
|
const MUTED_FG = DEFAULT_OVERLAY_PALETTE.mutedFg;
|
|
17
17
|
const SELECTED_BG = DEFAULT_OVERLAY_PALETTE.selectedBg;
|
|
18
|
+
const AUTOCOMPLETE_TITLE = ' Commands';
|
|
19
|
+
const AUTOCOMPLETE_EMPTY_QUERY = '/';
|
|
20
|
+
const AUTOCOMPLETE_HINTS = '[Tab] Complete [Up/Down] Navigate [Enter] Execute [Esc] Cancel';
|
|
18
21
|
|
|
19
22
|
interface CellStyle {
|
|
20
23
|
fg: string;
|
|
@@ -27,6 +30,23 @@ function putText(line: Line, startX: number, maxWidth: number, text: string, sty
|
|
|
27
30
|
putOverlayText(line, startX, maxWidth, text, style);
|
|
28
31
|
}
|
|
29
32
|
|
|
33
|
+
function autocompleteQueryText(query: string): string {
|
|
34
|
+
return query ? `/${query}` : AUTOCOMPLETE_EMPTY_QUERY;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function autocompleteScrollText(selected: string | number, total: string | number): string {
|
|
38
|
+
return `${selected}/${total}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function renderAutocompletePackageText(): string {
|
|
42
|
+
return [
|
|
43
|
+
AUTOCOMPLETE_TITLE.trim(),
|
|
44
|
+
autocompleteQueryText('<query>'),
|
|
45
|
+
autocompleteScrollText('<selected>', '<total>'),
|
|
46
|
+
AUTOCOMPLETE_HINTS,
|
|
47
|
+
].join('\n');
|
|
48
|
+
}
|
|
49
|
+
|
|
30
50
|
/**
|
|
31
51
|
* Render the slash command autocomplete dropdown as Line[] for overlay in the viewport.
|
|
32
52
|
*/
|
|
@@ -51,8 +71,8 @@ export function renderAutocompleteOverlay(
|
|
|
51
71
|
lines.push(createOverlayBorderLine(width, layout, '┌', '─', '┐', BORDER_FG));
|
|
52
72
|
|
|
53
73
|
const titleLine = createOverlayContentLine(width, layout);
|
|
54
|
-
const titleText =
|
|
55
|
-
const queryText = state.query
|
|
74
|
+
const titleText = AUTOCOMPLETE_TITLE;
|
|
75
|
+
const queryText = autocompleteQueryText(state.query);
|
|
56
76
|
const queryWidth = Math.min(Math.floor(layout.innerWidth / 2), Math.max(8, layout.innerWidth - getDisplayWidth(titleText) - 2));
|
|
57
77
|
const leftText = fitDisplay(titleText, Math.max(0, layout.innerWidth - queryWidth));
|
|
58
78
|
const rightText = truncateDisplay(queryText, queryWidth);
|
|
@@ -126,8 +146,8 @@ export function renderAutocompleteOverlay(
|
|
|
126
146
|
}
|
|
127
147
|
|
|
128
148
|
if (total > maxVisible) {
|
|
129
|
-
|
|
130
|
-
const scrollText =
|
|
149
|
+
const scrollLine = createOverlayContentLine(width, layout);
|
|
150
|
+
const scrollText = autocompleteScrollText(state.selectedIndex + 1, total);
|
|
131
151
|
putText(
|
|
132
152
|
scrollLine,
|
|
133
153
|
layout.margin + 2 + Math.max(0, layout.innerWidth - getDisplayWidth(scrollText)),
|
|
@@ -139,12 +159,11 @@ export function renderAutocompleteOverlay(
|
|
|
139
159
|
}
|
|
140
160
|
|
|
141
161
|
const footerLine = createOverlayContentLine(width, layout);
|
|
142
|
-
const hints = '[Tab] Complete [Up/Down] Navigate [Enter] Execute [Esc] Cancel';
|
|
143
162
|
putText(
|
|
144
163
|
footerLine,
|
|
145
164
|
layout.margin + 2,
|
|
146
165
|
layout.innerWidth,
|
|
147
|
-
fitDisplay(truncateDisplay(
|
|
166
|
+
fitDisplay(truncateDisplay(AUTOCOMPLETE_HINTS, layout.innerWidth), layout.innerWidth),
|
|
148
167
|
{ fg: MUTED_FG, dim: true },
|
|
149
168
|
);
|
|
150
169
|
lines.push(footerLine);
|
|
@@ -13,6 +13,21 @@ import { BookmarkModal } from '../input/bookmark-modal.ts';
|
|
|
13
13
|
import type { BookmarkEntry } from '@pellux/goodvibes-sdk/platform/bookmarks';
|
|
14
14
|
import { getOverlayContentBudget, getStableOverlayContentRows } from './overlay-viewport.ts';
|
|
15
15
|
|
|
16
|
+
const BOOKMARK_MODAL_TITLE = 'Bookmarks';
|
|
17
|
+
const BOOKMARK_MODAL_EMPTY_MESSAGE = 'No bookmarks - use Ctrl+B to bookmark a block';
|
|
18
|
+
const BOOKMARK_MODAL_COLUMNS = ' Key Label Time';
|
|
19
|
+
const BOOKMARK_MODAL_HINTS = ['Up/Down Navigate', 'Enter Jump', 'o Open file', 'd Remove', 'Esc Close'];
|
|
20
|
+
|
|
21
|
+
export function renderBookmarkModalPackageText(): string {
|
|
22
|
+
return [
|
|
23
|
+
BOOKMARK_MODAL_TITLE,
|
|
24
|
+
BOOKMARK_MODAL_EMPTY_MESSAGE,
|
|
25
|
+
BOOKMARK_MODAL_COLUMNS.trim(),
|
|
26
|
+
'[<start>-<end> of <count>]',
|
|
27
|
+
...BOOKMARK_MODAL_HINTS,
|
|
28
|
+
].join('\n');
|
|
29
|
+
}
|
|
30
|
+
|
|
16
31
|
// ---------------------------------------------------------------------------
|
|
17
32
|
// Helpers
|
|
18
33
|
// ---------------------------------------------------------------------------
|
|
@@ -60,7 +75,7 @@ export function renderBookmarkModal(
|
|
|
60
75
|
const relSelected = Math.max(0, modal.selectedIndex - modal.scrollOffset);
|
|
61
76
|
|
|
62
77
|
const items = visible.length === 0
|
|
63
|
-
? [{ label:
|
|
78
|
+
? [{ label: BOOKMARK_MODAL_EMPTY_MESSAGE, selected: false }]
|
|
64
79
|
: visible.map((entry, i) => ({
|
|
65
80
|
label: entryLabel(entry),
|
|
66
81
|
selected: i === relSelected,
|
|
@@ -68,7 +83,7 @@ export function renderBookmarkModal(
|
|
|
68
83
|
const sections: import('./modal-factory.ts').ModalSection[] = [
|
|
69
84
|
{
|
|
70
85
|
type: 'text',
|
|
71
|
-
content:
|
|
86
|
+
content: BOOKMARK_MODAL_COLUMNS,
|
|
72
87
|
style: { dim: true },
|
|
73
88
|
},
|
|
74
89
|
{ type: 'separator' },
|
|
@@ -90,11 +105,11 @@ export function renderBookmarkModal(
|
|
|
90
105
|
|
|
91
106
|
return ModalFactory.createModal(
|
|
92
107
|
{
|
|
93
|
-
title:
|
|
108
|
+
title: `${BOOKMARK_MODAL_TITLE} ${totalStr}`,
|
|
94
109
|
width: 80,
|
|
95
110
|
targetContentRows,
|
|
96
111
|
sections,
|
|
97
|
-
hints:
|
|
112
|
+
hints: BOOKMARK_MODAL_HINTS,
|
|
98
113
|
},
|
|
99
114
|
width,
|
|
100
115
|
);
|
package/src/renderer/buffer.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type Line, type Cell, createEmptyLine, createEmptyCell } from '../types/grid.ts';
|
|
2
2
|
|
|
3
|
+
const CELL_KEYS: readonly (keyof Cell)[] = ['char', 'fg', 'bg', 'bold', 'dim', 'underline', 'italic', 'strikethrough', 'link'];
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* TerminalBuffer - Represents a 2D grid of styled cells.
|
|
5
7
|
* Tracks a per-row dirty bitmap so the diff engine can skip rows that were
|
|
@@ -21,8 +23,8 @@ export class TerminalBuffer {
|
|
|
21
23
|
// already matches the current cell value (idempotent write).
|
|
22
24
|
const current = this.cells[y][x]!;
|
|
23
25
|
let changed = false;
|
|
24
|
-
for (const
|
|
25
|
-
if ((cell
|
|
26
|
+
for (const key of CELL_KEYS) {
|
|
27
|
+
if (Object.prototype.hasOwnProperty.call(cell, key) && cell[key] !== current[key]) {
|
|
26
28
|
changed = true;
|
|
27
29
|
break;
|
|
28
30
|
}
|