@pellux/goodvibes-agent 0.1.46 → 0.1.47
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 +3 -1
- package/docs/operator-capability-benchmark.md +4 -2
- package/package.json +1 -1
- package/src/cli/capabilities-command.ts +27 -1
- package/src/cli/help.ts +3 -2
- package/src/input/agent-workspace.ts +1 -0
- package/src/input/commands/capabilities-runtime.ts +14 -0
- package/src/operator/daemon-capability-audit.ts +354 -0
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -20,6 +20,7 @@ goodvibes-agent capabilities daemon
|
|
|
20
20
|
goodvibes-agent capabilities daemon gaps
|
|
21
21
|
goodvibes-agent capabilities daemon risk
|
|
22
22
|
goodvibes-agent capabilities daemon inventory
|
|
23
|
+
goodvibes-agent capabilities daemon coverage
|
|
23
24
|
```
|
|
24
25
|
|
|
25
26
|
If Bun reports untrusted lifecycle dependencies, trust only the package and dependencies required by this package:
|
|
@@ -48,7 +49,7 @@ bun run publish:check
|
|
|
48
49
|
|
|
49
50
|
Inside the Agent TUI, use `/agent`, `/home`, or `/operator` to open the operator workspace. It is the Agent-first fullscreen surface for setup, status, live daemon capability coverage, knowledge, local memory/skills, work-plan/approval review, automation observability, and explicit build delegation to GoodVibes TUI.
|
|
50
51
|
|
|
51
|
-
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, route risk posture, and isolated Agent Knowledge route coverage. Use `goodvibes-agent capabilities daemon inventory` for the full public daemon method inventory grouped by category, access, HTTP posture, and dangerous flags. Use `goodvibes-agent capabilities daemon gaps` or `/capabilities daemon gaps` to turn that live daemon audit into a prioritized gap plan that separates missing daemon routes from Agent UX work. Use `goodvibes-agent capabilities daemon risk` or `/approval risk` for route-risk-aware approval posture without mutating approval state.
|
|
52
|
+
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, route risk posture, and isolated Agent Knowledge route coverage. Use `goodvibes-agent capabilities daemon inventory` for the full public daemon method inventory grouped by category, access, HTTP posture, and dangerous flags. Use `goodvibes-agent capabilities daemon coverage` to map every daemon method to Agent UX posture: usable, read-only observable, explicit-confirmation, blocked, or not surfaced. Use `goodvibes-agent capabilities daemon gaps` or `/capabilities daemon gaps` to turn that live daemon audit into a prioritized gap plan that separates missing daemon routes from Agent UX work. Use `goodvibes-agent capabilities daemon risk` or `/approval risk` for route-risk-aware approval posture without mutating approval state.
|
|
52
53
|
|
|
53
54
|
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.
|
|
54
55
|
|
|
@@ -93,6 +94,7 @@ goodvibes-agent capabilities daemon --json
|
|
|
93
94
|
goodvibes-agent capabilities daemon gaps
|
|
94
95
|
goodvibes-agent capabilities daemon risk
|
|
95
96
|
goodvibes-agent capabilities daemon inventory
|
|
97
|
+
goodvibes-agent capabilities daemon coverage
|
|
96
98
|
```
|
|
97
99
|
|
|
98
100
|
This audit checks `/api/control-plane/methods` and `/api/goodvibes-agent/knowledge/status`. The gap plan, route-risk review, and full method inventory are derived from read-only daemon calls. None of these commands query default Knowledge/Wiki or HomeGraph.
|
|
@@ -20,6 +20,7 @@ goodvibes-agent capabilities daemon --json
|
|
|
20
20
|
goodvibes-agent capabilities daemon gaps
|
|
21
21
|
goodvibes-agent capabilities daemon risk
|
|
22
22
|
goodvibes-agent capabilities daemon inventory
|
|
23
|
+
goodvibes-agent capabilities daemon coverage
|
|
23
24
|
goodvibes-agent capabilities daemon knowledge
|
|
24
25
|
```
|
|
25
26
|
|
|
@@ -32,6 +33,7 @@ Inside the TUI:
|
|
|
32
33
|
/capabilities daemon
|
|
33
34
|
/capabilities daemon gaps
|
|
34
35
|
/capabilities daemon inventory
|
|
36
|
+
/capabilities daemon coverage
|
|
35
37
|
/approval risk
|
|
36
38
|
```
|
|
37
39
|
|
|
@@ -62,7 +64,7 @@ The benchmark measures two different GoodVibes layers:
|
|
|
62
64
|
|
|
63
65
|
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.
|
|
64
66
|
|
|
65
|
-
Use `goodvibes-agent capabilities daemon` for the live read-only daemon audit. It checks the public control-plane method catalog, route risk posture, and the isolated Agent Knowledge status route. Use `goodvibes-agent capabilities daemon inventory` for the full public daemon method inventory grouped by category, access, HTTP method, and dangerous flag. Use `goodvibes-agent capabilities daemon gaps` to convert that daemon-measured audit into a prioritized gap plan with `version_mismatch`, `agent_route_missing`, `required_method_missing`, `route_risk_review`, and `agent_ux_gap` rows. Use `goodvibes-agent capabilities daemon risk` or `/approval risk` for a route-risk-aware approval-center view over read-only, mutating, dangerous, and authenticated route metadata. These commands intentionally avoid default `/api/knowledge/*`, HomeGraph, and Home Assistant routes.
|
|
67
|
+
Use `goodvibes-agent capabilities daemon` for the live read-only daemon audit. It checks the public control-plane method catalog, route risk posture, and the isolated Agent Knowledge status route. Use `goodvibes-agent capabilities daemon inventory` for the full public daemon method inventory grouped by category, access, HTTP method, and dangerous flag. Use `goodvibes-agent capabilities daemon coverage` to map every public daemon method to Agent UX posture: usable, read-only observable, explicit-confirmation, blocked by product boundary, or not surfaced yet. Use `goodvibes-agent capabilities daemon gaps` to convert that daemon-measured audit into a prioritized gap plan with `version_mismatch`, `agent_route_missing`, `required_method_missing`, `route_risk_review`, and `agent_ux_gap` rows. Use `goodvibes-agent capabilities daemon risk` or `/approval risk` for a route-risk-aware approval-center view over read-only, mutating, dangerous, and authenticated route metadata. These commands intentionally avoid default `/api/knowledge/*`, HomeGraph, and Home Assistant routes.
|
|
66
68
|
|
|
67
69
|
## Capability Targets
|
|
68
70
|
|
|
@@ -84,7 +86,7 @@ Use `goodvibes-agent capabilities daemon` for the live read-only daemon audit. I
|
|
|
84
86
|
|
|
85
87
|
GoodVibes Agent should exceed OpenClaw/Hermes by making these properties true from day one:
|
|
86
88
|
|
|
87
|
-
- Capability surfaces are discoverable through `goodvibes-agent capabilities`, `goodvibes-agent capabilities daemon`, `goodvibes-agent capabilities daemon inventory`, `goodvibes-agent capabilities daemon gaps`, `goodvibes-agent capabilities daemon risk`, `/capabilities`, `/capabilities daemon`, `/approval risk`, onboarding, and the operator workspace.
|
|
89
|
+
- Capability surfaces are discoverable through `goodvibes-agent capabilities`, `goodvibes-agent capabilities daemon`, `goodvibes-agent capabilities daemon inventory`, `goodvibes-agent capabilities daemon coverage`, `goodvibes-agent capabilities daemon gaps`, `goodvibes-agent capabilities daemon risk`, `/capabilities`, `/capabilities daemon`, `/approval risk`, onboarding, and the operator workspace.
|
|
88
90
|
- Agent Knowledge isolation is a release gate, not a convention.
|
|
89
91
|
- Routine-to-schedule promotion preserves Agent Knowledge isolation, uses only public external daemon schedule routes, supports explicit delivery targets, and stores redacted receipts.
|
|
90
92
|
- Model-visible tools are policy-gated for serial, non-secret, non-destructive use.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.47",
|
|
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",
|
|
@@ -10,20 +10,23 @@ import {
|
|
|
10
10
|
buildDaemonCapabilityRouteRiskReport,
|
|
11
11
|
fetchLiveDaemonCapabilityAudit,
|
|
12
12
|
fetchLiveDaemonCapabilityInventory,
|
|
13
|
+
fetchLiveDaemonCapabilityUxCoverage,
|
|
13
14
|
filterDaemonCapabilityAuditAreas,
|
|
14
15
|
filterDaemonCapabilityGaps,
|
|
15
16
|
filterDaemonCapabilityInventoryGroups,
|
|
16
17
|
filterDaemonCapabilityRouteRiskAreas,
|
|
18
|
+
filterDaemonCapabilityUxGroups,
|
|
17
19
|
renderDaemonCapabilityAudit,
|
|
18
20
|
renderDaemonCapabilityFailure,
|
|
19
21
|
renderDaemonCapabilityGaps,
|
|
20
22
|
renderDaemonCapabilityInventory,
|
|
21
23
|
renderDaemonCapabilityRouteRisk,
|
|
24
|
+
renderDaemonCapabilityUxCoverage,
|
|
22
25
|
} from '../operator/daemon-capability-audit.ts';
|
|
23
26
|
import { resolveAgentDaemonConnection } from '../agent/routine-schedule-promotion.ts';
|
|
24
27
|
|
|
25
28
|
interface CapabilityCommandArgs {
|
|
26
|
-
readonly mode: 'benchmark' | 'daemon' | 'daemon-gaps' | 'daemon-risk' | 'daemon-inventory';
|
|
29
|
+
readonly mode: 'benchmark' | 'daemon' | 'daemon-gaps' | 'daemon-risk' | 'daemon-inventory' | 'daemon-ux';
|
|
27
30
|
readonly query: string | undefined;
|
|
28
31
|
}
|
|
29
32
|
|
|
@@ -46,6 +49,10 @@ function readCapabilityArgs(args: readonly string[]): CapabilityCommandArgs {
|
|
|
46
49
|
const query = values.slice(2).join(' ').trim();
|
|
47
50
|
return { mode: 'daemon-inventory', query: query.length > 0 ? query : undefined };
|
|
48
51
|
}
|
|
52
|
+
if (values[1] === 'coverage' || values[1] === 'ux' || values[1] === 'surface') {
|
|
53
|
+
const query = values.slice(2).join(' ').trim();
|
|
54
|
+
return { mode: 'daemon-ux', query: query.length > 0 ? query : undefined };
|
|
55
|
+
}
|
|
49
56
|
const query = values.slice(1).join(' ').trim();
|
|
50
57
|
return { mode: 'daemon', query: query.length > 0 ? query : undefined };
|
|
51
58
|
}
|
|
@@ -132,6 +139,25 @@ export async function handleCapabilitiesCommand(runtime: CliCommandRuntime): Pro
|
|
|
132
139
|
exitCode: 0,
|
|
133
140
|
};
|
|
134
141
|
}
|
|
142
|
+
if (args.mode === 'daemon-ux') {
|
|
143
|
+
const connection = resolveAgentDaemonConnection(runtime.configManager, runtime.homeDirectory);
|
|
144
|
+
const coverage = await fetchLiveDaemonCapabilityUxCoverage(connection);
|
|
145
|
+
if (!coverage.ok) {
|
|
146
|
+
return {
|
|
147
|
+
output: runtime.cli.flags.outputFormat === 'json'
|
|
148
|
+
? JSON.stringify(coverage, null, 2)
|
|
149
|
+
: renderDaemonCapabilityFailure(coverage),
|
|
150
|
+
exitCode: coverage.kind === 'auth_required' || coverage.kind === 'daemon_unavailable' ? 1 : 2,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const groups = filterDaemonCapabilityUxGroups(coverage.groups, args.query);
|
|
154
|
+
return {
|
|
155
|
+
output: runtime.cli.flags.outputFormat === 'json'
|
|
156
|
+
? JSON.stringify({ ...coverage, matchedGroupCount: groups.length, groups }, null, 2)
|
|
157
|
+
: renderDaemonCapabilityUxCoverage(coverage, groups),
|
|
158
|
+
exitCode: 0,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
135
161
|
const report = buildOperatorCapabilityBenchmarkReport();
|
|
136
162
|
const capabilities = filterOperatorCapabilities(report.capabilities, args.query);
|
|
137
163
|
if (runtime.cli.flags.outputFormat === 'json') {
|
package/src/cli/help.ts
CHANGED
|
@@ -106,6 +106,7 @@ export function renderGoodVibesHelp(binary = 'goodvibes-agent'): string {
|
|
|
106
106
|
` ${binary} capabilities daemon gaps`,
|
|
107
107
|
` ${binary} capabilities daemon risk`,
|
|
108
108
|
` ${binary} capabilities daemon inventory`,
|
|
109
|
+
` ${binary} capabilities daemon coverage`,
|
|
109
110
|
` ${binary} knowledge status`,
|
|
110
111
|
` ${binary} knowledge ask "What is GoodVibes Agent?"`,
|
|
111
112
|
` ${binary} ask "What is GoodVibes Agent?"`,
|
|
@@ -200,9 +201,9 @@ const COMMAND_HELP: Record<string, CommandHelp> = {
|
|
|
200
201
|
examples: ['compat', 'compat --json'],
|
|
201
202
|
},
|
|
202
203
|
capabilities: {
|
|
203
|
-
usage: ['capabilities [openclaw|hermes|query]', 'capabilities daemon [query]', 'capabilities daemon gaps [query]', 'capabilities daemon risk [query]', 'capabilities daemon inventory [query]', 'capabilities --json'],
|
|
204
|
+
usage: ['capabilities [openclaw|hermes|query]', 'capabilities daemon [query]', 'capabilities daemon gaps [query]', 'capabilities daemon risk [query]', 'capabilities daemon inventory [query]', 'capabilities daemon coverage [query]', 'capabilities --json'],
|
|
204
205
|
summary: 'Show the OpenClaw/Hermes capability benchmark, Agent readiness, live GoodVibes daemon method coverage, route risk, and daemon-measured product gaps.',
|
|
205
|
-
examples: ['capabilities', 'capabilities hermes', 'capabilities daemon', 'capabilities daemon gaps', 'capabilities daemon risk --json', 'capabilities daemon inventory channels --json'],
|
|
206
|
+
examples: ['capabilities', 'capabilities hermes', 'capabilities daemon', 'capabilities daemon gaps', 'capabilities daemon risk --json', 'capabilities daemon inventory channels --json', 'capabilities daemon coverage not_surfaced'],
|
|
206
207
|
},
|
|
207
208
|
knowledge: {
|
|
208
209
|
usage: [
|
|
@@ -492,6 +492,7 @@ export const AGENT_WORKSPACE_CATEGORIES: readonly AgentWorkspaceCategory[] = [
|
|
|
492
492
|
{ id: 'capabilities-daemon-gaps', label: 'Daemon gap plan', detail: 'Classify live daemon coverage into platform gaps, Agent route gaps, route-risk reviews, and Agent UX work without querying default Knowledge/Wiki or HomeGraph.', command: '/capabilities daemon gaps', kind: 'command', safety: 'read-only' },
|
|
493
493
|
{ id: 'capabilities-daemon-risk', label: 'Route risk review', detail: 'Inspect daemon read-only, mutating, dangerous, and authenticated route metadata for approval-center planning.', command: '/capabilities daemon risk', kind: 'command', safety: 'read-only' },
|
|
494
494
|
{ id: 'capabilities-daemon-inventory', label: 'Full method inventory', detail: 'List every public daemon method by category, HTTP posture, access, and dangerous flag without querying default Knowledge/Wiki or HomeGraph.', command: '/capabilities daemon inventory', kind: 'command', safety: 'read-only' },
|
|
495
|
+
{ id: 'capabilities-daemon-coverage', label: 'UX coverage matrix', detail: 'Map every public daemon method to Agent UX coverage: usable, read-only, explicit confirmation, blocked, or not surfaced.', command: '/capabilities daemon coverage', kind: 'command', safety: 'read-only' },
|
|
495
496
|
{ id: 'capabilities-daemon-knowledge', label: 'Knowledge coverage', detail: 'Filter the live daemon audit to the isolated Agent Knowledge segment.', command: '/capabilities daemon knowledge', kind: 'command', safety: 'read-only' },
|
|
496
497
|
{ id: 'capabilities-daemon-channels', label: 'Channel coverage', detail: 'Filter the live daemon audit to channel and delivery gateway route coverage.', command: '/capabilities daemon channels', kind: 'command', safety: 'read-only' },
|
|
497
498
|
{ id: 'capabilities-daemon-automation', label: 'Automation coverage', detail: 'Filter the live daemon audit to automation, schedule, run, and capacity route coverage.', command: '/capabilities daemon automation', kind: 'command', safety: 'read-only' },
|
|
@@ -9,15 +9,18 @@ import {
|
|
|
9
9
|
buildDaemonCapabilityRouteRiskReport,
|
|
10
10
|
fetchLiveDaemonCapabilityAudit,
|
|
11
11
|
fetchLiveDaemonCapabilityInventory,
|
|
12
|
+
fetchLiveDaemonCapabilityUxCoverage,
|
|
12
13
|
filterDaemonCapabilityAuditAreas,
|
|
13
14
|
filterDaemonCapabilityGaps,
|
|
14
15
|
filterDaemonCapabilityInventoryGroups,
|
|
15
16
|
filterDaemonCapabilityRouteRiskAreas,
|
|
17
|
+
filterDaemonCapabilityUxGroups,
|
|
16
18
|
renderDaemonCapabilityAudit,
|
|
17
19
|
renderDaemonCapabilityFailure,
|
|
18
20
|
renderDaemonCapabilityGaps,
|
|
19
21
|
renderDaemonCapabilityInventory,
|
|
20
22
|
renderDaemonCapabilityRouteRisk,
|
|
23
|
+
renderDaemonCapabilityUxCoverage,
|
|
21
24
|
} from '../../operator/daemon-capability-audit.ts';
|
|
22
25
|
import { resolveAgentDaemonConnection } from '../../agent/routine-schedule-promotion.ts';
|
|
23
26
|
|
|
@@ -61,6 +64,17 @@ export function registerCapabilitiesRuntimeCommands(registry: CommandRegistry):
|
|
|
61
64
|
ctx.print(renderDaemonCapabilityInventory(inventory, groups));
|
|
62
65
|
return;
|
|
63
66
|
}
|
|
67
|
+
if (args[1] === 'coverage' || args[1] === 'ux' || args[1] === 'surface') {
|
|
68
|
+
const coverage = await fetchLiveDaemonCapabilityUxCoverage(connection);
|
|
69
|
+
if (!coverage.ok) {
|
|
70
|
+
ctx.print(renderDaemonCapabilityFailure(coverage));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const query = args.slice(2).join(' ').trim() || undefined;
|
|
74
|
+
const groups = filterDaemonCapabilityUxGroups(coverage.groups, query);
|
|
75
|
+
ctx.print(renderDaemonCapabilityUxCoverage(coverage, groups));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
64
78
|
const query = args.slice(1).join(' ').trim() || undefined;
|
|
65
79
|
const areas = filterDaemonCapabilityAuditAreas(audit.areas, query);
|
|
66
80
|
ctx.print(renderDaemonCapabilityAudit(audit, areas));
|
|
@@ -182,6 +182,60 @@ export interface DaemonCapabilityInventoryReport {
|
|
|
182
182
|
readonly groups: readonly DaemonCapabilityInventoryGroup[];
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
+
export type DaemonCapabilityUxCoverage =
|
|
186
|
+
| 'usable'
|
|
187
|
+
| 'read_only'
|
|
188
|
+
| 'explicit_confirmation'
|
|
189
|
+
| 'blocked'
|
|
190
|
+
| 'not_surfaced';
|
|
191
|
+
|
|
192
|
+
export interface DaemonCapabilityUxMethod {
|
|
193
|
+
readonly id: string;
|
|
194
|
+
readonly title?: string;
|
|
195
|
+
readonly category: string;
|
|
196
|
+
readonly access: string;
|
|
197
|
+
readonly httpMethod: string;
|
|
198
|
+
readonly path?: string;
|
|
199
|
+
readonly dangerous: boolean;
|
|
200
|
+
readonly readOnly: boolean;
|
|
201
|
+
readonly mutating: boolean;
|
|
202
|
+
readonly uxCoverage: DaemonCapabilityUxCoverage;
|
|
203
|
+
readonly surface: string;
|
|
204
|
+
readonly next: string;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export interface DaemonCapabilityUxGroup {
|
|
208
|
+
readonly category: string;
|
|
209
|
+
readonly methodCount: number;
|
|
210
|
+
readonly usableMethodCount: number;
|
|
211
|
+
readonly readOnlyMethodCount: number;
|
|
212
|
+
readonly explicitConfirmationMethodCount: number;
|
|
213
|
+
readonly blockedMethodCount: number;
|
|
214
|
+
readonly notSurfacedMethodCount: number;
|
|
215
|
+
readonly methods: readonly DaemonCapabilityUxMethod[];
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export interface DaemonCapabilityUxCoverageReport {
|
|
219
|
+
readonly ok: true;
|
|
220
|
+
readonly kind: 'daemon.capabilities.ux_coverage';
|
|
221
|
+
readonly baseUrl: string;
|
|
222
|
+
readonly daemonVersion: string;
|
|
223
|
+
readonly expectedSdkVersion: string;
|
|
224
|
+
readonly daemonCompatible: boolean;
|
|
225
|
+
readonly methodCatalogRoute: typeof DAEMON_METHOD_CATALOG_ROUTE;
|
|
226
|
+
readonly methodCount: number;
|
|
227
|
+
readonly agentKnowledgeRoute: typeof AGENT_KNOWLEDGE_STATUS_ROUTE;
|
|
228
|
+
readonly agentKnowledgeRouteReady: boolean;
|
|
229
|
+
readonly defaultKnowledgeFallback: false;
|
|
230
|
+
readonly homeGraphFallback: false;
|
|
231
|
+
readonly usableMethodCount: number;
|
|
232
|
+
readonly readOnlyMethodCount: number;
|
|
233
|
+
readonly explicitConfirmationMethodCount: number;
|
|
234
|
+
readonly blockedMethodCount: number;
|
|
235
|
+
readonly notSurfacedMethodCount: number;
|
|
236
|
+
readonly groups: readonly DaemonCapabilityUxGroup[];
|
|
237
|
+
}
|
|
238
|
+
|
|
185
239
|
export interface DaemonCapabilityAuditFailure {
|
|
186
240
|
readonly ok: false;
|
|
187
241
|
readonly kind: DaemonCapabilityAuditFailureKind;
|
|
@@ -457,6 +511,167 @@ function compareInventoryGroups(left: DaemonCapabilityInventoryGroup, right: Dae
|
|
|
457
511
|
return left.category.localeCompare(right.category);
|
|
458
512
|
}
|
|
459
513
|
|
|
514
|
+
function compareUxGroups(left: DaemonCapabilityUxGroup, right: DaemonCapabilityUxGroup): number {
|
|
515
|
+
const gapDelta = right.notSurfacedMethodCount + right.blockedMethodCount
|
|
516
|
+
- (left.notSurfacedMethodCount + left.blockedMethodCount);
|
|
517
|
+
if (gapDelta !== 0) return gapDelta;
|
|
518
|
+
const countDelta = right.methodCount - left.methodCount;
|
|
519
|
+
if (countDelta !== 0) return countDelta;
|
|
520
|
+
return left.category.localeCompare(right.category);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const USABLE_METHOD_SURFACES = new Map<string, string>([
|
|
524
|
+
['control.status', 'status / smoke / compat'],
|
|
525
|
+
['control.auth.current', 'auth / status / smoke'],
|
|
526
|
+
['control.methods.list', 'capabilities daemon inventory / coverage / risk'],
|
|
527
|
+
['control.contract', 'compat / capability checks'],
|
|
528
|
+
['control.snapshot', 'status / capability checks'],
|
|
529
|
+
['companion.chat.sessions.create', 'chat session management'],
|
|
530
|
+
['companion.chat.sessions.get', 'chat session management'],
|
|
531
|
+
['companion.chat.sessions.list', 'chat session management'],
|
|
532
|
+
['companion.chat.sessions.update', 'chat session management'],
|
|
533
|
+
['companion.chat.messages.create', 'normal assistant chat'],
|
|
534
|
+
['companion.chat.messages.list', 'normal assistant chat'],
|
|
535
|
+
['sessions.messages.create', 'explicit build delegation to GoodVibes TUI'],
|
|
536
|
+
['sessions.list', 'delegations status'],
|
|
537
|
+
['tasks.list', 'delegations status'],
|
|
538
|
+
['projectPlanning.workPlan.snapshot', 'workplan'],
|
|
539
|
+
['approvals.list', 'approvals / approval risk'],
|
|
540
|
+
['automation.integration.snapshot', 'automation status'],
|
|
541
|
+
['automation.jobs.list', 'automation jobs'],
|
|
542
|
+
['automation.runs.list', 'automation runs'],
|
|
543
|
+
['automation.heartbeat.list', 'automation heartbeat'],
|
|
544
|
+
['schedules.list', 'schedules'],
|
|
545
|
+
['scheduler.capacity', 'automation capacity'],
|
|
546
|
+
['providers.list', 'provider/model setup'],
|
|
547
|
+
['providers.get', 'provider/model setup'],
|
|
548
|
+
['providers.usage.get', 'provider usage'],
|
|
549
|
+
['accounts.snapshot', 'provider account posture'],
|
|
550
|
+
['channels.status', 'channel onboarding/status'],
|
|
551
|
+
['channels.capabilities.list', 'channel onboarding/status'],
|
|
552
|
+
['channels.accounts.list', 'channel onboarding/status'],
|
|
553
|
+
['channels.setup.get', 'channel onboarding/status'],
|
|
554
|
+
['channels.doctor.get', 'channel diagnostics'],
|
|
555
|
+
['channels.actions.list', 'channel capabilities'],
|
|
556
|
+
['channels.tools.list', 'channel capabilities'],
|
|
557
|
+
['channels.targets.resolve', 'routine delivery planning'],
|
|
558
|
+
['channels.policies.list', 'channel safety posture'],
|
|
559
|
+
['mcp.config.get', 'MCP setup/status'],
|
|
560
|
+
['mcp.servers.list', 'MCP setup/status'],
|
|
561
|
+
['mcp.tools.list', 'MCP setup/status'],
|
|
562
|
+
['web_search.providers.list', 'research/tool readiness'],
|
|
563
|
+
['web_search.query', 'research/tool readiness'],
|
|
564
|
+
['voice.status', 'voice setup/status'],
|
|
565
|
+
['voice.providers.list', 'voice setup/status'],
|
|
566
|
+
['voice.voices.list', 'voice setup/status'],
|
|
567
|
+
['media.providers.list', 'media setup/status'],
|
|
568
|
+
['multimodal.providers.list', 'multimodal setup/status'],
|
|
569
|
+
['remote.snapshot', 'remote/node setup'],
|
|
570
|
+
['remote.peers.list', 'remote/node setup'],
|
|
571
|
+
['remote.work.list', 'remote work status'],
|
|
572
|
+
['remote.node_host.contract', 'remote/node setup'],
|
|
573
|
+
]);
|
|
574
|
+
|
|
575
|
+
const EXPLICIT_CONFIRMATION_METHOD_SURFACES = new Map<string, string>([
|
|
576
|
+
['approvals.approve', 'approvals approve --yes'],
|
|
577
|
+
['approvals.deny', 'approvals deny --yes'],
|
|
578
|
+
['approvals.cancel', 'approvals cancel --yes'],
|
|
579
|
+
['automation.jobs.run', 'automation jobs run --yes'],
|
|
580
|
+
['automation.jobs.pause', 'automation jobs pause --yes'],
|
|
581
|
+
['automation.jobs.resume', 'automation jobs resume --yes'],
|
|
582
|
+
['automation.runs.cancel', 'automation runs cancel --yes'],
|
|
583
|
+
['automation.runs.retry', 'automation runs retry --yes'],
|
|
584
|
+
['schedules.run', 'schedules run --yes'],
|
|
585
|
+
]);
|
|
586
|
+
|
|
587
|
+
const READ_ONLY_CATEGORY_SURFACES = new Map<string, string>([
|
|
588
|
+
['approvals', 'approvals / approval risk'],
|
|
589
|
+
['artifacts', 'artifact-aware chat and receipts'],
|
|
590
|
+
['automation', 'automation status'],
|
|
591
|
+
['channels', 'channel onboarding/status'],
|
|
592
|
+
['companion', 'chat session management'],
|
|
593
|
+
['control', 'status / capabilities'],
|
|
594
|
+
['mcp', 'MCP setup/status'],
|
|
595
|
+
['media', 'media setup/status'],
|
|
596
|
+
['multimodal', 'multimodal setup/status'],
|
|
597
|
+
['projectPlanning', 'workplan'],
|
|
598
|
+
['providers', 'provider/model setup'],
|
|
599
|
+
['remote', 'remote/node setup'],
|
|
600
|
+
['schedules', 'schedules'],
|
|
601
|
+
['scheduler', 'automation capacity'],
|
|
602
|
+
['sessions', 'delegations status'],
|
|
603
|
+
['tasks', 'delegations status'],
|
|
604
|
+
['voice', 'voice setup/status'],
|
|
605
|
+
['web_search', 'research/tool readiness'],
|
|
606
|
+
]);
|
|
607
|
+
|
|
608
|
+
function isDefaultKnowledgeMethod(method: DaemonCapabilityInventoryMethod): boolean {
|
|
609
|
+
return method.id.startsWith('knowledge.');
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function isHomeGraphMethod(method: DaemonCapabilityInventoryMethod): boolean {
|
|
613
|
+
return method.id.startsWith('homeassistant.') || method.id.toLowerCase().includes('homegraph');
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
function classifyDaemonMethodUx(method: DaemonCapabilityInventoryMethod): DaemonCapabilityUxMethod {
|
|
617
|
+
const explicitSurface = EXPLICIT_CONFIRMATION_METHOD_SURFACES.get(method.id);
|
|
618
|
+
if (explicitSurface) {
|
|
619
|
+
return {
|
|
620
|
+
...method,
|
|
621
|
+
uxCoverage: 'explicit_confirmation',
|
|
622
|
+
surface: explicitSurface,
|
|
623
|
+
next: 'Already exposed only through exact user commands plus confirmation.',
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
const usableSurface = USABLE_METHOD_SURFACES.get(method.id);
|
|
628
|
+
if (usableSurface) {
|
|
629
|
+
return {
|
|
630
|
+
...method,
|
|
631
|
+
uxCoverage: 'usable',
|
|
632
|
+
surface: usableSurface,
|
|
633
|
+
next: 'Keep this route covered by command/help/workspace smoke as the daemon contract evolves.',
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
if (isDefaultKnowledgeMethod(method)) {
|
|
638
|
+
return {
|
|
639
|
+
...method,
|
|
640
|
+
uxCoverage: 'blocked',
|
|
641
|
+
surface: 'blocked for Agent Knowledge',
|
|
642
|
+
next: 'Do not use default Knowledge/Wiki as an Agent fallback; use /api/goodvibes-agent/knowledge/* only.',
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
if (isHomeGraphMethod(method)) {
|
|
647
|
+
return {
|
|
648
|
+
...method,
|
|
649
|
+
uxCoverage: 'blocked',
|
|
650
|
+
surface: 'blocked for Agent product boundary',
|
|
651
|
+
next: 'Do not use HomeGraph or Home Assistant routes as Agent Knowledge fallback.',
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const readOnlySurface = READ_ONLY_CATEGORY_SURFACES.get(method.category);
|
|
656
|
+
if (method.readOnly && readOnlySurface) {
|
|
657
|
+
return {
|
|
658
|
+
...method,
|
|
659
|
+
uxCoverage: 'read_only',
|
|
660
|
+
surface: readOnlySurface,
|
|
661
|
+
next: 'Visible through read-only posture, inventory, or status surfaces; add focused UX if this becomes user-facing.',
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
return {
|
|
666
|
+
...method,
|
|
667
|
+
uxCoverage: 'not_surfaced',
|
|
668
|
+
surface: 'daemon inventory only',
|
|
669
|
+
next: method.mutating || method.dangerous
|
|
670
|
+
? 'Design an exact command, approval posture, and confirmation flow before exposing this side effect.'
|
|
671
|
+
: 'Add a focused Agent command or workspace card if this daemon capability is part of the product surface.',
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
|
|
460
675
|
function daemonVersionFromStatus(body: unknown): string {
|
|
461
676
|
if (!isRecord(body)) return 'unknown';
|
|
462
677
|
return readString(body, 'version')
|
|
@@ -740,6 +955,97 @@ export type DaemonCapabilityInventoryResult =
|
|
|
740
955
|
| DaemonCapabilityInventoryReport
|
|
741
956
|
| DaemonCapabilityAuditFailure;
|
|
742
957
|
|
|
958
|
+
export type DaemonCapabilityUxCoverageResult =
|
|
959
|
+
| DaemonCapabilityUxCoverageReport
|
|
960
|
+
| DaemonCapabilityAuditFailure;
|
|
961
|
+
|
|
962
|
+
export function buildDaemonCapabilityUxCoverageReport(
|
|
963
|
+
inventory: DaemonCapabilityInventoryReport,
|
|
964
|
+
): DaemonCapabilityUxCoverageReport {
|
|
965
|
+
const methods = inventory.groups
|
|
966
|
+
.flatMap((group) => group.methods)
|
|
967
|
+
.map(classifyDaemonMethodUx);
|
|
968
|
+
|
|
969
|
+
const groupsByCategory = new Map<string, DaemonCapabilityUxMethod[]>();
|
|
970
|
+
for (const method of methods) {
|
|
971
|
+
const existing = groupsByCategory.get(method.category) ?? [];
|
|
972
|
+
existing.push(method);
|
|
973
|
+
groupsByCategory.set(method.category, existing);
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
const groups = [...groupsByCategory.entries()].map(([category, categoryMethods]): DaemonCapabilityUxGroup => ({
|
|
977
|
+
category,
|
|
978
|
+
methodCount: categoryMethods.length,
|
|
979
|
+
usableMethodCount: categoryMethods.filter((method) => method.uxCoverage === 'usable').length,
|
|
980
|
+
readOnlyMethodCount: categoryMethods.filter((method) => method.uxCoverage === 'read_only').length,
|
|
981
|
+
explicitConfirmationMethodCount: categoryMethods.filter((method) => method.uxCoverage === 'explicit_confirmation').length,
|
|
982
|
+
blockedMethodCount: categoryMethods.filter((method) => method.uxCoverage === 'blocked').length,
|
|
983
|
+
notSurfacedMethodCount: categoryMethods.filter((method) => method.uxCoverage === 'not_surfaced').length,
|
|
984
|
+
methods: categoryMethods,
|
|
985
|
+
})).sort(compareUxGroups);
|
|
986
|
+
|
|
987
|
+
return {
|
|
988
|
+
ok: true,
|
|
989
|
+
kind: 'daemon.capabilities.ux_coverage',
|
|
990
|
+
baseUrl: inventory.baseUrl,
|
|
991
|
+
daemonVersion: inventory.daemonVersion,
|
|
992
|
+
expectedSdkVersion: inventory.expectedSdkVersion,
|
|
993
|
+
daemonCompatible: inventory.daemonCompatible,
|
|
994
|
+
methodCatalogRoute: inventory.methodCatalogRoute,
|
|
995
|
+
methodCount: inventory.methodCount,
|
|
996
|
+
agentKnowledgeRoute: inventory.agentKnowledgeRoute,
|
|
997
|
+
agentKnowledgeRouteReady: inventory.agentKnowledgeRouteReady,
|
|
998
|
+
defaultKnowledgeFallback: false,
|
|
999
|
+
homeGraphFallback: false,
|
|
1000
|
+
usableMethodCount: methods.filter((method) => method.uxCoverage === 'usable').length,
|
|
1001
|
+
readOnlyMethodCount: methods.filter((method) => method.uxCoverage === 'read_only').length,
|
|
1002
|
+
explicitConfirmationMethodCount: methods.filter((method) => method.uxCoverage === 'explicit_confirmation').length,
|
|
1003
|
+
blockedMethodCount: methods.filter((method) => method.uxCoverage === 'blocked').length,
|
|
1004
|
+
notSurfacedMethodCount: methods.filter((method) => method.uxCoverage === 'not_surfaced').length,
|
|
1005
|
+
groups,
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
export function filterDaemonCapabilityUxGroups(
|
|
1010
|
+
groups: readonly DaemonCapabilityUxGroup[],
|
|
1011
|
+
query: string | undefined,
|
|
1012
|
+
): readonly DaemonCapabilityUxGroup[] {
|
|
1013
|
+
const normalized = query?.trim().toLowerCase();
|
|
1014
|
+
if (!normalized) return groups;
|
|
1015
|
+
return groups.flatMap((group): DaemonCapabilityUxGroup[] => {
|
|
1016
|
+
const categoryMatches = group.category.toLowerCase().includes(normalized);
|
|
1017
|
+
const methods = categoryMatches
|
|
1018
|
+
? group.methods
|
|
1019
|
+
: group.methods.filter((method) => {
|
|
1020
|
+
return method.id.toLowerCase().includes(normalized)
|
|
1021
|
+
|| method.title?.toLowerCase().includes(normalized) === true
|
|
1022
|
+
|| method.uxCoverage.includes(normalized)
|
|
1023
|
+
|| method.surface.toLowerCase().includes(normalized)
|
|
1024
|
+
|| method.next.toLowerCase().includes(normalized)
|
|
1025
|
+
|| method.path?.toLowerCase().includes(normalized) === true;
|
|
1026
|
+
});
|
|
1027
|
+
if (methods.length === 0) return [];
|
|
1028
|
+
return [{
|
|
1029
|
+
category: group.category,
|
|
1030
|
+
methodCount: methods.length,
|
|
1031
|
+
usableMethodCount: methods.filter((method) => method.uxCoverage === 'usable').length,
|
|
1032
|
+
readOnlyMethodCount: methods.filter((method) => method.uxCoverage === 'read_only').length,
|
|
1033
|
+
explicitConfirmationMethodCount: methods.filter((method) => method.uxCoverage === 'explicit_confirmation').length,
|
|
1034
|
+
blockedMethodCount: methods.filter((method) => method.uxCoverage === 'blocked').length,
|
|
1035
|
+
notSurfacedMethodCount: methods.filter((method) => method.uxCoverage === 'not_surfaced').length,
|
|
1036
|
+
methods,
|
|
1037
|
+
}];
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
export async function fetchLiveDaemonCapabilityUxCoverage(
|
|
1042
|
+
connection: AgentDaemonConnection,
|
|
1043
|
+
): Promise<DaemonCapabilityUxCoverageResult> {
|
|
1044
|
+
const inventory = await fetchLiveDaemonCapabilityInventory(connection);
|
|
1045
|
+
if (!inventory.ok) return inventory;
|
|
1046
|
+
return buildDaemonCapabilityUxCoverageReport(inventory);
|
|
1047
|
+
}
|
|
1048
|
+
|
|
743
1049
|
export async function fetchLiveDaemonCapabilityInventory(
|
|
744
1050
|
connection: AgentDaemonConnection,
|
|
745
1051
|
): Promise<DaemonCapabilityInventoryResult> {
|
|
@@ -1120,6 +1426,54 @@ export function renderDaemonCapabilityInventory(
|
|
|
1120
1426
|
return lines.join('\n').trimEnd();
|
|
1121
1427
|
}
|
|
1122
1428
|
|
|
1429
|
+
function renderUxMethod(method: DaemonCapabilityUxMethod): string {
|
|
1430
|
+
const route = method.path ? ` ${method.httpMethod} ${method.path}` : ` ${method.httpMethod}`;
|
|
1431
|
+
return ` ${method.id} [${method.uxCoverage}]${route} -> ${method.surface}`;
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
export function renderDaemonCapabilityUxCoverage(
|
|
1435
|
+
report: DaemonCapabilityUxCoverageReport,
|
|
1436
|
+
groups: readonly DaemonCapabilityUxGroup[] = report.groups,
|
|
1437
|
+
): string {
|
|
1438
|
+
const lines: string[] = [
|
|
1439
|
+
'GoodVibes daemon-to-Agent UX coverage',
|
|
1440
|
+
` daemon: ${report.baseUrl}`,
|
|
1441
|
+
` SDK: Agent expects ${report.expectedSdkVersion}; daemon reports ${report.daemonVersion}`,
|
|
1442
|
+
` compatibility: ${report.daemonCompatible ? 'matched' : 'mismatch'}`,
|
|
1443
|
+
` method catalog: ${report.methodCount} methods from ${report.methodCatalogRoute}`,
|
|
1444
|
+
` Agent Knowledge: ${report.agentKnowledgeRouteReady ? 'ready' : 'missing'} ${report.agentKnowledgeRoute}`,
|
|
1445
|
+
' isolation: default Knowledge/Wiki fallback no; HomeGraph fallback no',
|
|
1446
|
+
` totals: ${report.usableMethodCount} usable; ${report.readOnlyMethodCount} read-only observable; ${report.explicitConfirmationMethodCount} explicit-confirmation; ${report.blockedMethodCount} blocked; ${report.notSurfacedMethodCount} not surfaced`,
|
|
1447
|
+
'',
|
|
1448
|
+
];
|
|
1449
|
+
|
|
1450
|
+
if (groups.length === 0) {
|
|
1451
|
+
lines.push('No daemon UX coverage rows matched this query.');
|
|
1452
|
+
return lines.join('\n');
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
for (const group of groups) {
|
|
1456
|
+
lines.push(`${group.category} (${group.methodCount})`);
|
|
1457
|
+
lines.push(` ${group.usableMethodCount} usable; ${group.readOnlyMethodCount} read-only; ${group.explicitConfirmationMethodCount} explicit-confirmation; ${group.blockedMethodCount} blocked; ${group.notSurfacedMethodCount} not surfaced`);
|
|
1458
|
+
const priorityMethods = group.methods
|
|
1459
|
+
.filter((method) => method.uxCoverage === 'not_surfaced' || method.uxCoverage === 'blocked')
|
|
1460
|
+
.slice(0, 12);
|
|
1461
|
+
const visibleMethods = priorityMethods.length > 0 ? priorityMethods : group.methods.slice(0, 12);
|
|
1462
|
+
for (const method of visibleMethods) {
|
|
1463
|
+
lines.push(renderUxMethod(method));
|
|
1464
|
+
if (method.uxCoverage === 'not_surfaced' || method.uxCoverage === 'blocked') {
|
|
1465
|
+
lines.push(` next: ${method.next}`);
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
if (group.methods.length > visibleMethods.length) {
|
|
1469
|
+
lines.push(` ... ${group.methods.length - visibleMethods.length} more; use --json or a narrower query for the full list.`);
|
|
1470
|
+
}
|
|
1471
|
+
lines.push('');
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
return lines.join('\n').trimEnd();
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1123
1477
|
export function renderDaemonCapabilityAudit(
|
|
1124
1478
|
audit: DaemonCapabilityAuditSuccess,
|
|
1125
1479
|
areas: readonly DaemonCapabilityAuditArea[] = audit.areas,
|
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.47';
|
|
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 {
|