@pellux/goodvibes-agent 0.1.8 → 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 +22 -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 +43 -21
- package/src/cli/help.ts +17 -4
- 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-indicator.ts +7 -6
- 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,28 @@
|
|
|
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
|
+
|
|
19
|
+
## 0.1.9 - 2026-05-31
|
|
20
|
+
|
|
21
|
+
- 75e5d4a Align shell surface delegation test
|
|
22
|
+
- a24c581 Use delegation wording in runtime indicator
|
|
23
|
+
- 259a75f Guard Agent knowledge isolation
|
|
24
|
+
- 59b6729 Align task help with Agent policy
|
|
25
|
+
- 0074a76 Classify stale daemon knowledge routes
|
|
26
|
+
|
|
5
27
|
## 0.1.8 - 2026-05-31
|
|
6
28
|
|
|
7
29
|
- 384c85a Remove stale WRFC artifact 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';
|
|
@@ -17,10 +17,12 @@ interface AgentDaemonConnection {
|
|
|
17
17
|
|
|
18
18
|
interface AgentKnowledgeFailure {
|
|
19
19
|
readonly ok: false;
|
|
20
|
-
readonly kind: 'daemon_unavailable' | 'auth_required' | 'daemon_route_unavailable' | 'daemon_error';
|
|
20
|
+
readonly kind: 'daemon_unavailable' | 'auth_required' | 'version_mismatch' | 'daemon_route_unavailable' | 'daemon_error';
|
|
21
21
|
readonly error: string;
|
|
22
22
|
readonly baseUrl: string;
|
|
23
23
|
readonly route: string;
|
|
24
|
+
readonly daemonVersion?: string;
|
|
25
|
+
readonly expectedSdkVersion?: string;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
interface AgentKnowledgeSuccess<TData> {
|
|
@@ -147,24 +149,8 @@ function hasFlag(args: readonly string[], flag: string): boolean {
|
|
|
147
149
|
return args.includes(flag);
|
|
148
150
|
}
|
|
149
151
|
|
|
150
|
-
function packageJsonPath(): string {
|
|
151
|
-
return join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'package.json');
|
|
152
|
-
}
|
|
153
|
-
|
|
154
152
|
function readPackageMetadata(): { readonly version: string; readonly sdkVersion: string } {
|
|
155
|
-
|
|
156
|
-
const parsed = JSON.parse(readFileSync(packageJsonPath(), 'utf-8')) as unknown;
|
|
157
|
-
if (!isRecord(parsed)) return { version: 'unknown', sdkVersion: 'unknown' };
|
|
158
|
-
const dependencies = isRecord(parsed.dependencies) ? parsed.dependencies : {};
|
|
159
|
-
return {
|
|
160
|
-
version: typeof parsed.version === 'string' ? parsed.version : 'unknown',
|
|
161
|
-
sdkVersion: typeof dependencies['@pellux/goodvibes-sdk'] === 'string'
|
|
162
|
-
? dependencies['@pellux/goodvibes-sdk']
|
|
163
|
-
: 'unknown',
|
|
164
|
-
};
|
|
165
|
-
} catch {
|
|
166
|
-
return { version: 'unknown', sdkVersion: 'unknown' };
|
|
167
|
-
}
|
|
153
|
+
return { version: VERSION, sdkVersion: SDK_VERSION };
|
|
168
154
|
}
|
|
169
155
|
|
|
170
156
|
function resolveDaemonConnection(runtime: CliCommandRuntime): AgentDaemonConnection {
|
|
@@ -200,13 +186,28 @@ async function fetchDaemonStatus(connection: AgentDaemonConnection): Promise<{ r
|
|
|
200
186
|
}
|
|
201
187
|
}
|
|
202
188
|
|
|
203
|
-
function classifyKnowledgeError(error: unknown, connection: AgentDaemonConnection, route: string): AgentKnowledgeFailure {
|
|
189
|
+
async function classifyKnowledgeError(error: unknown, connection: AgentDaemonConnection, route: string): Promise<AgentKnowledgeFailure> {
|
|
204
190
|
const message = summarizeError(error);
|
|
205
191
|
const lower = message.toLowerCase();
|
|
206
192
|
if (lower.includes('401') || lower.includes('unauthorized') || lower.includes('auth')) {
|
|
207
193
|
return { ok: false, kind: 'auth_required', error: message, baseUrl: connection.baseUrl, route };
|
|
208
194
|
}
|
|
209
195
|
if (lower.includes('404') || lower.includes('not found')) {
|
|
196
|
+
const metadata = readPackageMetadata();
|
|
197
|
+
const daemon = await fetchDaemonStatus(connection);
|
|
198
|
+
const daemonRecord = isRecord(daemon.body) ? daemon.body : {};
|
|
199
|
+
const daemonVersion = readString(daemonRecord, 'version') ?? 'unknown';
|
|
200
|
+
if (daemon.ok && daemonVersion !== metadata.sdkVersion) {
|
|
201
|
+
return {
|
|
202
|
+
ok: false,
|
|
203
|
+
kind: 'version_mismatch',
|
|
204
|
+
error: `External daemon SDK version ${daemonVersion} does not match Agent SDK pin ${metadata.sdkVersion}; Agent Knowledge route is unavailable.`,
|
|
205
|
+
baseUrl: connection.baseUrl,
|
|
206
|
+
route,
|
|
207
|
+
daemonVersion,
|
|
208
|
+
expectedSdkVersion: metadata.sdkVersion,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
210
211
|
return { ok: false, kind: 'daemon_route_unavailable', error: message, baseUrl: connection.baseUrl, route };
|
|
211
212
|
}
|
|
212
213
|
if (lower.includes('fetch') || lower.includes('connect') || lower.includes('econnrefused')) {
|
|
@@ -407,6 +408,12 @@ function formatFailure(failure: AgentKnowledgeFailure, json: boolean): string {
|
|
|
407
408
|
` ${failure.error}`,
|
|
408
409
|
` daemon: ${failure.baseUrl}`,
|
|
409
410
|
` route: ${failure.route}`,
|
|
411
|
+
failure.kind === 'version_mismatch' && failure.daemonVersion && failure.expectedSdkVersion
|
|
412
|
+
? ` versions: daemon=${failure.daemonVersion} expected=${failure.expectedSdkVersion}`
|
|
413
|
+
: null,
|
|
414
|
+
failure.kind === 'version_mismatch'
|
|
415
|
+
? ' next: update/restart the external GoodVibes daemon so /status matches the Agent SDK pin.'
|
|
416
|
+
: null,
|
|
410
417
|
failure.kind === 'daemon_route_unavailable'
|
|
411
418
|
? ' next: update/restart the external GoodVibes daemon to the SDK version required by this Agent package.'
|
|
412
419
|
: null,
|
|
@@ -530,6 +537,21 @@ export async function handleAgentKnowledgeCommand(runtime: CliCommandRuntime): P
|
|
|
530
537
|
};
|
|
531
538
|
}
|
|
532
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
|
+
|
|
533
555
|
export async function handleCompatCommand(runtime: CliCommandRuntime): Promise<CliCommandOutput> {
|
|
534
556
|
const connection = resolveDaemonConnection(runtime);
|
|
535
557
|
const metadata = readPackageMetadata();
|
package/src/cli/help.ts
CHANGED
|
@@ -41,11 +41,12 @@ 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',
|
|
47
48
|
' sessions List, show, export, or resume saved sessions',
|
|
48
|
-
' tasks List/show in-process tasks
|
|
49
|
+
' tasks List/show in-process runtime tasks (read-only)',
|
|
49
50
|
' pair|qrcode Print companion pairing payload and QR code',
|
|
50
51
|
' surfaces Inspect/check browser/listener/external surfaces (read-only)',
|
|
51
52
|
' listener test Test HTTP listener/webhook readiness',
|
|
@@ -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.',
|
|
@@ -198,9 +211,9 @@ const COMMAND_HELP: Record<string, CommandHelp> = {
|
|
|
198
211
|
examples: ['sessions list', 'sessions show latest-session', 'sessions export abc123 session.json'],
|
|
199
212
|
},
|
|
200
213
|
tasks: {
|
|
201
|
-
usage: ['tasks list', 'tasks show <taskId>'
|
|
202
|
-
summary: 'Inspect runtime tasks
|
|
203
|
-
examples: ['tasks list', 'tasks
|
|
214
|
+
usage: ['tasks list', 'tasks show <taskId>'],
|
|
215
|
+
summary: 'Inspect in-process runtime tasks. Agent blocks copied task submission; use run for one-shot work or delegate for explicit build/fix/review handoff.',
|
|
216
|
+
examples: ['tasks list', 'tasks show task-123', 'run "check provider readiness"', 'delegate "fix the failing tests"'],
|
|
204
217
|
},
|
|
205
218
|
surfaces: {
|
|
206
219
|
usage: ['surfaces [list]', 'surfaces check', 'surfaces show <surfaceId>'],
|
|
@@ -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
|
}
|