@pellux/goodvibes-agent 0.1.40 → 0.1.41
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 +4 -0
- package/README.md +11 -1
- package/docs/operator-capability-benchmark.md +6 -0
- package/package.json +1 -1
- package/src/cli/capabilities-command.ts +39 -4
- package/src/cli/help.ts +5 -4
- package/src/input/commands/capabilities-runtime.ts +23 -3
- package/src/operator/capability-benchmark.ts +1 -0
- package/src/operator/daemon-capability-audit.ts +554 -0
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@ bun add -g @pellux/goodvibes-agent
|
|
|
16
16
|
goodvibes-agent --help
|
|
17
17
|
goodvibes-agent status
|
|
18
18
|
goodvibes-agent capabilities
|
|
19
|
+
goodvibes-agent capabilities daemon
|
|
19
20
|
```
|
|
20
21
|
|
|
21
22
|
If Bun reports untrusted lifecycle dependencies, trust only the package and dependencies required by this package:
|
|
@@ -44,7 +45,7 @@ bun run publish:check
|
|
|
44
45
|
|
|
45
46
|
Inside the Agent TUI, use `/agent`, `/home`, or `/operator` to open the operator workspace. It is the Agent-first fullscreen surface for setup, status, knowledge, local memory/skills, work-plan/approval review, automation observability, and explicit build delegation to GoodVibes TUI.
|
|
46
47
|
|
|
47
|
-
Use `goodvibes-agent capabilities` or `/capabilities` to inspect the OpenClaw/Hermes benchmark, current Agent posture, configuration commands, usage paths, and remaining gaps.
|
|
48
|
+
Use `goodvibes-agent capabilities` or `/capabilities` to inspect the OpenClaw/Hermes benchmark, current Agent posture, configuration commands, usage paths, and remaining gaps. Use `goodvibes-agent capabilities daemon` or `/capabilities daemon` for a live read-only audit of the GoodVibes daemon method catalog and isolated Agent Knowledge route coverage.
|
|
48
49
|
|
|
49
50
|
Inside the workspace, use `/agent-profile guide` to author custom profile starters without leaving the Agent TUI. The guided flow lists starters, exports starter JSON, imports edited local starters, and creates isolated runtime profiles from them.
|
|
50
51
|
|
|
@@ -81,6 +82,15 @@ Starting a routine records local usage and prints its steps; it does not spawn b
|
|
|
81
82
|
|
|
82
83
|
Start or restart the daemon from GoodVibes TUI or the daemon host before launching Agent. Agent status and companion/knowledge routes connect to that external daemon, normally on `http://127.0.0.1:3421`.
|
|
83
84
|
|
|
85
|
+
To verify what the running daemon can expose for the Agent/OpenClaw/Hermes capability benchmark:
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
goodvibes-agent capabilities daemon
|
|
89
|
+
goodvibes-agent capabilities daemon --json
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
This audit checks `/api/control-plane/methods` and `/api/goodvibes-agent/knowledge/status`. It does not query default Knowledge/Wiki or HomeGraph.
|
|
93
|
+
|
|
84
94
|
Agent intentionally blocks daemon lifecycle commands:
|
|
85
95
|
|
|
86
96
|
```sh
|
|
@@ -15,6 +15,9 @@ Use the live benchmark from the package:
|
|
|
15
15
|
goodvibes-agent capabilities
|
|
16
16
|
goodvibes-agent capabilities --json
|
|
17
17
|
goodvibes-agent capabilities hermes
|
|
18
|
+
goodvibes-agent capabilities daemon
|
|
19
|
+
goodvibes-agent capabilities daemon --json
|
|
20
|
+
goodvibes-agent capabilities daemon knowledge
|
|
18
21
|
```
|
|
19
22
|
|
|
20
23
|
Inside the TUI:
|
|
@@ -23,6 +26,7 @@ Inside the TUI:
|
|
|
23
26
|
/capabilities
|
|
24
27
|
/capabilities openclaw
|
|
25
28
|
/capabilities knowledge
|
|
29
|
+
/capabilities daemon
|
|
26
30
|
```
|
|
27
31
|
|
|
28
32
|
## Research Baseline
|
|
@@ -52,6 +56,8 @@ The benchmark measures two different GoodVibes layers:
|
|
|
52
56
|
|
|
53
57
|
If the daemon already has a route but Agent lacks a good setup/workspace/CLI surface, the gap is treated as an Agent product gap rather than a missing platform capability.
|
|
54
58
|
|
|
59
|
+
Use `goodvibes-agent capabilities daemon` for the live read-only daemon audit. It checks the public control-plane method catalog and the isolated Agent Knowledge status route. It intentionally does not call default `/api/knowledge/*`, HomeGraph, or Home Assistant routes.
|
|
60
|
+
|
|
55
61
|
## Capability Targets
|
|
56
62
|
|
|
57
63
|
| Area | OpenClaw/Hermes Baseline | GoodVibes Agent Position |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.41",
|
|
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",
|
|
@@ -5,16 +5,51 @@ import {
|
|
|
5
5
|
filterOperatorCapabilities,
|
|
6
6
|
renderOperatorCapabilityBenchmark,
|
|
7
7
|
} from '../operator/capability-benchmark.ts';
|
|
8
|
+
import {
|
|
9
|
+
fetchLiveDaemonCapabilityAudit,
|
|
10
|
+
filterDaemonCapabilityAuditAreas,
|
|
11
|
+
renderDaemonCapabilityAudit,
|
|
12
|
+
renderDaemonCapabilityFailure,
|
|
13
|
+
} from '../operator/daemon-capability-audit.ts';
|
|
14
|
+
import { resolveAgentDaemonConnection } from '../agent/routine-schedule-promotion.ts';
|
|
15
|
+
|
|
16
|
+
interface CapabilityCommandArgs {
|
|
17
|
+
readonly mode: 'benchmark' | 'daemon';
|
|
18
|
+
readonly query: string | undefined;
|
|
19
|
+
}
|
|
8
20
|
|
|
9
|
-
function
|
|
21
|
+
function readCapabilityArgs(args: readonly string[]): CapabilityCommandArgs {
|
|
10
22
|
const values = args.filter((arg) => !arg.startsWith('--'));
|
|
11
|
-
|
|
23
|
+
if (values[0] === 'daemon') {
|
|
24
|
+
const query = values.slice(1).join(' ').trim();
|
|
25
|
+
return { mode: 'daemon', query: query.length > 0 ? query : undefined };
|
|
26
|
+
}
|
|
27
|
+
return { mode: 'benchmark', query: values.length > 0 ? values.join(' ') : undefined };
|
|
12
28
|
}
|
|
13
29
|
|
|
14
30
|
export async function handleCapabilitiesCommand(runtime: CliCommandRuntime): Promise<CliCommandOutput> {
|
|
15
|
-
const
|
|
31
|
+
const args = readCapabilityArgs(runtime.cli.commandArgs);
|
|
32
|
+
if (args.mode === 'daemon') {
|
|
33
|
+
const connection = resolveAgentDaemonConnection(runtime.configManager, runtime.homeDirectory);
|
|
34
|
+
const audit = await fetchLiveDaemonCapabilityAudit(connection);
|
|
35
|
+
if (!audit.ok) {
|
|
36
|
+
return {
|
|
37
|
+
output: runtime.cli.flags.outputFormat === 'json'
|
|
38
|
+
? JSON.stringify(audit, null, 2)
|
|
39
|
+
: renderDaemonCapabilityFailure(audit),
|
|
40
|
+
exitCode: audit.kind === 'auth_required' || audit.kind === 'daemon_unavailable' ? 1 : 2,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const areas = filterDaemonCapabilityAuditAreas(audit.areas, args.query);
|
|
44
|
+
return {
|
|
45
|
+
output: runtime.cli.flags.outputFormat === 'json'
|
|
46
|
+
? JSON.stringify({ ...audit, areas }, null, 2)
|
|
47
|
+
: renderDaemonCapabilityAudit(audit, areas),
|
|
48
|
+
exitCode: 0,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
16
51
|
const report = buildOperatorCapabilityBenchmarkReport();
|
|
17
|
-
const capabilities = filterOperatorCapabilities(report.capabilities, query);
|
|
52
|
+
const capabilities = filterOperatorCapabilities(report.capabilities, args.query);
|
|
18
53
|
if (runtime.cli.flags.outputFormat === 'json') {
|
|
19
54
|
return {
|
|
20
55
|
output: JSON.stringify({ ...report, capabilities }, null, 2),
|
package/src/cli/help.ts
CHANGED
|
@@ -42,7 +42,7 @@ export function renderGoodVibesHelp(binary = 'goodvibes-agent'): string {
|
|
|
42
42
|
' routines Inspect local routines and explicitly promote one to a daemon schedule',
|
|
43
43
|
' auth Inspect and manage local users, sessions, and bootstrap auth',
|
|
44
44
|
' compat Inspect Agent SDK pin, daemon version, and Agent knowledge route readiness',
|
|
45
|
-
' capabilities Show OpenClaw/Hermes
|
|
45
|
+
' capabilities Show OpenClaw/Hermes benchmark and live daemon coverage',
|
|
46
46
|
' knowledge Use isolated Agent Knowledge/Wiki routes',
|
|
47
47
|
' ask|search Shortcuts for isolated Agent Knowledge ask/search',
|
|
48
48
|
' delegate Explicitly delegate build/fix/review work to GoodVibes TUI',
|
|
@@ -102,6 +102,7 @@ export function renderGoodVibesHelp(binary = 'goodvibes-agent'): string {
|
|
|
102
102
|
` ${binary} routines promote daily-operations-sweep --cron "0 9 * * *" --timezone America/Chicago --yes`,
|
|
103
103
|
` ${binary} compat`,
|
|
104
104
|
` ${binary} capabilities`,
|
|
105
|
+
` ${binary} capabilities daemon`,
|
|
105
106
|
` ${binary} knowledge status`,
|
|
106
107
|
` ${binary} knowledge ask "What is GoodVibes Agent?"`,
|
|
107
108
|
` ${binary} ask "What is GoodVibes Agent?"`,
|
|
@@ -196,9 +197,9 @@ const COMMAND_HELP: Record<string, CommandHelp> = {
|
|
|
196
197
|
examples: ['compat', 'compat --json'],
|
|
197
198
|
},
|
|
198
199
|
capabilities: {
|
|
199
|
-
usage: ['capabilities [openclaw|hermes|query]', 'capabilities --json'],
|
|
200
|
-
summary: 'Show the OpenClaw/Hermes capability benchmark, Agent readiness,
|
|
201
|
-
examples: ['capabilities', 'capabilities hermes', 'capabilities knowledge --json'],
|
|
200
|
+
usage: ['capabilities [openclaw|hermes|query]', 'capabilities daemon [query]', 'capabilities --json'],
|
|
201
|
+
summary: 'Show the OpenClaw/Hermes capability benchmark, Agent readiness, and live GoodVibes daemon method coverage.',
|
|
202
|
+
examples: ['capabilities', 'capabilities hermes', 'capabilities daemon', 'capabilities daemon knowledge --json'],
|
|
202
203
|
},
|
|
203
204
|
knowledge: {
|
|
204
205
|
usage: [
|
|
@@ -4,14 +4,34 @@ import {
|
|
|
4
4
|
renderOperatorCapabilityBenchmark,
|
|
5
5
|
OPERATOR_CAPABILITY_BENCHMARKS,
|
|
6
6
|
} from '../../operator/capability-benchmark.ts';
|
|
7
|
+
import {
|
|
8
|
+
fetchLiveDaemonCapabilityAudit,
|
|
9
|
+
filterDaemonCapabilityAuditAreas,
|
|
10
|
+
renderDaemonCapabilityAudit,
|
|
11
|
+
renderDaemonCapabilityFailure,
|
|
12
|
+
} from '../../operator/daemon-capability-audit.ts';
|
|
13
|
+
import { resolveAgentDaemonConnection } from '../../agent/routine-schedule-promotion.ts';
|
|
7
14
|
|
|
8
15
|
export function registerCapabilitiesRuntimeCommands(registry: CommandRegistry): void {
|
|
9
16
|
registry.register({
|
|
10
17
|
name: 'capabilities',
|
|
11
18
|
aliases: ['caps', 'benchmark'],
|
|
12
|
-
description: 'Show the OpenClaw/Hermes capability benchmark and
|
|
13
|
-
usage: '[openclaw|hermes|query]',
|
|
14
|
-
handler(args, ctx) {
|
|
19
|
+
description: 'Show the OpenClaw/Hermes capability benchmark, Agent readiness, and live daemon coverage',
|
|
20
|
+
usage: '[daemon|openclaw|hermes|query]',
|
|
21
|
+
async handler(args, ctx) {
|
|
22
|
+
if (args[0] === 'daemon') {
|
|
23
|
+
const homeDirectory = ctx.platform.configManager.getHomeDirectory() ?? process.cwd();
|
|
24
|
+
const connection = resolveAgentDaemonConnection(ctx.platform.configManager, homeDirectory);
|
|
25
|
+
const audit = await fetchLiveDaemonCapabilityAudit(connection);
|
|
26
|
+
if (!audit.ok) {
|
|
27
|
+
ctx.print(renderDaemonCapabilityFailure(audit));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const query = args.slice(1).join(' ').trim() || undefined;
|
|
31
|
+
const areas = filterDaemonCapabilityAuditAreas(audit.areas, query);
|
|
32
|
+
ctx.print(renderDaemonCapabilityAudit(audit, areas));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
15
35
|
const query = args.join(' ').trim() || undefined;
|
|
16
36
|
const capabilities = filterOperatorCapabilities(OPERATOR_CAPABILITY_BENCHMARKS, query);
|
|
17
37
|
ctx.print(renderOperatorCapabilityBenchmark(capabilities));
|
|
@@ -211,6 +211,7 @@ export function filterOperatorCapabilities(
|
|
|
211
211
|
if (capability.posture.includes(normalized)) return true;
|
|
212
212
|
if (capability.competitors.some((competitor) => competitor === normalized)) return true;
|
|
213
213
|
return capability.goodvibesAgent.toLowerCase().includes(normalized)
|
|
214
|
+
|| capability.goodvibesDaemon?.toLowerCase().includes(normalized) === true
|
|
214
215
|
|| capability.competitorBaseline.toLowerCase().includes(normalized);
|
|
215
216
|
});
|
|
216
217
|
}
|
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
2
|
+
import { SDK_VERSION } from '../version.ts';
|
|
3
|
+
import type { AgentDaemonConnection } from '../agent/routine-schedule-promotion.ts';
|
|
4
|
+
|
|
5
|
+
export const DAEMON_METHOD_CATALOG_ROUTE = '/api/control-plane/methods';
|
|
6
|
+
export const AGENT_KNOWLEDGE_STATUS_ROUTE = '/api/goodvibes-agent/knowledge/status';
|
|
7
|
+
export const DAEMON_STATUS_ROUTE = '/status';
|
|
8
|
+
|
|
9
|
+
export type DaemonCapabilityAuditFailureKind =
|
|
10
|
+
| 'auth_required'
|
|
11
|
+
| 'daemon_unavailable'
|
|
12
|
+
| 'version_mismatch'
|
|
13
|
+
| 'daemon_route_unavailable'
|
|
14
|
+
| 'daemon_error';
|
|
15
|
+
|
|
16
|
+
export type DaemonCapabilityCoverage = 'ready' | 'partial' | 'missing';
|
|
17
|
+
export type DaemonCapabilityRouteCoverage = 'ready' | 'missing' | 'not_checked';
|
|
18
|
+
|
|
19
|
+
export interface DaemonCapabilityRequirement {
|
|
20
|
+
readonly id: string;
|
|
21
|
+
readonly title: string;
|
|
22
|
+
readonly competitorBaseline: string;
|
|
23
|
+
readonly agentUse: string;
|
|
24
|
+
readonly requiredMethodIds: readonly string[];
|
|
25
|
+
readonly optionalMethodIds: readonly string[];
|
|
26
|
+
readonly requiredAgentRoutes: readonly string[];
|
|
27
|
+
readonly next: readonly string[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface DaemonCapabilityAuditArea {
|
|
31
|
+
readonly id: string;
|
|
32
|
+
readonly title: string;
|
|
33
|
+
readonly coverage: DaemonCapabilityCoverage;
|
|
34
|
+
readonly competitorBaseline: string;
|
|
35
|
+
readonly agentUse: string;
|
|
36
|
+
readonly presentRequiredMethodIds: readonly string[];
|
|
37
|
+
readonly missingRequiredMethodIds: readonly string[];
|
|
38
|
+
readonly presentOptionalMethodIds: readonly string[];
|
|
39
|
+
readonly missingOptionalMethodIds: readonly string[];
|
|
40
|
+
readonly agentRoutes: readonly {
|
|
41
|
+
readonly route: string;
|
|
42
|
+
readonly coverage: DaemonCapabilityRouteCoverage;
|
|
43
|
+
}[];
|
|
44
|
+
readonly next: readonly string[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface DaemonCapabilityAuditSuccess {
|
|
48
|
+
readonly ok: true;
|
|
49
|
+
readonly kind: 'daemon.capabilities.audit';
|
|
50
|
+
readonly baseUrl: string;
|
|
51
|
+
readonly daemonVersion: string;
|
|
52
|
+
readonly expectedSdkVersion: string;
|
|
53
|
+
readonly daemonCompatible: boolean;
|
|
54
|
+
readonly methodCatalogRoute: typeof DAEMON_METHOD_CATALOG_ROUTE;
|
|
55
|
+
readonly methodCount: number;
|
|
56
|
+
readonly agentKnowledgeRoute: typeof AGENT_KNOWLEDGE_STATUS_ROUTE;
|
|
57
|
+
readonly agentKnowledgeRouteReady: boolean;
|
|
58
|
+
readonly defaultKnowledgeFallback: false;
|
|
59
|
+
readonly homeGraphFallback: false;
|
|
60
|
+
readonly warnings: readonly string[];
|
|
61
|
+
readonly areas: readonly DaemonCapabilityAuditArea[];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface DaemonCapabilityAuditFailure {
|
|
65
|
+
readonly ok: false;
|
|
66
|
+
readonly kind: DaemonCapabilityAuditFailureKind;
|
|
67
|
+
readonly error: string;
|
|
68
|
+
readonly baseUrl: string;
|
|
69
|
+
readonly route: string;
|
|
70
|
+
readonly daemonVersion?: string;
|
|
71
|
+
readonly expectedSdkVersion?: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type DaemonCapabilityAuditResult =
|
|
75
|
+
| DaemonCapabilityAuditSuccess
|
|
76
|
+
| DaemonCapabilityAuditFailure;
|
|
77
|
+
|
|
78
|
+
interface DaemonMethodSummary {
|
|
79
|
+
readonly id: string;
|
|
80
|
+
readonly title?: string;
|
|
81
|
+
readonly category?: string;
|
|
82
|
+
readonly invokable?: boolean;
|
|
83
|
+
readonly access?: string;
|
|
84
|
+
readonly http?: {
|
|
85
|
+
readonly method?: string;
|
|
86
|
+
readonly path?: string;
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface FetchJsonResult {
|
|
91
|
+
readonly ok: boolean;
|
|
92
|
+
readonly status: number;
|
|
93
|
+
readonly statusText: string;
|
|
94
|
+
readonly body: unknown;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export const DAEMON_CAPABILITY_REQUIREMENTS: readonly DaemonCapabilityRequirement[] = [
|
|
98
|
+
{
|
|
99
|
+
id: 'gateway-control',
|
|
100
|
+
title: 'Gateway Control Plane',
|
|
101
|
+
competitorBaseline: 'OpenClaw/Hermes expose an always-on gateway with health, auth, method discovery, and events.',
|
|
102
|
+
agentUse: 'Agent connects to the existing GoodVibes daemon, inspects posture, and never starts or owns daemon lifecycle.',
|
|
103
|
+
requiredMethodIds: [
|
|
104
|
+
'control.status',
|
|
105
|
+
'control.auth.current',
|
|
106
|
+
'control.methods.list',
|
|
107
|
+
'control.contract',
|
|
108
|
+
'control.snapshot',
|
|
109
|
+
],
|
|
110
|
+
optionalMethodIds: ['control.clients.list', 'control.events.catalog', 'control.events.stream', 'control.web'],
|
|
111
|
+
requiredAgentRoutes: [],
|
|
112
|
+
next: ['Add richer first-run guidance when auth or route contract checks fail.'],
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
id: 'chat-sessions',
|
|
116
|
+
title: 'Companion Chat And Shared Sessions',
|
|
117
|
+
competitorBaseline: 'Personal agents keep persistent chat and can route larger work through task/session surfaces.',
|
|
118
|
+
agentUse: 'Agent uses companion.chat for normal turns and shared sessions only for explicit TUI build/fix/review delegation.',
|
|
119
|
+
requiredMethodIds: [
|
|
120
|
+
'companion.chat.sessions.create',
|
|
121
|
+
'companion.chat.sessions.get',
|
|
122
|
+
'companion.chat.sessions.list',
|
|
123
|
+
'companion.chat.messages.create',
|
|
124
|
+
'companion.chat.messages.list',
|
|
125
|
+
'sessions.create',
|
|
126
|
+
'sessions.messages.create',
|
|
127
|
+
'sessions.list',
|
|
128
|
+
],
|
|
129
|
+
optionalMethodIds: ['companion.chat.events.stream', 'sessions.followUp', 'sessions.steer', 'sessions.integration.snapshot'],
|
|
130
|
+
requiredAgentRoutes: [],
|
|
131
|
+
next: ['Expose delegated session artifacts and status in the operator workspace without making WRFC default.'],
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
id: 'channels',
|
|
135
|
+
title: 'Channels And Delivery Gateway',
|
|
136
|
+
competitorBaseline: 'Gateway products receive and send across Slack, Discord, webhooks, mobile, and companion surfaces.',
|
|
137
|
+
agentUse: 'Agent inspects channel readiness and uses explicit delivery targets for scheduled routine promotion.',
|
|
138
|
+
requiredMethodIds: [
|
|
139
|
+
'channels.status',
|
|
140
|
+
'channels.capabilities.list',
|
|
141
|
+
'channels.accounts.list',
|
|
142
|
+
'channels.setup.get',
|
|
143
|
+
'channels.doctor.get',
|
|
144
|
+
'channels.actions.list',
|
|
145
|
+
'channels.tools.list',
|
|
146
|
+
'channels.targets.resolve',
|
|
147
|
+
'channels.policies.list',
|
|
148
|
+
],
|
|
149
|
+
optionalMethodIds: [
|
|
150
|
+
'channels.directory.query',
|
|
151
|
+
'channels.allowlist.resolve',
|
|
152
|
+
'channels.policies.audit',
|
|
153
|
+
'channels.agent_tools.list',
|
|
154
|
+
'channels.authorize',
|
|
155
|
+
],
|
|
156
|
+
requiredAgentRoutes: [],
|
|
157
|
+
next: ['Surface live per-account delivery errors and setup repairs in the Agent workspace.'],
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
id: 'agent-knowledge',
|
|
161
|
+
title: 'Isolated Agent Knowledge',
|
|
162
|
+
competitorBaseline: 'Persistent knowledge and memory are core personal-operator features.',
|
|
163
|
+
agentUse: 'Agent Knowledge is a separate product segment under /api/goodvibes-agent/knowledge/* with no default wiki or HomeGraph fallback.',
|
|
164
|
+
requiredMethodIds: [],
|
|
165
|
+
optionalMethodIds: [],
|
|
166
|
+
requiredAgentRoutes: [AGENT_KNOWLEDGE_STATUS_ROUTE],
|
|
167
|
+
next: ['Add artifact and multimodal ingestion UX only against the isolated Agent Knowledge route family.'],
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
id: 'automation-schedules',
|
|
171
|
+
title: 'Automation, Schedules, Runs, And Capacity',
|
|
172
|
+
competitorBaseline: 'Hermes/OpenClaw-style operators can schedule, run, pause, resume, and inspect recurring work.',
|
|
173
|
+
agentUse: 'Agent observes automation and promotes local routines to daemon schedules only through explicit confirmed commands.',
|
|
174
|
+
requiredMethodIds: [
|
|
175
|
+
'automation.integration.snapshot',
|
|
176
|
+
'automation.jobs.list',
|
|
177
|
+
'automation.runs.list',
|
|
178
|
+
'automation.heartbeat.list',
|
|
179
|
+
'schedules.list',
|
|
180
|
+
'schedules.create',
|
|
181
|
+
'scheduler.capacity',
|
|
182
|
+
],
|
|
183
|
+
optionalMethodIds: [
|
|
184
|
+
'automation.jobs.run',
|
|
185
|
+
'automation.jobs.pause',
|
|
186
|
+
'automation.jobs.resume',
|
|
187
|
+
'automation.runs.cancel',
|
|
188
|
+
'automation.runs.retry',
|
|
189
|
+
'schedules.run',
|
|
190
|
+
'schedules.enable',
|
|
191
|
+
'schedules.disable',
|
|
192
|
+
'schedules.delete',
|
|
193
|
+
],
|
|
194
|
+
requiredAgentRoutes: [],
|
|
195
|
+
next: ['Add live delivery/run history and failed delivery diagnostics for promoted Agent routines.'],
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
id: 'approvals-security',
|
|
199
|
+
title: 'Approvals, Policy, And Channel Safety',
|
|
200
|
+
competitorBaseline: 'Exposed agents need approval gates, pairing, allowlists, and policy inspection.',
|
|
201
|
+
agentUse: 'Agent keeps destructive or external effects behind exact commands plus confirmation and uses daemon approvals.',
|
|
202
|
+
requiredMethodIds: [
|
|
203
|
+
'approvals.list',
|
|
204
|
+
'approvals.approve',
|
|
205
|
+
'approvals.deny',
|
|
206
|
+
'approvals.cancel',
|
|
207
|
+
'channels.policies.list',
|
|
208
|
+
'channels.policies.audit',
|
|
209
|
+
],
|
|
210
|
+
optionalMethodIds: ['approvals.claim', 'channels.allowlist.edit', 'channels.allowlist.resolve'],
|
|
211
|
+
requiredAgentRoutes: [],
|
|
212
|
+
next: ['Build a route-risk-aware approval center in the fullscreen Agent workspace.'],
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
id: 'mcp-tools-artifacts',
|
|
216
|
+
title: 'MCP, Tools, Artifacts, And Web Search',
|
|
217
|
+
competitorBaseline: 'Modern personal operators expose managed tools, MCP servers, artifacts, and web/research tools.',
|
|
218
|
+
agentUse: 'Agent uses GoodVibes daemon tool surfaces through public SDK contracts and policy-gated model visibility.',
|
|
219
|
+
requiredMethodIds: [
|
|
220
|
+
'mcp.config.get',
|
|
221
|
+
'mcp.servers.list',
|
|
222
|
+
'mcp.tools.list',
|
|
223
|
+
'artifacts.create',
|
|
224
|
+
'artifacts.get',
|
|
225
|
+
'artifacts.list',
|
|
226
|
+
'web_search.providers.list',
|
|
227
|
+
'web_search.query',
|
|
228
|
+
],
|
|
229
|
+
optionalMethodIds: ['artifacts.content.get', 'mcp.config.reload'],
|
|
230
|
+
requiredAgentRoutes: [],
|
|
231
|
+
next: ['Add per-turn tool-palette narrowing so broad tool capability does not create noisy model schemas.'],
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
id: 'voice-media-nodes',
|
|
235
|
+
title: 'Voice, Media, Multimodal, And Remote Nodes',
|
|
236
|
+
competitorBaseline: 'OpenClaw/Hermes expose voice, media, mobile/node, and multimodal surfaces.',
|
|
237
|
+
agentUse: 'Agent inspects daemon voice/media/remote readiness and keeps execution explicit or read-only until user-selected.',
|
|
238
|
+
requiredMethodIds: [
|
|
239
|
+
'voice.status',
|
|
240
|
+
'voice.providers.list',
|
|
241
|
+
'voice.voices.list',
|
|
242
|
+
'voice.tts',
|
|
243
|
+
'voice.stt',
|
|
244
|
+
'media.providers.list',
|
|
245
|
+
'media.analyze',
|
|
246
|
+
'multimodal.providers.list',
|
|
247
|
+
'remote.snapshot',
|
|
248
|
+
'remote.peers.list',
|
|
249
|
+
'remote.work.list',
|
|
250
|
+
'remote.node_host.contract',
|
|
251
|
+
],
|
|
252
|
+
optionalMethodIds: [
|
|
253
|
+
'voice.realtime.session',
|
|
254
|
+
'voice.tts.stream',
|
|
255
|
+
'media.generate',
|
|
256
|
+
'media.transform',
|
|
257
|
+
'multimodal.analyze',
|
|
258
|
+
'remote.peers.invoke',
|
|
259
|
+
],
|
|
260
|
+
requiredAgentRoutes: [],
|
|
261
|
+
next: ['Turn daemon readiness into Agent setup cards for voice, media, browser, and node workflows.'],
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
id: 'providers-models',
|
|
265
|
+
title: 'Providers, Models, And Usage',
|
|
266
|
+
competitorBaseline: 'Personal operators need configurable providers, model routing, and usage posture.',
|
|
267
|
+
agentUse: 'Agent reads daemon provider/model state, keeps provider+model routing explicit, and avoids per-message routing hacks.',
|
|
268
|
+
requiredMethodIds: ['providers.list', 'providers.get', 'providers.usage.get'],
|
|
269
|
+
optionalMethodIds: ['accounts.snapshot'],
|
|
270
|
+
requiredAgentRoutes: [],
|
|
271
|
+
next: ['Add provider/model readiness remediation directly into onboarding/config workspaces.'],
|
|
272
|
+
},
|
|
273
|
+
] as const;
|
|
274
|
+
|
|
275
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
276
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function readString(record: Record<string, unknown>, key: string): string | null {
|
|
280
|
+
const value = record[key];
|
|
281
|
+
return typeof value === 'string' ? value : null;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function readMethodSummaries(body: unknown): readonly DaemonMethodSummary[] {
|
|
285
|
+
const methods = isRecord(body) && Array.isArray(body.methods) ? body.methods : [];
|
|
286
|
+
return methods.flatMap((value): DaemonMethodSummary[] => {
|
|
287
|
+
if (!isRecord(value)) return [];
|
|
288
|
+
const id = readString(value, 'id');
|
|
289
|
+
if (!id) return [];
|
|
290
|
+
const httpRecord = isRecord(value.http) ? value.http : null;
|
|
291
|
+
return [{
|
|
292
|
+
id,
|
|
293
|
+
title: readString(value, 'title') ?? undefined,
|
|
294
|
+
category: readString(value, 'category') ?? undefined,
|
|
295
|
+
access: readString(value, 'access') ?? undefined,
|
|
296
|
+
invokable: typeof value.invokable === 'boolean' ? value.invokable : undefined,
|
|
297
|
+
http: httpRecord
|
|
298
|
+
? {
|
|
299
|
+
method: readString(httpRecord, 'method') ?? undefined,
|
|
300
|
+
path: readString(httpRecord, 'path') ?? undefined,
|
|
301
|
+
}
|
|
302
|
+
: undefined,
|
|
303
|
+
}];
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function daemonVersionFromStatus(body: unknown): string {
|
|
308
|
+
if (!isRecord(body)) return 'unknown';
|
|
309
|
+
return readString(body, 'version')
|
|
310
|
+
?? readString(body, 'sdkVersion')
|
|
311
|
+
?? 'unknown';
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function buildHeaders(connection: AgentDaemonConnection): Headers {
|
|
315
|
+
const headers = new Headers({ accept: 'application/json' });
|
|
316
|
+
if (connection.token) headers.set('authorization', `Bearer ${connection.token}`);
|
|
317
|
+
return headers;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async function fetchJson(connection: AgentDaemonConnection, route: string): Promise<FetchJsonResult> {
|
|
321
|
+
const response = await fetch(`${connection.baseUrl}${route}`, {
|
|
322
|
+
method: 'GET',
|
|
323
|
+
headers: buildHeaders(connection),
|
|
324
|
+
});
|
|
325
|
+
const text = await response.text();
|
|
326
|
+
let body: unknown = text;
|
|
327
|
+
if (text.trim().length > 0) {
|
|
328
|
+
try {
|
|
329
|
+
body = JSON.parse(text) as unknown;
|
|
330
|
+
} catch {
|
|
331
|
+
body = text;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return {
|
|
335
|
+
ok: response.ok,
|
|
336
|
+
status: response.status,
|
|
337
|
+
statusText: response.statusText,
|
|
338
|
+
body,
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function failureFromResponse(
|
|
343
|
+
response: FetchJsonResult,
|
|
344
|
+
connection: AgentDaemonConnection,
|
|
345
|
+
route: string,
|
|
346
|
+
daemonVersion: string,
|
|
347
|
+
): DaemonCapabilityAuditFailure {
|
|
348
|
+
const detail = isRecord(response.body) && typeof response.body.error === 'string'
|
|
349
|
+
? response.body.error
|
|
350
|
+
: typeof response.body === 'string'
|
|
351
|
+
? response.body
|
|
352
|
+
: response.statusText;
|
|
353
|
+
const error = `HTTP ${response.status}${detail ? `: ${detail}` : ''}`;
|
|
354
|
+
if (response.status === 401 || response.status === 403) {
|
|
355
|
+
return { ok: false, kind: 'auth_required', error, baseUrl: connection.baseUrl, route };
|
|
356
|
+
}
|
|
357
|
+
if (response.status === 404 && daemonVersion !== 'unknown' && daemonVersion !== SDK_VERSION) {
|
|
358
|
+
return {
|
|
359
|
+
ok: false,
|
|
360
|
+
kind: 'version_mismatch',
|
|
361
|
+
error: `External daemon SDK version ${daemonVersion} does not match Agent SDK pin ${SDK_VERSION}; ${route} is unavailable.`,
|
|
362
|
+
baseUrl: connection.baseUrl,
|
|
363
|
+
route,
|
|
364
|
+
daemonVersion,
|
|
365
|
+
expectedSdkVersion: SDK_VERSION,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
if (response.status === 404) {
|
|
369
|
+
return { ok: false, kind: 'daemon_route_unavailable', error, baseUrl: connection.baseUrl, route };
|
|
370
|
+
}
|
|
371
|
+
return { ok: false, kind: 'daemon_error', error, baseUrl: connection.baseUrl, route };
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function failureFromThrown(error: unknown, connection: AgentDaemonConnection, route: string): DaemonCapabilityAuditFailure {
|
|
375
|
+
const message = summarizeError(error);
|
|
376
|
+
const lower = message.toLowerCase();
|
|
377
|
+
if (lower.includes('unauthorized') || lower.includes('401') || lower.includes('403')) {
|
|
378
|
+
return { ok: false, kind: 'auth_required', error: message, baseUrl: connection.baseUrl, route };
|
|
379
|
+
}
|
|
380
|
+
if (lower.includes('fetch') || lower.includes('connect') || lower.includes('econnrefused')) {
|
|
381
|
+
return { ok: false, kind: 'daemon_unavailable', error: message, baseUrl: connection.baseUrl, route };
|
|
382
|
+
}
|
|
383
|
+
if (lower.includes('404') || lower.includes('not found')) {
|
|
384
|
+
return { ok: false, kind: 'daemon_route_unavailable', error: message, baseUrl: connection.baseUrl, route };
|
|
385
|
+
}
|
|
386
|
+
return { ok: false, kind: 'daemon_error', error: message, baseUrl: connection.baseUrl, route };
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export function buildDaemonCapabilityAuditAreas(
|
|
390
|
+
methodIds: ReadonlySet<string>,
|
|
391
|
+
agentKnowledgeRouteReady: boolean | null,
|
|
392
|
+
): readonly DaemonCapabilityAuditArea[] {
|
|
393
|
+
return DAEMON_CAPABILITY_REQUIREMENTS.map((requirement) => {
|
|
394
|
+
const presentRequiredMethodIds = requirement.requiredMethodIds.filter((methodId) => methodIds.has(methodId));
|
|
395
|
+
const missingRequiredMethodIds = requirement.requiredMethodIds.filter((methodId) => !methodIds.has(methodId));
|
|
396
|
+
const presentOptionalMethodIds = requirement.optionalMethodIds.filter((methodId) => methodIds.has(methodId));
|
|
397
|
+
const missingOptionalMethodIds = requirement.optionalMethodIds.filter((methodId) => !methodIds.has(methodId));
|
|
398
|
+
const agentRoutes = requirement.requiredAgentRoutes.map((route) => ({
|
|
399
|
+
route,
|
|
400
|
+
coverage: agentKnowledgeRouteReady === null
|
|
401
|
+
? 'not_checked'
|
|
402
|
+
: agentKnowledgeRouteReady
|
|
403
|
+
? 'ready'
|
|
404
|
+
: 'missing',
|
|
405
|
+
} satisfies DaemonCapabilityAuditArea['agentRoutes'][number]));
|
|
406
|
+
const missingAgentRoutes = agentRoutes.filter((route) => route.coverage === 'missing');
|
|
407
|
+
const requiredCount = requirement.requiredMethodIds.length + requirement.requiredAgentRoutes.length;
|
|
408
|
+
const presentRequiredCount = presentRequiredMethodIds.length
|
|
409
|
+
+ agentRoutes.filter((route) => route.coverage === 'ready').length;
|
|
410
|
+
const coverage: DaemonCapabilityCoverage = requiredCount === presentRequiredCount
|
|
411
|
+
? 'ready'
|
|
412
|
+
: presentRequiredCount > 0 && missingRequiredMethodIds.length + missingAgentRoutes.length < requiredCount
|
|
413
|
+
? 'partial'
|
|
414
|
+
: 'missing';
|
|
415
|
+
return {
|
|
416
|
+
id: requirement.id,
|
|
417
|
+
title: requirement.title,
|
|
418
|
+
coverage,
|
|
419
|
+
competitorBaseline: requirement.competitorBaseline,
|
|
420
|
+
agentUse: requirement.agentUse,
|
|
421
|
+
presentRequiredMethodIds,
|
|
422
|
+
missingRequiredMethodIds,
|
|
423
|
+
presentOptionalMethodIds,
|
|
424
|
+
missingOptionalMethodIds,
|
|
425
|
+
agentRoutes,
|
|
426
|
+
next: requirement.next,
|
|
427
|
+
};
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export async function fetchLiveDaemonCapabilityAudit(
|
|
432
|
+
connection: AgentDaemonConnection,
|
|
433
|
+
): Promise<DaemonCapabilityAuditResult> {
|
|
434
|
+
let daemonVersion = 'unknown';
|
|
435
|
+
try {
|
|
436
|
+
const status = await fetchJson(connection, DAEMON_STATUS_ROUTE);
|
|
437
|
+
if (status.ok) {
|
|
438
|
+
daemonVersion = daemonVersionFromStatus(status.body);
|
|
439
|
+
} else if (status.status === 401 || status.status === 403) {
|
|
440
|
+
return failureFromResponse(status, connection, DAEMON_STATUS_ROUTE, daemonVersion);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const methods = await fetchJson(connection, DAEMON_METHOD_CATALOG_ROUTE);
|
|
444
|
+
if (!methods.ok) return failureFromResponse(methods, connection, DAEMON_METHOD_CATALOG_ROUTE, daemonVersion);
|
|
445
|
+
|
|
446
|
+
const methodSummaries = readMethodSummaries(methods.body);
|
|
447
|
+
const methodIds = new Set(methodSummaries.map((method) => method.id));
|
|
448
|
+
const warnings: string[] = [];
|
|
449
|
+
const daemonCompatible = daemonVersion === SDK_VERSION;
|
|
450
|
+
if (daemonVersion !== 'unknown' && !daemonCompatible) {
|
|
451
|
+
warnings.push(`External daemon SDK version ${daemonVersion} does not match Agent SDK pin ${SDK_VERSION}.`);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const agentKnowledge = await fetchJson(connection, AGENT_KNOWLEDGE_STATUS_ROUTE);
|
|
455
|
+
if (!agentKnowledge.ok) {
|
|
456
|
+
const failure = failureFromResponse(agentKnowledge, connection, AGENT_KNOWLEDGE_STATUS_ROUTE, daemonVersion);
|
|
457
|
+
if (failure.kind === 'auth_required') return failure;
|
|
458
|
+
warnings.push(`${AGENT_KNOWLEDGE_STATUS_ROUTE} is not ready: ${failure.error}`);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return {
|
|
462
|
+
ok: true,
|
|
463
|
+
kind: 'daemon.capabilities.audit',
|
|
464
|
+
baseUrl: connection.baseUrl,
|
|
465
|
+
daemonVersion,
|
|
466
|
+
expectedSdkVersion: SDK_VERSION,
|
|
467
|
+
daemonCompatible,
|
|
468
|
+
methodCatalogRoute: DAEMON_METHOD_CATALOG_ROUTE,
|
|
469
|
+
methodCount: methodSummaries.length,
|
|
470
|
+
agentKnowledgeRoute: AGENT_KNOWLEDGE_STATUS_ROUTE,
|
|
471
|
+
agentKnowledgeRouteReady: agentKnowledge.ok,
|
|
472
|
+
defaultKnowledgeFallback: false,
|
|
473
|
+
homeGraphFallback: false,
|
|
474
|
+
warnings,
|
|
475
|
+
areas: buildDaemonCapabilityAuditAreas(methodIds, agentKnowledge.ok),
|
|
476
|
+
};
|
|
477
|
+
} catch (error) {
|
|
478
|
+
return failureFromThrown(error, connection, daemonVersion === 'unknown' ? DAEMON_STATUS_ROUTE : DAEMON_METHOD_CATALOG_ROUTE);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
export function filterDaemonCapabilityAuditAreas(
|
|
483
|
+
areas: readonly DaemonCapabilityAuditArea[],
|
|
484
|
+
query: string | undefined,
|
|
485
|
+
): readonly DaemonCapabilityAuditArea[] {
|
|
486
|
+
const normalized = query?.trim().toLowerCase();
|
|
487
|
+
if (!normalized) return areas;
|
|
488
|
+
return areas.filter((area) => {
|
|
489
|
+
if (area.id.includes(normalized)) return true;
|
|
490
|
+
if (area.title.toLowerCase().includes(normalized)) return true;
|
|
491
|
+
if (area.coverage.includes(normalized)) return true;
|
|
492
|
+
if (area.agentUse.toLowerCase().includes(normalized)) return true;
|
|
493
|
+
return area.presentRequiredMethodIds.some((methodId) => methodId.includes(normalized))
|
|
494
|
+
|| area.missingRequiredMethodIds.some((methodId) => methodId.includes(normalized))
|
|
495
|
+
|| area.presentOptionalMethodIds.some((methodId) => methodId.includes(normalized))
|
|
496
|
+
|| area.missingOptionalMethodIds.some((methodId) => methodId.includes(normalized))
|
|
497
|
+
|| area.agentRoutes.some((route) => route.route.toLowerCase().includes(normalized));
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
export function renderDaemonCapabilityAudit(
|
|
502
|
+
audit: DaemonCapabilityAuditSuccess,
|
|
503
|
+
areas: readonly DaemonCapabilityAuditArea[] = audit.areas,
|
|
504
|
+
): string {
|
|
505
|
+
const lines: string[] = [
|
|
506
|
+
'GoodVibes daemon capability audit',
|
|
507
|
+
` daemon: ${audit.baseUrl}`,
|
|
508
|
+
` SDK: Agent expects ${audit.expectedSdkVersion}; daemon reports ${audit.daemonVersion}`,
|
|
509
|
+
` compatibility: ${audit.daemonCompatible ? 'matched' : 'mismatch'}`,
|
|
510
|
+
` method catalog: ${audit.methodCount} methods from ${audit.methodCatalogRoute}`,
|
|
511
|
+
` Agent Knowledge: ${audit.agentKnowledgeRouteReady ? 'ready' : 'missing'} ${audit.agentKnowledgeRoute}`,
|
|
512
|
+
' isolation: default Knowledge/Wiki fallback no; HomeGraph fallback no',
|
|
513
|
+
'',
|
|
514
|
+
];
|
|
515
|
+
|
|
516
|
+
for (const warning of audit.warnings) lines.push(` warning: ${warning}`);
|
|
517
|
+
if (audit.warnings.length > 0) lines.push('');
|
|
518
|
+
|
|
519
|
+
for (const area of areas) {
|
|
520
|
+
const requiredTotal = area.presentRequiredMethodIds.length + area.missingRequiredMethodIds.length;
|
|
521
|
+
const optionalTotal = area.presentOptionalMethodIds.length + area.missingOptionalMethodIds.length;
|
|
522
|
+
lines.push(`${area.title} [${area.coverage}]`);
|
|
523
|
+
lines.push(` baseline: ${area.competitorBaseline}`);
|
|
524
|
+
lines.push(` Agent use: ${area.agentUse}`);
|
|
525
|
+
lines.push(` required methods: ${area.presentRequiredMethodIds.length}/${requiredTotal}`);
|
|
526
|
+
if (area.missingRequiredMethodIds.length > 0) lines.push(` missing required: ${area.missingRequiredMethodIds.join(', ')}`);
|
|
527
|
+
if (optionalTotal > 0) {
|
|
528
|
+
lines.push(` optional methods: ${area.presentOptionalMethodIds.length}/${optionalTotal}`);
|
|
529
|
+
if (area.missingOptionalMethodIds.length > 0) lines.push(` missing optional: ${area.missingOptionalMethodIds.join(', ')}`);
|
|
530
|
+
}
|
|
531
|
+
for (const route of area.agentRoutes) lines.push(` route: ${route.route} [${route.coverage}]`);
|
|
532
|
+
lines.push(` next: ${area.next.join(' | ')}`);
|
|
533
|
+
lines.push('');
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
return lines.join('\n').trimEnd();
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
export function renderDaemonCapabilityFailure(failure: DaemonCapabilityAuditFailure): string {
|
|
540
|
+
const details = [
|
|
541
|
+
`GoodVibes daemon capability audit failed [${failure.kind}]`,
|
|
542
|
+
` daemon: ${failure.baseUrl}`,
|
|
543
|
+
` route: ${failure.route}`,
|
|
544
|
+
` error: ${failure.error}`,
|
|
545
|
+
];
|
|
546
|
+
if (failure.daemonVersion || failure.expectedSdkVersion) {
|
|
547
|
+
details.push(` SDK: Agent expects ${failure.expectedSdkVersion ?? SDK_VERSION}; daemon reports ${failure.daemonVersion ?? 'unknown'}`);
|
|
548
|
+
}
|
|
549
|
+
if (failure.kind === 'auth_required') details.push(' next: authenticate the Agent against the existing GoodVibes daemon; no token value was printed.');
|
|
550
|
+
if (failure.kind === 'daemon_unavailable') details.push(' next: start or reconnect the external GoodVibes daemon; Agent will not start it.');
|
|
551
|
+
if (failure.kind === 'version_mismatch') details.push(' next: update/restart the externally owned daemon to match the Agent SDK pin.');
|
|
552
|
+
if (failure.kind === 'daemon_route_unavailable') details.push(' next: verify the external daemon exposes the published SDK/operator routes.');
|
|
553
|
+
return details.join('\n');
|
|
554
|
+
}
|
package/src/version.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { join } from 'node:path';
|
|
|
6
6
|
// The prebuild script updates the fallback value before compilation.
|
|
7
7
|
// Uses import.meta.dir (Bun) to locate package.json relative to this file,
|
|
8
8
|
// which is correct regardless of the process working directory.
|
|
9
|
-
let _version = '0.1.
|
|
9
|
+
let _version = '0.1.41';
|
|
10
10
|
let _sdkVersion = '0.33.35';
|
|
11
11
|
try {
|
|
12
12
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8')) as {
|