@pellux/goodvibes-agent 0.1.3 → 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 +13 -0
- package/docs/getting-started.md +1 -1
- package/docs/release-and-publishing.md +1 -1
- package/package.json +1 -1
- package/src/cli/agent-knowledge-command.ts +116 -11
- package/src/cli/package-verification.ts +75 -0
- package/src/input/command-registry.ts +0 -1
- package/src/input/commands/knowledge.ts +32 -4
- package/src/input/commands/recall-query.ts +3 -2
- package/src/input/commands/runtime-services.ts +5 -1
- package/src/runtime/bootstrap-command-context.ts +0 -3
- package/src/runtime/bootstrap-command-parts.ts +1 -3
- package/src/runtime/bootstrap-core.ts +1 -1
- package/src/runtime/bootstrap-shell.ts +0 -2
- package/src/runtime/services.ts +3 -3
- package/src/version.ts +1 -1
- package/LICENSE +0 -21
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
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
|
+
|
|
12
|
+
## 0.1.4 - 2026-05-31
|
|
13
|
+
|
|
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.
|
|
15
|
+
- Pointed runtime orchestrator and multimodal writeback dependencies at Agent Knowledge so assistant-authored knowledge cannot land in default Knowledge/Wiki.
|
|
16
|
+
- Moved project planning and work-plan artifacts onto the Agent Knowledge store so Agent task state does not use the regular wiki segment.
|
|
17
|
+
|
|
5
18
|
## 0.1.3 - 2026-05-31
|
|
6
19
|
|
|
7
20
|
- Added local Agent personas with `/personas`: create/list/search/show/use/review/stale/delete, secret-looking value rejection, active persona prompt injection, and operator workspace status.
|
package/docs/getting-started.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Getting Started
|
|
2
2
|
|
|
3
|
-
GoodVibes Agent `0.1.
|
|
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
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-agent",
|
|
3
|
-
"version": "0.1.
|
|
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",
|
|
@@ -32,6 +32,42 @@ interface AgentKnowledgeSuccess<TData> {
|
|
|
32
32
|
|
|
33
33
|
type AgentKnowledgeResult<TData> = AgentKnowledgeSuccess<TData> | AgentKnowledgeFailure;
|
|
34
34
|
|
|
35
|
+
interface DaemonCallMethod {
|
|
36
|
+
readonly kind: string;
|
|
37
|
+
readonly route: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const AGENT_KNOWLEDGE_METHODS = {
|
|
41
|
+
status: {
|
|
42
|
+
kind: 'agentKnowledge.status',
|
|
43
|
+
route: '/api/goodvibes-agent/knowledge/status',
|
|
44
|
+
},
|
|
45
|
+
ask: {
|
|
46
|
+
kind: 'agentKnowledge.ask',
|
|
47
|
+
route: '/api/goodvibes-agent/knowledge/ask',
|
|
48
|
+
},
|
|
49
|
+
search: {
|
|
50
|
+
kind: 'agentKnowledge.search',
|
|
51
|
+
route: '/api/goodvibes-agent/knowledge/search',
|
|
52
|
+
},
|
|
53
|
+
ingestUrl: {
|
|
54
|
+
kind: 'agentKnowledge.ingest.url',
|
|
55
|
+
route: '/api/goodvibes-agent/knowledge/ingest/url',
|
|
56
|
+
},
|
|
57
|
+
} as const;
|
|
58
|
+
|
|
59
|
+
const DELEGATION_METHOD = {
|
|
60
|
+
kind: 'sessions.messages.create',
|
|
61
|
+
route: 'sessions.messages.create',
|
|
62
|
+
} as const;
|
|
63
|
+
|
|
64
|
+
interface DelegationResult {
|
|
65
|
+
readonly sessionId: string;
|
|
66
|
+
readonly message: unknown;
|
|
67
|
+
readonly task: string;
|
|
68
|
+
readonly wrfcRequested: boolean;
|
|
69
|
+
}
|
|
70
|
+
|
|
35
71
|
function isRecord(value: unknown): value is JsonRecord {
|
|
36
72
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
37
73
|
}
|
|
@@ -186,6 +222,62 @@ function createAgentSdk(connection: AgentDaemonConnection) {
|
|
|
186
222
|
});
|
|
187
223
|
}
|
|
188
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
|
+
|
|
189
281
|
function sourceLine(value: unknown): string {
|
|
190
282
|
const record = isRecord(value) ? value : {};
|
|
191
283
|
const title = cleanInline(record.title)
|
|
@@ -323,7 +415,7 @@ function formatFailure(failure: AgentKnowledgeFailure, json: boolean): string {
|
|
|
323
415
|
|
|
324
416
|
async function runKnowledgeCall<TData>(
|
|
325
417
|
runtime: CliCommandRuntime,
|
|
326
|
-
|
|
418
|
+
method: DaemonCallMethod,
|
|
327
419
|
call: (connection: AgentDaemonConnection) => Promise<TData>,
|
|
328
420
|
): Promise<AgentKnowledgeResult<TData>> {
|
|
329
421
|
const connection = resolveDaemonConnection(runtime);
|
|
@@ -333,14 +425,14 @@ async function runKnowledgeCall<TData>(
|
|
|
333
425
|
kind: 'auth_required',
|
|
334
426
|
error: `No daemon operator token found at ${connection.tokenPath}`,
|
|
335
427
|
baseUrl: connection.baseUrl,
|
|
336
|
-
route,
|
|
428
|
+
route: method.route,
|
|
337
429
|
};
|
|
338
430
|
}
|
|
339
431
|
try {
|
|
340
432
|
const data = await call(connection);
|
|
341
|
-
return { ok: true, kind:
|
|
433
|
+
return { ok: true, kind: method.kind, route: method.route, data };
|
|
342
434
|
} catch (error) {
|
|
343
|
-
return classifyKnowledgeError(error, connection, route);
|
|
435
|
+
return classifyKnowledgeError(error, connection, method.route);
|
|
344
436
|
}
|
|
345
437
|
}
|
|
346
438
|
|
|
@@ -348,9 +440,22 @@ export async function handleAgentKnowledgeCommand(runtime: CliCommandRuntime): P
|
|
|
348
440
|
const [sub = 'status', ...rest] = runtime.cli.commandArgs;
|
|
349
441
|
const normalized = sub.toLowerCase();
|
|
350
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
|
+
}
|
|
351
456
|
|
|
352
457
|
if (normalized === 'status') {
|
|
353
|
-
const result = await runKnowledgeCall(runtime,
|
|
458
|
+
const result = await runKnowledgeCall(runtime, AGENT_KNOWLEDGE_METHODS.status, async (connection) => (
|
|
354
459
|
await createAgentSdk(connection).knowledge.status()
|
|
355
460
|
));
|
|
356
461
|
if (!result.ok) return { output: formatFailure(result, json), exitCode: 1 };
|
|
@@ -366,7 +471,7 @@ export async function handleAgentKnowledgeCommand(runtime: CliCommandRuntime): P
|
|
|
366
471
|
const mode = readOptionValue(rest, '--mode');
|
|
367
472
|
const selectedMode = mode === 'concise' || mode === 'standard' || mode === 'detailed' ? mode : 'standard';
|
|
368
473
|
const limit = readPositiveInt(rest, '--limit', 8);
|
|
369
|
-
const result = await runKnowledgeCall(runtime,
|
|
474
|
+
const result = await runKnowledgeCall(runtime, AGENT_KNOWLEDGE_METHODS.ask, async (connection) => (
|
|
370
475
|
await createAgentSdk(connection).knowledge.ask({
|
|
371
476
|
query,
|
|
372
477
|
limit,
|
|
@@ -387,7 +492,7 @@ export async function handleAgentKnowledgeCommand(runtime: CliCommandRuntime): P
|
|
|
387
492
|
const query = commandValues(rest).join(' ').trim();
|
|
388
493
|
if (!query) return { output: 'Usage: goodvibes-agent knowledge search <query> [--limit <n>]', exitCode: 2 };
|
|
389
494
|
const limit = readPositiveInt(rest, '--limit', 10);
|
|
390
|
-
const result = await runKnowledgeCall(runtime,
|
|
495
|
+
const result = await runKnowledgeCall(runtime, AGENT_KNOWLEDGE_METHODS.search, async (connection) => (
|
|
391
496
|
await createAgentSdk(connection).knowledge.search({ query, limit })
|
|
392
497
|
));
|
|
393
498
|
if (!result.ok) return { output: formatFailure(result, json), exitCode: 1 };
|
|
@@ -403,8 +508,8 @@ export async function handleAgentKnowledgeCommand(runtime: CliCommandRuntime): P
|
|
|
403
508
|
if (!url) return { output: 'Usage: goodvibes-agent knowledge ingest-url <url> [--title <title>] [--tags a,b]', exitCode: 2 };
|
|
404
509
|
const title = readOptionValue(rest, '--title');
|
|
405
510
|
const tags = readStringList(rest, '--tags');
|
|
406
|
-
const result = await runKnowledgeCall(runtime,
|
|
407
|
-
await
|
|
511
|
+
const result = await runKnowledgeCall(runtime, AGENT_KNOWLEDGE_METHODS.ingestUrl, async (connection) => (
|
|
512
|
+
await postAgentKnowledgeJson(connection, AGENT_KNOWLEDGE_METHODS.ingestUrl.route, {
|
|
408
513
|
url,
|
|
409
514
|
title,
|
|
410
515
|
tags,
|
|
@@ -432,7 +537,7 @@ export async function handleCompatCommand(runtime: CliCommandRuntime): Promise<C
|
|
|
432
537
|
const daemonRecord = isRecord(daemon.body) ? daemon.body : {};
|
|
433
538
|
const daemonVersion = readString(daemonRecord, 'version') ?? 'unknown';
|
|
434
539
|
const versionCompatible = daemon.ok && daemonVersion === metadata.sdkVersion;
|
|
435
|
-
const knowledgeRoute = await runKnowledgeCall(runtime,
|
|
540
|
+
const knowledgeRoute = await runKnowledgeCall(runtime, AGENT_KNOWLEDGE_METHODS.status, async (routeConnection) => (
|
|
436
541
|
await createAgentSdk(routeConnection).knowledge.status()
|
|
437
542
|
));
|
|
438
543
|
const knowledgeRouteReady = knowledgeRoute.ok;
|
|
@@ -482,7 +587,7 @@ export async function handleDelegateCommand(runtime: CliCommandRuntime): Promise
|
|
|
482
587
|
exitCode: 2,
|
|
483
588
|
};
|
|
484
589
|
}
|
|
485
|
-
const result = await runKnowledgeCall(runtime,
|
|
590
|
+
const result = await runKnowledgeCall<DelegationResult>(runtime, DELEGATION_METHOD, async (connection) => {
|
|
486
591
|
const sdk = createAgentSdk(connection);
|
|
487
592
|
const created = await sdk.operator.invoke('sessions.create', {
|
|
488
593
|
title: `Agent delegation: ${task.slice(0, 72)}`,
|
|
@@ -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?.
|
|
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
|
|
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' | '
|
|
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,
|
|
@@ -242,7 +242,7 @@ export async function initializeBootstrapCore(
|
|
|
242
242
|
webSearchService: services.webSearchService,
|
|
243
243
|
channelRegistry: services.channelPlugins,
|
|
244
244
|
remoteRunnerRegistry: services.remoteRunnerRegistry,
|
|
245
|
-
knowledgeService: services.
|
|
245
|
+
knowledgeService: services.agentKnowledgeService,
|
|
246
246
|
archetypeLoader: services.archetypeLoader,
|
|
247
247
|
configManager,
|
|
248
248
|
providerRegistry: services.providerRegistry,
|
|
@@ -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/runtime/services.ts
CHANGED
|
@@ -440,7 +440,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
440
440
|
semanticService: homeGraphSemanticService,
|
|
441
441
|
});
|
|
442
442
|
const projectPlanningProjectId = projectPlanningProjectIdFromPath(workingDirectory);
|
|
443
|
-
const projectPlanningService = new ProjectPlanningService(
|
|
443
|
+
const projectPlanningService = new ProjectPlanningService(agentKnowledgeStore, {
|
|
444
444
|
defaultProjectId: projectPlanningProjectId,
|
|
445
445
|
});
|
|
446
446
|
const workPlanStore = new WorkPlanStore({
|
|
@@ -473,7 +473,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
473
473
|
}));
|
|
474
474
|
const mediaProviders = new MediaProviderRegistry();
|
|
475
475
|
ensureBuiltinMediaProviders(mediaProviders, artifactStore, providerRegistry);
|
|
476
|
-
const multimodalService = new MultimodalService(artifactStore, mediaProviders, voiceService,
|
|
476
|
+
const multimodalService = new MultimodalService(artifactStore, mediaProviders, voiceService, agentKnowledgeService);
|
|
477
477
|
const pluginManager = new PluginManager({
|
|
478
478
|
pathOptions: {
|
|
479
479
|
cwd: shellPaths.workingDirectory,
|
|
@@ -560,7 +560,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
560
560
|
webSearchService,
|
|
561
561
|
channelRegistry: channelPlugins,
|
|
562
562
|
remoteRunnerRegistry,
|
|
563
|
-
knowledgeService,
|
|
563
|
+
knowledgeService: agentKnowledgeService,
|
|
564
564
|
memoryRegistry,
|
|
565
565
|
archetypeLoader,
|
|
566
566
|
configManager,
|
package/src/version.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { join } from 'node:path';
|
|
|
6
6
|
// The prebuild script updates the fallback value before compilation.
|
|
7
7
|
// Uses import.meta.dir (Bun) to locate package.json relative to this file,
|
|
8
8
|
// which is correct regardless of the process working directory.
|
|
9
|
-
let _version = '0.1.
|
|
9
|
+
let _version = '0.1.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.
|