@pellux/goodvibes-agent 0.1.9 → 0.1.11
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 +41 -0
- package/README.md +1 -1
- package/docs/getting-started.md +1 -1
- package/docs/release-and-publishing.md +2 -2
- package/package.json +4 -1
- package/src/cli/agent-knowledge-command.ts +46 -20
- package/src/cli/help.ts +15 -2
- package/src/cli/management-commands.ts +3 -3
- package/src/cli/management.ts +7 -1
- package/src/cli/parser.ts +3 -0
- package/src/cli/service-posture.ts +6 -6
- package/src/cli/status.ts +9 -9
- package/src/cli/surface-command.ts +3 -3
- package/src/cli/types.ts +2 -0
- package/src/input/commands/cloudflare-runtime.ts +20 -5
- package/src/input/commands/confirmation.ts +24 -0
- package/src/input/commands/discovery-runtime.ts +16 -7
- package/src/input/commands/eval.ts +27 -14
- package/src/input/commands/experience-runtime.ts +66 -27
- package/src/input/commands/health-runtime.ts +1 -1
- package/src/input/commands/hooks-runtime.ts +79 -20
- package/src/input/commands/incident-runtime.ts +17 -6
- package/src/input/commands/integration-runtime.ts +93 -50
- package/src/input/commands/knowledge.ts +38 -12
- package/src/input/commands/local-auth-runtime.ts +36 -13
- package/src/input/commands/local-provider-runtime.ts +22 -11
- package/src/input/commands/local-runtime.ts +21 -11
- package/src/input/commands/local-setup.ts +35 -16
- package/src/input/commands/managed-runtime.ts +51 -20
- package/src/input/commands/marketplace-runtime.ts +31 -16
- package/src/input/commands/mcp-runtime.ts +65 -34
- package/src/input/commands/memory-product-runtime.ts +72 -35
- package/src/input/commands/memory.ts +9 -9
- package/src/input/commands/notify-runtime.ts +27 -8
- package/src/input/commands/operator-runtime.ts +85 -17
- package/src/input/commands/planning-runtime.ts +14 -2
- package/src/input/commands/platform-access-runtime.ts +88 -45
- package/src/input/commands/platform-services-runtime.ts +51 -25
- package/src/input/commands/product-runtime.ts +54 -27
- package/src/input/commands/profile-sync-runtime.ts +17 -6
- package/src/input/commands/recall-bundle.ts +38 -17
- package/src/input/commands/recall-query.ts +15 -4
- package/src/input/commands/recall-review.ts +9 -3
- package/src/input/commands/remote-runtime-setup.ts +45 -18
- package/src/input/commands/remote-runtime.ts +25 -9
- package/src/input/commands/replay-runtime.ts +9 -2
- package/src/input/commands/services-runtime.ts +21 -10
- package/src/input/commands/session-content.ts +53 -51
- package/src/input/commands/session-workflow.ts +10 -4
- package/src/input/commands/session.ts +1 -1
- package/src/input/commands/settings-sync-runtime.ts +40 -17
- package/src/input/commands/share-runtime.ts +12 -4
- package/src/input/commands/shell-core.ts +3 -3
- package/src/input/commands/subscription-runtime.ts +35 -20
- package/src/input/commands/teleport-runtime.ts +16 -5
- package/src/input/commands/work-plan-runtime.ts +23 -12
- package/src/input/handler-content-actions.ts +11 -62
- package/src/input/handler-interactions.ts +1 -1
- package/src/input/handler-onboarding-cloudflare.ts +48 -117
- package/src/input/handler.ts +1 -0
- package/src/input/keybindings.ts +1 -1
- package/src/input/mcp-workspace.ts +25 -49
- package/src/input/onboarding/onboarding-runtime-status.ts +8 -8
- package/src/input/onboarding/onboarding-wizard-apply.ts +13 -53
- package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +12 -12
- package/src/input/onboarding/onboarding-wizard-cloudflare.ts +2 -7
- package/src/input/onboarding/onboarding-wizard-constants.ts +7 -7
- package/src/input/onboarding/onboarding-wizard-external-surface-extra-specs.ts +4 -4
- package/src/input/onboarding/onboarding-wizard-steps.ts +13 -13
- package/src/input/profile-picker-modal.ts +13 -31
- package/src/input/session-picker-modal.ts +4 -30
- package/src/input/settings-modal-agent-policy.ts +18 -0
- package/src/input/settings-modal-subscriptions.ts +3 -3
- package/src/input/settings-modal-types.ts +17 -0
- package/src/input/settings-modal.ts +30 -29
- package/src/main.ts +3 -26
- package/src/panels/incident-review-panel.ts +1 -1
- package/src/panels/local-auth-panel.ts +4 -4
- package/src/panels/provider-account-snapshot.ts +1 -1
- package/src/panels/provider-health-domains.ts +2 -2
- package/src/panels/settings-sync-panel.ts +2 -2
- package/src/panels/subscription-panel.ts +7 -7
- package/src/renderer/block-actions.ts +1 -1
- package/src/renderer/help-overlay.ts +2 -2
- package/src/renderer/mcp-workspace.ts +12 -12
- package/src/renderer/process-modal.ts +17 -8
- package/src/renderer/profile-picker-modal.ts +3 -11
- package/src/renderer/session-picker-modal.ts +2 -10
- package/src/renderer/settings-modal.ts +12 -8
- package/src/renderer/ui-factory.ts +4 -32
- package/src/runtime/bootstrap-shell.ts +0 -13
- package/src/runtime/bootstrap.ts +0 -10
- package/src/runtime/onboarding/derivation.ts +6 -6
- package/src/verification/live-verifier.ts +148 -13
- package/src/version.ts +10 -3
- package/src/input/commands/quit-shared.ts +0 -162
- package/src/renderer/git-status.ts +0 -89
|
@@ -86,7 +86,7 @@ export class SettingsSyncPanel extends ScrollableListPanel<ResolvedEntry> {
|
|
|
86
86
|
: [buildPanelLine(width, [[' No recent sync or managed-setting failures.', C.dim]])]),
|
|
87
87
|
// Conflicts
|
|
88
88
|
...(snapshot.conflicts.length > 0
|
|
89
|
-
? snapshot.conflicts.map((conflict) => buildPanelLine(width, [[` ${conflict.key}`.padEnd(30), C.value], [` ${conflict.source}`.padEnd(10), C.warn], [` resolve: /settingssync resolve ${conflict.key} local|synced`.slice(0, Math.max(0, width - 42)), C.dim]]))
|
|
89
|
+
? snapshot.conflicts.map((conflict) => buildPanelLine(width, [[` ${conflict.key}`.padEnd(30), C.value], [` ${conflict.source}`.padEnd(10), C.warn], [` resolve: /settingssync resolve ${conflict.key} local|synced --yes`.slice(0, Math.max(0, width - 42)), C.dim]]))
|
|
90
90
|
: [buildPanelLine(width, [[' No settings conflicts detected.', C.dim]])]),
|
|
91
91
|
// Rollback History
|
|
92
92
|
...(snapshot.rollbackHistory.length > 0
|
|
@@ -108,7 +108,7 @@ export class SettingsSyncPanel extends ScrollableListPanel<ResolvedEntry> {
|
|
|
108
108
|
buildPanelLine(width, [[' managed ', C.label], [String(selectedEntry.managedValue ?? '(unset)').slice(0, Math.max(0, width - 11)), C.warn]]),
|
|
109
109
|
], C)
|
|
110
110
|
: []),
|
|
111
|
-
buildPanelLine(width, [[' ↑/↓ browse /settingssync show <key>
|
|
111
|
+
buildPanelLine(width, [[' ↑/↓ browse /settingssync show <key> mutations require --yes: resolve/apply-staged ', C.dim]]),
|
|
112
112
|
];
|
|
113
113
|
|
|
114
114
|
return this.renderList(width, height, {
|
|
@@ -78,8 +78,8 @@ export class SubscriptionPanel extends ScrollableListPanel<SubscriptionRow> {
|
|
|
78
78
|
protected override getEmptyStateMessage() { return ' No provider subscriptions are active yet.'; }
|
|
79
79
|
protected override getEmptyStateActions() {
|
|
80
80
|
return [
|
|
81
|
-
{ command: '/subscription login openai start', summary: 'start the first-class OpenAI subscription flow' },
|
|
82
|
-
{ command: '/login provider <name> start', summary: 'use the front-door auth surface for supported providers' },
|
|
81
|
+
{ command: '/subscription login openai start --yes', summary: 'start the first-class OpenAI subscription flow' },
|
|
82
|
+
{ command: '/login provider <name> start --yes', summary: 'use the front-door auth surface for supported providers' },
|
|
83
83
|
{ command: '/services auth-review', summary: 'inspect configured service auth posture and stored secrets' },
|
|
84
84
|
];
|
|
85
85
|
}
|
|
@@ -187,7 +187,7 @@ export class SubscriptionPanel extends ScrollableListPanel<SubscriptionRow> {
|
|
|
187
187
|
{ label: 'selected', value: (this.rows[this.selectedIndex]?.provider ?? 'none'), valueColor: this.rows[this.selectedIndex] ? C.value : C.dim },
|
|
188
188
|
{ label: 'status', value: this.rows[this.selectedIndex] ? statusOf(this.rows[this.selectedIndex]!) : 'n/a', valueColor: this.rows[this.selectedIndex] ? statusColor(statusOf(this.rows[this.selectedIndex]!)) : C.dim },
|
|
189
189
|
], C),
|
|
190
|
-
buildGuidanceLine(width, '/subscription login <provider> start', 'start or repair browser login for the selected provider route', C),
|
|
190
|
+
buildGuidanceLine(width, '/subscription login <provider> start --yes', 'start or repair browser login for the selected provider route', C),
|
|
191
191
|
];
|
|
192
192
|
|
|
193
193
|
// Empty state: render posture + base empty state
|
|
@@ -205,7 +205,7 @@ export class SubscriptionPanel extends ScrollableListPanel<SubscriptionRow> {
|
|
|
205
205
|
intro,
|
|
206
206
|
sections: [{ lines: [...summaryLines, ...emptyLines] }],
|
|
207
207
|
footerLines: [
|
|
208
|
-
buildGuidanceLine(width, '/subscription login <provider> start', 'start browser-based provider login from the packaged subscription surface', C),
|
|
208
|
+
buildGuidanceLine(width, '/subscription login <provider> start --yes', 'start browser-based provider login from the packaged subscription surface', C),
|
|
209
209
|
buildPanelLine(width, [[' Up/Down move Enter/X sign out selected provider r refresh', C.dim]]),
|
|
210
210
|
],
|
|
211
211
|
palette: C,
|
|
@@ -240,9 +240,9 @@ export class SubscriptionPanel extends ScrollableListPanel<SubscriptionRow> {
|
|
|
240
240
|
detailRows.push(buildPanelLine(width, [[` Press Enter or X again to sign out ${selectedRow.provider}.`, C.warn]]));
|
|
241
241
|
}
|
|
242
242
|
} else if (selectedRow.pending) {
|
|
243
|
-
detailRows.push(buildPanelLine(width, [[' Login is pending. Finish with /subscription login <provider> finish <code
|
|
243
|
+
detailRows.push(buildPanelLine(width, [[' Login is pending. Finish with /subscription login <provider> finish <code> --yes.', C.warn]]));
|
|
244
244
|
} else if (selectedRow.hasOAuthConfig) {
|
|
245
|
-
detailRows.push(buildPanelLine(width, [[' Ready for login. Start with /subscription login <provider> start.', C.dim]]));
|
|
245
|
+
detailRows.push(buildPanelLine(width, [[' Ready for login. Start with /subscription login <provider> start --yes.', C.dim]]));
|
|
246
246
|
} else {
|
|
247
247
|
detailRows.push(buildPanelLine(width, [[' Add a provider-specific OAuth config or enable a built-in subscription provider to use subscription login.', C.bad]]));
|
|
248
248
|
}
|
|
@@ -255,7 +255,7 @@ export class SubscriptionPanel extends ScrollableListPanel<SubscriptionRow> {
|
|
|
255
255
|
header: headerLines,
|
|
256
256
|
footer: [
|
|
257
257
|
...detailRows,
|
|
258
|
-
buildGuidanceLine(width, '/subscription login <provider> start', 'start browser-based provider login from the packaged subscription surface', C),
|
|
258
|
+
buildGuidanceLine(width, '/subscription login <provider> start --yes', 'start browser-based provider login from the packaged subscription surface', C),
|
|
259
259
|
buildPanelLine(width, [[' Up/Down move Enter/X sign out selected provider r refresh', C.dim]]),
|
|
260
260
|
],
|
|
261
261
|
});
|
|
@@ -12,7 +12,7 @@ const ALL_ACTIONS: BlockAction[] = [
|
|
|
12
12
|
{ id: 'copy', label: 'Copy', key: 'c' },
|
|
13
13
|
{ id: 'bookmark', label: 'Bookmark', key: 'b' },
|
|
14
14
|
{ id: 'toggle', label: 'Collapse/Expand',key: 'Tab' },
|
|
15
|
-
{ id: 'apply', label: '
|
|
15
|
+
{ id: 'apply', label: 'Diff apply blocked', key: 'a' },
|
|
16
16
|
{ id: 'rerun', label: 'Re-run tool', key: 'r' },
|
|
17
17
|
];
|
|
18
18
|
|
|
@@ -215,7 +215,7 @@ export function renderShortcutsOverlay(
|
|
|
215
215
|
row(kb('clear-prompt'), 'Clear prompt'),
|
|
216
216
|
row(kb('delete-word'), 'Delete word backward'),
|
|
217
217
|
row(kb('kill-line'), 'Kill to end of line'),
|
|
218
|
-
row(kb('apply-diff-line-start'), '
|
|
218
|
+
row(kb('apply-diff-line-start'), 'Diff apply blocked / line start'),
|
|
219
219
|
row(kb('next-error-line-end'), 'Next error / line end'),
|
|
220
220
|
'',
|
|
221
221
|
' Actions',
|
|
@@ -223,7 +223,7 @@ export function renderShortcutsOverlay(
|
|
|
223
223
|
row('Tab', 'Collapse/expand block'),
|
|
224
224
|
row(kb('bookmark'), 'Bookmark block'),
|
|
225
225
|
row(kb('block-copy'), 'Copy block to clipboard'),
|
|
226
|
-
row(kb('block-save'), '
|
|
226
|
+
row(kb('block-save'), 'Block save blocked; use /share --yes'),
|
|
227
227
|
row(kb('copy-selection'), 'Copy selection'),
|
|
228
228
|
row('F2', 'Process monitor'),
|
|
229
229
|
row('?', 'Help overlay'),
|
|
@@ -82,16 +82,16 @@ function selectedDetailLines(workspace: McpWorkspace, width: number): WorkspaceR
|
|
|
82
82
|
if (workspace.mode === 'form') {
|
|
83
83
|
const field = workspace.formFields[workspace.formIndex];
|
|
84
84
|
lines.push(
|
|
85
|
-
workspace.editingServerName ? `
|
|
86
|
-
'
|
|
85
|
+
workspace.editingServerName ? `Previewing server: ${workspace.editingServerName}` : 'Drafting an MCP server command',
|
|
86
|
+
'Workspace writes/reloads are blocked. Use the generated /mcp add ... --yes command from the prompt.',
|
|
87
87
|
field ? `${field.label}: ${field.help}` : '',
|
|
88
|
-
'Project
|
|
88
|
+
'Project/global config locations are shown for review. This workspace does not write or reload MCP config.',
|
|
89
89
|
);
|
|
90
90
|
} else if (workspace.mode === 'delete-confirm') {
|
|
91
91
|
lines.push(
|
|
92
|
-
`
|
|
93
|
-
'
|
|
94
|
-
'Press
|
|
92
|
+
`Removal blocked for server: ${workspace.editingServerName ?? '(unknown)'}`,
|
|
93
|
+
'Use /mcp remove <server> --scope <project|global> --yes from the prompt for explicit removal.',
|
|
94
|
+
'Press n or Esc to return.',
|
|
95
95
|
);
|
|
96
96
|
} else {
|
|
97
97
|
const selected = workspace.selectedRow;
|
|
@@ -207,19 +207,19 @@ function buildControlRows(workspace: McpWorkspace, width: number, height: number
|
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
function footerText(workspace: McpWorkspace): string {
|
|
210
|
-
if (workspace.mode === 'form') return 'Focus server
|
|
211
|
-
if (workspace.mode === 'delete-confirm') return 'Focus remove
|
|
212
|
-
return 'Focus MCP workspace · Up/Down choose · Enter
|
|
210
|
+
if (workspace.mode === 'form') return 'Focus server command preview · Up/Down field · Left/Right cycle · Type edit · Enter show command · Esc back';
|
|
211
|
+
if (workspace.mode === 'delete-confirm') return 'Focus remove guidance · n/Esc cancel';
|
|
212
|
+
return 'Focus MCP workspace · Up/Down choose · Enter view/action · a draft · d removal command · r reload command · t tools · Esc close';
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
export function renderMcpWorkspace(workspace: McpWorkspace, width: number, height: number): Line[] {
|
|
216
216
|
const metrics = getFullscreenWorkspaceMetrics({ width, height });
|
|
217
217
|
const connected = workspace.servers.filter((server) => server.connected).length;
|
|
218
|
-
const stateLabel = workspace.mode === 'browse' ? 'Browse' : workspace.mode === 'form' ? '
|
|
218
|
+
const stateLabel = workspace.mode === 'browse' ? 'Browse' : workspace.mode === 'form' ? 'Command Preview' : 'Remove Guidance';
|
|
219
219
|
const mainHeader = workspace.mode === 'form'
|
|
220
|
-
? 'MCP server
|
|
220
|
+
? 'MCP server command preview'
|
|
221
221
|
: workspace.mode === 'delete-confirm'
|
|
222
|
-
? '
|
|
222
|
+
? 'MCP remove command'
|
|
223
223
|
: `Servers ${connected}/${workspace.servers.length} connected · Tools ${workspace.tools.length}`;
|
|
224
224
|
|
|
225
225
|
return renderFullscreenWorkspace({
|
|
@@ -45,6 +45,12 @@ export interface ProcessModalDeps {
|
|
|
45
45
|
readonly agentManager: Pick<AgentManager, 'list' | 'getStatus'>;
|
|
46
46
|
readonly processManager: Pick<ProcessManager, 'list' | 'getStatus' | 'stop'>;
|
|
47
47
|
readonly wrfcController: Pick<WrfcController, 'getChain'> & Partial<Pick<WrfcController, 'listChains'>>;
|
|
48
|
+
/**
|
|
49
|
+
* GoodVibes Agent must not present local AgentManager records as an owned
|
|
50
|
+
* execution lane. Tests for copied TUI primitives can opt into read-only
|
|
51
|
+
* display, but product runtime should keep this hidden.
|
|
52
|
+
*/
|
|
53
|
+
readonly agentEntries: 'hidden' | 'read-only';
|
|
48
54
|
}
|
|
49
55
|
|
|
50
56
|
type WrfcChainLike = {
|
|
@@ -504,14 +510,17 @@ export class ProcessModal {
|
|
|
504
510
|
const now = Date.now();
|
|
505
511
|
const result: ProcessEntry[] = [];
|
|
506
512
|
|
|
507
|
-
//
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
513
|
+
// Local AgentManager activity is hidden in the Agent product. Build/fix/review
|
|
514
|
+
// execution belongs to explicit GoodVibes TUI delegation, not a local lane.
|
|
515
|
+
if (this.deps.agentEntries === 'read-only') {
|
|
516
|
+
result.push(...buildAgentEntries(
|
|
517
|
+
manager.list(),
|
|
518
|
+
this.deps,
|
|
519
|
+
now,
|
|
520
|
+
(key) => this.groupOrder.get(key),
|
|
521
|
+
(key) => this.ensureGroupOrder(key),
|
|
522
|
+
));
|
|
523
|
+
}
|
|
515
524
|
|
|
516
525
|
// Background exec processes — only show running
|
|
517
526
|
const pm = this.deps.processManager;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Shows a list of saved profiles with:
|
|
6
6
|
* - name, timestamp (formatted), settings preview
|
|
7
|
-
* Footer hints: [Up/Down] Navigate [Enter] Load [
|
|
7
|
+
* Footer hints: [Up/Down] Navigate [Enter] Load [Esc] Close
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { Line } from '../types/grid.ts';
|
|
@@ -51,7 +51,7 @@ export function renderProfilePickerModal(
|
|
|
51
51
|
});
|
|
52
52
|
sections.push({
|
|
53
53
|
type: 'text',
|
|
54
|
-
content: '
|
|
54
|
+
content: 'Use /profiles save <name> --yes to save the current settings as a profile.',
|
|
55
55
|
style: { fg: '240', dim: true },
|
|
56
56
|
});
|
|
57
57
|
} else {
|
|
@@ -107,14 +107,6 @@ export function renderProfilePickerModal(
|
|
|
107
107
|
style: { fg: '#00ffcc' },
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
|
-
if (modal.deleteConfirmationTarget) {
|
|
111
|
-
sections.push({
|
|
112
|
-
type: 'text',
|
|
113
|
-
content: `Press [d] again to permanently delete ${modal.deleteConfirmationTarget}.`,
|
|
114
|
-
style: { fg: '#f59e0b', dim: true },
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
|
|
118
110
|
return ModalFactory.createModal(
|
|
119
111
|
{
|
|
120
112
|
title: 'Profiles',
|
|
@@ -122,7 +114,7 @@ export function renderProfilePickerModal(
|
|
|
122
114
|
margin: boxMargin,
|
|
123
115
|
targetContentRows,
|
|
124
116
|
sections,
|
|
125
|
-
hints: ['[Up/Down] Navigate', '[Enter] Load', '
|
|
117
|
+
hints: ['[Up/Down] Navigate', '[Enter] Load', '/profiles save|delete --yes', '[Esc] Close'],
|
|
126
118
|
},
|
|
127
119
|
width,
|
|
128
120
|
);
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Shows a list of saved sessions with:
|
|
6
6
|
* - name, timestamp (formatted), message count
|
|
7
|
-
* Footer hints: [Enter] Load [
|
|
7
|
+
* Footer hints: [Enter] Load [Esc] Close
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { Line } from '../types/grid.ts';
|
|
@@ -105,14 +105,6 @@ export function renderSessionPickerModal(
|
|
|
105
105
|
});
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
if (modal.deleteConfirmationTarget) {
|
|
109
|
-
sections.push({
|
|
110
|
-
type: 'text',
|
|
111
|
-
content: `Deletion is armed for ${modal.deleteConfirmationTarget}. Move selection or press Esc to cancel.`,
|
|
112
|
-
style: { fg: '244', dim: true },
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
|
|
116
108
|
return ModalFactory.createModal(
|
|
117
109
|
{
|
|
118
110
|
title: 'Sessions',
|
|
@@ -120,7 +112,7 @@ export function renderSessionPickerModal(
|
|
|
120
112
|
margin: boxMargin,
|
|
121
113
|
targetContentRows,
|
|
122
114
|
sections,
|
|
123
|
-
hints: ['[\u2191\u2193] Navigate', '[Enter] Load', '
|
|
115
|
+
hints: ['[\u2191\u2193] Navigate', '[Enter] Load', 'Delete: /session delete <id> --yes', '[Esc] Close'],
|
|
124
116
|
},
|
|
125
117
|
width,
|
|
126
118
|
);
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import type { Line } from '../types/grid.ts';
|
|
9
9
|
import type { SettingsModal, SettingEntry, FlagEntry, McpEntry, SubscriptionEntry, SettingsCategory } from '../input/settings-modal.ts';
|
|
10
|
-
import { SETTINGS_CATEGORIES, SETTINGS_CATEGORY_GROUPS } from '../input/settings-modal.ts';
|
|
10
|
+
import { isExternalDaemonOwnedSettingKey, SETTINGS_CATEGORIES, SETTINGS_CATEGORY_GROUPS } from '../input/settings-modal.ts';
|
|
11
11
|
import { getDisplayWidth, wrapText } from '../utils/terminal-width.ts';
|
|
12
12
|
import { CATEGORY_LABELS, describeUiRouting, formatValue, getSettingLabel, inferSubscriptionRouteReason, valueColor } from './settings-modal-helpers.ts';
|
|
13
13
|
import { isSecretConfigKey } from '../config/secret-config.ts';
|
|
@@ -34,10 +34,10 @@ const CATEGORY_INFO: Record<SettingsCategory, string> = {
|
|
|
34
34
|
wrfc: 'WRFC is external to normal Agent operation. Review these copied compatibility values only for explicit GoodVibes TUI build delegation.',
|
|
35
35
|
helper: 'Helper model defaults used by helper subsystems when they do not use the main chat route.',
|
|
36
36
|
tts: 'Text-to-speech provider, voice, and optional spoken-turn LLM overrides.',
|
|
37
|
-
service: '
|
|
38
|
-
controlPlane: '
|
|
39
|
-
httpListener: 'HTTP listener settings for webhook and integration ingress.',
|
|
40
|
-
web: '
|
|
37
|
+
service: 'External daemon service posture. Agent shows these copied compatibility keys for inspection only and does not install, start, stop, restart, or autostart services.',
|
|
38
|
+
controlPlane: 'External daemon control-plane settings for local admin/API access. Agent connects to this daemon and does not mutate its bind posture.',
|
|
39
|
+
httpListener: 'External HTTP listener settings for webhook and integration ingress. Agent does not start or expose the listener.',
|
|
40
|
+
web: 'External browser surface settings. Agent does not own the web listener or network bind lifecycle.',
|
|
41
41
|
batch: 'Batch execution settings, including local vs Cloudflare queue behavior.',
|
|
42
42
|
automation: 'Scheduled and automated run settings, concurrency, timeout, catch-up, cooldown, and retention behavior.',
|
|
43
43
|
watchers: 'File/process watcher heartbeat, polling, and recovery-window behavior.',
|
|
@@ -49,10 +49,10 @@ const CATEGORY_INFO: Record<SettingsCategory, string> = {
|
|
|
49
49
|
surfaces: 'External app surfaces such as Slack, Discord, ntfy, Home Assistant, Telegram, webhooks, chat bridges, and messaging providers.',
|
|
50
50
|
cloudflare: 'Optional Cloudflare control plane, batch queue, Worker, Tunnel, Access, DNS, KV, Durable Objects, Secrets Store, and R2 settings.',
|
|
51
51
|
release: 'Release-channel preference.',
|
|
52
|
-
danger: 'High-impact switches
|
|
52
|
+
danger: 'High-impact daemon and listener switches. Agent renders daemon-owned switches read-only; use GoodVibes TUI or the daemon host to change them.',
|
|
53
53
|
tools: 'Tool LLM and helper model routing. Empty provider/model values inherit the active chat route unless a specific helper/tool route is set.',
|
|
54
54
|
flags: 'Feature flags are SDK runtime gates. They are separate from normal config keys because they enable or disable staged runtime behavior.',
|
|
55
|
-
network: '
|
|
55
|
+
network: 'Read-only view of external daemon control-plane, HTTP listener, and browser web bind posture plus editable non-daemon network settings.',
|
|
56
56
|
};
|
|
57
57
|
|
|
58
58
|
const ENUM_VALUE_DESCRIPTIONS: Record<string, Record<string, string>> = {
|
|
@@ -479,6 +479,10 @@ function footerText(modal: SettingsModal): string {
|
|
|
479
479
|
if (modal.currentCategory === 'subscriptions') return 'Focus settings · Up/Down provider · Left categories · Tab pane · Enter review/sign out · Esc close';
|
|
480
480
|
if (modal.currentCategory === 'mcp') return 'Focus settings · Up/Down server · Left categories · Tab pane · Enter edit trust · Esc close';
|
|
481
481
|
if (modal.currentCategory === 'flags') return 'Focus feature flags · Up/Down flag · Left categories · Tab pane · Enter/Space toggle · Esc close';
|
|
482
|
+
const selected = modal.getSelected();
|
|
483
|
+
if (selected && isExternalDaemonOwnedSettingKey(selected.setting.key)) {
|
|
484
|
+
return 'Read-only external daemon setting · Change from GoodVibes TUI or daemon host · Esc close';
|
|
485
|
+
}
|
|
482
486
|
return 'Focus settings · Up/Down setting · Left categories · Tab pane · Enter/Space edit/toggle · R reset · Esc close';
|
|
483
487
|
}
|
|
484
488
|
|
|
@@ -488,7 +492,7 @@ export function renderSettingsModal(
|
|
|
488
492
|
viewportHeight = 24,
|
|
489
493
|
): Line[] {
|
|
490
494
|
const notices = [
|
|
491
|
-
...(modal.lastSaveTriggeredRestart ? [`
|
|
495
|
+
...(modal.lastSaveTriggeredRestart ? [`External daemon owner must restart ${modal.lastSaveTriggeredRestart}`] : []),
|
|
492
496
|
...(modal.lastSettingEffectMessage ? [modal.lastSettingEffectMessage] : []),
|
|
493
497
|
];
|
|
494
498
|
const metrics = getFullscreenWorkspaceMetrics({ width, height: viewportHeight });
|
|
@@ -2,7 +2,6 @@ import { type Line, type Cell, createEmptyLine, createEmptyCell } from '../types
|
|
|
2
2
|
import { LAYOUT } from './layout.ts';
|
|
3
3
|
import { VERSION } from '../version.ts';
|
|
4
4
|
import { fitDisplay, getDisplayWidth, truncateDisplay, wrapText, interpolateColor } from '../utils/terminal-width.ts';
|
|
5
|
-
import type { GitHeaderInfo } from './git-status.ts';
|
|
6
5
|
import { renderConversationFragment, renderConversationStatusLine, type ConversationStatusSegment } from './conversation-surface.ts';
|
|
7
6
|
import { GLYPHS } from './ui-primitives.ts';
|
|
8
7
|
|
|
@@ -11,22 +10,6 @@ const GRADIENT_CYCLE_FRAMES = 50;
|
|
|
11
10
|
/** Number of frames before rotating to the next thinking phrase (~30 seconds at 80ms/frame). */
|
|
12
11
|
const PHRASE_ROTATION_FRAMES = 375;
|
|
13
12
|
|
|
14
|
-
/** Build the git segment string and its display width. Single source of truth for header layout. */
|
|
15
|
-
function buildGitSegment(gitInfo: GitHeaderInfo): { text: string; width: number } {
|
|
16
|
-
const branch = ` git:${gitInfo.branch}`;
|
|
17
|
-
if (gitInfo.dirty) {
|
|
18
|
-
const text = `${branch} * `;
|
|
19
|
-
return { text, width: getDisplayWidth(text) };
|
|
20
|
-
}
|
|
21
|
-
if (gitInfo.ahead > 0 || gitInfo.behind > 0) {
|
|
22
|
-
const arrows = (gitInfo.ahead > 0 ? ` +${gitInfo.ahead}` : '') + (gitInfo.behind > 0 ? ` -${gitInfo.behind}` : '');
|
|
23
|
-
const text = `${branch}${arrows} `;
|
|
24
|
-
return { text, width: getDisplayWidth(text) };
|
|
25
|
-
}
|
|
26
|
-
const text = `${branch} `;
|
|
27
|
-
return { text, width: getDisplayWidth(text) };
|
|
28
|
-
}
|
|
29
|
-
|
|
30
13
|
/** Format a number: up to 999, then 1.0k, 1.0M, 1.0B, 1.0T */
|
|
31
14
|
function fmtNum(n: number): string {
|
|
32
15
|
if (n < 1000) return String(n);
|
|
@@ -40,7 +23,7 @@ function fmtNum(n: number): string {
|
|
|
40
23
|
* UIFactory - Generates standard UI fragments without needing Ink/React overhead.
|
|
41
24
|
*/
|
|
42
25
|
export class UIFactory {
|
|
43
|
-
public static createHeader(width: number, model: string, provider: string, title?: string
|
|
26
|
+
public static createHeader(width: number, model: string, provider: string, title?: string): Line[] {
|
|
44
27
|
const lines: Line[] = [];
|
|
45
28
|
const CYAN = '#00ffff';
|
|
46
29
|
const GREY = '244';
|
|
@@ -56,9 +39,8 @@ export class UIFactory {
|
|
|
56
39
|
// Optional conversation title — shown after brand/ver, truncated to fit
|
|
57
40
|
if (title) {
|
|
58
41
|
const titleStr = `│ ${title} `;
|
|
59
|
-
// Reserve space for
|
|
60
|
-
const
|
|
61
|
-
const rightReserved = getDisplayWidth(stats + prov) + gitReserved;
|
|
42
|
+
// Reserve space for model/provider on the right.
|
|
43
|
+
const rightReserved = getDisplayWidth(stats + prov);
|
|
62
44
|
const maxTitleW = width - curX - rightReserved - 1;
|
|
63
45
|
let displayTitle: string;
|
|
64
46
|
if (getDisplayWidth(titleStr) <= maxTitleW) {
|
|
@@ -76,19 +58,9 @@ export class UIFactory {
|
|
|
76
58
|
}
|
|
77
59
|
for (const char of displayTitle) { if (curX < width) line[curX++] = { char, fg: TITLE_COLOR, bg: '', bold: false, dim: true, underline: false, italic: false, strikethrough: false }; }
|
|
78
60
|
}
|
|
79
|
-
// Build git info segment
|
|
80
|
-
let gitStr = '';
|
|
81
|
-
let gitFg = '238';
|
|
82
|
-
if (gitInfo) {
|
|
83
|
-
gitStr = buildGitSegment(gitInfo).text;
|
|
84
|
-
if (gitInfo.dirty || gitInfo.ahead > 0 || gitInfo.behind > 0) {
|
|
85
|
-
gitFg = '220'; // yellow when dirty or out-of-sync
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
61
|
const rightSideText = stats + prov;
|
|
89
|
-
const rightSideW = getDisplayWidth(rightSideText)
|
|
62
|
+
const rightSideW = getDisplayWidth(rightSideText);
|
|
90
63
|
let rightX = width - rightSideW;
|
|
91
|
-
for (const char of gitStr) { if (rightX >= 0 && rightX < width) line[rightX++] = { char, fg: gitFg, bg: '', bold: false, dim: !gitInfo?.dirty && !(gitInfo?.ahead || gitInfo?.behind), underline: false, italic: false, strikethrough: false }; }
|
|
92
64
|
for (const char of stats) { if (rightX < width) line[rightX++] = { char, fg: CYAN, bg: '', bold: true, dim: false, underline: false, italic: false, strikethrough: false }; }
|
|
93
65
|
for (const char of prov) { if (rightX < width) line[rightX++] = { char, fg: GREY, bg: '', bold: false, dim: true, underline: false, italic: false, strikethrough: false }; }
|
|
94
66
|
lines.push(line);
|
|
@@ -11,8 +11,6 @@ import type { OpsControlPlane } from '@/runtime/index.ts';
|
|
|
11
11
|
import { CommandRegistry } from '../input/command-registry.ts';
|
|
12
12
|
import { registerBuiltinCommands } from '../input/commands.ts';
|
|
13
13
|
import { InputHistory } from '../input/input-history.ts';
|
|
14
|
-
import { GitStatusProvider } from '../renderer/git-status.ts';
|
|
15
|
-
import type { GitHeaderInfo } from '../renderer/git-status.ts';
|
|
16
14
|
import type { PermissionRequestHandler } from '@pellux/goodvibes-sdk/platform/permissions';
|
|
17
15
|
import { registerBuiltinPanels } from '../panels/builtin-panels.ts';
|
|
18
16
|
import { SystemMessagesPanel } from '../panels/system-messages-panel.ts';
|
|
@@ -38,8 +36,6 @@ import { createKnowledgeApi } from '@pellux/goodvibes-sdk/platform/knowledge';
|
|
|
38
36
|
export interface BootstrapShellState {
|
|
39
37
|
readonly commandRegistry: CommandRegistry;
|
|
40
38
|
readonly commandContext: CommandContext;
|
|
41
|
-
readonly gitStatusProvider: GitStatusProvider;
|
|
42
|
-
readonly lastGitInfoRef: { value: GitHeaderInfo | undefined };
|
|
43
39
|
readonly inputHistory: InputHistory;
|
|
44
40
|
readonly systemMessageRouter: SystemMessageRouter;
|
|
45
41
|
}
|
|
@@ -260,13 +256,6 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
|
|
|
260
256
|
});
|
|
261
257
|
commandContextRef = commandContext;
|
|
262
258
|
|
|
263
|
-
const gitStatusProvider = new GitStatusProvider(services.workingDirectory);
|
|
264
|
-
const lastGitInfoRef = { value: undefined as GitHeaderInfo | undefined };
|
|
265
|
-
gitStatusProvider.getStatus().then((info) => {
|
|
266
|
-
lastGitInfoRef.value = info;
|
|
267
|
-
requestRender();
|
|
268
|
-
}).catch(() => { /* non-fatal */ });
|
|
269
|
-
|
|
270
259
|
const saveHistory = configManager.get('behavior.saveHistory') as boolean;
|
|
271
260
|
const inputHistory = new InputHistory({
|
|
272
261
|
historyPath: services.shellPaths.resolveUserPath(GOODVIBES_AGENT_SURFACE_ROOT, 'input-history.json'),
|
|
@@ -276,8 +265,6 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
|
|
|
276
265
|
return {
|
|
277
266
|
commandRegistry,
|
|
278
267
|
commandContext,
|
|
279
|
-
gitStatusProvider,
|
|
280
|
-
lastGitInfoRef,
|
|
281
268
|
inputHistory,
|
|
282
269
|
systemMessageRouter,
|
|
283
270
|
};
|
package/src/runtime/bootstrap.ts
CHANGED
|
@@ -16,8 +16,6 @@ import { logger } from '@pellux/goodvibes-sdk/platform/utils';
|
|
|
16
16
|
import type { PermissionRequestHandler } from '@pellux/goodvibes-sdk/platform/permissions';
|
|
17
17
|
import type { CommandContext } from '../input/command-registry.ts';
|
|
18
18
|
import type { InputHistory } from '../input/input-history.ts';
|
|
19
|
-
import type { GitStatusProvider } from '../renderer/git-status.ts';
|
|
20
|
-
import type { GitHeaderInfo } from '../renderer/git-status.ts';
|
|
21
19
|
import type { SelectionManager } from '../input/selection.ts';
|
|
22
20
|
import type { Compositor } from '../renderer/compositor.ts';
|
|
23
21
|
|
|
@@ -81,10 +79,6 @@ export type BootstrapContext = RuntimeContext & {
|
|
|
81
79
|
uiServices: UiRuntimeServices;
|
|
82
80
|
/** Persists and navigates input history across sessions. */
|
|
83
81
|
inputHistory: InputHistory;
|
|
84
|
-
/** Provides git branch/dirty state for the header. */
|
|
85
|
-
gitStatusProvider: GitStatusProvider;
|
|
86
|
-
/** Mutable ref so async git refreshes propagate without closure capture issues. */
|
|
87
|
-
lastGitInfoRef: { value: GitHeaderInfo | undefined };
|
|
88
82
|
/** Unsubscribe functions owned by bootstrap (cleared on shutdown). */
|
|
89
83
|
bootstrapUnsubs: Array<() => void>;
|
|
90
84
|
/** Ref holding the periodic agent-status interval (use ref — not local var — to keep shutdown in sync). */
|
|
@@ -291,9 +285,7 @@ export async function bootstrapRuntime(
|
|
|
291
285
|
systemMessageRouterRef.value = systemMessageRouter;
|
|
292
286
|
const commandRegistry = shell.commandRegistry;
|
|
293
287
|
const commandContext = shell.commandContext;
|
|
294
|
-
const gitStatusProvider = shell.gitStatusProvider;
|
|
295
288
|
const inputHistory = shell.inputHistory;
|
|
296
|
-
const lastGitInfoRef = shell.lastGitInfoRef;
|
|
297
289
|
const pluginCommandRegistry = {
|
|
298
290
|
register(command: {
|
|
299
291
|
readonly name: string;
|
|
@@ -507,8 +499,6 @@ export async function bootstrapRuntime(
|
|
|
507
499
|
commandContext,
|
|
508
500
|
uiServices,
|
|
509
501
|
inputHistory,
|
|
510
|
-
gitStatusProvider,
|
|
511
|
-
lastGitInfoRef,
|
|
512
502
|
bootstrapUnsubs,
|
|
513
503
|
agentStatusIntervalRef,
|
|
514
504
|
orchestratorRefs,
|
|
@@ -267,7 +267,7 @@ function describeLocalTuiOnly(snapshot: OnboardingSnapshotState): string {
|
|
|
267
267
|
return 'Use GoodVibes Agent in this terminal while connecting only to an externally managed daemon. Agent does not enable service mode, HTTP listeners, external app surfaces, or network setup.';
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
-
return 'Keep Agent local-only by not
|
|
270
|
+
return 'Keep Agent local-only by not requesting browser access, service posture changes, HTTP listeners, external app surfaces, or network setup from the daemon owner.';
|
|
271
271
|
}
|
|
272
272
|
|
|
273
273
|
function describeBrowserAccess(snapshot: OnboardingSnapshotState): string {
|
|
@@ -278,14 +278,14 @@ function describeBrowserAccess(snapshot: OnboardingSnapshotState): string {
|
|
|
278
278
|
|
|
279
279
|
function describeRemoteDeviceAccess(snapshot: OnboardingSnapshotState): string {
|
|
280
280
|
return hasRemoteDeviceAccess(snapshot)
|
|
281
|
-
? '
|
|
282
|
-
: '
|
|
281
|
+
? 'Review external daemon surfaces reachable from other devices on your LAN. Local authentication is required.'
|
|
282
|
+
: 'Review the external daemon surfaces required for other-device LAN access. Local authentication is required.';
|
|
283
283
|
}
|
|
284
284
|
|
|
285
285
|
function describeWebhookIngress(snapshot: OnboardingSnapshotState): string {
|
|
286
286
|
return hasWebhookOrEventIngress(snapshot)
|
|
287
|
-
? '
|
|
288
|
-
: '
|
|
287
|
+
? 'Review the external HTTP listener used for incoming webhooks, callbacks, and automation events.'
|
|
288
|
+
: 'Review the external HTTP listener required for incoming webhooks, callbacks, and automation events.';
|
|
289
289
|
}
|
|
290
290
|
|
|
291
291
|
function describeExternalIntegrations(snapshot: OnboardingSnapshotState): string {
|
|
@@ -306,7 +306,7 @@ function describeCloudflareBatch(snapshot: OnboardingSnapshotState): string {
|
|
|
306
306
|
return 'Review Cloudflare Workers/Queues batch processing, token storage, and optional remote daemon provisioning settings.';
|
|
307
307
|
}
|
|
308
308
|
|
|
309
|
-
return 'Optionally configure Cloudflare Workers and Queues for explicit or eligible background batch jobs.
|
|
309
|
+
return 'Optionally configure Cloudflare Workers and Queues for explicit or eligible background batch jobs. The external daemon still owns execution.';
|
|
310
310
|
}
|
|
311
311
|
|
|
312
312
|
function getAcknowledgementAccepted(
|