@pellux/goodvibes-agent 0.1.25 → 0.1.27

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 CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  All notable changes to GoodVibes Agent will be recorded here.
4
4
 
5
+ ## 0.1.27 - 2026-05-31
6
+
7
+ - b5be172 Add capabilities CLI handler
8
+ - cafe1fb Add Agent capability benchmark surface
9
+
10
+ ## 0.1.26 - 2026-05-31
11
+
12
+ - dfb6147 Harden Agent read tool policy
13
+
5
14
  ## 0.1.25 - 2026-05-31
6
15
 
7
16
  - cba5f6d Harden Agent find tool policy
package/README.md CHANGED
@@ -15,6 +15,7 @@ Install the public alpha package with Bun:
15
15
  bun add -g @pellux/goodvibes-agent
16
16
  goodvibes-agent --help
17
17
  goodvibes-agent status
18
+ goodvibes-agent capabilities
18
19
  ```
19
20
 
20
21
  If Bun reports untrusted lifecycle dependencies, trust only the package and dependencies required by this package:
@@ -43,6 +44,8 @@ bun run publish:check
43
44
 
44
45
  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.
45
46
 
47
+ Use `goodvibes-agent capabilities` or `/capabilities` to inspect the OpenClaw/Hermes benchmark, current Agent posture, configuration commands, usage paths, and remaining gaps.
48
+
46
49
  Local Agent behavior is editable from the TUI:
47
50
 
48
51
  ```text
@@ -81,6 +84,7 @@ GoodVibes TUI owns coding execution: file edits, git/worktree workflows, coding
81
84
  Package-facing docs are intentionally narrow during the near-fork baseline:
82
85
 
83
86
  - [Getting Started](docs/getting-started.md)
87
+ - [Operator Capability Benchmark](docs/operator-capability-benchmark.md)
84
88
  - [Deployment And Services](docs/deployment-and-services.md)
85
89
  - [Release And Publishing](docs/release-and-publishing.md)
86
90
 
package/docs/README.md CHANGED
@@ -7,6 +7,7 @@ GoodVibes Agent is a near-fork of the GoodVibes terminal foundation with a diffe
7
7
  Current package docs:
8
8
 
9
9
  - [Getting Started](getting-started.md)
10
+ - [Operator Capability Benchmark](operator-capability-benchmark.md)
10
11
  - [Deployment And Services](deployment-and-services.md)
11
12
  - [Release And Publishing](release-and-publishing.md)
12
13
 
@@ -18,6 +19,7 @@ Important baseline constraints:
18
19
  - Agent connects to an externally managed daemon.
19
20
  - Agent does not start, stop, restart, install, uninstall, or own daemon/listener/web/service lifecycle.
20
21
  - Agent Knowledge/Wiki uses only `/api/goodvibes-agent/knowledge/*`; there is no default Knowledge/Wiki, HomeGraph, or Home Assistant fallback.
22
+ - Agent exposes `goodvibes-agent capabilities` and `/capabilities` to compare OpenClaw/Hermes capability targets against current Agent readiness and configuration paths.
21
23
  - Local personas, routines, and Agent skills are stored under the Agent surface root and are injected only into the serial Agent conversation.
22
24
  - Normal assistant chat is not coding-session delegation.
23
25
  - Build/fix/review delegation to GoodVibes TUI must be explicit; WRFC is not the default Agent behavior.
@@ -0,0 +1,75 @@
1
+ # Operator Capability Benchmark
2
+
3
+ GoodVibes Agent is built to compete directly with OpenClaw and Hermes Agent in the personal-operator assistant space, while keeping the GoodVibes product boundary explicit:
4
+
5
+ - Agent uses the GoodVibes TUI shell, renderer, input, fullscreen workspace, command registry, and release bones.
6
+ - Agent connects to an externally managed GoodVibes daemon.
7
+ - Agent Knowledge/Wiki uses only `/api/goodvibes-agent/knowledge/*`.
8
+ - Agent never falls back to default Knowledge/Wiki, HomeGraph, or Home Assistant routes.
9
+ - Agent keeps ordinary work serial in the main conversation.
10
+ - Agent delegates explicit build/fix/review work to GoodVibes TUI; WRFC is never default.
11
+
12
+ Use the live benchmark from the package:
13
+
14
+ ```sh
15
+ goodvibes-agent capabilities
16
+ goodvibes-agent capabilities --json
17
+ goodvibes-agent capabilities hermes
18
+ ```
19
+
20
+ Inside the TUI:
21
+
22
+ ```text
23
+ /capabilities
24
+ /capabilities openclaw
25
+ /capabilities knowledge
26
+ ```
27
+
28
+ ## Research Baseline
29
+
30
+ Primary sources used for the benchmark:
31
+
32
+ - OpenClaw README: https://github.com/openclaw/openclaw/blob/main/README.md
33
+ - OpenClaw FAQ: https://docs.openclaw.ai/help/faq
34
+ - OpenClaw Memory: https://docs.openclaw.ai/concepts/memory
35
+ - Hermes README: https://github.com/NousResearch/hermes-agent
36
+ - Hermes Tools: https://hermes-agent.nousresearch.com/docs/user-guide/features/tools/
37
+ - Hermes Cron: https://hermes-agent.nousresearch.com/docs/user-guide/features/cron/
38
+ - Hermes Profiles: https://hermes-agent.nousresearch.com/docs/user-guide/profiles/
39
+
40
+ ## Capability Targets
41
+
42
+ | Area | OpenClaw/Hermes Baseline | GoodVibes Agent Position |
43
+ | --- | --- | --- |
44
+ | Terminal operator UI | Interactive CLI/TUI, commands, sessions | Near-fork GoodVibes TUI compositor/input/fullscreen foundation |
45
+ | Always-on gateway | Gateway/service owns channels, sessions, tools, events | External GoodVibes daemon, never Agent-owned lifecycle |
46
+ | Channels | WhatsApp, Telegram, Slack, Discord, Signal, iMessage, web chat | GoodVibes daemon channel and companion surfaces with Agent-side policy |
47
+ | Knowledge/memory | Durable memory, semantic search, wiki/claim layers | Isolated Agent Knowledge routes plus local memory/skills/personas/routines |
48
+ | Skills/procedural memory | Skills directories, registries, skill lifecycle | Local Agent skills with review/stale/source/provenance fields |
49
+ | Scheduling | Natural-language cron, run/pause/resume/edit/remove, delivery | Guarded automation/schedule routes plus local routines; hidden model scheduling blocked |
50
+ | Tools/MCP | Broad toolsets, MCP, browser, media, terminal, files | GoodVibes SDK tools with Agent policy guards and MCP/provider integrations |
51
+ | Voice/media/canvas/nodes | Voice, TTS, mobile nodes, live canvas, browser automation | GoodVibes media/voice/browser primitives copied in; Agent-first setup still being wired |
52
+ | Build/code work | Direct terminal/file/code tools and subagents | Explicit delegation to GoodVibes TUI; local WRFC/spawn fanout blocked |
53
+ | Profiles | Independent profiles with own config/memory/skills/gateway | Agent surface root, homes, bundles, sessions; named profiles are next |
54
+ | Security | DM pairing, approvals, sandboxing, allowlists | Daemon approvals, auth diagnostics, secret refs, confirmation gates, model-tool policy |
55
+
56
+ ## Exceed Targets
57
+
58
+ GoodVibes Agent should exceed OpenClaw/Hermes by making these properties true from day one:
59
+
60
+ - Capability surfaces are discoverable through `goodvibes-agent capabilities`, `/capabilities`, onboarding, and the operator workspace.
61
+ - Agent Knowledge isolation is a release gate, not a convention.
62
+ - Model-visible tools are policy-gated for serial, non-secret, non-destructive use.
63
+ - Personal assistant state is Agent-local unless an explicit Agent Knowledge ingest route is used.
64
+ - Build work is delegated to the product that owns coding execution instead of turning the personal operator into a second coding TUI.
65
+ - Release gates prove package installability, Bun/TypeScript-only source, external-daemon posture, no authored JavaScript, no explicit `any`, and no forbidden default wiki/HomeGraph package-facing docs.
66
+
67
+ ## Current Gaps To Close
68
+
69
+ - Agent-first channel onboarding workspace for pairing, account visibility, delivery defaults, and channel safety.
70
+ - Richer Agent Knowledge ingest/review workspace for URLs, bookmarks, artifacts, issue queues, and consolidation.
71
+ - Named Agent profiles with isolated local registries and command aliases.
72
+ - Voice/media/browser/node setup workspaces.
73
+ - Delegation receipts and artifact review inside the operator workspace.
74
+ - Approval center with route risk labels and saved policy presets.
75
+ - Intent-gated tool exposure so the model sees fewer irrelevant tools per turn while retaining broad capability coverage.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-agent",
3
- "version": "0.1.25",
3
+ "version": "0.1.27",
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",
@@ -33,6 +33,7 @@
33
33
  "CHANGELOG.md",
34
34
  "docs/README.md",
35
35
  "docs/getting-started.md",
36
+ "docs/operator-capability-benchmark.md",
36
37
  "docs/deployment-and-services.md",
37
38
  "docs/release-and-publishing.md"
38
39
  ],
@@ -0,0 +1,28 @@
1
+ import type { CliCommandOutput } from './types.ts';
2
+ import type { CliCommandRuntime } from './management.ts';
3
+ import {
4
+ buildOperatorCapabilityBenchmarkReport,
5
+ filterOperatorCapabilities,
6
+ renderOperatorCapabilityBenchmark,
7
+ } from '../operator/capability-benchmark.ts';
8
+
9
+ function readCapabilityQuery(args: readonly string[]): string | undefined {
10
+ const values = args.filter((arg) => !arg.startsWith('--'));
11
+ return values.length > 0 ? values.join(' ') : undefined;
12
+ }
13
+
14
+ export async function handleCapabilitiesCommand(runtime: CliCommandRuntime): Promise<CliCommandOutput> {
15
+ const query = readCapabilityQuery(runtime.cli.commandArgs);
16
+ const report = buildOperatorCapabilityBenchmarkReport();
17
+ const capabilities = filterOperatorCapabilities(report.capabilities, query);
18
+ if (runtime.cli.flags.outputFormat === 'json') {
19
+ return {
20
+ output: JSON.stringify({ ...report, capabilities }, null, 2),
21
+ exitCode: 0,
22
+ };
23
+ }
24
+ return {
25
+ output: renderOperatorCapabilityBenchmark(capabilities),
26
+ exitCode: 0,
27
+ };
28
+ }
package/src/cli/help.ts CHANGED
@@ -40,6 +40,7 @@ export function renderGoodVibesHelp(binary = 'goodvibes-agent'): string {
40
40
  ' providers List/inspect/use provider config/auth posture',
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
+ ' capabilities Show OpenClaw/Hermes capability parity and Agent readiness',
43
44
  ' knowledge Use isolated Agent Knowledge/Wiki routes',
44
45
  ' ask|search Shortcuts for isolated Agent Knowledge ask/search',
45
46
  ' delegate Explicitly delegate build/fix/review work to GoodVibes TUI',
@@ -94,6 +95,7 @@ export function renderGoodVibesHelp(binary = 'goodvibes-agent'): string {
94
95
  ` ${binary} models use openai:gpt-5.2`,
95
96
  ` ${binary} providers inspect openai`,
96
97
  ` ${binary} compat`,
98
+ ` ${binary} capabilities`,
97
99
  ` ${binary} knowledge status`,
98
100
  ` ${binary} knowledge ask "What is GoodVibes Agent?"`,
99
101
  ` ${binary} ask "What is GoodVibes Agent?"`,
@@ -162,6 +164,11 @@ const COMMAND_HELP: Record<string, CommandHelp> = {
162
164
  summary: 'Inspect package SDK pin, live daemon version, and Agent-specific knowledge route readiness.',
163
165
  examples: ['compat', 'compat --json'],
164
166
  },
167
+ capabilities: {
168
+ usage: ['capabilities [openclaw|hermes|query]', 'capabilities --json'],
169
+ summary: 'Show the OpenClaw/Hermes capability benchmark, Agent readiness, configuration commands, usage paths, and next product gaps.',
170
+ examples: ['capabilities', 'capabilities hermes', 'capabilities knowledge --json'],
171
+ },
165
172
  knowledge: {
166
173
  usage: [
167
174
  'knowledge status',
@@ -33,6 +33,7 @@ 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
35
  import { handleAgentKnowledgeCommand, handleAgentKnowledgeShortcutCommand, handleCompatCommand, handleDelegateCommand } from './agent-knowledge-command.ts';
36
+ import { handleCapabilitiesCommand } from './capabilities-command.ts';
36
37
  import { GOODVIBES_AGENT_SURFACE_ROOT } from '../config/surface.ts';
37
38
 
38
39
  export interface CliCommandRuntime {
@@ -698,6 +699,11 @@ export async function handleGoodVibesCliCommand(runtime: CliCommandRuntime): Pro
698
699
  console.log(result.output);
699
700
  return { handled: true, exitCode: result.exitCode };
700
701
  }
702
+ case 'capabilities': {
703
+ const result = await handleCapabilitiesCommand(runtime);
704
+ console.log(result.output);
705
+ return { handled: true, exitCode: result.exitCode };
706
+ }
701
707
  case 'knowledge': {
702
708
  const result = await handleAgentKnowledgeCommand(runtime);
703
709
  console.log(result.output);
@@ -41,6 +41,7 @@ const REQUIRED_TARBALL_PATHS = [
41
41
  '.goodvibes/GOODVIBES.md',
42
42
  'docs/README.md',
43
43
  'docs/getting-started.md',
44
+ 'docs/operator-capability-benchmark.md',
44
45
  'docs/deployment-and-services.md',
45
46
  'docs/release-and-publishing.md',
46
47
  ] as const;
package/src/cli/parser.ts CHANGED
@@ -28,6 +28,10 @@ const COMMAND_ALIASES: Readonly<Record<string, GoodVibesCliCommand>> = {
28
28
  auth: 'auth',
29
29
  compat: 'compat',
30
30
  compatibility: 'compat',
31
+ capabilities: 'capabilities',
32
+ capability: 'capabilities',
33
+ caps: 'capabilities',
34
+ benchmark: 'capabilities',
31
35
  knowledge: 'knowledge',
32
36
  know: 'knowledge',
33
37
  kb: 'knowledge',
package/src/cli/types.ts CHANGED
@@ -11,6 +11,7 @@ export type GoodVibesCliCommand =
11
11
  | 'providers'
12
12
  | 'auth'
13
13
  | 'compat'
14
+ | 'capabilities'
14
15
  | 'knowledge'
15
16
  | 'ask'
16
17
  | 'search'
@@ -0,0 +1,20 @@
1
+ import type { CommandRegistry } from '../command-registry.ts';
2
+ import {
3
+ filterOperatorCapabilities,
4
+ renderOperatorCapabilityBenchmark,
5
+ OPERATOR_CAPABILITY_BENCHMARKS,
6
+ } from '../../operator/capability-benchmark.ts';
7
+
8
+ export function registerCapabilitiesRuntimeCommands(registry: CommandRegistry): void {
9
+ registry.register({
10
+ name: 'capabilities',
11
+ aliases: ['caps', 'benchmark'],
12
+ description: 'Show the OpenClaw/Hermes capability benchmark and Agent readiness',
13
+ usage: '[openclaw|hermes|query]',
14
+ handler(args, ctx) {
15
+ const query = args.join(' ').trim() || undefined;
16
+ const capabilities = filterOperatorCapabilities(OPERATOR_CAPABILITY_BENCHMARKS, query);
17
+ ctx.print(renderOperatorCapabilityBenchmark(capabilities));
18
+ },
19
+ });
20
+ }
@@ -58,6 +58,7 @@ import { registerDelegationRuntimeCommands } from './commands/delegation-runtime
58
58
  import { registerPersonasRuntimeCommands } from './commands/personas-runtime.ts';
59
59
  import { registerAgentSkillsRuntimeCommands } from './commands/agent-skills-runtime.ts';
60
60
  import { registerRoutinesRuntimeCommands } from './commands/routines-runtime.ts';
61
+ import { registerCapabilitiesRuntimeCommands } from './commands/capabilities-runtime.ts';
61
62
 
62
63
  /**
63
64
  * registerBuiltinCommands - Register all built-in slash commands into the registry.
@@ -66,6 +67,7 @@ import { registerRoutinesRuntimeCommands } from './commands/routines-runtime.ts'
66
67
  export function registerBuiltinCommands(registry: CommandRegistry): void {
67
68
  registerShellCoreCommands(registry);
68
69
  registerAgentWorkspaceRuntimeCommands(registry);
70
+ registerCapabilitiesRuntimeCommands(registry);
69
71
  registerPersonasRuntimeCommands(registry);
70
72
  registerAgentSkillsRuntimeCommands(registry);
71
73
  registerRoutinesRuntimeCommands(registry);
@@ -0,0 +1,225 @@
1
+ export type CompetitorProduct = 'openclaw' | 'hermes';
2
+
3
+ export type CapabilityPosture =
4
+ | 'ready'
5
+ | 'configurable'
6
+ | 'external-daemon'
7
+ | 'explicit-delegation'
8
+ | 'guarded'
9
+ | 'in-progress';
10
+
11
+ export interface OperatorCapabilityBenchmark {
12
+ readonly id: string;
13
+ readonly title: string;
14
+ readonly posture: CapabilityPosture;
15
+ readonly competitors: readonly CompetitorProduct[];
16
+ readonly competitorBaseline: string;
17
+ readonly goodvibesAgent: string;
18
+ readonly configure: readonly string[];
19
+ readonly use: readonly string[];
20
+ readonly exceedsBy: readonly string[];
21
+ readonly next: readonly string[];
22
+ }
23
+
24
+ export interface OperatorCapabilityBenchmarkReport {
25
+ readonly generatedAt: string;
26
+ readonly packageName: '@pellux/goodvibes-agent';
27
+ readonly benchmarkSources: readonly string[];
28
+ readonly capabilities: readonly OperatorCapabilityBenchmark[];
29
+ }
30
+
31
+ export const OPERATOR_CAPABILITY_BENCHMARK_SOURCES = [
32
+ 'https://github.com/openclaw/openclaw/blob/main/README.md',
33
+ 'https://docs.openclaw.ai/help/faq',
34
+ 'https://docs.openclaw.ai/concepts/memory',
35
+ 'https://github.com/NousResearch/hermes-agent',
36
+ 'https://hermes-agent.nousresearch.com/docs/user-guide/features/tools/',
37
+ 'https://hermes-agent.nousresearch.com/docs/user-guide/features/cron/',
38
+ 'https://hermes-agent.nousresearch.com/docs/user-guide/profiles/',
39
+ ] as const;
40
+
41
+ export const OPERATOR_CAPABILITY_BENCHMARKS: readonly OperatorCapabilityBenchmark[] = [
42
+ {
43
+ id: 'terminal-operator-ui',
44
+ title: 'Terminal Operator UI',
45
+ posture: 'ready',
46
+ competitors: ['openclaw', 'hermes'],
47
+ competitorBaseline: 'Interactive CLI/TUI entry point with command discovery and persistent sessions.',
48
+ goodvibesAgent: 'Near-fork GoodVibes TUI shell, renderer, compositor, input, fullscreen workspace, history, command registry, and release gates.',
49
+ configure: ['goodvibes-agent onboarding', 'goodvibes-agent status', 'goodvibes-agent --no-alt-screen'],
50
+ use: ['goodvibes-agent', '/agent', '/help'],
51
+ exceedsBy: ['Diff-rendered GoodVibes compositor foundation', 'fullscreen operator workspace', 'strict TypeScript/Bun release gates'],
52
+ next: ['Continue porting more GoodVibes TUI modal/workspace affordances into Agent-first setup flows.'],
53
+ },
54
+ {
55
+ id: 'external-gateway',
56
+ title: 'Always-On Gateway / Daemon',
57
+ posture: 'external-daemon',
58
+ competitors: ['openclaw', 'hermes'],
59
+ competitorBaseline: 'Always-on gateway/service provides channel ingress, sessions, tools, events, and scheduled execution.',
60
+ goodvibesAgent: 'Connects to the GoodVibes daemon owned by GoodVibes TUI/daemon tooling; Agent never starts, stops, or owns daemon lifecycle.',
61
+ configure: ['goodvibes-agent compat', 'goodvibes-agent service check', 'goodvibes-agent control-plane status'],
62
+ use: ['goodvibes-agent status', 'goodvibes-agent doctor'],
63
+ exceedsBy: ['Typed SDK/operator contracts', 'explicit external-daemon boundary', 'no hidden lifecycle mutation from Agent'],
64
+ next: ['Improve daemon route readiness dashboard and setup diagnostics for first-run users.'],
65
+ },
66
+ {
67
+ id: 'multi-channel',
68
+ title: 'Channels And Companion Surfaces',
69
+ posture: 'configurable',
70
+ competitors: ['openclaw', 'hermes'],
71
+ competitorBaseline: 'Messaging gateway for WhatsApp, Telegram, Slack, Discord, Signal, iMessage, web chat, and related platforms.',
72
+ goodvibesAgent: 'Uses GoodVibes daemon channel, companion, pairing, QR, and session surfaces while keeping side effects behind explicit user action.',
73
+ configure: ['goodvibes-agent pair', 'goodvibes-agent qrcode', 'goodvibes-agent surfaces check'],
74
+ use: ['/channels', '/communication', '/pair'],
75
+ exceedsBy: ['Agent-owned safety policy over shared channel routes', 'read-only inspection by default', 'explicit approval path for external side effects'],
76
+ next: ['Add Agent-first channel onboarding workspace that exposes connected accounts, delivery defaults, and pairing state without daemon ownership.'],
77
+ },
78
+ {
79
+ id: 'isolated-knowledge-wiki',
80
+ title: 'Isolated Agent Knowledge / Wiki',
81
+ posture: 'ready',
82
+ competitors: ['openclaw', 'hermes'],
83
+ competitorBaseline: 'Persistent memory and knowledge/wiki layers with search, recall, provenance, and freshness checks.',
84
+ goodvibesAgent: 'Uses only /api/goodvibes-agent/knowledge/*; never falls back to default Knowledge/Wiki, HomeGraph, or Home Assistant routes.',
85
+ configure: ['goodvibes-agent compat', 'goodvibes-agent knowledge status'],
86
+ use: ['goodvibes-agent ask <question>', 'goodvibes-agent search <query>', '/knowledge ask <question>', '/knowledge ingest-url <url> --yes'],
87
+ exceedsBy: ['Dedicated product segment for Agent knowledge', 'route-level isolation', 'package gates that reject default wiki/HomeGraph fallback'],
88
+ next: ['Add richer Agent Knowledge ingestion workflows for artifacts, URLs, bookmarks, and review queues in the fullscreen workspace.'],
89
+ },
90
+ {
91
+ id: 'local-memory-skills-personas',
92
+ title: 'Local Memory, Skills, Personas, And Routines',
93
+ posture: 'ready',
94
+ competitors: ['openclaw', 'hermes'],
95
+ competitorBaseline: 'Skills/procedural memory, persona files, profile state, durable memory, and recurring workflows.',
96
+ goodvibesAgent: 'Typed local registries for Agent personas, skills, routines, and local memory; active items are injected into the serial main conversation.',
97
+ configure: ['/personas create ...', '/agent-skills create ...', '/routines create ...', '/memory add ...'],
98
+ use: ['/personas use <id>', '/skills local list', '/routines start <id>', '/memory search <query>'],
99
+ exceedsBy: ['Review/stale lifecycle fields', 'secret-value rejection', 'Agent-local state that does not contaminate wiki or HomeGraph'],
100
+ next: ['Add CLI parity for local registry lifecycle and a curated starter pack of operator skills/routines.'],
101
+ },
102
+ {
103
+ id: 'automation-schedules',
104
+ title: 'Automation, Schedules, And Routines',
105
+ posture: 'guarded',
106
+ competitors: ['openclaw', 'hermes'],
107
+ competitorBaseline: 'Cron/scheduler can create, pause, resume, run, remove, and deliver recurring tasks from natural language.',
108
+ goodvibesAgent: 'Observes public automation/schedule routes and allows only explicitly confirmed side-effecting route calls; hidden model scheduling/spawn paths are blocked.',
109
+ configure: ['/schedule list', '/routines create ...', 'goodvibes-agent status'],
110
+ use: ['/schedule list', '/routines start <id>'],
111
+ exceedsBy: ['No recursive hidden scheduler creation from model tools', 'explicit confirmation for side effects', 'local routines separate from daemon jobs'],
112
+ next: ['Build Agent-first routine-to-daemon schedule promotion flow with preview, confirmation, delivery target selection, and audit trail.'],
113
+ },
114
+ {
115
+ id: 'tool-gateway-mcp',
116
+ title: 'Tools, MCP, And Managed Integrations',
117
+ posture: 'configurable',
118
+ competitors: ['openclaw', 'hermes'],
119
+ competitorBaseline: 'Broad toolsets, MCP integration, browser/web/media tools, and configurable platform-specific tool availability.',
120
+ goodvibesAgent: 'Uses GoodVibes SDK tool registry, MCP inspection, provider tools, web search, media, plugins, and policy-gated model-visible tools.',
121
+ configure: ['/mcp servers', '/plugin list', 'goodvibes-agent providers', 'goodvibes-agent models'],
122
+ use: ['/mcp tools', '/provider current', 'goodvibes-agent search <query>'],
123
+ exceedsBy: ['Model-visible tool policies for read/write/network boundaries', 'typed contract gates', 'per-tool denial messages aligned with Agent product policy'],
124
+ next: ['Add a task-intent tool picker so only relevant tools are exposed per turn, reducing schema noise while preserving capability breadth.'],
125
+ },
126
+ {
127
+ id: 'voice-media-canvas',
128
+ title: 'Voice, Media, Canvas, And Nodes',
129
+ posture: 'in-progress',
130
+ competitors: ['openclaw', 'hermes'],
131
+ competitorBaseline: 'Voice/TTS, mobile nodes, live canvas, browser automation, image/video generation, and multimodal analysis.',
132
+ goodvibesAgent: 'Carries GoodVibes voice/media/browser/node primitives, but Agent-first setup and canvas/workspace UX still needs product wiring.',
133
+ configure: ['/tts status', '/provider media', '/agent'],
134
+ use: ['/tts speak <text>', '/image', '/media'],
135
+ exceedsBy: ['Shared GoodVibes media/provider substrate', 'future fullscreen workspaces can make setup inspectable and reversible'],
136
+ next: ['Implement Agent setup workspaces for voice, media, browser, and node surfaces with capability tests and non-leaky credential handling.'],
137
+ },
138
+ {
139
+ id: 'explicit-build-delegation',
140
+ title: 'Build/Fix/Review Delegation',
141
+ posture: 'explicit-delegation',
142
+ competitors: ['openclaw', 'hermes'],
143
+ competitorBaseline: 'Terminal/file/code tools and subagents can execute software tasks directly.',
144
+ goodvibesAgent: 'Main assistant stays serial. Explicit build/fix/review/code work is delegated to GoodVibes TUI/shared-session contracts; WRFC is opt-in only.',
145
+ configure: ['goodvibes-agent delegate --help', 'goodvibes-agent compat'],
146
+ use: ['goodvibes-agent delegate "fix the failing tests"', 'goodvibes-agent delegate --wrfc "implement and review the feature"'],
147
+ exceedsBy: ['Keeps personal operator brain separate from coding execution', 'delegates one authoritative TUI-owned execution chain', 'blocks local sibling agent fanout'],
148
+ next: ['Expose delegation receipts and artifacts in the operator workspace, with status recovery and user steering.'],
149
+ },
150
+ {
151
+ id: 'profiles-isolation',
152
+ title: 'Profiles And Isolated Operator State',
153
+ posture: 'configurable',
154
+ competitors: ['hermes'],
155
+ competitorBaseline: 'Profiles run independent agents with isolated configs, sessions, skills, memory, cron jobs, and gateway state.',
156
+ goodvibesAgent: 'Uses Agent surface root, explicit home/working-dir flags, local registries, sessions, and bundle export/import; daemon remains shared/external by design.',
157
+ configure: ['GOODVIBES_AGENT_HOME=<path> goodvibes-agent status', 'goodvibes-agent bundle export goodvibes-agent-bundle.json'],
158
+ use: ['goodvibes-agent --daemon-home <path> status', 'goodvibes-agent sessions list'],
159
+ exceedsBy: ['Typed support bundles', 'explicit daemon boundary', 'no accidental cross-product knowledge fallback'],
160
+ next: ['Add named Agent profiles with command aliases, isolated local registry paths, and profile-aware onboarding.'],
161
+ },
162
+ {
163
+ id: 'security-approvals',
164
+ title: 'Security, Approvals, And Policy',
165
+ posture: 'ready',
166
+ competitors: ['openclaw', 'hermes'],
167
+ competitorBaseline: 'Command approval, DM pairing, sandboxing, allowlists, and safety defaults for exposed channels.',
168
+ goodvibesAgent: 'Uses daemon approvals, local auth diagnostics, secret refs, explicit confirmation gates, and Agent model-tool policy guards.',
169
+ configure: ['goodvibes-agent auth status', 'goodvibes-agent secrets providers', '/approvals list'],
170
+ use: ['/policy status', '/approvals list', 'goodvibes-agent doctor'],
171
+ exceedsBy: ['No token printing', 'secret-value rejection in local registries', 'policy tests for hidden spawn/lifecycle/default-knowledge failures'],
172
+ next: ['Add user-facing approval center in the Agent workspace with route risk labels and saved policy presets.'],
173
+ },
174
+ ] as const;
175
+
176
+ export function buildOperatorCapabilityBenchmarkReport(now = new Date()): OperatorCapabilityBenchmarkReport {
177
+ return {
178
+ generatedAt: now.toISOString(),
179
+ packageName: '@pellux/goodvibes-agent',
180
+ benchmarkSources: OPERATOR_CAPABILITY_BENCHMARK_SOURCES,
181
+ capabilities: OPERATOR_CAPABILITY_BENCHMARKS,
182
+ };
183
+ }
184
+
185
+ export function filterOperatorCapabilities(
186
+ capabilities: readonly OperatorCapabilityBenchmark[],
187
+ query: string | undefined,
188
+ ): readonly OperatorCapabilityBenchmark[] {
189
+ const normalized = query?.trim().toLowerCase();
190
+ if (!normalized) return capabilities;
191
+ return capabilities.filter((capability) => {
192
+ if (capability.id.includes(normalized)) return true;
193
+ if (capability.title.toLowerCase().includes(normalized)) return true;
194
+ if (capability.posture.includes(normalized)) return true;
195
+ if (capability.competitors.some((competitor) => competitor === normalized)) return true;
196
+ return capability.goodvibesAgent.toLowerCase().includes(normalized)
197
+ || capability.competitorBaseline.toLowerCase().includes(normalized);
198
+ });
199
+ }
200
+
201
+ export function renderOperatorCapabilityBenchmark(
202
+ capabilities: readonly OperatorCapabilityBenchmark[] = OPERATOR_CAPABILITY_BENCHMARKS,
203
+ ): string {
204
+ const lines: string[] = [
205
+ 'GoodVibes Agent capability benchmark',
206
+ ' Goal: match and exceed OpenClaw/Hermes personal-operator capabilities without default wiki/HomeGraph fallback or hidden local agent fanout.',
207
+ '',
208
+ ];
209
+
210
+ for (const capability of capabilities) {
211
+ lines.push(`${capability.title} [${capability.posture}]`);
212
+ lines.push(` competitors: ${capability.competitors.join(', ')}`);
213
+ lines.push(` baseline: ${capability.competitorBaseline}`);
214
+ lines.push(` Agent: ${capability.goodvibesAgent}`);
215
+ lines.push(` configure: ${capability.configure.join(' | ')}`);
216
+ lines.push(` use: ${capability.use.join(' | ')}`);
217
+ lines.push(` exceeds: ${capability.exceedsBy.join(' | ')}`);
218
+ lines.push(` next: ${capability.next.join(' | ')}`);
219
+ lines.push('');
220
+ }
221
+
222
+ lines.push('Sources:');
223
+ for (const source of OPERATOR_CAPABILITY_BENCHMARK_SOURCES) lines.push(` ${source}`);
224
+ return lines.join('\n');
225
+ }
@@ -0,0 +1,24 @@
1
+ import type { Tool } from '@pellux/goodvibes-sdk/platform/types';
2
+
3
+ const CONTEXT_TOOL_DENIAL = [
4
+ 'GoodVibes Agent does not expose copied GoodVibes runtime context through model tools in the main conversation.',
5
+ 'The copied context tool can describe TUI/default runtime assumptions that are not the Agent product boundary.',
6
+ 'Use explicit Agent CLI/slash commands such as status, compat, capabilities, and isolated Agent Knowledge instead.',
7
+ ].join(' ');
8
+
9
+ export function wrapBlockedContextToolForAgentPolicy(tool: Tool): void {
10
+ tool.definition.description = [
11
+ 'Blocked in GoodVibes Agent main conversation: copied runtime context.',
12
+ 'Use explicit Agent CLI/slash status, compat, capabilities, and Agent Knowledge commands for product-scoped context.',
13
+ 'Default Knowledge/Wiki, HomeGraph, and copied TUI runtime assumptions are not Agent fallbacks.',
14
+ ].join(' ');
15
+ tool.definition.sideEffects = [];
16
+ tool.definition.parameters = {
17
+ type: 'object',
18
+ properties: {},
19
+ additionalProperties: false,
20
+ };
21
+ tool.execute = async () => ({ success: false, error: CONTEXT_TOOL_DENIAL });
22
+ }
23
+
24
+ export const AGENT_CONTEXT_TOOL_DENIAL_MESSAGE = CONTEXT_TOOL_DENIAL;
@@ -0,0 +1,179 @@
1
+ import type { Tool } from '@pellux/goodvibes-sdk/platform/types';
2
+
3
+ type ReadFileArgs = {
4
+ readonly path?: unknown;
5
+ readonly image_mode?: unknown;
6
+ readonly [key: string]: unknown;
7
+ };
8
+
9
+ type ReadToolArgs = {
10
+ readonly files?: unknown;
11
+ readonly image_mode?: unknown;
12
+ readonly max_image_size?: unknown;
13
+ readonly [key: string]: unknown;
14
+ };
15
+
16
+ const READ_IMAGE_MODES = ['default', 'metadata-only', 'thumbnail-only'] as const;
17
+ const READ_IMAGE_MODE_SET = new Set<string>(READ_IMAGE_MODES);
18
+ const MAX_READ_FILES = 10;
19
+ const MAX_READ_IMAGE_SIZE_BYTES = 5 * 1024 * 1024;
20
+
21
+ const SECRET_FILE_EXTENSIONS = new Set([
22
+ '',
23
+ '.cfg',
24
+ '.conf',
25
+ '.env',
26
+ '.ini',
27
+ '.json',
28
+ '.properties',
29
+ '.toml',
30
+ '.txt',
31
+ '.yaml',
32
+ '.yml',
33
+ ]);
34
+
35
+ const SECRET_EXACT_SEGMENTS = new Set([
36
+ '.netrc',
37
+ '.npmrc',
38
+ '.pypirc',
39
+ 'apikey',
40
+ 'api_key',
41
+ 'credentials',
42
+ 'id_dsa',
43
+ 'id_ecdsa',
44
+ 'id_ed25519',
45
+ 'id_rsa',
46
+ 'password',
47
+ 'passwords',
48
+ 'passwd',
49
+ 'secret',
50
+ 'secrets',
51
+ 'token',
52
+ 'tokens',
53
+ ]);
54
+
55
+ const SECRET_EXACT_FILE_NAMES = new Set([
56
+ 'known_hosts',
57
+ ]);
58
+
59
+ const PRIVATE_KEY_EXTENSIONS = new Set([
60
+ '.key',
61
+ '.p12',
62
+ '.pem',
63
+ '.pfx',
64
+ ]);
65
+
66
+ const READ_POLICY_DENIAL = [
67
+ 'GoodVibes Agent only exposes bounded, non-secret project reads from the main conversation.',
68
+ 'Hidden paths, secret-looking files, broad batches, unoptimized image extraction, and oversized image reads are disabled here.',
69
+ 'Use explicit Agent CLI/slash commands or GoodVibes TUI delegation when the user intentionally asks for sensitive or deeper local inspection.',
70
+ ].join(' ');
71
+
72
+ export const AGENT_READ_IMAGE_MODES = READ_IMAGE_MODES;
73
+ export const AGENT_MAX_READ_FILES = MAX_READ_FILES;
74
+ export const AGENT_MAX_READ_IMAGE_SIZE_BYTES = MAX_READ_IMAGE_SIZE_BYTES;
75
+ export const AGENT_READ_POLICY_DENIAL_MESSAGE = READ_POLICY_DENIAL;
76
+
77
+ export function wrapReadToolForAgentPolicy(tool: Tool): void {
78
+ narrowReadToolDefinitionForAgentPolicy(tool);
79
+ const originalExecute = tool.execute.bind(tool);
80
+ tool.execute = async (args) => {
81
+ const readArgs = args as ReadToolArgs;
82
+ const denial = validateReadToolInvocationForAgentPolicy(readArgs);
83
+ if (denial) return { success: false, error: denial };
84
+ return originalExecute(args);
85
+ };
86
+ }
87
+
88
+ export function validateReadToolInvocationForAgentPolicy(args: ReadToolArgs): string | null {
89
+ if (Array.isArray(args.files) && args.files.length > MAX_READ_FILES) return READ_POLICY_DENIAL;
90
+ if (args.image_mode === 'unoptimized') return READ_POLICY_DENIAL;
91
+ if (typeof args.image_mode === 'string' && !READ_IMAGE_MODE_SET.has(args.image_mode)) return READ_POLICY_DENIAL;
92
+ if (typeof args.max_image_size === 'number' && args.max_image_size > MAX_READ_IMAGE_SIZE_BYTES) {
93
+ return READ_POLICY_DENIAL;
94
+ }
95
+
96
+ if (!Array.isArray(args.files)) return null;
97
+ for (const file of args.files) {
98
+ if (!isRecord(file)) continue;
99
+ const fileArgs = file as ReadFileArgs;
100
+ if (fileArgs.image_mode === 'unoptimized') return READ_POLICY_DENIAL;
101
+ if (typeof fileArgs.image_mode === 'string' && !READ_IMAGE_MODE_SET.has(fileArgs.image_mode)) {
102
+ return READ_POLICY_DENIAL;
103
+ }
104
+ if (typeof fileArgs.path === 'string' && isBlockedReadPath(fileArgs.path)) return READ_POLICY_DENIAL;
105
+ }
106
+
107
+ return null;
108
+ }
109
+
110
+ export function isBlockedReadPath(path: string): boolean {
111
+ const segments = path.replaceAll('\\', '/').split('/').filter((segment) => segment.length > 0);
112
+ for (const segment of segments) {
113
+ if (segment === '.' || segment === '..') continue;
114
+ const lower = segment.toLowerCase();
115
+ if (lower.startsWith('.')) return true;
116
+ if (SECRET_EXACT_SEGMENTS.has(lower)) return true;
117
+ }
118
+
119
+ const fileName = segments.at(-1)?.toLowerCase() ?? '';
120
+ if (fileName.length === 0) return false;
121
+ if (SECRET_EXACT_FILE_NAMES.has(fileName)) return true;
122
+ const extension = getExtension(fileName);
123
+ if (PRIVATE_KEY_EXTENSIONS.has(extension)) return true;
124
+ const stem = extension.length > 0 ? fileName.slice(0, -extension.length) : fileName;
125
+ return SECRET_EXACT_SEGMENTS.has(stem) && SECRET_FILE_EXTENSIONS.has(extension);
126
+ }
127
+
128
+ function narrowReadToolDefinitionForAgentPolicy(tool: Tool): void {
129
+ tool.definition.description = [
130
+ 'Read ordinary project files for GoodVibes Agent with bounded, non-secret, main-conversation policy.',
131
+ 'Hidden paths, secret-looking files, broad batches, unoptimized image extraction, and oversized image reads are disabled.',
132
+ ].join(' ');
133
+ tool.definition.sideEffects = ['read_fs'];
134
+ tool.definition.concurrency = 'serial';
135
+
136
+ const properties = tool.definition.parameters.properties;
137
+ if (!isRecord(properties)) return;
138
+
139
+ const files = properties.files;
140
+ if (isRecord(files)) {
141
+ files.maxItems = MAX_READ_FILES;
142
+ files.description = 'Ordinary non-hidden, non-secret-looking project files to read. Sensitive paths require explicit user-directed workflows.';
143
+ const itemSchema = files.items;
144
+ if (isRecord(itemSchema)) {
145
+ const fileProperties = itemSchema.properties;
146
+ if (isRecord(fileProperties)) {
147
+ const pathProperty = fileProperties.path;
148
+ if (isRecord(pathProperty)) {
149
+ pathProperty.description = 'Relative or absolute path to a non-hidden, non-secret-looking project file.';
150
+ }
151
+ narrowImageModeProperty(fileProperties, 'image_mode');
152
+ }
153
+ }
154
+ }
155
+
156
+ narrowImageModeProperty(properties, 'image_mode');
157
+ const maxImageSize = properties.max_image_size;
158
+ if (isRecord(maxImageSize)) {
159
+ maxImageSize.maximum = MAX_READ_IMAGE_SIZE_BYTES;
160
+ maxImageSize.description = 'Maximum image file size in bytes allowed by GoodVibes Agent read policy.';
161
+ }
162
+ }
163
+
164
+ function narrowImageModeProperty(properties: Record<string, unknown>, key: string): void {
165
+ const property = properties[key];
166
+ if (!isRecord(property)) return;
167
+ property.enum = [...READ_IMAGE_MODES];
168
+ property.description = 'Image handling mode allowed by GoodVibes Agent read policy. Full-resolution unoptimized extraction is disabled.';
169
+ }
170
+
171
+ function getExtension(fileName: string): string {
172
+ const dotIndex = fileName.lastIndexOf('.');
173
+ if (dotIndex <= 0) return '';
174
+ return fileName.slice(dotIndex);
175
+ }
176
+
177
+ function isRecord(value: unknown): value is Record<string, unknown> {
178
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
179
+ }
@@ -4,7 +4,11 @@ import {
4
4
  wrapAnalyzeToolForAgentPolicy,
5
5
  wrapRegistryToolForAgentPolicy,
6
6
  } from './agent-analysis-registry-policy.ts';
7
+ import {
8
+ wrapBlockedContextToolForAgentPolicy,
9
+ } from './agent-context-policy.ts';
7
10
  import { wrapFindToolForAgentPolicy } from './agent-find-policy.ts';
11
+ import { wrapReadToolForAgentPolicy } from './agent-read-policy.ts';
8
12
  import { wrapWebSearchToolForAgentPolicy } from './agent-web-search-policy.ts';
9
13
 
10
14
  type AgentToolArgs = {
@@ -198,6 +202,8 @@ export function installAgentToolPolicyGuard(registry: ToolRegistry, options: Age
198
202
  for (const tool of registry.list()) {
199
203
  if (tool.definition.name === 'exec') {
200
204
  wrapExecToolForAgentPolicy(tool);
205
+ } else if (tool.definition.name === 'read') {
206
+ wrapReadToolForAgentPolicy(tool);
201
207
  } else if (tool.definition.name === 'remote') {
202
208
  wrapModeRestrictedToolForAgentPolicy(tool, {
203
209
  allowedModes: READ_ONLY_REMOTE_TOOL_MODES,
@@ -224,6 +230,8 @@ export function installAgentToolPolicyGuard(registry: ToolRegistry, options: Age
224
230
  wrapFetchToolForAgentPolicy(tool);
225
231
  } else if (tool.definition.name === 'state') {
226
232
  wrapStateToolForAgentPolicy(tool);
233
+ } else if (tool.definition.name === 'goodvibes_context') {
234
+ wrapBlockedContextToolForAgentPolicy(tool);
227
235
  } else if (tool.definition.name === 'goodvibes_settings') {
228
236
  wrapBlockedSettingsToolForAgentPolicy(tool);
229
237
  } else if (tool.definition.name === 'inspect') {
@@ -548,6 +556,11 @@ export const AGENT_INSPECT_WRITE_DENIAL_MESSAGE = INSPECT_WRITE_DENIAL;
548
556
  export const AGENT_DURABLE_WORKFLOW_MUTATION_DENIAL_MESSAGE = DURABLE_WORKFLOW_MUTATION_DENIAL;
549
557
  export const AGENT_CONTROL_MUTATION_DENIAL_MESSAGE = CONTROL_MUTATION_DENIAL;
550
558
 
559
+ export {
560
+ AGENT_CONTEXT_TOOL_DENIAL_MESSAGE,
561
+ wrapBlockedContextToolForAgentPolicy,
562
+ } from './agent-context-policy.ts';
563
+
551
564
  export {
552
565
  AGENT_ANALYZE_NETWORK_DENIAL_MESSAGE,
553
566
  AGENT_READ_ONLY_ANALYZE_TOOL_MODES,
@@ -559,6 +572,16 @@ export {
559
572
  wrapRegistryToolForAgentPolicy,
560
573
  } from './agent-analysis-registry-policy.ts';
561
574
 
575
+ export {
576
+ AGENT_MAX_READ_FILES,
577
+ AGENT_MAX_READ_IMAGE_SIZE_BYTES,
578
+ AGENT_READ_IMAGE_MODES,
579
+ AGENT_READ_POLICY_DENIAL_MESSAGE,
580
+ isBlockedReadPath,
581
+ validateReadToolInvocationForAgentPolicy,
582
+ wrapReadToolForAgentPolicy,
583
+ } from './agent-read-policy.ts';
584
+
562
585
  export {
563
586
  AGENT_FIND_POLICY_DENIAL_MESSAGE,
564
587
  AGENT_READ_ONLY_FIND_OUTPUT_FORMATS,
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.25';
9
+ let _version = '0.1.27';
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 {