@pellux/goodvibes-agent 0.1.10 → 0.1.12
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 +32 -0
- package/package.json +1 -1
- package/src/cli/agent-knowledge-command.ts +30 -3
- package/src/cli/help.ts +2 -2
- package/src/input/commands/cloudflare-runtime.ts +20 -5
- package/src/input/commands/confirmation.ts +24 -0
- package/src/input/commands/discovery-runtime.ts +16 -7
- package/src/input/commands/eval.ts +27 -14
- package/src/input/commands/experience-runtime.ts +65 -26
- package/src/input/commands/health-runtime.ts +1 -1
- package/src/input/commands/hooks-runtime.ts +50 -19
- package/src/input/commands/incident-runtime.ts +17 -6
- package/src/input/commands/integration-runtime.ts +93 -50
- package/src/input/commands/knowledge.ts +38 -12
- package/src/input/commands/local-auth-runtime.ts +36 -13
- package/src/input/commands/local-provider-runtime.ts +22 -11
- package/src/input/commands/local-runtime.ts +21 -11
- package/src/input/commands/local-setup.ts +35 -16
- package/src/input/commands/managed-runtime.ts +51 -20
- package/src/input/commands/marketplace-runtime.ts +31 -16
- package/src/input/commands/mcp-runtime.ts +65 -34
- package/src/input/commands/memory-product-runtime.ts +72 -35
- package/src/input/commands/memory.ts +9 -9
- package/src/input/commands/notify-runtime.ts +27 -8
- package/src/input/commands/operator-runtime.ts +85 -17
- package/src/input/commands/planning-runtime.ts +14 -2
- package/src/input/commands/platform-access-runtime.ts +88 -45
- package/src/input/commands/platform-services-runtime.ts +51 -25
- package/src/input/commands/product-runtime.ts +54 -27
- package/src/input/commands/profile-sync-runtime.ts +17 -6
- package/src/input/commands/recall-bundle.ts +38 -17
- package/src/input/commands/recall-query.ts +15 -4
- package/src/input/commands/recall-review.ts +9 -3
- package/src/input/commands/remote-runtime-setup.ts +45 -18
- package/src/input/commands/remote-runtime.ts +25 -9
- package/src/input/commands/replay-runtime.ts +9 -2
- package/src/input/commands/services-runtime.ts +21 -10
- package/src/input/commands/session-content.ts +53 -51
- package/src/input/commands/session-workflow.ts +10 -4
- package/src/input/commands/session.ts +1 -1
- package/src/input/commands/settings-sync-runtime.ts +40 -17
- package/src/input/commands/share-runtime.ts +12 -4
- package/src/input/commands/shell-core.ts +3 -3
- package/src/input/commands/subscription-runtime.ts +35 -20
- package/src/input/commands/teleport-runtime.ts +16 -5
- package/src/input/commands/work-plan-runtime.ts +23 -12
- package/src/input/handler-content-actions.ts +11 -62
- package/src/input/handler-interactions.ts +1 -1
- package/src/input/handler-onboarding-cloudflare.ts +48 -117
- package/src/input/keybindings.ts +1 -1
- package/src/input/mcp-workspace.ts +25 -49
- package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +8 -8
- package/src/input/onboarding/onboarding-wizard-cloudflare.ts +1 -6
- package/src/input/profile-picker-modal.ts +13 -31
- package/src/input/session-picker-modal.ts +4 -30
- package/src/input/settings-modal-subscriptions.ts +3 -3
- package/src/panels/incident-review-panel.ts +1 -1
- package/src/panels/local-auth-panel.ts +4 -4
- package/src/panels/provider-account-snapshot.ts +1 -1
- package/src/panels/provider-health-domains.ts +2 -2
- package/src/panels/settings-sync-panel.ts +2 -2
- package/src/panels/subscription-panel.ts +7 -7
- package/src/renderer/block-actions.ts +1 -1
- package/src/renderer/help-overlay.ts +2 -2
- package/src/renderer/mcp-workspace.ts +12 -12
- package/src/renderer/profile-picker-modal.ts +3 -11
- package/src/renderer/session-picker-modal.ts +2 -10
- package/src/verification/live-verifier.ts +100 -68
- package/src/version.ts +1 -1
|
@@ -209,7 +209,7 @@ export async function buildProviderAccountSnapshot(
|
|
|
209
209
|
}
|
|
210
210
|
if (pending) {
|
|
211
211
|
issues.push('Provider has a pending OAuth login that has not been completed yet.');
|
|
212
|
-
recommendedActions.push(`Finish /subscription login ${providerId} finish <code> or clear the pending login.`);
|
|
212
|
+
recommendedActions.push(`Finish /subscription login ${providerId} finish <code> --yes or clear the pending login.`);
|
|
213
213
|
}
|
|
214
214
|
if (hasSubscription && hasApiKey) {
|
|
215
215
|
issues.push('Provider has both subscription and API-key auth paths; routing must remain explicit.');
|
|
@@ -54,13 +54,13 @@ export function buildProviderHealthDomainSummaries(
|
|
|
54
54
|
summary: auth.bootstrapCredentialPresent
|
|
55
55
|
? 'bootstrap credential file still present'
|
|
56
56
|
: `${auth.userCount} users / ${auth.sessionCount} sessions`,
|
|
57
|
-
next: auth.bootstrapCredentialPresent ? '/auth local clear-bootstrap-file' : '/auth local review',
|
|
57
|
+
next: auth.bootstrapCredentialPresent ? '/auth local clear-bootstrap-file --yes' : '/auth local review',
|
|
58
58
|
details: [
|
|
59
59
|
auth.bootstrapCredentialPresent ? 'bootstrap credential file should be cleared after rotation' : `${auth.userCount} local auth users configured`,
|
|
60
60
|
auth.userCount <= 1 ? 'only one local auth user configured' : `${auth.sessionCount} active local auth sessions`,
|
|
61
61
|
].filter(Boolean),
|
|
62
62
|
nextSteps: auth.bootstrapCredentialPresent
|
|
63
|
-
? ['/auth local review', '/auth local rotate-password <user> <password>', '/auth local clear-bootstrap-file']
|
|
63
|
+
? ['/auth local review', '/auth local rotate-password <user> <password> --yes', '/auth local clear-bootstrap-file --yes']
|
|
64
64
|
: ['/auth local review'],
|
|
65
65
|
});
|
|
66
66
|
|
|
@@ -86,7 +86,7 @@ export class SettingsSyncPanel extends ScrollableListPanel<ResolvedEntry> {
|
|
|
86
86
|
: [buildPanelLine(width, [[' No recent sync or managed-setting failures.', C.dim]])]),
|
|
87
87
|
// Conflicts
|
|
88
88
|
...(snapshot.conflicts.length > 0
|
|
89
|
-
? snapshot.conflicts.map((conflict) => buildPanelLine(width, [[` ${conflict.key}`.padEnd(30), C.value], [` ${conflict.source}`.padEnd(10), C.warn], [` resolve: /settingssync resolve ${conflict.key} local|synced`.slice(0, Math.max(0, width - 42)), C.dim]]))
|
|
89
|
+
? snapshot.conflicts.map((conflict) => buildPanelLine(width, [[` ${conflict.key}`.padEnd(30), C.value], [` ${conflict.source}`.padEnd(10), C.warn], [` resolve: /settingssync resolve ${conflict.key} local|synced --yes`.slice(0, Math.max(0, width - 42)), C.dim]]))
|
|
90
90
|
: [buildPanelLine(width, [[' No settings conflicts detected.', C.dim]])]),
|
|
91
91
|
// Rollback History
|
|
92
92
|
...(snapshot.rollbackHistory.length > 0
|
|
@@ -108,7 +108,7 @@ export class SettingsSyncPanel extends ScrollableListPanel<ResolvedEntry> {
|
|
|
108
108
|
buildPanelLine(width, [[' managed ', C.label], [String(selectedEntry.managedValue ?? '(unset)').slice(0, Math.max(0, width - 11)), C.warn]]),
|
|
109
109
|
], C)
|
|
110
110
|
: []),
|
|
111
|
-
buildPanelLine(width, [[' ↑/↓ browse /settingssync show <key>
|
|
111
|
+
buildPanelLine(width, [[' ↑/↓ browse /settingssync show <key> mutations require --yes: resolve/apply-staged ', C.dim]]),
|
|
112
112
|
];
|
|
113
113
|
|
|
114
114
|
return this.renderList(width, height, {
|
|
@@ -78,8 +78,8 @@ export class SubscriptionPanel extends ScrollableListPanel<SubscriptionRow> {
|
|
|
78
78
|
protected override getEmptyStateMessage() { return ' No provider subscriptions are active yet.'; }
|
|
79
79
|
protected override getEmptyStateActions() {
|
|
80
80
|
return [
|
|
81
|
-
{ command: '/subscription login openai start', summary: 'start the first-class OpenAI subscription flow' },
|
|
82
|
-
{ command: '/login provider <name> start', summary: 'use the front-door auth surface for supported providers' },
|
|
81
|
+
{ command: '/subscription login openai start --yes', summary: 'start the first-class OpenAI subscription flow' },
|
|
82
|
+
{ command: '/login provider <name> start --yes', summary: 'use the front-door auth surface for supported providers' },
|
|
83
83
|
{ command: '/services auth-review', summary: 'inspect configured service auth posture and stored secrets' },
|
|
84
84
|
];
|
|
85
85
|
}
|
|
@@ -187,7 +187,7 @@ export class SubscriptionPanel extends ScrollableListPanel<SubscriptionRow> {
|
|
|
187
187
|
{ label: 'selected', value: (this.rows[this.selectedIndex]?.provider ?? 'none'), valueColor: this.rows[this.selectedIndex] ? C.value : C.dim },
|
|
188
188
|
{ label: 'status', value: this.rows[this.selectedIndex] ? statusOf(this.rows[this.selectedIndex]!) : 'n/a', valueColor: this.rows[this.selectedIndex] ? statusColor(statusOf(this.rows[this.selectedIndex]!)) : C.dim },
|
|
189
189
|
], C),
|
|
190
|
-
buildGuidanceLine(width, '/subscription login <provider> start', 'start or repair browser login for the selected provider route', C),
|
|
190
|
+
buildGuidanceLine(width, '/subscription login <provider> start --yes', 'start or repair browser login for the selected provider route', C),
|
|
191
191
|
];
|
|
192
192
|
|
|
193
193
|
// Empty state: render posture + base empty state
|
|
@@ -205,7 +205,7 @@ export class SubscriptionPanel extends ScrollableListPanel<SubscriptionRow> {
|
|
|
205
205
|
intro,
|
|
206
206
|
sections: [{ lines: [...summaryLines, ...emptyLines] }],
|
|
207
207
|
footerLines: [
|
|
208
|
-
buildGuidanceLine(width, '/subscription login <provider> start', 'start browser-based provider login from the packaged subscription surface', C),
|
|
208
|
+
buildGuidanceLine(width, '/subscription login <provider> start --yes', 'start browser-based provider login from the packaged subscription surface', C),
|
|
209
209
|
buildPanelLine(width, [[' Up/Down move Enter/X sign out selected provider r refresh', C.dim]]),
|
|
210
210
|
],
|
|
211
211
|
palette: C,
|
|
@@ -240,9 +240,9 @@ export class SubscriptionPanel extends ScrollableListPanel<SubscriptionRow> {
|
|
|
240
240
|
detailRows.push(buildPanelLine(width, [[` Press Enter or X again to sign out ${selectedRow.provider}.`, C.warn]]));
|
|
241
241
|
}
|
|
242
242
|
} else if (selectedRow.pending) {
|
|
243
|
-
detailRows.push(buildPanelLine(width, [[' Login is pending. Finish with /subscription login <provider> finish <code
|
|
243
|
+
detailRows.push(buildPanelLine(width, [[' Login is pending. Finish with /subscription login <provider> finish <code> --yes.', C.warn]]));
|
|
244
244
|
} else if (selectedRow.hasOAuthConfig) {
|
|
245
|
-
detailRows.push(buildPanelLine(width, [[' Ready for login. Start with /subscription login <provider> start.', C.dim]]));
|
|
245
|
+
detailRows.push(buildPanelLine(width, [[' Ready for login. Start with /subscription login <provider> start --yes.', C.dim]]));
|
|
246
246
|
} else {
|
|
247
247
|
detailRows.push(buildPanelLine(width, [[' Add a provider-specific OAuth config or enable a built-in subscription provider to use subscription login.', C.bad]]));
|
|
248
248
|
}
|
|
@@ -255,7 +255,7 @@ export class SubscriptionPanel extends ScrollableListPanel<SubscriptionRow> {
|
|
|
255
255
|
header: headerLines,
|
|
256
256
|
footer: [
|
|
257
257
|
...detailRows,
|
|
258
|
-
buildGuidanceLine(width, '/subscription login <provider> start', 'start browser-based provider login from the packaged subscription surface', C),
|
|
258
|
+
buildGuidanceLine(width, '/subscription login <provider> start --yes', 'start browser-based provider login from the packaged subscription surface', C),
|
|
259
259
|
buildPanelLine(width, [[' Up/Down move Enter/X sign out selected provider r refresh', C.dim]]),
|
|
260
260
|
],
|
|
261
261
|
});
|
|
@@ -12,7 +12,7 @@ const ALL_ACTIONS: BlockAction[] = [
|
|
|
12
12
|
{ id: 'copy', label: 'Copy', key: 'c' },
|
|
13
13
|
{ id: 'bookmark', label: 'Bookmark', key: 'b' },
|
|
14
14
|
{ id: 'toggle', label: 'Collapse/Expand',key: 'Tab' },
|
|
15
|
-
{ id: 'apply', label: '
|
|
15
|
+
{ id: 'apply', label: 'Diff apply blocked', key: 'a' },
|
|
16
16
|
{ id: 'rerun', label: 'Re-run tool', key: 'r' },
|
|
17
17
|
];
|
|
18
18
|
|
|
@@ -215,7 +215,7 @@ export function renderShortcutsOverlay(
|
|
|
215
215
|
row(kb('clear-prompt'), 'Clear prompt'),
|
|
216
216
|
row(kb('delete-word'), 'Delete word backward'),
|
|
217
217
|
row(kb('kill-line'), 'Kill to end of line'),
|
|
218
|
-
row(kb('apply-diff-line-start'), '
|
|
218
|
+
row(kb('apply-diff-line-start'), 'Diff apply blocked / line start'),
|
|
219
219
|
row(kb('next-error-line-end'), 'Next error / line end'),
|
|
220
220
|
'',
|
|
221
221
|
' Actions',
|
|
@@ -223,7 +223,7 @@ export function renderShortcutsOverlay(
|
|
|
223
223
|
row('Tab', 'Collapse/expand block'),
|
|
224
224
|
row(kb('bookmark'), 'Bookmark block'),
|
|
225
225
|
row(kb('block-copy'), 'Copy block to clipboard'),
|
|
226
|
-
row(kb('block-save'), '
|
|
226
|
+
row(kb('block-save'), 'Block save blocked; use /share --yes'),
|
|
227
227
|
row(kb('copy-selection'), 'Copy selection'),
|
|
228
228
|
row('F2', 'Process monitor'),
|
|
229
229
|
row('?', 'Help overlay'),
|
|
@@ -82,16 +82,16 @@ function selectedDetailLines(workspace: McpWorkspace, width: number): WorkspaceR
|
|
|
82
82
|
if (workspace.mode === 'form') {
|
|
83
83
|
const field = workspace.formFields[workspace.formIndex];
|
|
84
84
|
lines.push(
|
|
85
|
-
workspace.editingServerName ? `
|
|
86
|
-
'
|
|
85
|
+
workspace.editingServerName ? `Previewing server: ${workspace.editingServerName}` : 'Drafting an MCP server command',
|
|
86
|
+
'Workspace writes/reloads are blocked. Use the generated /mcp add ... --yes command from the prompt.',
|
|
87
87
|
field ? `${field.label}: ${field.help}` : '',
|
|
88
|
-
'Project
|
|
88
|
+
'Project/global config locations are shown for review. This workspace does not write or reload MCP config.',
|
|
89
89
|
);
|
|
90
90
|
} else if (workspace.mode === 'delete-confirm') {
|
|
91
91
|
lines.push(
|
|
92
|
-
`
|
|
93
|
-
'
|
|
94
|
-
'Press
|
|
92
|
+
`Removal blocked for server: ${workspace.editingServerName ?? '(unknown)'}`,
|
|
93
|
+
'Use /mcp remove <server> --scope <project|global> --yes from the prompt for explicit removal.',
|
|
94
|
+
'Press n or Esc to return.',
|
|
95
95
|
);
|
|
96
96
|
} else {
|
|
97
97
|
const selected = workspace.selectedRow;
|
|
@@ -207,19 +207,19 @@ function buildControlRows(workspace: McpWorkspace, width: number, height: number
|
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
function footerText(workspace: McpWorkspace): string {
|
|
210
|
-
if (workspace.mode === 'form') return 'Focus server
|
|
211
|
-
if (workspace.mode === 'delete-confirm') return 'Focus remove
|
|
212
|
-
return 'Focus MCP workspace · Up/Down choose · Enter
|
|
210
|
+
if (workspace.mode === 'form') return 'Focus server command preview · Up/Down field · Left/Right cycle · Type edit · Enter show command · Esc back';
|
|
211
|
+
if (workspace.mode === 'delete-confirm') return 'Focus remove guidance · n/Esc cancel';
|
|
212
|
+
return 'Focus MCP workspace · Up/Down choose · Enter view/action · a draft · d removal command · r reload command · t tools · Esc close';
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
export function renderMcpWorkspace(workspace: McpWorkspace, width: number, height: number): Line[] {
|
|
216
216
|
const metrics = getFullscreenWorkspaceMetrics({ width, height });
|
|
217
217
|
const connected = workspace.servers.filter((server) => server.connected).length;
|
|
218
|
-
const stateLabel = workspace.mode === 'browse' ? 'Browse' : workspace.mode === 'form' ? '
|
|
218
|
+
const stateLabel = workspace.mode === 'browse' ? 'Browse' : workspace.mode === 'form' ? 'Command Preview' : 'Remove Guidance';
|
|
219
219
|
const mainHeader = workspace.mode === 'form'
|
|
220
|
-
? 'MCP server
|
|
220
|
+
? 'MCP server command preview'
|
|
221
221
|
: workspace.mode === 'delete-confirm'
|
|
222
|
-
? '
|
|
222
|
+
? 'MCP remove command'
|
|
223
223
|
: `Servers ${connected}/${workspace.servers.length} connected · Tools ${workspace.tools.length}`;
|
|
224
224
|
|
|
225
225
|
return renderFullscreenWorkspace({
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Shows a list of saved profiles with:
|
|
6
6
|
* - name, timestamp (formatted), settings preview
|
|
7
|
-
* Footer hints: [Up/Down] Navigate [Enter] Load [
|
|
7
|
+
* Footer hints: [Up/Down] Navigate [Enter] Load [Esc] Close
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { Line } from '../types/grid.ts';
|
|
@@ -51,7 +51,7 @@ export function renderProfilePickerModal(
|
|
|
51
51
|
});
|
|
52
52
|
sections.push({
|
|
53
53
|
type: 'text',
|
|
54
|
-
content: '
|
|
54
|
+
content: 'Use /profiles save <name> --yes to save the current settings as a profile.',
|
|
55
55
|
style: { fg: '240', dim: true },
|
|
56
56
|
});
|
|
57
57
|
} else {
|
|
@@ -107,14 +107,6 @@ export function renderProfilePickerModal(
|
|
|
107
107
|
style: { fg: '#00ffcc' },
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
|
-
if (modal.deleteConfirmationTarget) {
|
|
111
|
-
sections.push({
|
|
112
|
-
type: 'text',
|
|
113
|
-
content: `Press [d] again to permanently delete ${modal.deleteConfirmationTarget}.`,
|
|
114
|
-
style: { fg: '#f59e0b', dim: true },
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
|
|
118
110
|
return ModalFactory.createModal(
|
|
119
111
|
{
|
|
120
112
|
title: 'Profiles',
|
|
@@ -122,7 +114,7 @@ export function renderProfilePickerModal(
|
|
|
122
114
|
margin: boxMargin,
|
|
123
115
|
targetContentRows,
|
|
124
116
|
sections,
|
|
125
|
-
hints: ['[Up/Down] Navigate', '[Enter] Load', '
|
|
117
|
+
hints: ['[Up/Down] Navigate', '[Enter] Load', '/profiles save|delete --yes', '[Esc] Close'],
|
|
126
118
|
},
|
|
127
119
|
width,
|
|
128
120
|
);
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Shows a list of saved sessions with:
|
|
6
6
|
* - name, timestamp (formatted), message count
|
|
7
|
-
* Footer hints: [Enter] Load [
|
|
7
|
+
* Footer hints: [Enter] Load [Esc] Close
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { Line } from '../types/grid.ts';
|
|
@@ -105,14 +105,6 @@ export function renderSessionPickerModal(
|
|
|
105
105
|
});
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
if (modal.deleteConfirmationTarget) {
|
|
109
|
-
sections.push({
|
|
110
|
-
type: 'text',
|
|
111
|
-
content: `Deletion is armed for ${modal.deleteConfirmationTarget}. Move selection or press Esc to cancel.`,
|
|
112
|
-
style: { fg: '244', dim: true },
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
|
|
116
108
|
return ModalFactory.createModal(
|
|
117
109
|
{
|
|
118
110
|
title: 'Sessions',
|
|
@@ -120,7 +112,7 @@ export function renderSessionPickerModal(
|
|
|
120
112
|
margin: boxMargin,
|
|
121
113
|
targetContentRows,
|
|
122
114
|
sections,
|
|
123
|
-
hints: ['[\u2191\u2193] Navigate', '[Enter] Load', '
|
|
115
|
+
hints: ['[\u2191\u2193] Navigate', '[Enter] Load', 'Delete: /session delete <id> --yes', '[Esc] Close'],
|
|
124
116
|
},
|
|
125
117
|
width,
|
|
126
118
|
);
|
|
@@ -3,6 +3,7 @@ import { join, resolve } from 'node:path';
|
|
|
3
3
|
import { spawn } from 'node:child_process';
|
|
4
4
|
import { auditGoodVibesHome } from '../config/goodvibes-home-audit.ts';
|
|
5
5
|
import { buildVerificationLedger } from './verification-ledger.ts';
|
|
6
|
+
import { SDK_VERSION } from '../version.ts';
|
|
6
7
|
|
|
7
8
|
export type LiveVerificationStatus = 'pass' | 'warn' | 'fail' | 'skip';
|
|
8
9
|
|
|
@@ -271,6 +272,25 @@ function countStatuses(checks: readonly LiveVerificationCheck[]): Record<LiveVer
|
|
|
271
272
|
);
|
|
272
273
|
}
|
|
273
274
|
|
|
275
|
+
export function buildAgentKnowledgeLiveSkipCheck(
|
|
276
|
+
id: string,
|
|
277
|
+
title: string,
|
|
278
|
+
daemonVersion: string,
|
|
279
|
+
expectedSdkVersion = SDK_VERSION,
|
|
280
|
+
): LiveVerificationCheck {
|
|
281
|
+
return {
|
|
282
|
+
id,
|
|
283
|
+
title,
|
|
284
|
+
status: 'skip',
|
|
285
|
+
summary: `Skipped because external daemon SDK ${daemonVersion} does not match Agent SDK pin ${expectedSdkVersion}.`,
|
|
286
|
+
detail: [
|
|
287
|
+
'Agent Knowledge is intentionally isolated under /api/goodvibes-agent/knowledge/*.',
|
|
288
|
+
'An older daemon cannot validate those routes, and Agent must not fall back to default Knowledge/Wiki or HomeGraph.',
|
|
289
|
+
'Update/restart the external daemon, then rerun live verification.',
|
|
290
|
+
].join('\n'),
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
274
294
|
export async function buildLiveVerificationReport(options: LiveVerificationOptions): Promise<LiveVerificationReport> {
|
|
275
295
|
const homeDir = resolve(options.homeDir);
|
|
276
296
|
const projectRoot = resolve(options.projectRoot);
|
|
@@ -278,6 +298,7 @@ export async function buildLiveVerificationReport(options: LiveVerificationOptio
|
|
|
278
298
|
const daemonBaseUrl = resolveDaemonBaseUrl(homeDir, options.daemonBaseUrl);
|
|
279
299
|
const token = options.token ?? readDaemonToken(homeDir);
|
|
280
300
|
const checks: LiveVerificationCheck[] = [];
|
|
301
|
+
let daemonSdkVersion: string | null = null;
|
|
281
302
|
|
|
282
303
|
const ledger = buildVerificationLedger(projectRoot);
|
|
283
304
|
checks.push({
|
|
@@ -328,7 +349,7 @@ export async function buildLiveVerificationReport(options: LiveVerificationOptio
|
|
|
328
349
|
'Agent CLI compatibility JSON command',
|
|
329
350
|
await runCommand(binaryPath, ['compat', '--json'], projectRoot),
|
|
330
351
|
'Agent CLI compatibility returned parseable JSON.',
|
|
331
|
-
{ parseJson: true },
|
|
352
|
+
{ parseJson: true, warnOnNonZero: true },
|
|
332
353
|
));
|
|
333
354
|
checks.push(commandCheck(
|
|
334
355
|
'cli-agent-knowledge-status',
|
|
@@ -392,6 +413,7 @@ export async function buildLiveVerificationReport(options: LiveVerificationOptio
|
|
|
392
413
|
const version = typeof parsed.sdkVersion === 'string'
|
|
393
414
|
? parsed.sdkVersion
|
|
394
415
|
: typeof parsed.version === 'string' ? parsed.version : 'unknown';
|
|
416
|
+
daemonSdkVersion = version;
|
|
395
417
|
return { status: 'pass', summary: `/status returned 200, version ${version}.` };
|
|
396
418
|
} catch {
|
|
397
419
|
return { status: 'warn', summary: '/status returned 200 but was not parseable JSON.' };
|
|
@@ -438,78 +460,88 @@ export async function buildLiveVerificationReport(options: LiveVerificationOptio
|
|
|
438
460
|
},
|
|
439
461
|
));
|
|
440
462
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
463
|
+
const daemonVersionMismatch = daemonSdkVersion !== null && daemonSdkVersion !== 'unknown' && daemonSdkVersion !== SDK_VERSION;
|
|
464
|
+
if (daemonVersionMismatch) {
|
|
465
|
+
const mismatchedDaemonVersion = daemonSdkVersion ?? 'unknown';
|
|
466
|
+
checks.push(
|
|
467
|
+
buildAgentKnowledgeLiveSkipCheck('agent-knowledge-status', 'Agent Knowledge isolated /status', mismatchedDaemonVersion),
|
|
468
|
+
buildAgentKnowledgeLiveSkipCheck('agent-knowledge-ask-isolated', 'Agent Knowledge isolated ask', mismatchedDaemonVersion),
|
|
469
|
+
buildAgentKnowledgeLiveSkipCheck('agent-knowledge-search-isolated', 'Agent Knowledge isolated search', mismatchedDaemonVersion),
|
|
470
|
+
);
|
|
471
|
+
} else {
|
|
472
|
+
checks.push(await fetchJsonCheck(
|
|
473
|
+
'agent-knowledge-status',
|
|
474
|
+
'Agent Knowledge isolated /status',
|
|
475
|
+
`${daemonBaseUrl}/api/goodvibes-agent/knowledge/status`,
|
|
476
|
+
token,
|
|
477
|
+
{
|
|
478
|
+
validate: (status, body) => {
|
|
479
|
+
if (status !== 200) return { status: 'fail', summary: `/api/goodvibes-agent/knowledge/status returned ${status}.` };
|
|
480
|
+
try {
|
|
481
|
+
JSON.parse(body);
|
|
482
|
+
return { status: 'pass', summary: 'Agent Knowledge status route returned parseable JSON.' };
|
|
483
|
+
} catch {
|
|
484
|
+
return { status: 'fail', summary: 'Agent Knowledge status was not parseable JSON.' };
|
|
485
|
+
}
|
|
486
|
+
},
|
|
455
487
|
},
|
|
456
|
-
|
|
457
|
-
));
|
|
488
|
+
));
|
|
458
489
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
490
|
+
checks.push(await fetchJsonCheck(
|
|
491
|
+
'agent-knowledge-ask-isolated',
|
|
492
|
+
'Agent Knowledge isolated ask',
|
|
493
|
+
`${daemonBaseUrl}/api/goodvibes-agent/knowledge/ask`,
|
|
494
|
+
token,
|
|
495
|
+
{
|
|
496
|
+
method: 'POST',
|
|
497
|
+
body: {
|
|
498
|
+
query: 'What is GoodVibes Agent?',
|
|
499
|
+
limit: 5,
|
|
500
|
+
mode: 'concise',
|
|
501
|
+
includeSources: true,
|
|
502
|
+
includeConfidence: true,
|
|
503
|
+
includeLinkedObjects: true,
|
|
504
|
+
},
|
|
505
|
+
validate: (status, body) => {
|
|
506
|
+
if (status !== 200) return { status: 'fail', summary: `/api/goodvibes-agent/knowledge/ask returned ${status}.` };
|
|
507
|
+
try {
|
|
508
|
+
JSON.parse(body);
|
|
509
|
+
} catch {
|
|
510
|
+
return { status: 'fail', summary: 'Agent Knowledge ask was not parseable JSON.' };
|
|
511
|
+
}
|
|
512
|
+
const lower = body.toLowerCase();
|
|
513
|
+
if (lower.includes('home assistant') || lower.includes('homegraph') || lower.includes('home graph')) {
|
|
514
|
+
return { status: 'fail', summary: 'Agent Knowledge ask returned HomeGraph/Home Assistant contamination.' };
|
|
515
|
+
}
|
|
516
|
+
return { status: 'pass', summary: 'Agent Knowledge ask stayed on the isolated Agent route.' };
|
|
517
|
+
},
|
|
473
518
|
},
|
|
474
|
-
|
|
475
|
-
if (status !== 200) return { status: 'fail', summary: `/api/goodvibes-agent/knowledge/ask returned ${status}.` };
|
|
476
|
-
try {
|
|
477
|
-
JSON.parse(body);
|
|
478
|
-
} catch {
|
|
479
|
-
return { status: 'fail', summary: 'Agent Knowledge ask was not parseable JSON.' };
|
|
480
|
-
}
|
|
481
|
-
const lower = body.toLowerCase();
|
|
482
|
-
if (lower.includes('home assistant') || lower.includes('homegraph') || lower.includes('home graph')) {
|
|
483
|
-
return { status: 'fail', summary: 'Agent Knowledge ask returned HomeGraph/Home Assistant contamination.' };
|
|
484
|
-
}
|
|
485
|
-
return { status: 'pass', summary: 'Agent Knowledge ask stayed on the isolated Agent route.' };
|
|
486
|
-
},
|
|
487
|
-
},
|
|
488
|
-
));
|
|
519
|
+
));
|
|
489
520
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
521
|
+
checks.push(await fetchJsonCheck(
|
|
522
|
+
'agent-knowledge-search-isolated',
|
|
523
|
+
'Agent Knowledge isolated search',
|
|
524
|
+
`${daemonBaseUrl}/api/goodvibes-agent/knowledge/search`,
|
|
525
|
+
token,
|
|
526
|
+
{
|
|
527
|
+
method: 'POST',
|
|
528
|
+
body: { query: 'What is GoodVibes Agent?', limit: 5 },
|
|
529
|
+
validate: (status, body) => {
|
|
530
|
+
if (status !== 200) return { status: 'fail', summary: `/api/goodvibes-agent/knowledge/search returned ${status}.` };
|
|
531
|
+
try {
|
|
532
|
+
JSON.parse(body);
|
|
533
|
+
} catch {
|
|
534
|
+
return { status: 'fail', summary: 'Agent Knowledge search was not parseable JSON.' };
|
|
535
|
+
}
|
|
536
|
+
const lower = body.toLowerCase();
|
|
537
|
+
if (lower.includes('home assistant') || lower.includes('homegraph') || lower.includes('home graph')) {
|
|
538
|
+
return { status: 'fail', summary: 'Agent Knowledge search returned HomeGraph/Home Assistant contamination.' };
|
|
539
|
+
}
|
|
540
|
+
return { status: 'pass', summary: 'Agent Knowledge search stayed on the isolated Agent route.' };
|
|
541
|
+
},
|
|
510
542
|
},
|
|
511
|
-
|
|
512
|
-
|
|
543
|
+
));
|
|
544
|
+
}
|
|
513
545
|
|
|
514
546
|
const counts = countStatuses(checks);
|
|
515
547
|
const ok = counts.fail === 0 && (!options.strict || counts.warn === 0);
|
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.12';
|
|
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 {
|