@pellux/goodvibes-agent 0.1.106 → 0.1.108

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.
Files changed (79) hide show
  1. package/CHANGELOG.md +41 -10
  2. package/README.md +28 -9
  3. package/docs/README.md +5 -5
  4. package/docs/{runtime-connection.md → connected-services.md} +8 -8
  5. package/docs/getting-started.md +29 -10
  6. package/docs/release-and-publishing.md +4 -4
  7. package/package.json +2 -12
  8. package/src/agent/reminder-schedule-format.ts +75 -0
  9. package/src/agent/reminder-schedule.ts +494 -0
  10. package/src/agent/routine-schedule-format.ts +7 -7
  11. package/src/agent/routine-schedule-promotion.ts +1 -1
  12. package/src/agent/routine-schedule-receipts.ts +1 -1
  13. package/src/agent/skill-discovery.ts +2 -0
  14. package/src/cli/agent-knowledge-args.ts +93 -0
  15. package/src/cli/agent-knowledge-command.ts +200 -369
  16. package/src/cli/agent-knowledge-format.ts +58 -7
  17. package/src/cli/agent-knowledge-methods.ts +84 -0
  18. package/src/cli/agent-knowledge-runtime.ts +240 -0
  19. package/src/cli/config-overrides.ts +2 -2
  20. package/src/cli/help.ts +31 -11
  21. package/src/cli/management-commands.ts +2 -2
  22. package/src/cli/management.ts +8 -8
  23. package/src/cli/package-verification.ts +7 -3
  24. package/src/cli/parser.ts +10 -2
  25. package/src/cli/service-posture.ts +6 -6
  26. package/src/cli/status.ts +32 -32
  27. package/src/cli/tui-startup.ts +3 -1
  28. package/src/input/agent-workspace-activation.ts +24 -13
  29. package/src/input/agent-workspace-basic-command-editors.ts +448 -0
  30. package/src/input/agent-workspace-categories.ts +42 -34
  31. package/src/input/agent-workspace-channels.ts +3 -3
  32. package/src/input/agent-workspace-command-editor.ts +65 -0
  33. package/src/input/agent-workspace-editors.ts +17 -2
  34. package/src/input/agent-workspace-knowledge-query-editor.ts +74 -0
  35. package/src/input/agent-workspace-knowledge-url-editor.ts +95 -0
  36. package/src/input/agent-workspace-reminder-schedule-editor.ts +125 -0
  37. package/src/input/agent-workspace-routine-schedule-editor.ts +127 -0
  38. package/src/input/agent-workspace-setup.ts +2 -2
  39. package/src/input/agent-workspace-types.ts +20 -2
  40. package/src/input/agent-workspace-voice-media.ts +5 -5
  41. package/src/input/agent-workspace.ts +39 -2
  42. package/src/input/commands/agent-runtime-profile-runtime.ts +1 -1
  43. package/src/input/commands/agent-skills-runtime.ts +94 -2
  44. package/src/input/commands/brief-runtime.ts +126 -0
  45. package/src/input/commands/channels-runtime.ts +47 -0
  46. package/src/input/commands/health-runtime.ts +10 -10
  47. package/src/input/commands/knowledge.ts +176 -1
  48. package/src/input/commands/planning-runtime.ts +1 -1
  49. package/src/input/commands/platform-access-runtime.ts +10 -10
  50. package/src/input/commands/policy-dispatch.ts +1 -1
  51. package/src/input/commands/schedule-runtime.ts +42 -5
  52. package/src/input/commands/security-runtime.ts +1 -1
  53. package/src/input/commands/session-content.ts +1 -1
  54. package/src/input/commands/session-workflow.ts +1 -1
  55. package/src/input/commands/shell-core.ts +4 -2
  56. package/src/input/commands/tasks-runtime.ts +3 -3
  57. package/src/input/commands.ts +4 -0
  58. package/src/input/handler-onboarding.ts +4 -4
  59. package/src/input/handler.ts +3 -2
  60. package/src/input/onboarding/onboarding-wizard-helpers.ts +1 -1
  61. package/src/input/onboarding/onboarding-wizard-operator-steps.ts +13 -13
  62. package/src/input/onboarding/onboarding-wizard-steps.ts +8 -8
  63. package/src/input/settings-modal-agent-policy.ts +1 -1
  64. package/src/input/slash-command-parser.ts +60 -0
  65. package/src/panels/builtin/agent.ts +1 -1
  66. package/src/panels/builtin/operations.ts +2 -2
  67. package/src/panels/provider-account-snapshot.ts +1 -1
  68. package/src/panels/provider-health-domains.ts +6 -6
  69. package/src/panels/subscription-panel.ts +1 -1
  70. package/src/panels/tasks-panel.ts +3 -3
  71. package/src/renderer/agent-workspace.ts +43 -30
  72. package/src/renderer/help-overlay.ts +1 -1
  73. package/src/renderer/settings-modal.ts +13 -13
  74. package/src/runtime/bootstrap-hook-bridge.ts +1 -1
  75. package/src/runtime/bootstrap.ts +8 -8
  76. package/src/runtime/index.ts +2 -2
  77. package/src/runtime/onboarding/derivation.ts +6 -6
  78. package/src/shell/service-settings-sync.ts +1 -1
  79. package/src/version.ts +1 -1
@@ -221,6 +221,51 @@ export function formatConnectors(data: unknown): string {
221
221
  ].join('\n');
222
222
  }
223
223
 
224
+ export function formatConnector(data: unknown, id: string): string {
225
+ const record = isRecord(data) ? data : {};
226
+ const connector = isRecord(record.connector) ? record.connector : record;
227
+ const connectorId = cleanInline(connector.id) || id;
228
+ const name = cleanInline(connector.displayName) || connectorId;
229
+ const description = cleanInline(connector.description);
230
+ const sourceType = cleanInline(connector.sourceType);
231
+ const capabilities = readArray(connector, 'capabilities').map(cleanInline).filter(Boolean);
232
+ const examples = readArray(connector, 'examples');
233
+ return [
234
+ `Agent Knowledge connector: ${connectorId}`,
235
+ ` name: ${name}`,
236
+ sourceType ? ` sourceType: ${sourceType}` : null,
237
+ description ? ` description: ${description}` : null,
238
+ capabilities.length > 0 ? ` capabilities: ${capabilities.join(', ')}` : null,
239
+ ` examples: ${examples.length}`,
240
+ ' route: /api/goodvibes-agent/knowledge/connectors/{id}',
241
+ ].filter((line): line is string => Boolean(line)).join('\n');
242
+ }
243
+
244
+ export function formatConnectorDoctor(data: unknown, id: string): string {
245
+ const record = isRecord(data) ? data : {};
246
+ const ready = readBoolean(record, 'ready');
247
+ const summary = cleanInline(record.summary);
248
+ const checks = readArray(record, 'checks');
249
+ const hints = readArray(record, 'hints').map(cleanInline).filter(Boolean);
250
+ return [
251
+ `Agent Knowledge connector doctor: ${cleanInline(record.connectorId) || id}`,
252
+ ` ready: ${ready === null ? 'unknown' : yesNo(ready)}`,
253
+ summary ? ` summary: ${summary}` : null,
254
+ checks.length > 0 ? ' checks:' : null,
255
+ ...checks.slice(0, 10).map((check) => {
256
+ const item = isRecord(check) ? check : {};
257
+ const checkId = cleanInline(item.id) || 'check';
258
+ const status = cleanInline(item.status) || 'unknown';
259
+ const label = cleanInline(item.label);
260
+ const detail = cleanInline(item.detail);
261
+ return ` - ${checkId} [${status}] ${label}${detail ? ` - ${detail}` : ''}`;
262
+ }),
263
+ hints.length > 0 ? ' hints:' : null,
264
+ ...hints.slice(0, 8).map((hint) => ` - ${hint}`),
265
+ ' route: /api/goodvibes-agent/knowledge/connectors/{id}/doctor',
266
+ ].filter((line): line is string => Boolean(line)).join('\n');
267
+ }
268
+
224
269
  export function formatAsk(data: unknown, query: string): string {
225
270
  const record = isRecord(data) ? data : {};
226
271
  const answer = isRecord(record.answer) ? record.answer : record;
@@ -261,18 +306,24 @@ export function formatSearch(data: unknown, query: string): string {
261
306
  ].join('\n');
262
307
  }
263
308
 
264
- export function formatIngest(data: unknown, url: string): string {
309
+ export function formatIngest(
310
+ data: unknown,
311
+ target: string,
312
+ label = 'ingest-url',
313
+ route = '/api/goodvibes-agent/knowledge/ingest/url',
314
+ targetLabel = 'url',
315
+ ): string {
265
316
  const record = isRecord(data) ? data : {};
266
317
  const source = isRecord(record.source) ? record.source : record;
267
318
  const sourceId = cleanInline(source.id);
268
- const canonicalUri = cleanInline(source.canonicalUri) || cleanInline(source.sourceUri) || url;
319
+ const canonicalUri = cleanInline(source.canonicalUri) || cleanInline(source.sourceUri) || target;
269
320
  const artifactId = cleanInline(record.artifactId);
270
321
  return [
271
- 'Agent Knowledge ingest-url accepted',
322
+ `Agent Knowledge ${label} accepted`,
272
323
  ` source: ${sourceId || '(pending)'}`,
273
- ` url: ${canonicalUri}`,
324
+ ` ${targetLabel}: ${canonicalUri}`,
274
325
  artifactId ? ` artifact: ${artifactId}` : null,
275
- ' route: /api/goodvibes-agent/knowledge/ingest/url',
326
+ ` route: ${route}`,
276
327
  ].filter((line): line is string => Boolean(line)).join('\n');
277
328
  }
278
329
 
@@ -316,10 +367,10 @@ export function formatFailure(failure: AgentKnowledgeFailureLike, json: boolean)
316
367
  ? ` versions: runtime=${failure.daemonVersion} expected=${failure.expectedSdkVersion}`
317
368
  : null,
318
369
  failure.kind === 'version_mismatch'
319
- ? ' next: update/restart the external GoodVibes runtime so /status matches the Agent SDK pin.'
370
+ ? ' next: update connected GoodVibes services so /status matches the Agent SDK pin.'
320
371
  : null,
321
372
  failure.kind === 'daemon_route_unavailable'
322
- ? ' next: update/restart the external GoodVibes runtime to the SDK version required by this Agent package.'
373
+ ? ' next: update connected GoodVibes services to the SDK version required by this Agent package.'
323
374
  : null,
324
375
  ].filter((line): line is string => Boolean(line)).join('\n');
325
376
  }
@@ -0,0 +1,84 @@
1
+ export interface DaemonCallMethod {
2
+ readonly kind: string;
3
+ readonly route: string;
4
+ }
5
+
6
+ export const AGENT_KNOWLEDGE_METHODS = {
7
+ status: {
8
+ kind: 'agentKnowledge.status',
9
+ route: '/api/goodvibes-agent/knowledge/status',
10
+ },
11
+ ask: {
12
+ kind: 'agentKnowledge.ask',
13
+ route: '/api/goodvibes-agent/knowledge/ask',
14
+ },
15
+ search: {
16
+ kind: 'agentKnowledge.search',
17
+ route: '/api/goodvibes-agent/knowledge/search',
18
+ },
19
+ sourcesList: {
20
+ kind: 'agentKnowledge.sources.list',
21
+ route: '/api/goodvibes-agent/knowledge/sources',
22
+ },
23
+ nodesList: {
24
+ kind: 'agentKnowledge.nodes.list',
25
+ route: '/api/goodvibes-agent/knowledge/nodes',
26
+ },
27
+ issuesList: {
28
+ kind: 'agentKnowledge.issues.list',
29
+ route: '/api/goodvibes-agent/knowledge/issues',
30
+ },
31
+ itemGet: {
32
+ kind: 'agentKnowledge.item.get',
33
+ route: '/api/goodvibes-agent/knowledge/items/{id}',
34
+ },
35
+ map: {
36
+ kind: 'agentKnowledge.map',
37
+ route: '/api/goodvibes-agent/knowledge/map',
38
+ },
39
+ connectorsList: {
40
+ kind: 'agentKnowledge.connectors.list',
41
+ route: '/api/goodvibes-agent/knowledge/connectors',
42
+ },
43
+ connectorGet: {
44
+ kind: 'agentKnowledge.connector.get',
45
+ route: '/api/goodvibes-agent/knowledge/connectors/{id}',
46
+ },
47
+ connectorDoctor: {
48
+ kind: 'agentKnowledge.connector.doctor',
49
+ route: '/api/goodvibes-agent/knowledge/connectors/{id}/doctor',
50
+ },
51
+ ingestUrl: {
52
+ kind: 'agentKnowledge.ingest.url',
53
+ route: '/api/goodvibes-agent/knowledge/ingest/url',
54
+ },
55
+ ingestArtifact: {
56
+ kind: 'agentKnowledge.ingest.artifact',
57
+ route: '/api/goodvibes-agent/knowledge/ingest/artifact',
58
+ },
59
+ ingestUrls: {
60
+ kind: 'agentKnowledge.ingest.urls',
61
+ route: '/api/goodvibes-agent/knowledge/ingest/urls',
62
+ },
63
+ ingestBookmarks: {
64
+ kind: 'agentKnowledge.ingest.bookmarks',
65
+ route: '/api/goodvibes-agent/knowledge/ingest/bookmarks',
66
+ },
67
+ ingestBrowserHistory: {
68
+ kind: 'agentKnowledge.ingest.browserHistory',
69
+ route: '/api/goodvibes-agent/knowledge/ingest/browser-history',
70
+ },
71
+ ingestConnector: {
72
+ kind: 'agentKnowledge.ingest.connector',
73
+ route: '/api/goodvibes-agent/knowledge/ingest/connector',
74
+ },
75
+ reindex: {
76
+ kind: 'agentKnowledge.reindex',
77
+ route: '/api/goodvibes-agent/knowledge/reindex',
78
+ },
79
+ } as const;
80
+
81
+ export const DELEGATION_METHOD = {
82
+ kind: 'sessions.messages.create',
83
+ route: 'sessions.messages.create',
84
+ } as const;
@@ -0,0 +1,240 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { createBrowserAgentSdk } from '@pellux/goodvibes-sdk/browser/agent';
4
+ import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
5
+ import { SDK_VERSION, VERSION } from '../version.ts';
6
+ import type { CliCommandRuntime } from './management.ts';
7
+ import type { DaemonCallMethod } from './agent-knowledge-methods.ts';
8
+
9
+ export type JsonRecord = Record<string, unknown>;
10
+
11
+ export interface AgentDaemonConnection {
12
+ readonly baseUrl: string;
13
+ readonly token: string | null;
14
+ readonly tokenPath: string;
15
+ }
16
+
17
+ export interface AgentKnowledgeFailure {
18
+ readonly ok: false;
19
+ readonly kind: 'daemon_unavailable' | 'auth_required' | 'version_mismatch' | 'daemon_route_unavailable' | 'daemon_error';
20
+ readonly error: string;
21
+ readonly baseUrl: string;
22
+ readonly route: string;
23
+ readonly daemonVersion?: string;
24
+ readonly expectedSdkVersion?: string;
25
+ }
26
+
27
+ export interface AgentKnowledgeSuccess<TData> {
28
+ readonly ok: true;
29
+ readonly kind: string;
30
+ readonly route: string;
31
+ readonly data: TData;
32
+ }
33
+
34
+ export type AgentKnowledgeResult<TData> = AgentKnowledgeSuccess<TData> | AgentKnowledgeFailure;
35
+
36
+ export function isRecord(value: unknown): value is JsonRecord {
37
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
38
+ }
39
+
40
+ export function readString(record: JsonRecord | null, key: string): string | null {
41
+ const value = record?.[key];
42
+ return typeof value === 'string' ? value : null;
43
+ }
44
+
45
+ export function readPackageMetadata(): { readonly version: string; readonly sdkVersion: string } {
46
+ return { version: VERSION, sdkVersion: SDK_VERSION };
47
+ }
48
+
49
+ export function resolveDaemonConnection(runtime: CliCommandRuntime): AgentDaemonConnection {
50
+ const host = String(runtime.configManager.get('controlPlane.host') ?? '127.0.0.1');
51
+ const port = Number(runtime.configManager.get('controlPlane.port') ?? 3421);
52
+ const baseUrl = `http://${host}:${Number.isFinite(port) ? port : 3421}`;
53
+ const tokenPath = join(runtime.homeDirectory, '.goodvibes', 'daemon', 'operator-tokens.json');
54
+ if (!existsSync(tokenPath)) return { baseUrl, token: null, tokenPath };
55
+ try {
56
+ const parsed = JSON.parse(readFileSync(tokenPath, 'utf-8')) as unknown;
57
+ const token = isRecord(parsed) && typeof parsed.token === 'string' ? parsed.token : null;
58
+ return { baseUrl, token, tokenPath };
59
+ } catch {
60
+ return { baseUrl, token: null, tokenPath };
61
+ }
62
+ }
63
+
64
+ export async function fetchDaemonStatus(connection: AgentDaemonConnection): Promise<{ readonly ok: boolean; readonly status: number; readonly body: unknown }> {
65
+ try {
66
+ const response = await fetch(`${connection.baseUrl}/status`, {
67
+ headers: connection.token ? { authorization: `Bearer ${connection.token}` } : undefined,
68
+ });
69
+ const text = await response.text();
70
+ let body: unknown = text;
71
+ try {
72
+ body = JSON.parse(text) as unknown;
73
+ } catch {
74
+ body = text;
75
+ }
76
+ return { ok: response.ok, status: response.status, body };
77
+ } catch (error) {
78
+ return { ok: false, status: 0, body: summarizeError(error) };
79
+ }
80
+ }
81
+
82
+ export async function classifyKnowledgeError(error: unknown, connection: AgentDaemonConnection, route: string): Promise<AgentKnowledgeFailure> {
83
+ const message = summarizeError(error);
84
+ const lower = message.toLowerCase();
85
+ if (lower.includes('401') || lower.includes('unauthorized') || lower.includes('auth')) {
86
+ return { ok: false, kind: 'auth_required', error: message, baseUrl: connection.baseUrl, route };
87
+ }
88
+ if (lower.includes('404') || lower.includes('not found')) {
89
+ const metadata = readPackageMetadata();
90
+ const daemon = await fetchDaemonStatus(connection);
91
+ const daemonRecord = isRecord(daemon.body) ? daemon.body : {};
92
+ const daemonVersion = readString(daemonRecord, 'version') ?? 'unknown';
93
+ if (daemon.ok && daemonVersion !== metadata.sdkVersion) {
94
+ return {
95
+ ok: false,
96
+ kind: 'version_mismatch',
97
+ error: `Connected GoodVibes service SDK version ${daemonVersion} does not match Agent SDK pin ${metadata.sdkVersion}; Agent Knowledge route is unavailable.`,
98
+ baseUrl: connection.baseUrl,
99
+ route,
100
+ daemonVersion,
101
+ expectedSdkVersion: metadata.sdkVersion,
102
+ };
103
+ }
104
+ return { ok: false, kind: 'daemon_route_unavailable', error: message, baseUrl: connection.baseUrl, route };
105
+ }
106
+ if (lower.includes('fetch') || lower.includes('connect') || lower.includes('econnrefused')) {
107
+ return { ok: false, kind: 'daemon_unavailable', error: message, baseUrl: connection.baseUrl, route };
108
+ }
109
+ return { ok: false, kind: 'daemon_error', error: message, baseUrl: connection.baseUrl, route };
110
+ }
111
+
112
+ export function createAgentSdk(connection: AgentDaemonConnection) {
113
+ return createBrowserAgentSdk({
114
+ baseUrl: connection.baseUrl,
115
+ authToken: connection.token,
116
+ });
117
+ }
118
+
119
+ export async function postAgentKnowledgeJson<TData>(
120
+ connection: AgentDaemonConnection,
121
+ route: string,
122
+ body: JsonRecord,
123
+ ): Promise<TData> {
124
+ const response = await fetch(`${connection.baseUrl}${route}`, {
125
+ method: 'POST',
126
+ headers: {
127
+ authorization: `Bearer ${connection.token ?? ''}`,
128
+ 'content-type': 'application/json',
129
+ },
130
+ body: JSON.stringify(body),
131
+ });
132
+ const text = await response.text();
133
+ let parsed: unknown = text;
134
+ if (text.trim()) {
135
+ try {
136
+ parsed = JSON.parse(text) as unknown;
137
+ } catch {
138
+ parsed = text;
139
+ }
140
+ }
141
+ if (!response.ok) {
142
+ const detail = isRecord(parsed) && typeof parsed.error === 'string' ? parsed.error : text;
143
+ throw new Error(`HTTP ${response.status} ${response.statusText}${detail ? `: ${detail}` : ''}`);
144
+ }
145
+ return parsed as TData;
146
+ }
147
+
148
+ function queryRoute(route: string, query: JsonRecord): string {
149
+ const params = new URLSearchParams();
150
+ for (const [key, value] of Object.entries(query)) {
151
+ if (value === undefined || value === null || value === '') continue;
152
+ if (Array.isArray(value)) {
153
+ for (const item of value) {
154
+ if (typeof item === 'string' && item.trim().length > 0) params.append(key, item);
155
+ }
156
+ continue;
157
+ }
158
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
159
+ params.set(key, String(value));
160
+ }
161
+ }
162
+ const suffix = params.toString();
163
+ return suffix ? `${route}?${suffix}` : route;
164
+ }
165
+
166
+ export async function getAgentKnowledgeJson<TData>(
167
+ connection: AgentDaemonConnection,
168
+ route: string,
169
+ query: JsonRecord = {},
170
+ ): Promise<TData> {
171
+ const response = await fetch(`${connection.baseUrl}${queryRoute(route, query)}`, {
172
+ headers: {
173
+ authorization: `Bearer ${connection.token ?? ''}`,
174
+ },
175
+ });
176
+ const text = await response.text();
177
+ let parsed: unknown = text;
178
+ if (text.trim()) {
179
+ try {
180
+ parsed = JSON.parse(text) as unknown;
181
+ } catch {
182
+ parsed = text;
183
+ }
184
+ }
185
+ if (!response.ok) {
186
+ const detail = isRecord(parsed) && typeof parsed.error === 'string' ? parsed.error : text;
187
+ throw new Error(`HTTP ${response.status} ${response.statusText}${detail ? `: ${detail}` : ''}`);
188
+ }
189
+ return parsed as TData;
190
+ }
191
+
192
+ export function findDisallowedKnowledgeScopeFlag(args: readonly string[]): string | null {
193
+ const disallowed = [
194
+ '--space',
195
+ '--knowledge-space',
196
+ '--knowledge-space-id',
197
+ ['--knowledge', 'SpaceId'].join(''),
198
+ '--include-all-spaces',
199
+ ['--include', 'AllSpaces'].join(''),
200
+ ['--home', 'graph'].join(''),
201
+ ['--home', '-graph'].join(''),
202
+ ];
203
+ for (const token of args) {
204
+ for (const flag of disallowed) {
205
+ if (token === flag || token.startsWith(`${flag}=`)) return flag;
206
+ }
207
+ }
208
+ return null;
209
+ }
210
+
211
+ export function formatScopeFlagRejection(flag: string): string {
212
+ return [
213
+ `Agent Knowledge is isolated; ${flag} is not accepted.`,
214
+ 'GoodVibes Agent must not use default Knowledge/Wiki or non-Agent product spaces.',
215
+ 'Use only /api/goodvibes-agent/knowledge/* Agent-owned routes.',
216
+ ].join('\n');
217
+ }
218
+
219
+ export async function runKnowledgeCall<TData>(
220
+ runtime: CliCommandRuntime,
221
+ method: DaemonCallMethod,
222
+ call: (connection: AgentDaemonConnection) => Promise<TData>,
223
+ ): Promise<AgentKnowledgeResult<TData>> {
224
+ const connection = resolveDaemonConnection(runtime);
225
+ if (!connection.token) {
226
+ return {
227
+ ok: false,
228
+ kind: 'auth_required',
229
+ error: `No runtime operator token found at ${connection.tokenPath}`,
230
+ baseUrl: connection.baseUrl,
231
+ route: method.route,
232
+ };
233
+ }
234
+ try {
235
+ const data = await call(connection);
236
+ return { ok: true, kind: method.kind, route: method.route, data };
237
+ } catch (error) {
238
+ return classifyKnowledgeError(error, connection, method.route);
239
+ }
240
+ }
@@ -36,13 +36,13 @@ function parseRuntimeUrl(rawValue: string, source: string): { readonly host: str
36
36
  }
37
37
 
38
38
  if (url.protocol !== 'http:') {
39
- throw new ConfigError(`${source} must use http:// because Agent connects to the local GoodVibes runtime API.`);
39
+ throw new ConfigError(`${source} must use http:// because Agent connects to the local GoodVibes API.`);
40
40
  }
41
41
  if (!url.hostname) {
42
42
  throw new ConfigError(`${source} must include a hostname.`);
43
43
  }
44
44
  if ((url.pathname && url.pathname !== '/') || url.search || url.hash) {
45
- throw new ConfigError(`${source} must point at the runtime root, not a path, query, or hash.`);
45
+ throw new ConfigError(`${source} must point at the connected GoodVibes API root, not a path, query, or hash.`);
46
46
  }
47
47
 
48
48
  const port = url.port ? Number.parseInt(url.port, 10) : 3421;
package/src/cli/help.ts CHANGED
@@ -27,6 +27,20 @@ export function renderGoodVibesHelp(binary = 'goodvibes-agent'): string {
27
27
  return [
28
28
  `Usage: ${binary} [OPTIONS] [PROMPT]`,
29
29
  ` ${binary} [OPTIONS] <COMMAND> [ARGS]`,
30
+ '',
31
+ 'Primary use:',
32
+ ` ${binary} Launch the interactive Agent TUI`,
33
+ ` ${binary} setup Open first-run Agent setup`,
34
+ '',
35
+ 'Inside the TUI:',
36
+ ' /agent Open the operator workspace',
37
+ ' /setup Open setup/config flows',
38
+ ' /model Choose provider and model',
39
+ ' /knowledge Use isolated Agent Knowledge',
40
+ ' /personas, /skills Tune local Agent behavior',
41
+ ' /routines Run local routines in the main conversation',
42
+ ' /schedule remind Create confirmed reminders or inspect schedules',
43
+ ' /delegate Explicitly hand build/fix/review work to GoodVibes TUI',
30
44
  '',
31
45
  'Commands:',
32
46
  ' tui [path] Start the interactive Agent terminal UI (default)',
@@ -40,9 +54,9 @@ export function renderGoodVibesHelp(binary = 'goodvibes-agent'): string {
40
54
  ' personas Manage local Agent personas',
41
55
  ' skills Manage local Agent skills and skill bundles',
42
56
  ' memory Manage Agent-owned durable memory records',
43
- ' routines Inspect local routines and explicitly promote one to an external schedule',
44
- ' auth Inspect Agent auth posture and external runtime token state',
45
- ' compat Inspect Agent SDK pin, runtime version, and Agent knowledge route readiness',
57
+ ' routines Inspect local routines and explicitly promote one to a connected schedule',
58
+ ' auth Inspect Agent auth posture and connection token state',
59
+ ' compat Inspect Agent SDK pin, connected service version, and Agent Knowledge route readiness',
46
60
  ' knowledge Use isolated Agent Knowledge/Wiki routes',
47
61
  ' ask|search Shortcuts for isolated Agent Knowledge ask/search',
48
62
  ' delegate Explicitly delegate build/fix/review work to GoodVibes TUI',
@@ -60,7 +74,7 @@ export function renderGoodVibesHelp(binary = 'goodvibes-agent'): string {
60
74
  ' -m, --model <registryKey> Override model. provider:model infers --provider',
61
75
  ' --provider <id> Override provider',
62
76
  ' --agent-profile <name> Use an isolated Agent profile home',
63
- ' --runtime-url <url> External GoodVibes runtime API root',
77
+ ' --runtime-url <url> Connected GoodVibes API root',
64
78
  ' -C, --cd <dir> Set working directory for this launch',
65
79
  ' --working-dir <dir> Alias for --cd',
66
80
  ' -c, --config <key=value> Override a config value for this launch',
@@ -135,7 +149,7 @@ const COMMAND_HELP: Record<string, CommandHelp> = {
135
149
  },
136
150
  status: {
137
151
  usage: ['status', 'status --json', '--runtime-url http://127.0.0.1:3421 status'],
138
- summary: 'Print Agent config, provider, auth, runtime connection, and setup posture.',
152
+ summary: 'Print Agent config, provider, auth, connected-service state, and setup posture.',
139
153
  examples: ['status', 'status --json', '--runtime-url http://127.0.0.1:3421 status'],
140
154
  },
141
155
  doctor: {
@@ -150,7 +164,7 @@ const COMMAND_HELP: Record<string, CommandHelp> = {
150
164
  },
151
165
  profiles: {
152
166
  usage: ['profiles list', 'profiles templates', 'profiles templates export <id> <path> --yes', 'profiles templates import <path> --yes', 'profiles show <name>', 'profiles create <name> [--template <id>] --yes', 'profiles delete <name> --yes', '--agent-profile <name>'],
153
- summary: 'Create and inspect isolated Agent profile homes, with starter templates for household, research, travel, operations, personal productivity, and local imported starters. A profile changes Agent-local config, sessions, memory, personas, skills, routines, and setup paths without changing the shared GoodVibes runtime.',
167
+ summary: 'Create and inspect isolated Agent profile homes, with starter templates for household, research, travel, operations, personal productivity, and local imported starters. A profile changes Agent-local config, sessions, memory, personas, skills, routines, and setup paths without changing connected GoodVibes services.',
154
168
  examples: ['profiles templates', 'profiles templates export research ./research-starter.json --yes', 'profiles templates import ./research-starter.json --yes', 'profiles create household --template household --yes', '--agent-profile household status'],
155
169
  },
156
170
  personas: {
@@ -237,7 +251,7 @@ const COMMAND_HELP: Record<string, CommandHelp> = {
237
251
  'routines receipt <receipt-id>',
238
252
  'routines promote <id> (--cron <expr>|--every <interval>|--at <iso-time>) [--timezone <tz>] [--name <schedule-name>] [--provider <id>] [--model <model>] [--delivery-channel <channel[:route[:label]]>|--delivery-route <route[:label]>|--delivery-webhook <url>|--delivery-link <url>] [--disabled] --yes',
239
253
  ],
240
- summary: 'Inspect Agent-local routines, review local promotion receipts, reconcile receipts against live external schedules, and explicitly promote a reviewed routine into a GoodVibes schedule. Without --yes, promote only prints the schedules.create preview.',
254
+ summary: 'Inspect Agent-local routines, review local promotion receipts, reconcile receipts against live connected schedules, and explicitly promote a reviewed routine into a GoodVibes schedule. Without --yes, promote only prints the schedules.create preview.',
241
255
  examples: [
242
256
  'routines list',
243
257
  'routines show daily-operations-sweep',
@@ -254,7 +268,7 @@ const COMMAND_HELP: Record<string, CommandHelp> = {
254
268
  },
255
269
  auth: {
256
270
  usage: ['auth', 'auth status', 'auth review', 'auth users', 'auth sessions'],
257
- summary: 'Inspect Agent auth posture and external runtime token state. Runtime user/session administration belongs to the runtime-owning TUI or host tooling.',
271
+ summary: 'Inspect Agent auth posture and connection token state. Runtime user/session administration stays outside Agent.',
258
272
  examples: ['auth', 'auth status', 'auth users'],
259
273
  },
260
274
  compat: {
@@ -269,10 +283,13 @@ const COMMAND_HELP: Record<string, CommandHelp> = {
269
283
  'knowledge search <query> [--limit <n>]',
270
284
  'knowledge list [--kind sources|nodes|issues] [--limit <n>]',
271
285
  'knowledge get <id>',
272
- 'knowledge connectors',
286
+ 'knowledge connectors [connectorId|doctor <connectorId>]',
273
287
  'knowledge ingest-url <url> [--title <title>] [--tags a,b] --yes',
288
+ 'knowledge ingest-file <path> [--title <title>] [--tags a,b] --yes',
289
+ 'knowledge ingest-connector <connectorId> [--input <json-or-text>|--path <path>|--content <text>] --yes',
274
290
  'knowledge import-urls <path> --yes',
275
291
  'knowledge import-bookmarks <path> --yes',
292
+ 'knowledge import-browser-history [--browsers chrome,firefox] [--sources history,bookmark] --yes',
276
293
  'knowledge reindex --yes',
277
294
  ],
278
295
  summary: 'Call isolated Agent Knowledge/Wiki routes under /api/goodvibes-agent/knowledge. No default wiki or non-Agent fallback.',
@@ -282,8 +299,12 @@ const COMMAND_HELP: Record<string, CommandHelp> = {
282
299
  'knowledge search "release checklist"',
283
300
  'knowledge list --kind sources',
284
301
  'knowledge connectors',
302
+ 'knowledge connectors doctor url',
285
303
  'knowledge ingest-url https://example.com/page --title "Reference" --yes',
304
+ 'knowledge ingest-file ./docs/guide.md --title "Guide" --yes',
305
+ 'knowledge ingest-connector url --input https://example.com/reference --yes',
286
306
  'knowledge import-urls ~/agent-links.txt --yes',
307
+ 'knowledge import-browser-history --browsers chrome,firefox --sources history,bookmark --yes',
287
308
  ],
288
309
  },
289
310
  ask: {
@@ -321,7 +342,7 @@ const COMMAND_HELP: Record<string, CommandHelp> = {
321
342
  },
322
343
  tasks: {
323
344
  usage: ['tasks list', 'tasks show <taskId>'],
324
- summary: 'Inspect in-process runtime tasks. Agent blocks runtime-owned task submission; use run for one-shot work or delegate for explicit build/fix/review handoff.',
345
+ summary: 'Inspect connected-service task summaries. Agent blocks service-owned task submission; use run for one-shot work or delegate for explicit build/fix/review handoff.',
325
346
  examples: ['tasks list', 'tasks show task-123', 'run "check provider readiness"', 'delegate "fix the failing tests"'],
326
347
  },
327
348
  bundle: {
@@ -342,7 +363,6 @@ const COMMAND_HELP: Record<string, CommandHelp> = {
342
363
  };
343
364
 
344
365
  const HELP_ALIASES: Record<string, string> = {
345
- app: 'tui',
346
366
  exec: 'run',
347
367
  onboarding: 'setup',
348
368
  provider: 'providers',
@@ -279,7 +279,7 @@ export async function handleTasks(runtime: CliCommandRuntime): Promise<string> {
279
279
  const [sub = 'list', ...rest] = runtime.cli.commandArgs;
280
280
  if (sub === 'submit') {
281
281
  return [
282
- 'GoodVibes Agent blocks CLI task submission from the runtime-owned task workflow.',
282
+ 'GoodVibes Agent blocks CLI task submission from the service-owned task workflow.',
283
283
  ' policy: do normal assistant work in the main Agent conversation or use `goodvibes-agent run <prompt>` for an explicit one-shot run.',
284
284
  ' build/fix/review: use `goodvibes-agent delegate <task>` for explicit GoodVibes TUI handoff.',
285
285
  ' result: no local task was started.',
@@ -289,7 +289,7 @@ export async function handleTasks(runtime: CliCommandRuntime): Promise<string> {
289
289
  const tasks = [...services.runtimeStore.getState().tasks.tasks.values()];
290
290
  if (sub === 'list') {
291
291
  return tasks.length === 0
292
- ? 'GoodVibes tasks\n No in-process runtime tasks are currently recorded.'
292
+ ? 'GoodVibes tasks\n No connected-service tasks are currently recorded.'
293
293
  : ['GoodVibes tasks', ...tasks.map((task) => ` ${task.id} ${task.status} ${task.kind} ${task.title}`)].join('\n');
294
294
  }
295
295
  if (sub === 'show') {
@@ -544,9 +544,9 @@ async function renderAuth(runtime: CliCommandRuntime): Promise<string> {
544
544
  ]);
545
545
  if (blocked.has(sub)) {
546
546
  return [
547
- 'Unsupported: runtime auth user/session administration is external to GoodVibes Agent.',
548
- 'GoodVibes Agent connects to an already-running runtime and does not create, delete, rotate, revoke, or clear runtime users, sessions, or bootstrap credentials.',
549
- 'Use the runtime-owning GoodVibes TUI or host tooling for runtime auth administration.',
547
+ 'Unsupported: connected-service auth user/session administration is outside GoodVibes Agent.',
548
+ 'GoodVibes Agent does not create, delete, rotate, revoke, or clear connected-service users, sessions, or bootstrap credentials.',
549
+ 'Use the owning GoodVibes host for auth administration.',
550
550
  ].join('\n');
551
551
  }
552
552
  if (sub !== 'status' && sub !== 'review' && sub !== 'list' && sub !== 'users' && sub !== 'sessions') {
@@ -566,21 +566,21 @@ async function renderAuth(runtime: CliCommandRuntime): Promise<string> {
566
566
  if (sub === 'users' || sub === 'sessions') {
567
567
  return formatJsonOrText(runtime.cli)(value, [
568
568
  `GoodVibes Agent auth ${sub}`,
569
- ' owner: external GoodVibes runtime',
569
+ ' owner: connected GoodVibes services',
570
570
  ` operator token: ${paths.operatorTokenPresent ? 'present' : 'missing'}`,
571
571
  ` operator token path: ${paths.operatorTokenPath}`,
572
- ` ${sub}: managed by the runtime-owning TUI or host tooling`,
573
- ' Agent does not enumerate or mutate runtime users/sessions from the local CLI.',
572
+ ` ${sub}: managed outside Agent`,
573
+ ' Agent does not enumerate or mutate connected-service users/sessions from the local CLI.',
574
574
  ].join('\n'));
575
575
  }
576
576
  return formatJsonOrText(runtime.cli)(value, [
577
577
  'GoodVibes Agent auth',
578
- ' owner: external GoodVibes runtime',
578
+ ' owner: connected GoodVibes services',
579
579
  ` permission mode: ${String(value.permissionMode)}`,
580
580
  ` operator token: ${paths.operatorTokenPresent ? 'present' : 'missing'} (${paths.operatorTokenPath})`,
581
581
  ` compatibility user store: ${paths.userStorePresent ? 'present' : 'missing'} (${paths.userStorePath})`,
582
582
  ` compatibility bootstrap credential: ${paths.bootstrapCredentialPresent ? 'present' : 'missing'} (${paths.bootstrapCredentialPath})`,
583
- ' runtime user/session administration: external',
583
+ ' connected-service user/session administration: outside Agent',
584
584
  ' next: goodvibes-agent providers',
585
585
  ' next: goodvibes-agent subscription providers',
586
586
  ].join('\n'));
@@ -41,7 +41,7 @@ const REQUIRED_TARBALL_PATHS = [
41
41
  'tsconfig.json',
42
42
  'docs/README.md',
43
43
  'docs/getting-started.md',
44
- 'docs/runtime-connection.md',
44
+ 'docs/connected-services.md',
45
45
  'docs/release-and-publishing.md',
46
46
  ] as const;
47
47
  const FORBIDDEN_TARBALL_PREFIXES = ['.github/', 'src/test/', 'src/.test/', '.goodvibes/', 'vendor/'] as const;
@@ -64,7 +64,7 @@ const PACKAGE_FACING_TEXT_PATHS = [
64
64
  'CHANGELOG.md',
65
65
  'docs/README.md',
66
66
  'docs/getting-started.md',
67
- 'docs/runtime-connection.md',
67
+ 'docs/connected-services.md',
68
68
  'docs/release-and-publishing.md',
69
69
  ] as const;
70
70
  const PACKAGE_FACING_FORBIDDEN_TEXT = [
@@ -101,6 +101,10 @@ const PACKAGE_FACING_FORBIDDEN_TEXT = [
101
101
  ['goodvibes-agent', 'remote'].join(' '),
102
102
  ['goodvibes-agent', 'bridge'].join(' '),
103
103
  ['goodvibes-agent', 'web'].join(' '),
104
+ ['goodvibes-agent', 'launch'].join(' '),
105
+ ['goodvibes-agent', 'start'].join(' '),
106
+ ['tui', '|launch'].join(''),
107
+ ['tui', '|launch|start'].join(''),
104
108
  'Every plan must have a multi-agent execution strategy',
105
109
  'NEVER skip WRFC',
106
110
  'ALWAYS work in parallel when implementing a plan',
@@ -116,7 +120,7 @@ const PACKAGE_FACING_REQUIRED_TEXT: readonly {
116
120
  { path: 'README.md', required: ['/api/goodvibes-agent/knowledge', 'bun add -g --trust @pellux/goodvibes-agent', 'bun pm -g untrusted'] },
117
121
  { path: 'docs/README.md', required: ['/api/goodvibes-agent/knowledge'] },
118
122
  { path: 'docs/getting-started.md', required: ['/api/goodvibes-agent/knowledge', 'bun add -g --trust @pellux/goodvibes-agent', 'bun pm -g untrusted'] },
119
- { path: 'docs/runtime-connection.md', required: ['/api/goodvibes-agent/knowledge'] },
123
+ { path: 'docs/connected-services.md', required: ['/api/goodvibes-agent/knowledge'] },
120
124
  { path: 'docs/release-and-publishing.md', required: ['/api/goodvibes-agent/knowledge', 'bun add -g --trust @pellux/goodvibes-agent', 'bun pm -g untrusted'] },
121
125
  ];
122
126
  const NON_COMMAND_ROUTE_ROOTS = new Set(['api', 'status']);