@pellux/goodvibes-agent 0.1.4 → 0.1.5

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,13 @@
2
2
 
3
3
  All notable changes to GoodVibes Agent will be recorded here.
4
4
 
5
+ ## 0.1.5 - 2026-05-31
6
+
7
+ - Hardened package-facing release checks so shipped docs and Agent guidance cannot reintroduce default Knowledge/Wiki, HomeGraph, Home Assistant, copied TUI daemon, or copied WRFC-first policy text.
8
+ - Removed the generic default `knowledgeApi` client from the active Agent command context so slash commands must use the isolated Agent Knowledge API.
9
+ - Changed CLI `knowledge ingest-url` to post directly to `/api/goodvibes-agent/knowledge/ingest/url` instead of invoking the generic knowledge operator method.
10
+ - Rejected `--space`, `--knowledge-space`, `--knowledgeSpaceId`, `--includeAllSpaces`, and HomeGraph-style flags in CLI and slash Agent Knowledge commands before any daemon call.
11
+
5
12
  ## 0.1.4 - 2026-05-31
6
13
 
7
14
  - Hardened Agent Knowledge route isolation for CLI JSON output and diagnostics: Agent status, ask, search, and ingest-url now report explicit `agentKnowledge.*` identities and `/api/goodvibes-agent/knowledge/*` routes.
@@ -1,6 +1,6 @@
1
1
  # Getting Started
2
2
 
3
- GoodVibes Agent `0.1.4` is the current installable public alpha of the personal operator assistant built on the GoodVibes TUI foundation.
3
+ GoodVibes Agent `0.1.5` is the current installable public alpha of the personal operator assistant built on the GoodVibes TUI foundation.
4
4
 
5
5
  ## Requirements
6
6
 
@@ -1,6 +1,6 @@
1
1
  # Release And Publishing
2
2
 
3
- GoodVibes Agent `0.1.4` is the current installable public alpha release.
3
+ GoodVibes Agent `0.1.5` is the current installable public alpha release.
4
4
 
5
5
  ## Package Identity
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-agent",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
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",
@@ -222,6 +222,62 @@ function createAgentSdk(connection: AgentDaemonConnection) {
222
222
  });
223
223
  }
224
224
 
225
+ async function postAgentKnowledgeJson<TData>(
226
+ connection: AgentDaemonConnection,
227
+ route: string,
228
+ body: JsonRecord,
229
+ ): Promise<TData> {
230
+ const response = await fetch(`${connection.baseUrl}${route}`, {
231
+ method: 'POST',
232
+ headers: {
233
+ authorization: `Bearer ${connection.token ?? ''}`,
234
+ 'content-type': 'application/json',
235
+ },
236
+ body: JSON.stringify(body),
237
+ });
238
+ const text = await response.text();
239
+ let parsed: unknown = text;
240
+ if (text.trim()) {
241
+ try {
242
+ parsed = JSON.parse(text) as unknown;
243
+ } catch {
244
+ parsed = text;
245
+ }
246
+ }
247
+ if (!response.ok) {
248
+ const detail = isRecord(parsed) && typeof parsed.error === 'string' ? parsed.error : text;
249
+ throw new Error(`HTTP ${response.status} ${response.statusText}${detail ? `: ${detail}` : ''}`);
250
+ }
251
+ return parsed as TData;
252
+ }
253
+
254
+ function findDisallowedKnowledgeScopeFlag(args: readonly string[]): string | null {
255
+ const disallowed = [
256
+ '--space',
257
+ '--knowledge-space',
258
+ '--knowledge-space-id',
259
+ '--knowledgeSpaceId',
260
+ '--include-all-spaces',
261
+ '--includeAllSpaces',
262
+ '--homegraph',
263
+ '--home-graph',
264
+ ];
265
+ for (const token of args) {
266
+ for (const flag of disallowed) {
267
+ if (token === flag || token.startsWith(`${flag}=`)) return flag;
268
+ }
269
+ }
270
+ return null;
271
+ }
272
+
273
+ function formatScopeFlagRejection(flag: string): string {
274
+ return [
275
+ `Agent Knowledge is isolated; ${flag} is not accepted.`,
276
+ 'GoodVibes Agent must not use default Knowledge/Wiki, HomeGraph, or Home Assistant spaces.',
277
+ 'Use only /api/goodvibes-agent/knowledge/* Agent-owned routes.',
278
+ ].join('\n');
279
+ }
280
+
225
281
  function sourceLine(value: unknown): string {
226
282
  const record = isRecord(value) ? value : {};
227
283
  const title = cleanInline(record.title)
@@ -384,6 +440,19 @@ export async function handleAgentKnowledgeCommand(runtime: CliCommandRuntime): P
384
440
  const [sub = 'status', ...rest] = runtime.cli.commandArgs;
385
441
  const normalized = sub.toLowerCase();
386
442
  const json = runtime.cli.flags.outputFormat === 'json';
443
+ const disallowedScopeFlag = findDisallowedKnowledgeScopeFlag(rest);
444
+ if (disallowedScopeFlag) {
445
+ const failure = {
446
+ ok: false,
447
+ kind: 'agent_knowledge_scope_rejected',
448
+ error: formatScopeFlagRejection(disallowedScopeFlag),
449
+ route: '/api/goodvibes-agent/knowledge/*',
450
+ };
451
+ return {
452
+ output: json ? JSON.stringify(failure, null, 2) : failure.error,
453
+ exitCode: 2,
454
+ };
455
+ }
387
456
 
388
457
  if (normalized === 'status') {
389
458
  const result = await runKnowledgeCall(runtime, AGENT_KNOWLEDGE_METHODS.status, async (connection) => (
@@ -440,7 +509,7 @@ export async function handleAgentKnowledgeCommand(runtime: CliCommandRuntime): P
440
509
  const title = readOptionValue(rest, '--title');
441
510
  const tags = readStringList(rest, '--tags');
442
511
  const result = await runKnowledgeCall(runtime, AGENT_KNOWLEDGE_METHODS.ingestUrl, async (connection) => (
443
- await createAgentSdk(connection).operator.invoke('knowledge.ingest.url', {
512
+ await postAgentKnowledgeJson(connection, AGENT_KNOWLEDGE_METHODS.ingestUrl.route, {
444
513
  url,
445
514
  title,
446
515
  tags,
@@ -21,6 +21,10 @@ export interface PackageCliVerificationReport {
21
21
  readonly requiredPathsPresent: readonly string[];
22
22
  readonly forbiddenPaths: readonly string[];
23
23
  };
24
+ readonly packageFacingText: {
25
+ readonly checkedPaths: readonly string[];
26
+ readonly failures: readonly string[];
27
+ };
24
28
  readonly issues: readonly string[];
25
29
  }
26
30
 
@@ -46,6 +50,43 @@ const FORBIDDEN_TARBALL_DOCS = [
46
50
  'docs/homeassistant-surface.md',
47
51
  'docs/wrfc/',
48
52
  ] as const;
53
+ const PACKAGE_FACING_TEXT_PATHS = [
54
+ 'README.md',
55
+ 'docs/README.md',
56
+ 'docs/getting-started.md',
57
+ 'docs/deployment-and-services.md',
58
+ 'docs/release-and-publishing.md',
59
+ '.goodvibes/GOODVIBES.md',
60
+ '.goodvibes/agents/reviewer.md',
61
+ '.goodvibes/skills/add-provider/SKILL.md',
62
+ ] as const;
63
+ const PACKAGE_FACING_FORBIDDEN_TEXT = [
64
+ '/api/knowledge',
65
+ '/api/homeassistant',
66
+ 'homeassistant.homeGraph',
67
+ 'includeAllSpaces',
68
+ 'knowledgeSpaceId',
69
+ '@pellux/goodvibes-tui',
70
+ '@pellux/goodvibes-daemon',
71
+ 'goodvibes-daemon',
72
+ '~/.goodvibes/tui',
73
+ 'Every plan must have a multi-agent execution strategy',
74
+ 'NEVER skip WRFC',
75
+ 'ALWAYS work in parallel when implementing a plan',
76
+ 'PRIMARY GOAL: Fully complete and functional code',
77
+ 'You are a code reviewer for the WRFC',
78
+ 'ReviewerReport',
79
+ '"wrfcId"',
80
+ ] as const;
81
+ const PACKAGE_FACING_REQUIRED_TEXT: readonly {
82
+ readonly path: typeof PACKAGE_FACING_TEXT_PATHS[number];
83
+ readonly required: readonly string[];
84
+ }[] = [
85
+ { path: 'README.md', required: ['/api/goodvibes-agent/knowledge'] },
86
+ { path: 'docs/README.md', required: ['/api/goodvibes-agent/knowledge'] },
87
+ { path: 'docs/getting-started.md', required: ['/api/goodvibes-agent/knowledge'] },
88
+ { path: 'docs/release-and-publishing.md', required: ['/api/goodvibes-agent/knowledge'] },
89
+ ];
49
90
 
50
91
  function readPackageJson(root: string): Record<string, unknown> {
51
92
  return JSON.parse(readFileSync(join(root, 'package.json'), 'utf-8')) as Record<string, unknown>;
@@ -82,6 +123,35 @@ function npmPackDryRun(root: string): { readonly files: readonly string[]; reado
82
123
  };
83
124
  }
84
125
 
126
+ export function verifyPackageFacingText(root: string): { readonly checkedPaths: readonly string[]; readonly failures: readonly string[] } {
127
+ const failures: string[] = [];
128
+ for (const path of PACKAGE_FACING_TEXT_PATHS) {
129
+ const absolutePath = join(root, path);
130
+ if (!existsSync(absolutePath)) {
131
+ failures.push(`package-facing text is missing: ${path}`);
132
+ continue;
133
+ }
134
+ const content = readFileSync(absolutePath, 'utf-8');
135
+ for (const forbidden of PACKAGE_FACING_FORBIDDEN_TEXT) {
136
+ if (content.includes(forbidden)) {
137
+ failures.push(`package-facing text ${path} contains forbidden default/TUI route or policy: ${forbidden}`);
138
+ }
139
+ }
140
+ const requirement = PACKAGE_FACING_REQUIRED_TEXT.find((entry) => entry.path === path);
141
+ if (requirement) {
142
+ for (const required of requirement.required) {
143
+ if (!content.includes(required)) {
144
+ failures.push(`package-facing text ${path} is missing required Agent route/policy text: ${required}`);
145
+ }
146
+ }
147
+ }
148
+ }
149
+ return {
150
+ checkedPaths: PACKAGE_FACING_TEXT_PATHS,
151
+ failures,
152
+ };
153
+ }
154
+
85
155
  export function verifyPackageCliInstall(root: string): PackageCliVerificationReport {
86
156
  const pkg = readPackageJson(root);
87
157
  const bin = pkg.bin && typeof pkg.bin === 'object' ? pkg.bin as Record<string, string | undefined> : {};
@@ -93,6 +163,7 @@ export function verifyPackageCliInstall(root: string): PackageCliVerificationRep
93
163
  return FORBIDDEN_TARBALL_DOCS.some((docPath) => path === docPath || path.startsWith(docPath));
94
164
  });
95
165
  const issues: string[] = [];
166
+ const packageFacingText = verifyPackageFacingText(root);
96
167
 
97
168
  for (const item of bins) {
98
169
  if (!item.target) issues.push(`package.json bin is missing ${item.command}.`);
@@ -107,6 +178,9 @@ export function verifyPackageCliInstall(root: string): PackageCliVerificationRep
107
178
  for (const path of forbiddenPaths) {
108
179
  issues.push(`npm tarball includes forbidden path: ${path}`);
109
180
  }
181
+ for (const failure of packageFacingText.failures) {
182
+ issues.push(failure);
183
+ }
110
184
 
111
185
  return {
112
186
  packageName: String(pkg.name ?? ''),
@@ -118,6 +192,7 @@ export function verifyPackageCliInstall(root: string): PackageCliVerificationRep
118
192
  requiredPathsPresent,
119
193
  forbiddenPaths,
120
194
  },
195
+ packageFacingText,
121
196
  issues,
122
197
  };
123
198
  }
@@ -203,7 +203,6 @@ export interface CommandContext
203
203
  readonly peer?: PeerClient;
204
204
  readonly providerApi?: ProviderApi;
205
205
  readonly agentKnowledgeApi?: KnowledgeApi;
206
- readonly knowledgeApi?: KnowledgeApi;
207
206
  readonly hookApi?: HookApi;
208
207
  readonly mcpApi?: McpApi;
209
208
  readonly opsApi?: OpsApi;
@@ -22,6 +22,33 @@ function readFlag(args: string[], name: string): string | undefined {
22
22
  return index >= 0 ? args[index + 1] : undefined;
23
23
  }
24
24
 
25
+ function findDisallowedKnowledgeScopeFlag(args: readonly string[]): string | null {
26
+ const disallowed = [
27
+ '--space',
28
+ '--knowledge-space',
29
+ '--knowledge-space-id',
30
+ '--knowledgeSpaceId',
31
+ '--include-all-spaces',
32
+ '--includeAllSpaces',
33
+ '--homegraph',
34
+ '--home-graph',
35
+ ];
36
+ for (const token of args) {
37
+ for (const flag of disallowed) {
38
+ if (token === flag || token.startsWith(`${flag}=`)) return flag;
39
+ }
40
+ }
41
+ return null;
42
+ }
43
+
44
+ function printScopeFlagRejection(context: CommandContext, flag: string): void {
45
+ context.print([
46
+ `[knowledge] Agent Knowledge is isolated; ${flag} is not accepted.`,
47
+ '[knowledge] GoodVibes Agent must not use default Knowledge/Wiki, HomeGraph, or Home Assistant spaces.',
48
+ '[knowledge] Use only /api/goodvibes-agent/knowledge/* Agent-owned routes.',
49
+ ].join('\n'));
50
+ }
51
+
25
52
  function readStringListFlag(args: string[], name: string): string[] {
26
53
  const value = readFlag(args, name);
27
54
  if (!value) return [];
@@ -143,6 +170,11 @@ export const knowledgeCommand: SlashCommand = {
143
170
  }
144
171
  const sub = (args[0] ?? 'status').toLowerCase();
145
172
  const rest = args.slice(1);
173
+ const disallowedScopeFlag = findDisallowedKnowledgeScopeFlag(rest);
174
+ if (disallowedScopeFlag) {
175
+ printScopeFlagRejection(context, disallowedScopeFlag);
176
+ return;
177
+ }
146
178
 
147
179
  switch (sub) {
148
180
  case 'ask': {
@@ -154,10 +186,6 @@ export const knowledgeCommand: SlashCommand = {
154
186
  context.print('[knowledge] Usage: /knowledge ask <query> [--limit <n>] [--mode <concise|standard|detailed>]');
155
187
  return;
156
188
  }
157
- if (readFlag(rest, '--space') || readFlag(rest, '--knowledge-space')) {
158
- context.print('[knowledge] Agent Knowledge is isolated. --space/--knowledge-space is not accepted because Agent must not fall back to default Knowledge/Wiki or HomeGraph.');
159
- return;
160
- }
161
189
  const requestedMode = readFlag(rest, '--mode') as KnowledgeAskMode | undefined;
162
190
  const mode: KnowledgeAskMode = requestedMode && ['concise', 'standard', 'detailed'].includes(requestedMode)
163
191
  ? requestedMode
@@ -4,9 +4,10 @@ import type { MemorySearchFilter } from '@pellux/goodvibes-sdk/platform/state';
4
4
  import { VALID_CLASSES, VALID_SCOPES, isValidClass, isValidScope } from './recall-shared.ts';
5
5
 
6
6
  export function getMemoryApi(context: CommandContext): MemoryApi | null {
7
- const memoryApi = context.clients?.knowledgeApi?.memory;
7
+ const memoryApi = context.clients?.agentKnowledgeApi?.memory;
8
8
  if (!memoryApi) {
9
- context.print('[recall] Memory API is not available in this runtime.');
9
+ context.print('[recall] Agent Memory API is not available in this runtime.');
10
+ context.print('[recall] Refusing to use default Knowledge/Wiki or HomeGraph fallback.');
10
11
  return null;
11
12
  }
12
13
  return memoryApi;
@@ -246,8 +246,12 @@ export async function compactConversation(context: CommandContext): Promise<void
246
246
  );
247
247
  }
248
248
 
249
+ export function requireAgentKnowledgeApi(context: CommandContext): KnowledgeApi {
250
+ return requireContextValue(context.clients?.agentKnowledgeApi, 'clients.agentKnowledgeApi');
251
+ }
252
+
249
253
  export function requireKnowledgeApi(context: CommandContext): KnowledgeApi {
250
- return requireContextValue(context.clients?.knowledgeApi, 'clients.knowledgeApi');
254
+ return requireAgentKnowledgeApi(context);
251
255
  }
252
256
 
253
257
  export function requireHookApi(context: CommandContext): HookApi {
@@ -101,7 +101,6 @@ export type CreateBootstrapCommandContextOptions = {
101
101
  operatorClient?: OperatorClient;
102
102
  peerClient?: PeerClient;
103
103
  agentKnowledgeApi?: KnowledgeApi;
104
- knowledgeApi?: KnowledgeApi;
105
104
  hookApi?: HookApi;
106
105
  mcpApi?: McpApi;
107
106
  opsApi?: OpsApi;
@@ -172,7 +171,6 @@ export function createBootstrapCommandContext(
172
171
  operatorClient,
173
172
  peerClient,
174
173
  agentKnowledgeApi,
175
- knowledgeApi,
176
174
  hookApi,
177
175
  mcpApi,
178
176
  opsApi,
@@ -249,7 +247,6 @@ export function createBootstrapCommandContext(
249
247
  peerClient,
250
248
  agentKnowledgeApi,
251
249
  providerApi,
252
- knowledgeApi,
253
250
  hookApi,
254
251
  mcpApi,
255
252
  opsApi,
@@ -118,7 +118,6 @@ export interface BootstrapCommandSectionOptions {
118
118
  readonly peerClient?: PeerClient;
119
119
  readonly providerApi?: ProviderApi;
120
120
  readonly agentKnowledgeApi?: KnowledgeApi;
121
- readonly knowledgeApi?: KnowledgeApi;
122
121
  readonly hookApi?: HookApi;
123
122
  readonly mcpApi?: McpApi;
124
123
  readonly opsApi?: OpsApi;
@@ -373,7 +372,7 @@ export function createBootstrapCommandExtensionsSection(
373
372
  export function createBootstrapCommandClientsSection(
374
373
  options: Pick<
375
374
  BootstrapCommandSectionOptions,
376
- 'operatorClient' | 'peerClient' | 'providerApi' | 'agentKnowledgeApi' | 'knowledgeApi' | 'hookApi' | 'mcpApi' | 'opsApi' | 'directTransport'
375
+ 'operatorClient' | 'peerClient' | 'providerApi' | 'agentKnowledgeApi' | 'hookApi' | 'mcpApi' | 'opsApi' | 'directTransport'
377
376
  >,
378
377
  ): BootstrapCommandClientSection {
379
378
  return {
@@ -381,7 +380,6 @@ export function createBootstrapCommandClientsSection(
381
380
  peer: options.peerClient,
382
381
  providerApi: options.providerApi,
383
382
  agentKnowledgeApi: options.agentKnowledgeApi,
384
- knowledgeApi: options.knowledgeApi,
385
383
  hookApi: options.hookApi,
386
384
  mcpApi: options.mcpApi,
387
385
  opsApi: options.opsApi,
@@ -172,7 +172,6 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
172
172
  const {
173
173
  directTransport,
174
174
  hookApi,
175
- knowledgeApi,
176
175
  mcpApi,
177
176
  opsApi,
178
177
  providerApi,
@@ -241,7 +240,6 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
241
240
  operatorClient: directTransport.operator,
242
241
  peerClient: directTransport.peer,
243
242
  agentKnowledgeApi,
244
- knowledgeApi: agentKnowledgeApi,
245
243
  hookApi,
246
244
  mcpApi,
247
245
  opsApi,
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.4';
9
+ let _version = '0.1.5';
10
10
  try {
11
11
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8'));
12
12
  _version = pkg.version ?? _version;
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Michael Davis and GoodVibes contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.