@pellux/goodvibes-agent 0.1.9 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +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 +18 -19
- package/src/cli/help.ts +13 -0
- 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/experience-runtime.ts +1 -1
- package/src/input/commands/hooks-runtime.ts +30 -2
- package/src/input/handler.ts +1 -0
- 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 +4 -4
- package/src/input/onboarding/onboarding-wizard-cloudflare.ts +1 -1
- 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/settings-modal-agent-policy.ts +18 -0
- 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/renderer/process-modal.ts +17 -8
- 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
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GoodVibes Agent will be recorded here.
|
|
4
4
|
|
|
5
|
+
## 0.1.10 - 2026-05-31
|
|
6
|
+
|
|
7
|
+
- 93aba19 Block agent-spawning hook authoring
|
|
8
|
+
- 735839a Prune git header renderer from Agent
|
|
9
|
+
- 95d22fd Remove unused git shell bootstrap wiring
|
|
10
|
+
- 1392ebc Remove Agent write-quit commit helper
|
|
11
|
+
- df6572f Remove coding header posture from Agent shell
|
|
12
|
+
- 2b8e679 Align live verification with Agent routes
|
|
13
|
+
- 70eb800 Add stable typecheck release scripts
|
|
14
|
+
- 6b57500 Hide local agent activity in Agent UI
|
|
15
|
+
- 7166188 Add Agent Knowledge CLI shortcuts
|
|
16
|
+
- 386c19d Align service diagnostics with Agent boundaries
|
|
17
|
+
- e8b19db Lock daemon-owned settings in Agent
|
|
18
|
+
|
|
5
19
|
## 0.1.9 - 2026-05-31
|
|
6
20
|
|
|
7
21
|
- 75e5d4a Align shell surface delegation test
|
package/README.md
CHANGED
package/docs/getting-started.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Getting Started
|
|
2
2
|
|
|
3
|
-
GoodVibes Agent `0.1.
|
|
3
|
+
GoodVibes Agent `0.1.9` is the current installable public alpha of the personal operator assistant built on the GoodVibes TUI foundation.
|
|
4
4
|
|
|
5
5
|
## Requirements
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Release And Publishing
|
|
2
2
|
|
|
3
|
-
GoodVibes Agent `0.1.
|
|
3
|
+
GoodVibes Agent `0.1.9` is the current installable public alpha release.
|
|
4
4
|
|
|
5
5
|
## Package Identity
|
|
6
6
|
|
|
@@ -17,7 +17,7 @@ Before any release candidate:
|
|
|
17
17
|
|
|
18
18
|
```sh
|
|
19
19
|
bun install
|
|
20
|
-
|
|
20
|
+
bun run typecheck
|
|
21
21
|
bun run build
|
|
22
22
|
bun run package:install-check
|
|
23
23
|
bun run publish:check
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Near-fork GoodVibes operator assistant with the GoodVibes TUI shell, renderer, input, fullscreen workspace, and daemon-connected Agent product brain.",
|
|
6
6
|
"type": "module",
|
|
@@ -40,6 +40,8 @@
|
|
|
40
40
|
"dev": "bun run src/main.ts",
|
|
41
41
|
"tui": "bun run src/main.ts",
|
|
42
42
|
"dev:watch": "bun --watch src/main.ts",
|
|
43
|
+
"typecheck": "bunx tsc --noEmit",
|
|
44
|
+
"check:types": "bun run typecheck",
|
|
43
45
|
"prebuild": "bun run scripts/prebuild.ts",
|
|
44
46
|
"build": "bun build src/main.ts --compile --outfile dist/goodvibes-agent",
|
|
45
47
|
"build:linux-x64": "bun build src/main.ts --compile --target=bun-linux-x64 --outfile dist/goodvibes-agent-linux-x64",
|
|
@@ -61,6 +63,7 @@
|
|
|
61
63
|
"build:all": "bun run scripts/build.ts --all",
|
|
62
64
|
"perf:check": "bun run scripts/perf-check.ts",
|
|
63
65
|
"architecture:check": "bun run scripts/check-architecture.ts",
|
|
66
|
+
"audit:home": "bun run scripts/audit-goodvibes-home.ts",
|
|
64
67
|
"foundation:artifacts": "bun run scripts/export-foundation-artifacts.ts",
|
|
65
68
|
"verification:ledger": "bun run scripts/verification-ledger.ts",
|
|
66
69
|
"verification:live": "bun run scripts/verify-live.ts",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
-
import {
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import { join } from 'node:path';
|
|
4
3
|
import { createBrowserAgentSdk } from '@pellux/goodvibes-sdk/browser/agent';
|
|
5
4
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
5
|
+
import { SDK_VERSION, VERSION } from '../version.ts';
|
|
6
6
|
import type { CliCommandOutput } from './types.ts';
|
|
7
7
|
import type { CliCommandRuntime } from './management.ts';
|
|
8
8
|
import { formatJsonOrText, yesNo } from './management.ts';
|
|
@@ -149,24 +149,8 @@ function hasFlag(args: readonly string[], flag: string): boolean {
|
|
|
149
149
|
return args.includes(flag);
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
function packageJsonPath(): string {
|
|
153
|
-
return join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'package.json');
|
|
154
|
-
}
|
|
155
|
-
|
|
156
152
|
function readPackageMetadata(): { readonly version: string; readonly sdkVersion: string } {
|
|
157
|
-
|
|
158
|
-
const parsed = JSON.parse(readFileSync(packageJsonPath(), 'utf-8')) as unknown;
|
|
159
|
-
if (!isRecord(parsed)) return { version: 'unknown', sdkVersion: 'unknown' };
|
|
160
|
-
const dependencies = isRecord(parsed.dependencies) ? parsed.dependencies : {};
|
|
161
|
-
return {
|
|
162
|
-
version: typeof parsed.version === 'string' ? parsed.version : 'unknown',
|
|
163
|
-
sdkVersion: typeof dependencies['@pellux/goodvibes-sdk'] === 'string'
|
|
164
|
-
? dependencies['@pellux/goodvibes-sdk']
|
|
165
|
-
: 'unknown',
|
|
166
|
-
};
|
|
167
|
-
} catch {
|
|
168
|
-
return { version: 'unknown', sdkVersion: 'unknown' };
|
|
169
|
-
}
|
|
153
|
+
return { version: VERSION, sdkVersion: SDK_VERSION };
|
|
170
154
|
}
|
|
171
155
|
|
|
172
156
|
function resolveDaemonConnection(runtime: CliCommandRuntime): AgentDaemonConnection {
|
|
@@ -553,6 +537,21 @@ export async function handleAgentKnowledgeCommand(runtime: CliCommandRuntime): P
|
|
|
553
537
|
};
|
|
554
538
|
}
|
|
555
539
|
|
|
540
|
+
export async function handleAgentKnowledgeShortcutCommand(
|
|
541
|
+
runtime: CliCommandRuntime,
|
|
542
|
+
subcommand: 'ask' | 'search',
|
|
543
|
+
): Promise<CliCommandOutput> {
|
|
544
|
+
return handleAgentKnowledgeCommand({
|
|
545
|
+
...runtime,
|
|
546
|
+
cli: {
|
|
547
|
+
...runtime.cli,
|
|
548
|
+
command: 'knowledge',
|
|
549
|
+
rawCommand: 'knowledge',
|
|
550
|
+
commandArgs: [subcommand, ...runtime.cli.commandArgs],
|
|
551
|
+
},
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
|
|
556
555
|
export async function handleCompatCommand(runtime: CliCommandRuntime): Promise<CliCommandOutput> {
|
|
557
556
|
const connection = resolveDaemonConnection(runtime);
|
|
558
557
|
const metadata = readPackageMetadata();
|
package/src/cli/help.ts
CHANGED
|
@@ -41,6 +41,7 @@ export function renderGoodVibesHelp(binary = 'goodvibes-agent'): string {
|
|
|
41
41
|
' auth Inspect and manage local users, sessions, and bootstrap auth',
|
|
42
42
|
' compat Inspect Agent SDK pin, daemon version, and Agent knowledge route readiness',
|
|
43
43
|
' knowledge Use isolated Agent Knowledge/Wiki routes',
|
|
44
|
+
' ask|search Shortcuts for isolated Agent Knowledge ask/search',
|
|
44
45
|
' delegate Explicitly delegate build/fix/review work to GoodVibes TUI',
|
|
45
46
|
' subscription Start/finish/logout provider subscription sessions',
|
|
46
47
|
' secrets List, set, link, delete, and test GoodVibes secret refs',
|
|
@@ -95,6 +96,8 @@ export function renderGoodVibesHelp(binary = 'goodvibes-agent'): string {
|
|
|
95
96
|
` ${binary} compat`,
|
|
96
97
|
` ${binary} knowledge status`,
|
|
97
98
|
` ${binary} knowledge ask "What is GoodVibes Agent?"`,
|
|
99
|
+
` ${binary} ask "What is GoodVibes Agent?"`,
|
|
100
|
+
` ${binary} search "release checklist"`,
|
|
98
101
|
` ${binary} delegate --wrfc "fix the failing tests in ~/work/project"`,
|
|
99
102
|
` ${binary} surfaces`,
|
|
100
103
|
` ${binary} surfaces check`,
|
|
@@ -174,6 +177,16 @@ const COMMAND_HELP: Record<string, CommandHelp> = {
|
|
|
174
177
|
'knowledge ingest-url https://example.com/page --title "Reference"',
|
|
175
178
|
],
|
|
176
179
|
},
|
|
180
|
+
ask: {
|
|
181
|
+
usage: ['ask <question> [--limit <n>] [--mode concise|standard|detailed]'],
|
|
182
|
+
summary: 'Shortcut for isolated Agent Knowledge ask. This never queries default Knowledge/Wiki or HomeGraph.',
|
|
183
|
+
examples: ['ask "What is GoodVibes Agent?"', 'ask "release checklist" --mode concise'],
|
|
184
|
+
},
|
|
185
|
+
search: {
|
|
186
|
+
usage: ['search <query> [--limit <n>]'],
|
|
187
|
+
summary: 'Shortcut for isolated Agent Knowledge search. This never queries default Knowledge/Wiki or HomeGraph.',
|
|
188
|
+
examples: ['search "release checklist"', 'search "operator workspace" --limit 5'],
|
|
189
|
+
},
|
|
177
190
|
delegate: {
|
|
178
191
|
usage: ['delegate [--wrfc] <build/fix/review task>'],
|
|
179
192
|
summary: 'Create one shared-session task request for GoodVibes TUI. WRFC is requested only with --wrfc.',
|
|
@@ -329,9 +329,9 @@ export async function buildControlPlaneStatusResult(runtime: CliCommandRuntime):
|
|
|
329
329
|
};
|
|
330
330
|
const issues: string[] = [];
|
|
331
331
|
if (enabled === true && !reachable) issues.push(`Control plane is enabled but not reachable on ${binding.host}:${binding.port}.`);
|
|
332
|
-
if (enabled === true && service.enabled !== true) issues.push('Control plane is enabled but service
|
|
333
|
-
if (enabled === true && service.autostart !== true) issues.push('Control plane is enabled but
|
|
334
|
-
if (enabled === true && service.restartOnFailure !== true) issues.push('Control plane is enabled but
|
|
332
|
+
if (enabled === true && service.enabled !== true) issues.push('Control plane is enabled on the external daemon config, but Agent service ownership is disabled.');
|
|
333
|
+
if (enabled === true && service.autostart !== true) issues.push('Control plane is enabled on the external daemon config, but daemon autostart is off.');
|
|
334
|
+
if (enabled === true && service.restartOnFailure !== true) issues.push('Control plane is enabled on the external daemon config, but restart-on-failure is off.');
|
|
335
335
|
if (isNetworkFacing(enabled, binding) && !auth.userStorePresent) issues.push('Network-facing control plane has no local auth user store.');
|
|
336
336
|
if (isNetworkFacing(enabled, binding) && auth.bootstrapCredentialPresent) issues.push('Network-facing control plane still has a bootstrap credential file.');
|
|
337
337
|
return {
|
package/src/cli/management.ts
CHANGED
|
@@ -32,7 +32,7 @@ import { handleServiceCommand } from './service-command.ts';
|
|
|
32
32
|
import { handleBundleCommand } from './bundle-command.ts';
|
|
33
33
|
import { buildListenerTestResult, formatListenerTestResult, handleSurfacesCommand } from './surface-command.ts';
|
|
34
34
|
import { buildControlPlaneStatusResult, formatControlPlaneStatus, handleSecrets, handleSessions, handleTasks, renderPairing, renderRemote, renderSubscriptions, renderWeb } from './management-commands.ts';
|
|
35
|
-
import { handleAgentKnowledgeCommand, handleCompatCommand, handleDelegateCommand } from './agent-knowledge-command.ts';
|
|
35
|
+
import { handleAgentKnowledgeCommand, handleAgentKnowledgeShortcutCommand, handleCompatCommand, handleDelegateCommand } from './agent-knowledge-command.ts';
|
|
36
36
|
import { GOODVIBES_AGENT_SURFACE_ROOT } from '../config/surface.ts';
|
|
37
37
|
|
|
38
38
|
export interface CliCommandRuntime {
|
|
@@ -703,6 +703,12 @@ export async function handleGoodVibesCliCommand(runtime: CliCommandRuntime): Pro
|
|
|
703
703
|
console.log(result.output);
|
|
704
704
|
return { handled: true, exitCode: result.exitCode };
|
|
705
705
|
}
|
|
706
|
+
case 'ask':
|
|
707
|
+
case 'search': {
|
|
708
|
+
const result = await handleAgentKnowledgeShortcutCommand(runtime, runtime.cli.command);
|
|
709
|
+
console.log(result.output);
|
|
710
|
+
return { handled: true, exitCode: result.exitCode };
|
|
711
|
+
}
|
|
706
712
|
case 'delegate': {
|
|
707
713
|
const result = await handleDelegateCommand(runtime);
|
|
708
714
|
console.log(result.output);
|
package/src/cli/parser.ts
CHANGED
|
@@ -31,6 +31,9 @@ const COMMAND_ALIASES: Readonly<Record<string, GoodVibesCliCommand>> = {
|
|
|
31
31
|
knowledge: 'knowledge',
|
|
32
32
|
know: 'knowledge',
|
|
33
33
|
kb: 'knowledge',
|
|
34
|
+
ask: 'ask',
|
|
35
|
+
search: 'search',
|
|
36
|
+
find: 'search',
|
|
34
37
|
delegate: 'delegate',
|
|
35
38
|
build: 'delegate',
|
|
36
39
|
subscription: 'subscription',
|
|
@@ -397,19 +397,19 @@ export async function buildCliServicePosture(
|
|
|
397
397
|
const issues: string[] = [];
|
|
398
398
|
|
|
399
399
|
if (serverBackedEnabled && !config.enabled) {
|
|
400
|
-
issues.push('
|
|
400
|
+
issues.push('Daemon-owned surfaces are configured, but Agent service ownership is disabled.');
|
|
401
401
|
}
|
|
402
402
|
if (config.enabled && !config.autostart) {
|
|
403
|
-
issues.push('
|
|
403
|
+
issues.push('External daemon service config has autostart off.');
|
|
404
404
|
}
|
|
405
405
|
if (config.enabled && !config.restartOnFailure) {
|
|
406
|
-
issues.push('
|
|
406
|
+
issues.push('External daemon service config has restart-on-failure off.');
|
|
407
407
|
}
|
|
408
408
|
if (config.enabled && !status.installed) {
|
|
409
|
-
issues.push('
|
|
409
|
+
issues.push('External daemon service config is enabled, but no platform service definition is installed.');
|
|
410
410
|
}
|
|
411
411
|
if (config.enabled && !status.running) {
|
|
412
|
-
issues.push('
|
|
412
|
+
issues.push('External daemon service config is enabled, but the managed service is not running.');
|
|
413
413
|
}
|
|
414
414
|
if (status.actionError) {
|
|
415
415
|
issues.push(`Service manager reported an error: ${status.actionError}`);
|
|
@@ -454,7 +454,7 @@ function yesNo(value: boolean): string {
|
|
|
454
454
|
export function formatCliServicePosture(posture: CliServicePosture, json = false): string {
|
|
455
455
|
if (json) return JSON.stringify(posture, null, 2);
|
|
456
456
|
return [
|
|
457
|
-
'GoodVibes service',
|
|
457
|
+
'GoodVibes external daemon service',
|
|
458
458
|
` enabled: ${yesNo(posture.config.enabled)}`,
|
|
459
459
|
` autostart: ${yesNo(posture.config.autostart)}`,
|
|
460
460
|
` restartOnFailure: ${yesNo(posture.config.restartOnFailure)}`,
|
package/src/cli/status.ts
CHANGED
|
@@ -122,10 +122,10 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
|
|
|
122
122
|
id: 'service-disabled-for-server-surfaces',
|
|
123
123
|
area: 'service',
|
|
124
124
|
severity: 'warning',
|
|
125
|
-
summary: '
|
|
125
|
+
summary: 'Daemon-owned surfaces are configured while Agent service ownership is disabled.',
|
|
126
126
|
cause: 'One or more daemon, control-plane, listener, or web settings are enabled while service.enabled is false.',
|
|
127
|
-
impact: 'The
|
|
128
|
-
action: '
|
|
127
|
+
impact: 'The external daemon host must own availability for those surfaces; Agent will not start or enable them.',
|
|
128
|
+
action: 'Manage surface/service posture from GoodVibes TUI or the daemon host, then use Agent for read-only diagnostics.',
|
|
129
129
|
});
|
|
130
130
|
}
|
|
131
131
|
|
|
@@ -134,10 +134,10 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
|
|
|
134
134
|
id: 'service-autostart-disabled',
|
|
135
135
|
area: 'service',
|
|
136
136
|
severity: 'warning',
|
|
137
|
-
summary: '
|
|
137
|
+
summary: 'External daemon service config has autostart off.',
|
|
138
138
|
cause: 'service.enabled is true and service.autostart is false.',
|
|
139
139
|
impact: 'The external GoodVibes daemon may not be available after login or reboot even though service mode is selected.',
|
|
140
|
-
action: '
|
|
140
|
+
action: 'Configure daemon autostart from GoodVibes TUI or the daemon host; Agent will not mutate this setting.',
|
|
141
141
|
});
|
|
142
142
|
}
|
|
143
143
|
|
|
@@ -146,10 +146,10 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
|
|
|
146
146
|
id: 'service-restart-disabled',
|
|
147
147
|
area: 'service',
|
|
148
148
|
severity: 'warning',
|
|
149
|
-
summary: '
|
|
149
|
+
summary: 'External daemon service config has restart-on-failure off.',
|
|
150
150
|
cause: 'service.enabled is true and service.restartOnFailure is false.',
|
|
151
151
|
impact: 'A crashed daemon or listener may stay down until manually restarted.',
|
|
152
|
-
action: '
|
|
152
|
+
action: 'Configure restart-on-failure from GoodVibes TUI or the daemon host; Agent will not mutate this setting.',
|
|
153
153
|
});
|
|
154
154
|
}
|
|
155
155
|
|
|
@@ -163,7 +163,7 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
|
|
|
163
163
|
summary: issue,
|
|
164
164
|
cause: 'The service lifecycle inspection found a mismatch between configured service/surface state and observed host state.',
|
|
165
165
|
impact: 'Daemon, control-plane, listener, or web availability may not match the configuration.',
|
|
166
|
-
action: '
|
|
166
|
+
action: 'Use goodvibes-agent service check for read-only detail, then manage the daemon from GoodVibes TUI or your daemon host tooling.',
|
|
167
167
|
});
|
|
168
168
|
}
|
|
169
169
|
}
|
|
@@ -326,7 +326,7 @@ export function renderCliStatus(options: CliStatusOptions): string {
|
|
|
326
326
|
? ` operatorTokens: ${options.auth.operatorTokenPresent ? 'present' : 'missing'} (${options.auth.operatorTokenPath})`
|
|
327
327
|
: ' operatorTokens: unknown',
|
|
328
328
|
'',
|
|
329
|
-
'Service:',
|
|
329
|
+
'External Daemon Service:',
|
|
330
330
|
` enabled: ${yesNo(serviceEnabled)}`,
|
|
331
331
|
` autostart: ${yesNo(serviceAutostart)}`,
|
|
332
332
|
` restartOnFailure: ${yesNo(restartOnFailure)}`,
|
|
@@ -217,9 +217,9 @@ export async function buildListenerTestResult(runtime: CliCommandRuntime): Promi
|
|
|
217
217
|
}).filter((surface) => surface.enabled === true);
|
|
218
218
|
const issues: string[] = [];
|
|
219
219
|
if (enabled !== true) issues.push('HTTP listener is disabled.');
|
|
220
|
-
if (enabled === true && service.enabled !== true) issues.push('HTTP listener is enabled but service
|
|
221
|
-
if (enabled === true && service.autostart !== true) issues.push('HTTP listener is enabled but
|
|
222
|
-
if (enabled === true && service.restartOnFailure !== true) issues.push('HTTP listener is enabled but
|
|
220
|
+
if (enabled === true && service.enabled !== true) issues.push('HTTP listener is enabled on the external daemon config, but Agent service ownership is disabled.');
|
|
221
|
+
if (enabled === true && service.autostart !== true) issues.push('HTTP listener is enabled on the external daemon config, but daemon autostart is off.');
|
|
222
|
+
if (enabled === true && service.restartOnFailure !== true) issues.push('HTTP listener is enabled on the external daemon config, but restart-on-failure is off.');
|
|
223
223
|
if (isNetworkFacing(enabled, binding) && !auth.userStorePresent) issues.push('Network-facing listener has no local auth user store.');
|
|
224
224
|
if (isNetworkFacing(enabled, binding) && auth.bootstrapCredentialPresent) issues.push('Network-facing listener still has a bootstrap credential file.');
|
|
225
225
|
for (const surface of surfaces) {
|
package/src/cli/types.ts
CHANGED
|
@@ -165,7 +165,7 @@ export function registerExperienceRuntimeCommands(registry: CommandRegistry): vo
|
|
|
165
165
|
['shell', 'Shell execution approval with side-effect and credential review.'],
|
|
166
166
|
['file', 'File mutation approval with config/notebook differentiation.'],
|
|
167
167
|
['network', 'Network access approval with host/scope review.'],
|
|
168
|
-
['delegate', '
|
|
168
|
+
['delegate', 'Explicit GoodVibes TUI build delegation approval; local Agent spawn is blocked.'],
|
|
169
169
|
['mcp', 'MCP trust escalation approval with host/path review.'],
|
|
170
170
|
['remote', 'Remote dispatch approval with trust/artifact review.'],
|
|
171
171
|
['hook', 'Hook execution approval with deny/mutate authority review.'],
|
|
@@ -1,6 +1,26 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
1
2
|
import type { CommandRegistry } from '../command-registry.ts';
|
|
2
3
|
import { requireHookApi } from './runtime-services.ts';
|
|
3
4
|
|
|
5
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
6
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function containsAgentHookType(value: unknown): boolean {
|
|
10
|
+
if (Array.isArray(value)) return value.some((entry) => containsAgentHookType(entry));
|
|
11
|
+
if (!isRecord(value)) return false;
|
|
12
|
+
if (value.type === 'agent') return true;
|
|
13
|
+
return Object.values(value).some((entry) => containsAgentHookType(entry));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function fileContainsAgentHookType(path: string): boolean {
|
|
17
|
+
try {
|
|
18
|
+
return containsAgentHookType(JSON.parse(readFileSync(path, 'utf-8')) as unknown);
|
|
19
|
+
} catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
4
24
|
export function registerHooksRuntimeCommands(registry: CommandRegistry): void {
|
|
5
25
|
registry.register({
|
|
6
26
|
name: 'hooks',
|
|
@@ -25,10 +45,14 @@ export function registerHooksRuntimeCommands(registry: CommandRegistry): void {
|
|
|
25
45
|
if (subcommand === 'scaffold') {
|
|
26
46
|
const [name, match, type] = args.slice(1);
|
|
27
47
|
if (!name || !match || !type) {
|
|
28
|
-
ctx.print('Usage: /hooks scaffold <name> <match> <command|prompt|
|
|
48
|
+
ctx.print('Usage: /hooks scaffold <name> <match> <command|prompt|http|ts>');
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (type === 'agent') {
|
|
52
|
+
ctx.print('Blocked: GoodVibes Agent does not author local agent-spawning hooks. Use command, prompt, http, or ts hooks, or delegate explicit build work to GoodVibes TUI.');
|
|
29
53
|
return;
|
|
30
54
|
}
|
|
31
|
-
if (!['command', 'prompt', '
|
|
55
|
+
if (!['command', 'prompt', 'http', 'ts'].includes(type)) {
|
|
32
56
|
ctx.print(`Unknown hook type: ${type}`);
|
|
33
57
|
return;
|
|
34
58
|
}
|
|
@@ -118,6 +142,10 @@ export function registerHooksRuntimeCommands(registry: CommandRegistry): void {
|
|
|
118
142
|
ctx.print('Usage: /hooks import <path> [merge|replace]');
|
|
119
143
|
return;
|
|
120
144
|
}
|
|
145
|
+
if (fileContainsAgentHookType(path)) {
|
|
146
|
+
ctx.print('Blocked: hook bundle contains type=agent entries. GoodVibes Agent does not import local agent-spawning hooks.');
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
121
149
|
await workbench.import(path, strategy);
|
|
122
150
|
ctx.print(`Imported managed hooks from ${path} using ${strategy} strategy.`);
|
|
123
151
|
return;
|
package/src/input/handler.ts
CHANGED
|
@@ -238,6 +238,7 @@ export class InputHandler {
|
|
|
238
238
|
agentManager: uiServices.agents.agentManager,
|
|
239
239
|
processManager: uiServices.shell.processManager,
|
|
240
240
|
wrfcController: uiServices.agents.wrfcController,
|
|
241
|
+
agentEntries: 'hidden',
|
|
241
242
|
});
|
|
242
243
|
this.liveTailModal = new LiveTailModal({
|
|
243
244
|
agentManager: uiServices.agents.agentManager,
|
|
@@ -19,24 +19,24 @@ export function runtimePortDiagnostic(
|
|
|
19
19
|
if (status) {
|
|
20
20
|
const reason = status.reason ? ` ${status.reason}` : '';
|
|
21
21
|
if (status.mode === 'blocked') {
|
|
22
|
-
return `The configured endpoint ${status.baseUrl} is occupied but was not usable by this
|
|
22
|
+
return `The configured endpoint ${status.baseUrl} is occupied but was not usable by this Agent instance.${reason}`;
|
|
23
23
|
}
|
|
24
24
|
if (status.mode === 'disabled') {
|
|
25
25
|
return `The configured endpoint ${status.baseUrl} is disabled in the runtime service configuration.${reason}`;
|
|
26
26
|
}
|
|
27
27
|
if (status.mode === 'unavailable') {
|
|
28
|
-
return `The configured endpoint ${status.baseUrl} is unavailable
|
|
28
|
+
return `The configured endpoint ${status.baseUrl} is unavailable to Agent.${reason}`;
|
|
29
29
|
}
|
|
30
30
|
if (status.mode === 'external') {
|
|
31
31
|
const version = status.version ? ` version ${status.version}` : '';
|
|
32
32
|
return `An existing GoodVibes service was verified at ${status.baseUrl}${version}.`;
|
|
33
33
|
}
|
|
34
|
-
return `
|
|
34
|
+
return `A GoodVibes service reports embedded mode at ${status.baseUrl}; Agent still treats daemon lifecycle as external.`;
|
|
35
35
|
}
|
|
36
36
|
if (portInUse) {
|
|
37
|
-
return `The configured port ${binding.host}:${binding.port} is occupied
|
|
37
|
+
return `The configured port ${binding.host}:${binding.port} is occupied; another GoodVibes process or another service may own it.`;
|
|
38
38
|
}
|
|
39
|
-
return `No process is listening on ${binding.host}:${binding.port}
|
|
39
|
+
return `No process is listening on ${binding.host}:${binding.port}.`;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
export function getRuntimeEndpointStatus(
|
|
@@ -79,9 +79,9 @@ export function formatRuntimeActiveSuccessMessage(
|
|
|
79
79
|
return `${label} is already running as a verified external GoodVibes service at ${status.baseUrl}${version}.`;
|
|
80
80
|
}
|
|
81
81
|
if (status?.mode === 'embedded') {
|
|
82
|
-
return `${label}
|
|
82
|
+
return `${label} reports embedded mode at ${status.baseUrl}; Agent does not own that service lifecycle.`;
|
|
83
83
|
}
|
|
84
84
|
return endpoint === 'daemon'
|
|
85
|
-
? 'The GoodVibes daemon is
|
|
86
|
-
: 'The HTTP listener is
|
|
85
|
+
? 'The GoodVibes daemon is reachable to Agent.'
|
|
86
|
+
: 'The HTTP listener is reachable to Agent.';
|
|
87
87
|
}
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
getExternalSurfaceAutoStartFieldId,
|
|
16
16
|
isExternalSurfaceSelectedByDefault,
|
|
17
17
|
} from './onboarding-wizard-external-surfaces.ts';
|
|
18
|
-
import { buildGoodVibesSecretKey, buildGoodVibesSecretRef,
|
|
18
|
+
import { buildGoodVibesSecretKey, buildGoodVibesSecretRef, isSecretReferenceValue } from './onboarding-wizard-helpers.ts';
|
|
19
19
|
import type { OnboardingWizardController } from './onboarding-wizard.ts';
|
|
20
20
|
|
|
21
21
|
export function buildOnboardingApplyRequest(controller: OnboardingWizardController): OnboardingApplyRequest {
|
|
@@ -203,7 +203,7 @@ function addCloudflareOperations(
|
|
|
203
203
|
setConfig('cloudflare.workerCron', controller.getStringFieldValue('cloudflare.worker-cron', config?.workerCron ?? '*/5 * * * *'));
|
|
204
204
|
setConfig('cloudflare.queueName', controller.getStringFieldValue('cloudflare.queue-name', config?.queueName ?? 'goodvibes-batch'));
|
|
205
205
|
setConfig('cloudflare.deadLetterQueueName', controller.getStringFieldValue('cloudflare.dead-letter-queue-name', config?.deadLetterQueueName ?? 'goodvibes-batch-dlq'));
|
|
206
|
-
setConfig('cloudflare.tunnelName', controller.getStringFieldValue('cloudflare.tunnel-name', config?.tunnelName ?? 'goodvibes-daemon'));
|
|
206
|
+
setConfig('cloudflare.tunnelName', controller.getStringFieldValue('cloudflare.tunnel-name', config?.tunnelName ?? 'goodvibes-agent-daemon'));
|
|
207
207
|
setConfig('cloudflare.tunnelId', controller.getStringFieldValue('cloudflare.tunnel-id', config?.tunnelId ?? ''));
|
|
208
208
|
setConfig('cloudflare.tunnelTokenRef', controller.getStringFieldValue('cloudflare.tunnel-token-ref', config?.tunnelTokenRef ?? ''));
|
|
209
209
|
setConfig('cloudflare.accessAppId', controller.getStringFieldValue('cloudflare.access-app-id', config?.accessAppId ?? ''));
|
|
@@ -222,56 +222,16 @@ function addCloudflareOperations(
|
|
|
222
222
|
}
|
|
223
223
|
|
|
224
224
|
export function addNetworkOperations(
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
225
|
+
_controller: OnboardingWizardController,
|
|
226
|
+
_operations: OnboardingApplyOperation[],
|
|
227
|
+
_customNetwork: boolean,
|
|
228
|
+
_enabled: {
|
|
229
|
+
readonly controlPlane: boolean;
|
|
230
|
+
readonly controlPlaneRemote: boolean;
|
|
231
|
+
readonly httpListener: boolean;
|
|
232
|
+
readonly web: boolean;
|
|
233
|
+
},
|
|
234
234
|
): void {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
value: unknown,
|
|
238
|
-
): void => {
|
|
239
|
-
operations.push({ kind: 'set-config', key, value });
|
|
240
|
-
};
|
|
241
|
-
const networkFacingEnabled = {
|
|
242
|
-
controlPlane: enabled.controlPlaneRemote,
|
|
243
|
-
httpListener: enabled.httpListener,
|
|
244
|
-
web: enabled.web,
|
|
245
|
-
};
|
|
246
|
-
const sharedIpDefault = controller.getSharedIpDefault(networkFacingEnabled);
|
|
247
|
-
const sharedIp = controller.getBooleanFieldValue('network.shared-ip', sharedIpDefault);
|
|
248
|
-
const sharedHost = controller.getStringFieldValue('network.shared-ip-address', controller.getSharedIpHostDefault(networkFacingEnabled)) || '0.0.0.0';
|
|
249
|
-
const controlPlaneHost = sharedIp
|
|
250
|
-
? sharedHost
|
|
251
|
-
: controller.getStringFieldValue('network.service-ip', controller.runtimeSnapshot?.bindSettings.controlPlane.host ?? '0.0.0.0');
|
|
252
|
-
const httpListenerHost = sharedIp
|
|
253
|
-
? sharedHost
|
|
254
|
-
: controller.getStringFieldValue('network.webhook-ip', controller.runtimeSnapshot?.bindSettings.httpListener.host ?? '0.0.0.0');
|
|
255
|
-
const webHost = sharedIp
|
|
256
|
-
? sharedHost
|
|
257
|
-
: controller.getStringFieldValue('network.browser-ip', controller.runtimeSnapshot?.bindSettings.web.host ?? '0.0.0.0');
|
|
258
|
-
|
|
259
|
-
if (enabled.controlPlane) {
|
|
260
|
-
setConfig('controlPlane.hostMode', enabled.controlPlaneRemote ? (customNetwork ? 'custom' : 'network') : 'local');
|
|
261
|
-
setConfig('controlPlane.host', enabled.controlPlaneRemote ? (customNetwork ? controlPlaneHost : '0.0.0.0') : '127.0.0.1');
|
|
262
|
-
setConfig('controlPlane.port', controller.getPortFieldValue('network.service-port', controller.runtimeSnapshot?.bindSettings.controlPlane.port ?? 3421));
|
|
263
|
-
setConfig('controlPlane.allowRemote', enabled.controlPlaneRemote && (customNetwork ? !isLoopbackAddress(controlPlaneHost) : true));
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
if (enabled.httpListener) {
|
|
267
|
-
setConfig('httpListener.hostMode', customNetwork ? 'custom' : 'network');
|
|
268
|
-
setConfig('httpListener.host', customNetwork ? httpListenerHost : '0.0.0.0');
|
|
269
|
-
setConfig('httpListener.port', controller.getPortFieldValue('network.webhook-port', controller.runtimeSnapshot?.bindSettings.httpListener.port ?? 3422));
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
if (enabled.web) {
|
|
273
|
-
setConfig('web.hostMode', customNetwork ? 'custom' : 'network');
|
|
274
|
-
setConfig('web.host', customNetwork ? webHost : '0.0.0.0');
|
|
275
|
-
setConfig('web.port', controller.getPortFieldValue('network.browser-port', controller.runtimeSnapshot?.bindSettings.web.port ?? 3423));
|
|
276
|
-
}
|
|
235
|
+
// Agent onboarding intentionally never mutates daemon/listener/web bind posture.
|
|
236
|
+
// Network fields are advisory for the external daemon owner.
|
|
277
237
|
}
|
|
@@ -26,7 +26,7 @@ export function buildCloudflareStep(controller: OnboardingWizardController): Onb
|
|
|
26
26
|
const bind = controller.runtimeSnapshot?.bindSettings.controlPlane;
|
|
27
27
|
const defaultDaemonBaseUrl = normalizeText(config?.daemonBaseUrl)
|
|
28
28
|
|| `http://${bind?.host && bind.host !== '0.0.0.0' && bind.host !== '::' ? bind.host : '127.0.0.1'}:${bind?.port ?? 3421}`;
|
|
29
|
-
const resultMessage = controller.textState.get('cloudflare.action-status') ?? 'No Cloudflare
|
|
29
|
+
const resultMessage = controller.textState.get('cloudflare.action-status') ?? 'No Cloudflare action has run in this wizard session.';
|
|
30
30
|
const fields: OnboardingWizardFieldDefinition[] = [
|
|
31
31
|
{
|
|
32
32
|
kind: 'checklist',
|
|
@@ -96,7 +96,7 @@ export function buildCloudflareStep(controller: OnboardingWizardController): Onb
|
|
|
96
96
|
kind: 'text',
|
|
97
97
|
id: 'cloudflare.bootstrap-env-name',
|
|
98
98
|
label: 'Bootstrap token environment variable',
|
|
99
|
-
hint: '
|
|
99
|
+
hint: 'Agent reads this environment variable once and passes the value to the SDK token-create route. It is not persisted.',
|
|
100
100
|
placeholder: 'GOODVIBES_CLOUDFLARE_BOOTSTRAP_TOKEN',
|
|
101
101
|
defaultValue: 'GOODVIBES_CLOUDFLARE_BOOTSTRAP_TOKEN',
|
|
102
102
|
});
|
|
@@ -227,8 +227,8 @@ export function buildCloudflareStep(controller: OnboardingWizardController): Onb
|
|
|
227
227
|
id: 'cloudflare.tunnel-name',
|
|
228
228
|
label: 'Tunnel name',
|
|
229
229
|
hint: 'Cloudflare Tunnel name to create or reuse.',
|
|
230
|
-
placeholder: 'goodvibes-daemon',
|
|
231
|
-
defaultValue: config?.tunnelName || 'goodvibes-daemon',
|
|
230
|
+
placeholder: 'goodvibes-agent-daemon',
|
|
231
|
+
defaultValue: config?.tunnelName || 'goodvibes-agent-daemon',
|
|
232
232
|
},
|
|
233
233
|
{
|
|
234
234
|
kind: 'text',
|
|
@@ -176,7 +176,7 @@ export function buildCloudflareProvisionRequest(controller: OnboardingWizardCont
|
|
|
176
176
|
daemonHostname: controller.getStringFieldValue('cloudflare.daemon-hostname', controller.runtimeSnapshot?.config.cloudflare.daemonHostname ?? ''),
|
|
177
177
|
queueName: controller.getStringFieldValue('cloudflare.queue-name', controller.runtimeSnapshot?.config.cloudflare.queueName ?? 'goodvibes-batch'),
|
|
178
178
|
deadLetterQueueName: controller.getStringFieldValue('cloudflare.dead-letter-queue-name', controller.runtimeSnapshot?.config.cloudflare.deadLetterQueueName ?? 'goodvibes-batch-dlq'),
|
|
179
|
-
tunnelName: controller.getStringFieldValue('cloudflare.tunnel-name', controller.runtimeSnapshot?.config.cloudflare.tunnelName ?? 'goodvibes-daemon'),
|
|
179
|
+
tunnelName: controller.getStringFieldValue('cloudflare.tunnel-name', controller.runtimeSnapshot?.config.cloudflare.tunnelName ?? 'goodvibes-agent-daemon'),
|
|
180
180
|
tunnelId: controller.getStringFieldValue('cloudflare.tunnel-id', controller.runtimeSnapshot?.config.cloudflare.tunnelId ?? ''),
|
|
181
181
|
tunnelServiceUrl: controller.getStringFieldValue('cloudflare.tunnel-service-url', ''),
|
|
182
182
|
tunnelTokenRef: controller.getStringFieldValue('cloudflare.tunnel-token-ref', controller.runtimeSnapshot?.config.cloudflare.tunnelTokenRef ?? ''),
|