@pellux/goodvibes-agent 0.1.26 → 0.1.28

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.28 - 2026-05-31
6
+
7
+ - 77a9dc4 Add isolated Agent runtime profiles
8
+
9
+ ## 0.1.27 - 2026-05-31
10
+
11
+ - b5be172 Add capabilities CLI handler
12
+ - cafe1fb Add Agent capability benchmark surface
13
+
5
14
  ## 0.1.26 - 2026-05-31
6
15
 
7
16
  - dfb6147 Harden Agent read 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,18 @@ 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
+
49
+ Use isolated Agent runtime profiles when one machine needs separate operator identities or local state:
50
+
51
+ ```sh
52
+ goodvibes-agent profiles create household --yes
53
+ goodvibes-agent --agent-profile household status
54
+ GOODVIBES_AGENT_HOME=/path/to/agent-home goodvibes-agent status
55
+ ```
56
+
57
+ Profiles isolate Agent-local config, sessions, local memory, personas, skills, routines, and setup state. The daemon is still external and shared unless your daemon host is separately configured otherwise.
58
+
46
59
  Local Agent behavior is editable from the TUI:
47
60
 
48
61
  ```text
@@ -81,6 +94,7 @@ GoodVibes TUI owns coding execution: file edits, git/worktree workflows, coding
81
94
  Package-facing docs are intentionally narrow during the near-fork baseline:
82
95
 
83
96
  - [Getting Started](docs/getting-started.md)
97
+ - [Operator Capability Benchmark](docs/operator-capability-benchmark.md)
84
98
  - [Deployment And Services](docs/deployment-and-services.md)
85
99
  - [Release And Publishing](docs/release-and-publishing.md)
86
100
 
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,8 @@ 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.
23
+ - Agent supports isolated runtime homes with `GOODVIBES_AGENT_HOME=<path>` and named profile homes with `goodvibes-agent profiles create <name> --yes` plus `--agent-profile <name>`.
21
24
  - Local personas, routines, and Agent skills are stored under the Agent surface root and are injected only into the serial Agent conversation.
22
25
  - Normal assistant chat is not coding-session delegation.
23
26
  - Build/fix/review delegation to GoodVibes TUI must be explicit; WRFC is not the default Agent behavior.
@@ -1,6 +1,6 @@
1
1
  # Getting Started
2
2
 
3
- GoodVibes Agent `0.1.9` is the current installable public alpha of the personal operator assistant built on the GoodVibes TUI foundation.
3
+ GoodVibes Agent is the installable public alpha of the personal operator assistant built on the GoodVibes TUI foundation.
4
4
 
5
5
  ## Requirements
6
6
 
@@ -37,6 +37,24 @@ bun run dev
37
37
 
38
38
  Once the TUI opens, run `/agent`, `/home`, or `/operator` to open the Agent operator workspace. That fullscreen workspace is the current front door for setup/config, knowledge status, local memory and skills, read-only work/approval/automation views, and explicit GoodVibes TUI build delegation.
39
39
 
40
+ ## Isolated Agent Profiles
41
+
42
+ Use a separate Agent home when you want isolated local state:
43
+
44
+ ```sh
45
+ GOODVIBES_AGENT_HOME=/path/to/agent-home goodvibes-agent status
46
+ ```
47
+
48
+ Use named runtime profiles for repeatable local identities:
49
+
50
+ ```sh
51
+ goodvibes-agent profiles create household --yes
52
+ goodvibes-agent --agent-profile household status
53
+ goodvibes-agent --agent-profile household
54
+ ```
55
+
56
+ Named profiles isolate Agent-local config, sessions, memory, personas, skills, routines, and setup state under a profile-specific home. They do not start or isolate the external daemon by themselves.
57
+
40
58
  ## Local Personas, Routines, And Skills
41
59
 
42
60
  Personas, routines, and reusable Agent skills are local to GoodVibes Agent. They do not write into default Knowledge/Wiki or HomeGraph.
@@ -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 | `GOODVIBES_AGENT_HOME` and named `--agent-profile` homes isolate Agent-local state; daemon remains external |
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
+ - Profile-aware onboarding summaries and profile export/import shortcuts from the Agent workspace.
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.26",
3
+ "version": "0.1.28",
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,119 @@
1
+ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ export interface AgentRuntimeProfileResolution {
5
+ readonly id: string;
6
+ readonly homeDirectory: string;
7
+ }
8
+
9
+ export interface AgentRuntimeProfileInfo extends AgentRuntimeProfileResolution {
10
+ readonly createdAt: string | null;
11
+ }
12
+
13
+ export interface AgentRuntimeProfileCommandResult {
14
+ readonly ok: boolean;
15
+ readonly kind:
16
+ | 'agent.profiles.list'
17
+ | 'agent.profiles.show'
18
+ | 'agent.profiles.create'
19
+ | 'agent.profiles.delete'
20
+ | 'agent.profiles.error';
21
+ readonly data?: {
22
+ readonly profiles?: readonly AgentRuntimeProfileInfo[];
23
+ readonly profile?: AgentRuntimeProfileInfo;
24
+ readonly nextCommand?: string;
25
+ };
26
+ readonly error?: string;
27
+ }
28
+
29
+ const PROFILE_CREATED_FILE = 'profile.json';
30
+ const PROFILE_ID_PATTERN = /^[a-z0-9](?:[a-z0-9._-]{0,62}[a-z0-9])?$/;
31
+
32
+ export function normalizeAgentRuntimeProfileId(value: string): string {
33
+ return value
34
+ .trim()
35
+ .toLowerCase()
36
+ .replace(/[^a-z0-9._-]+/g, '-')
37
+ .replace(/^[._-]+|[._-]+$/g, '')
38
+ .replace(/[-_]{2,}/g, '-');
39
+ }
40
+
41
+ export function assertValidAgentRuntimeProfileId(value: string): string {
42
+ const raw = value.trim();
43
+ if (raw.includes('..') || raw.includes('/') || raw.includes('\\')) {
44
+ throw new Error('Agent profile names cannot contain path traversal sequences.');
45
+ }
46
+ const normalized = normalizeAgentRuntimeProfileId(value);
47
+ if (!PROFILE_ID_PATTERN.test(normalized)) {
48
+ throw new Error('Agent profile names must normalize to 1-64 lowercase letters, numbers, dots, underscores, or dashes.');
49
+ }
50
+ return normalized;
51
+ }
52
+
53
+ export function getAgentRuntimeProfilesRoot(baseHomeDirectory: string): string {
54
+ return join(baseHomeDirectory, '.goodvibes', 'agent', 'profile-homes');
55
+ }
56
+
57
+ export function resolveAgentRuntimeProfileHome(baseHomeDirectory: string, profileName: string): AgentRuntimeProfileResolution {
58
+ const id = assertValidAgentRuntimeProfileId(profileName);
59
+ return {
60
+ id,
61
+ homeDirectory: join(getAgentRuntimeProfilesRoot(baseHomeDirectory), id),
62
+ };
63
+ }
64
+
65
+ function readProfileCreatedAt(homeDirectory: string): string | null {
66
+ const path = join(homeDirectory, PROFILE_CREATED_FILE);
67
+ if (!existsSync(path)) return null;
68
+ try {
69
+ const raw = JSON.parse(readFileSync(path, 'utf-8'));
70
+ if (raw && typeof raw === 'object' && typeof raw.createdAt === 'string') return raw.createdAt;
71
+ } catch {
72
+ return null;
73
+ }
74
+ return null;
75
+ }
76
+
77
+ function buildProfileInfo(baseHomeDirectory: string, id: string): AgentRuntimeProfileInfo {
78
+ const { homeDirectory } = resolveAgentRuntimeProfileHome(baseHomeDirectory, id);
79
+ return {
80
+ id,
81
+ homeDirectory,
82
+ createdAt: readProfileCreatedAt(homeDirectory),
83
+ };
84
+ }
85
+
86
+ export function listAgentRuntimeProfiles(baseHomeDirectory: string): readonly AgentRuntimeProfileInfo[] {
87
+ const root = getAgentRuntimeProfilesRoot(baseHomeDirectory);
88
+ if (!existsSync(root)) return [];
89
+ return readdirSync(root)
90
+ .filter((entry) => PROFILE_ID_PATTERN.test(entry) && !entry.includes('..'))
91
+ .filter((entry) => {
92
+ try {
93
+ return statSync(join(root, entry)).isDirectory();
94
+ } catch {
95
+ return false;
96
+ }
97
+ })
98
+ .sort((left, right) => left.localeCompare(right))
99
+ .map((entry) => buildProfileInfo(baseHomeDirectory, entry));
100
+ }
101
+
102
+ export function createAgentRuntimeProfile(baseHomeDirectory: string, profileName: string): AgentRuntimeProfileInfo {
103
+ const resolution = resolveAgentRuntimeProfileHome(baseHomeDirectory, profileName);
104
+ mkdirSync(resolution.homeDirectory, { recursive: true });
105
+ const createdAt = new Date().toISOString();
106
+ writeFileSync(
107
+ join(resolution.homeDirectory, PROFILE_CREATED_FILE),
108
+ `${JSON.stringify({ id: resolution.id, createdAt }, null, 2)}\n`,
109
+ 'utf-8',
110
+ );
111
+ return { ...resolution, createdAt };
112
+ }
113
+
114
+ export function deleteAgentRuntimeProfile(baseHomeDirectory: string, profileName: string): boolean {
115
+ const resolution = resolveAgentRuntimeProfileHome(baseHomeDirectory, profileName);
116
+ if (!existsSync(resolution.homeDirectory)) return false;
117
+ rmSync(resolution.homeDirectory, { recursive: true, force: true });
118
+ return true;
119
+ }
@@ -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
+ }
@@ -11,7 +11,14 @@ const COMMANDS = [
11
11
  'status',
12
12
  'models',
13
13
  'providers',
14
+ 'profiles',
14
15
  'auth',
16
+ 'compat',
17
+ 'capabilities',
18
+ 'knowledge',
19
+ 'ask',
20
+ 'search',
21
+ 'delegate',
15
22
  'subscription',
16
23
  'secrets',
17
24
  'sessions',
@@ -34,6 +41,7 @@ const OPTIONS = [
34
41
  '--version',
35
42
  '--model',
36
43
  '--provider',
44
+ '--agent-profile',
37
45
  '--cd',
38
46
  '--working-dir',
39
47
  '--daemon-home',
@@ -23,6 +23,7 @@ import {
23
23
  } from './index.ts';
24
24
  import { buildCliServicePosture } from './service-posture.ts';
25
25
  import { GOODVIBES_AGENT_SURFACE_ROOT } from '../config/surface.ts';
26
+ import { resolveAgentRuntimeProfileHome } from '../agent/runtime-profile.ts';
26
27
 
27
28
  type ShellEntrypointOwnership = {
28
29
  readonly workingDirectory: string;
@@ -41,10 +42,17 @@ export type PreparedShellCliRuntime = {
41
42
  readonly bootstrapHomeDirectory: string;
42
43
  };
43
44
 
44
- function resolveShellEntrypointOwnership(roots: ShellEntrypointRoots, workingDirOverride?: string): ShellEntrypointOwnership {
45
+ function resolveShellEntrypointOwnership(
46
+ roots: ShellEntrypointRoots,
47
+ workingDirOverride?: string,
48
+ agentProfile?: string,
49
+ ): ShellEntrypointOwnership {
50
+ const homeDirectory = agentProfile
51
+ ? resolveAgentRuntimeProfileHome(roots.homeDirectory, agentProfile).homeDirectory
52
+ : roots.homeDirectory;
45
53
  return {
46
54
  workingDirectory: workingDirOverride ?? roots.defaultWorkingDirectory,
47
- homeDirectory: roots.homeDirectory,
55
+ homeDirectory,
48
56
  };
49
57
  }
50
58
 
@@ -86,10 +94,21 @@ export async function prepareShellCliRuntime(
86
94
  process.exit(2);
87
95
  }
88
96
 
97
+ let ownership: ShellEntrypointOwnership;
98
+ try {
99
+ ownership = resolveShellEntrypointOwnership(
100
+ roots,
101
+ cli.flags.workingDir ?? (cli.command === 'tui' ? cli.commandArgs[0] : undefined),
102
+ cli.command === 'profiles' ? undefined : cli.flags.agentProfile,
103
+ );
104
+ } catch (error) {
105
+ console.error(error instanceof Error ? error.message : String(error));
106
+ process.exit(2);
107
+ }
89
108
  const {
90
109
  workingDirectory: bootstrapWorkingDir,
91
110
  homeDirectory: bootstrapHomeDirectory,
92
- } = resolveShellEntrypointOwnership(roots, cli.flags.workingDir ?? (cli.command === 'tui' ? cli.commandArgs[0] : undefined));
111
+ } = ownership;
93
112
  configureActivityLogger(join(bootstrapWorkingDir, '.goodvibes', 'logs'));
94
113
  const configManager = new ConfigManager({
95
114
  workingDir: bootstrapWorkingDir,
package/src/cli/help.ts CHANGED
@@ -38,8 +38,10 @@ export function renderGoodVibesHelp(binary = 'goodvibes-agent'): string {
38
38
  ' onboarding [status] Open Agent onboarding, or print onboarding status',
39
39
  ' models [provider] List/use/pin selectable models and recent model history',
40
40
  ' providers List/inspect/use provider config/auth posture',
41
+ ' profiles Manage isolated Agent runtime profile homes',
41
42
  ' auth Inspect and manage local users, sessions, and bootstrap auth',
42
43
  ' compat Inspect Agent SDK pin, daemon version, and Agent knowledge route readiness',
44
+ ' capabilities Show OpenClaw/Hermes capability parity and Agent readiness',
43
45
  ' knowledge Use isolated Agent Knowledge/Wiki routes',
44
46
  ' ask|search Shortcuts for isolated Agent Knowledge ask/search',
45
47
  ' delegate Explicitly delegate build/fix/review work to GoodVibes TUI',
@@ -61,6 +63,7 @@ export function renderGoodVibesHelp(binary = 'goodvibes-agent'): string {
61
63
  'Options:',
62
64
  ' -m, --model <registryKey> Override model. provider:model infers --provider',
63
65
  ' --provider <id> Override provider',
66
+ ' --agent-profile <name> Use an isolated Agent runtime profile home',
64
67
  ' -C, --cd <dir> Set working directory for this launch',
65
68
  ' --working-dir <dir> Alias for --cd',
66
69
  ' --daemon-home <dir> Override daemon home for daemon-backed commands',
@@ -93,7 +96,10 @@ export function renderGoodVibesHelp(binary = 'goodvibes-agent'): string {
93
96
  ` ${binary} models current`,
94
97
  ` ${binary} models use openai:gpt-5.2`,
95
98
  ` ${binary} providers inspect openai`,
99
+ ` ${binary} profiles create household --yes`,
100
+ ` ${binary} --agent-profile household`,
96
101
  ` ${binary} compat`,
102
+ ` ${binary} capabilities`,
97
103
  ` ${binary} knowledge status`,
98
104
  ` ${binary} knowledge ask "What is GoodVibes Agent?"`,
99
105
  ` ${binary} ask "What is GoodVibes Agent?"`,
@@ -147,6 +153,11 @@ const COMMAND_HELP: Record<string, CommandHelp> = {
147
153
  summary: 'Inspect and change provider setup, auth posture, model counts, and setup class.',
148
154
  examples: ['providers', 'providers inspect openai-subscriber', 'providers use openai openai:gpt-5.4'],
149
155
  },
156
+ profiles: {
157
+ usage: ['profiles list', 'profiles show <name>', 'profiles create <name> --yes', 'profiles delete <name> --yes', '--agent-profile <name>'],
158
+ summary: 'Create and inspect isolated Agent runtime profile homes. A profile changes Agent-local config, sessions, memory, personas, skills, routines, and setup paths without changing the externally owned daemon.',
159
+ examples: ['profiles create household --yes', 'profiles list', '--agent-profile household status', '--agent-profile household'],
160
+ },
150
161
  models: {
151
162
  usage: ['models [provider]', 'models current', 'models use <registryKey>', 'models pin <registryKey>', 'models recent'],
152
163
  summary: 'List, inspect, select, pin, and review model choices.',
@@ -162,6 +173,11 @@ const COMMAND_HELP: Record<string, CommandHelp> = {
162
173
  summary: 'Inspect package SDK pin, live daemon version, and Agent-specific knowledge route readiness.',
163
174
  examples: ['compat', 'compat --json'],
164
175
  },
176
+ capabilities: {
177
+ usage: ['capabilities [openclaw|hermes|query]', 'capabilities --json'],
178
+ summary: 'Show the OpenClaw/Hermes capability benchmark, Agent readiness, configuration commands, usage paths, and next product gaps.',
179
+ examples: ['capabilities', 'capabilities hermes', 'capabilities knowledge --json'],
180
+ },
165
181
  knowledge: {
166
182
  usage: [
167
183
  'knowledge status',
@@ -273,6 +289,7 @@ const HELP_ALIASES: Record<string, string> = {
273
289
  setup: 'onboarding',
274
290
  provider: 'providers',
275
291
  model: 'models',
292
+ profile: 'profiles',
276
293
  subscriptions: 'subscription',
277
294
  secret: 'secrets',
278
295
  session: 'sessions',
@@ -33,6 +33,8 @@ 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';
37
+ import { handleProfilesCommand } from './profiles-command.ts';
36
38
  import { GOODVIBES_AGENT_SURFACE_ROOT } from '../config/surface.ts';
37
39
 
38
40
  export interface CliCommandRuntime {
@@ -698,6 +700,16 @@ export async function handleGoodVibesCliCommand(runtime: CliCommandRuntime): Pro
698
700
  console.log(result.output);
699
701
  return { handled: true, exitCode: result.exitCode };
700
702
  }
703
+ case 'capabilities': {
704
+ const result = await handleCapabilitiesCommand(runtime);
705
+ console.log(result.output);
706
+ return { handled: true, exitCode: result.exitCode };
707
+ }
708
+ case 'profiles': {
709
+ const result = await handleProfilesCommand(runtime);
710
+ console.log(result.output);
711
+ return { handled: true, exitCode: result.exitCode };
712
+ }
701
713
  case 'knowledge': {
702
714
  const result = await handleAgentKnowledgeCommand(runtime);
703
715
  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
@@ -25,9 +25,15 @@ const COMMAND_ALIASES: Readonly<Record<string, GoodVibesCliCommand>> = {
25
25
  model: 'models',
26
26
  providers: 'providers',
27
27
  provider: 'providers',
28
+ profiles: 'profiles',
29
+ profile: 'profiles',
28
30
  auth: 'auth',
29
31
  compat: 'compat',
30
32
  compatibility: 'compat',
33
+ capabilities: 'capabilities',
34
+ capability: 'capabilities',
35
+ caps: 'capabilities',
36
+ benchmark: 'capabilities',
31
37
  knowledge: 'knowledge',
32
38
  know: 'knowledge',
33
39
  kb: 'knowledge',
@@ -69,6 +75,7 @@ function createDefaultFlags(): GoodVibesCliFlags {
69
75
  return {
70
76
  provider: undefined,
71
77
  model: undefined,
78
+ agentProfile: undefined,
72
79
  daemonHome: undefined,
73
80
  workingDir: undefined,
74
81
  help: false,
@@ -275,6 +282,12 @@ export function parseGoodVibesCli(
275
282
  }
276
283
  continue;
277
284
  }
285
+ if (name === '--agent-profile') {
286
+ const consumed = getValue(argv, index, inlineValue, name, errors);
287
+ index = consumed.nextIndex;
288
+ if (consumed.value !== undefined) flags = withFlag(flags, 'agentProfile', consumed.value);
289
+ continue;
290
+ }
278
291
  if (name === '--daemon-home') {
279
292
  const consumed = getValue(argv, index, inlineValue, name, errors);
280
293
  index = consumed.nextIndex;
@@ -0,0 +1,208 @@
1
+ import {
2
+ createAgentRuntimeProfile,
3
+ deleteAgentRuntimeProfile,
4
+ listAgentRuntimeProfiles,
5
+ resolveAgentRuntimeProfileHome,
6
+ type AgentRuntimeProfileCommandResult,
7
+ type AgentRuntimeProfileInfo,
8
+ } from '../agent/runtime-profile.ts';
9
+ import type { CliCommandOutput, GoodVibesCliParseResult } from './types.ts';
10
+
11
+ interface ProfilesCommandRuntime {
12
+ readonly cli: GoodVibesCliParseResult;
13
+ readonly homeDirectory: string;
14
+ }
15
+
16
+ function hasYes(args: readonly string[]): boolean {
17
+ return args.includes('--yes');
18
+ }
19
+
20
+ function commandValues(args: readonly string[]): readonly string[] {
21
+ const values: string[] = [];
22
+ for (let index = 0; index < args.length; index += 1) {
23
+ const token = args[index]!;
24
+ if (!token.startsWith('--')) values.push(token);
25
+ }
26
+ return values;
27
+ }
28
+
29
+ function profileLine(profile: AgentRuntimeProfileInfo): string {
30
+ const created = profile.createdAt ? ` created=${profile.createdAt}` : '';
31
+ return ` ${profile.id} home=${profile.homeDirectory}${created}`;
32
+ }
33
+
34
+ function renderProfilesResult(result: AgentRuntimeProfileCommandResult): string {
35
+ if (!result.ok) return result.error ?? 'Agent profile command failed.';
36
+ if (result.kind === 'agent.profiles.list') {
37
+ const profiles = result.data?.profiles ?? [];
38
+ if (profiles.length === 0) return 'No Agent runtime profiles. Use: goodvibes-agent profiles create <name> --yes';
39
+ return [
40
+ `Agent runtime profiles (${profiles.length})`,
41
+ ...profiles.map(profileLine),
42
+ ].join('\n');
43
+ }
44
+ const profile = result.data?.profile;
45
+ if (result.kind === 'agent.profiles.create' && profile) {
46
+ return [
47
+ `Agent runtime profile created: ${profile.id}`,
48
+ ` home: ${profile.homeDirectory}`,
49
+ ` use: ${result.data?.nextCommand ?? `goodvibes-agent --agent-profile ${profile.id}`}`,
50
+ ].join('\n');
51
+ }
52
+ if (result.kind === 'agent.profiles.delete' && profile) {
53
+ return `Agent runtime profile deleted: ${profile.id}`;
54
+ }
55
+ return 'Agent profile command completed.';
56
+ }
57
+
58
+ function renderProfilesOutput(result: AgentRuntimeProfileCommandResult, format: string): string {
59
+ if (format === 'json') return JSON.stringify(result, null, 2);
60
+ return renderProfilesResult(result);
61
+ }
62
+
63
+ export async function handleProfilesCommand(runtime: ProfilesCommandRuntime): Promise<CliCommandOutput> {
64
+ const [sub = 'list', ...rawRest] = runtime.cli.commandArgs;
65
+ const values = commandValues(rawRest);
66
+
67
+ try {
68
+ if (sub === 'list' || sub === 'ls') {
69
+ const result: AgentRuntimeProfileCommandResult = {
70
+ ok: true,
71
+ kind: 'agent.profiles.list',
72
+ data: { profiles: listAgentRuntimeProfiles(runtime.homeDirectory) },
73
+ };
74
+ return {
75
+ output: renderProfilesOutput(result, runtime.cli.flags.outputFormat),
76
+ exitCode: 0,
77
+ };
78
+ }
79
+
80
+ if (sub === 'show' || sub === 'path' || sub === 'home') {
81
+ const name = values[0];
82
+ if (!name) {
83
+ const result: AgentRuntimeProfileCommandResult = {
84
+ ok: false,
85
+ kind: 'agent.profiles.error',
86
+ error: 'Usage: goodvibes-agent profiles show <name>',
87
+ };
88
+ return {
89
+ output: renderProfilesOutput(result, runtime.cli.flags.outputFormat),
90
+ exitCode: 2,
91
+ };
92
+ }
93
+ const profile = resolveAgentRuntimeProfileHome(runtime.homeDirectory, name);
94
+ const result: AgentRuntimeProfileCommandResult = {
95
+ ok: true,
96
+ kind: 'agent.profiles.show',
97
+ data: { profile: { ...profile, createdAt: null } },
98
+ };
99
+ const text = [`Agent runtime profile: ${profile.id}`, ` home: ${profile.homeDirectory}`, ` use: goodvibes-agent --agent-profile ${profile.id}`].join('\n');
100
+ return {
101
+ output: runtime.cli.flags.outputFormat === 'json' ? JSON.stringify(result, null, 2) : text,
102
+ exitCode: 0,
103
+ };
104
+ }
105
+
106
+ if (sub === 'create') {
107
+ const name = values[0];
108
+ if (!name) {
109
+ const result: AgentRuntimeProfileCommandResult = {
110
+ ok: false,
111
+ kind: 'agent.profiles.error',
112
+ error: 'Usage: goodvibes-agent profiles create <name> --yes',
113
+ };
114
+ return {
115
+ output: renderProfilesOutput(result, runtime.cli.flags.outputFormat),
116
+ exitCode: 2,
117
+ };
118
+ }
119
+ if (!hasYes(rawRest)) {
120
+ const result: AgentRuntimeProfileCommandResult = {
121
+ ok: false,
122
+ kind: 'agent.profiles.error',
123
+ error: `Refusing to create Agent runtime profile ${name} without --yes.`,
124
+ };
125
+ return {
126
+ output: renderProfilesOutput(result, runtime.cli.flags.outputFormat),
127
+ exitCode: 2,
128
+ };
129
+ }
130
+ const profile = createAgentRuntimeProfile(runtime.homeDirectory, name);
131
+ const result: AgentRuntimeProfileCommandResult = {
132
+ ok: true,
133
+ kind: 'agent.profiles.create',
134
+ data: {
135
+ profile,
136
+ nextCommand: `goodvibes-agent --agent-profile ${profile.id}`,
137
+ },
138
+ };
139
+ return {
140
+ output: renderProfilesOutput(result, runtime.cli.flags.outputFormat),
141
+ exitCode: 0,
142
+ };
143
+ }
144
+
145
+ if (sub === 'delete' || sub === 'remove') {
146
+ const name = values[0];
147
+ if (!name) {
148
+ const result: AgentRuntimeProfileCommandResult = {
149
+ ok: false,
150
+ kind: 'agent.profiles.error',
151
+ error: 'Usage: goodvibes-agent profiles delete <name> --yes',
152
+ };
153
+ return {
154
+ output: renderProfilesOutput(result, runtime.cli.flags.outputFormat),
155
+ exitCode: 2,
156
+ };
157
+ }
158
+ if (!hasYes(rawRest)) {
159
+ const result: AgentRuntimeProfileCommandResult = {
160
+ ok: false,
161
+ kind: 'agent.profiles.error',
162
+ error: `Refusing to delete Agent runtime profile ${name} without --yes.`,
163
+ };
164
+ return {
165
+ output: renderProfilesOutput(result, runtime.cli.flags.outputFormat),
166
+ exitCode: 2,
167
+ };
168
+ }
169
+ const resolution = resolveAgentRuntimeProfileHome(runtime.homeDirectory, name);
170
+ const deleted = deleteAgentRuntimeProfile(runtime.homeDirectory, name);
171
+ const result: AgentRuntimeProfileCommandResult = deleted
172
+ ? {
173
+ ok: true,
174
+ kind: 'agent.profiles.delete',
175
+ data: { profile: { ...resolution, createdAt: null } },
176
+ }
177
+ : {
178
+ ok: false,
179
+ kind: 'agent.profiles.error',
180
+ error: `Agent runtime profile not found: ${resolution.id}`,
181
+ };
182
+ return {
183
+ output: renderProfilesOutput(result, runtime.cli.flags.outputFormat),
184
+ exitCode: deleted ? 0 : 1,
185
+ };
186
+ }
187
+
188
+ const result: AgentRuntimeProfileCommandResult = {
189
+ ok: false,
190
+ kind: 'agent.profiles.error',
191
+ error: 'Usage: goodvibes-agent profiles [list|show <name>|create <name> --yes|delete <name> --yes]',
192
+ };
193
+ return {
194
+ output: renderProfilesOutput(result, runtime.cli.flags.outputFormat),
195
+ exitCode: 2,
196
+ };
197
+ } catch (error) {
198
+ const result: AgentRuntimeProfileCommandResult = {
199
+ ok: false,
200
+ kind: 'agent.profiles.error',
201
+ error: error instanceof Error ? error.message : String(error),
202
+ };
203
+ return {
204
+ output: renderProfilesOutput(result, runtime.cli.flags.outputFormat),
205
+ exitCode: 2,
206
+ };
207
+ }
208
+ }
package/src/cli/types.ts CHANGED
@@ -9,8 +9,10 @@ export type GoodVibesCliCommand =
9
9
  | 'onboarding'
10
10
  | 'models'
11
11
  | 'providers'
12
+ | 'profiles'
12
13
  | 'auth'
13
14
  | 'compat'
15
+ | 'capabilities'
14
16
  | 'knowledge'
15
17
  | 'ask'
16
18
  | 'search'
@@ -41,6 +43,7 @@ export interface CliCommandOutput {
41
43
  export interface GoodVibesCliFlags {
42
44
  readonly provider: string | undefined;
43
45
  readonly model: string | undefined;
46
+ readonly agentProfile: string | undefined;
44
47
  readonly daemonHome: string | undefined;
45
48
  readonly workingDir: string | undefined;
46
49
  readonly help: boolean;
@@ -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);
package/src/main.ts CHANGED
@@ -70,7 +70,7 @@ async function main() {
70
70
  const stdin = process.stdin;
71
71
  const { cli, configManager, bootstrapWorkingDir, bootstrapHomeDirectory } = await prepareShellCliRuntime(process.argv.slice(2), {
72
72
  defaultWorkingDirectory: process.env['GOODVIBES_WORKING_DIR'] ?? process.cwd(),
73
- homeDirectory: homedir(),
73
+ homeDirectory: process.env['GOODVIBES_AGENT_HOME'] ?? homedir(),
74
74
  }, 'goodvibes-agent');
75
75
 
76
76
  const ctx: BootstrapContext = await bootstrapRuntime(stdout, {
@@ -267,7 +267,7 @@ async function main() {
267
267
 
268
268
  const submitInput = (text: string, content?: ContentPart[], options: { readonly spokenOutput?: boolean } = {}) => {
269
269
  input.clearModalStack();
270
- scrollLocked = true; // Re-lock on any user input
270
+ scrollLocked = true; // Re-lock on user input
271
271
  const AT_MODEL_RE = /@model:([^\s]+)/g;
272
272
  let processedText = text;
273
273
  let atModelMatch: RegExpExecArray | null;
@@ -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: 'ready',
154
+ competitors: ['hermes'],
155
+ competitorBaseline: 'Profiles run independent agents with isolated configs, sessions, skills, memory, cron jobs, and gateway state.',
156
+ goodvibesAgent: 'Supports GOODVIBES_AGENT_HOME and named --agent-profile homes for isolated Agent-local config, sessions, memory, personas, skills, routines, setup, and bundles; daemon remains shared/external by design.',
157
+ configure: ['GOODVIBES_AGENT_HOME=<path> goodvibes-agent status', 'goodvibes-agent profiles create household --yes', 'goodvibes-agent bundle export goodvibes-agent-bundle.json'],
158
+ use: ['goodvibes-agent --agent-profile household', 'goodvibes-agent --agent-profile household status', 'goodvibes-agent sessions list'],
159
+ exceedsBy: ['Typed support bundles', 'explicit daemon boundary', 'no accidental cross-product knowledge fallback', 'profile isolation without hidden daemon lifecycle ownership'],
160
+ next: ['Add profile-aware onboarding summaries and profile export/import shortcuts from the Agent workspace.'],
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;
@@ -4,6 +4,9 @@ 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';
8
11
  import { wrapReadToolForAgentPolicy } from './agent-read-policy.ts';
9
12
  import { wrapWebSearchToolForAgentPolicy } from './agent-web-search-policy.ts';
@@ -227,6 +230,8 @@ export function installAgentToolPolicyGuard(registry: ToolRegistry, options: Age
227
230
  wrapFetchToolForAgentPolicy(tool);
228
231
  } else if (tool.definition.name === 'state') {
229
232
  wrapStateToolForAgentPolicy(tool);
233
+ } else if (tool.definition.name === 'goodvibes_context') {
234
+ wrapBlockedContextToolForAgentPolicy(tool);
230
235
  } else if (tool.definition.name === 'goodvibes_settings') {
231
236
  wrapBlockedSettingsToolForAgentPolicy(tool);
232
237
  } else if (tool.definition.name === 'inspect') {
@@ -551,6 +556,11 @@ export const AGENT_INSPECT_WRITE_DENIAL_MESSAGE = INSPECT_WRITE_DENIAL;
551
556
  export const AGENT_DURABLE_WORKFLOW_MUTATION_DENIAL_MESSAGE = DURABLE_WORKFLOW_MUTATION_DENIAL;
552
557
  export const AGENT_CONTROL_MUTATION_DENIAL_MESSAGE = CONTROL_MUTATION_DENIAL;
553
558
 
559
+ export {
560
+ AGENT_CONTEXT_TOOL_DENIAL_MESSAGE,
561
+ wrapBlockedContextToolForAgentPolicy,
562
+ } from './agent-context-policy.ts';
563
+
554
564
  export {
555
565
  AGENT_ANALYZE_NETWORK_DENIAL_MESSAGE,
556
566
  AGENT_READ_ONLY_ANALYZE_TOOL_MODES,
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.26';
9
+ let _version = '0.1.28';
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 {