@pellux/goodvibes-agent 0.1.117 → 1.0.0
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 -4
- package/README.md +35 -14
- package/bin/goodvibes-agent.ts +16 -2
- package/dist/package/main.js +176073 -170980
- package/docs/README.md +11 -5
- package/docs/channels-remote-and-api.md +50 -0
- package/docs/connected-host.md +3 -3
- package/docs/getting-started.md +29 -15
- package/docs/knowledge-artifacts-and-multimodal.md +91 -0
- package/docs/project-planning.md +79 -0
- package/docs/providers-and-routing.md +46 -0
- package/docs/release-and-publishing.md +44 -9
- package/docs/tools-and-commands.md +123 -0
- package/docs/voice-and-live-tts.md +51 -0
- package/package.json +2 -5
- package/src/agent/channel-delivery.ts +201 -0
- package/src/agent/media-generation.ts +159 -0
- package/src/agent/memory-prompt.ts +0 -1
- package/src/agent/note-registry.ts +329 -0
- package/src/agent/operator-actions.ts +343 -0
- package/src/agent/persona-registry.ts +15 -14
- package/src/agent/record-labels.ts +107 -0
- package/src/agent/reminder-schedule-format.ts +33 -24
- package/src/agent/reminder-schedule.ts +26 -25
- package/src/agent/routine-registry.ts +13 -12
- package/src/agent/routine-schedule-args.ts +2 -1
- package/src/agent/routine-schedule-format.ts +77 -53
- package/src/agent/routine-schedule-promotion.ts +34 -32
- package/src/agent/routine-schedule-receipts.ts +28 -26
- package/src/agent/runtime-profile-starters.ts +2 -2
- package/src/agent/runtime-profile.ts +18 -17
- package/src/agent/skill-registry.ts +25 -24
- package/src/cli/agent-knowledge-args.ts +5 -1
- package/src/cli/agent-knowledge-command.ts +39 -33
- package/src/cli/agent-knowledge-format.ts +80 -67
- package/src/cli/agent-knowledge-methods.ts +1 -1
- package/src/cli/agent-knowledge-runtime.ts +32 -26
- package/src/cli/bundle-command.ts +13 -8
- package/src/cli/config-overrides.ts +37 -36
- package/src/cli/external-runtime.ts +10 -4
- package/src/cli/help.ts +29 -11
- package/src/cli/local-library-command.ts +134 -68
- package/src/cli/management-commands.ts +98 -62
- package/src/cli/management.ts +52 -26
- package/src/cli/memory-command.ts +66 -32
- package/src/cli/parser.ts +37 -24
- package/src/cli/profiles-command.ts +52 -35
- package/src/cli/provider-auth-routes.ts +2 -1
- package/src/cli/routines-command.ts +44 -36
- package/src/cli/service-posture.ts +6 -6
- package/src/cli/status.ts +46 -121
- package/src/core/conversation-message-snapshot.ts +131 -0
- package/src/input/agent-workspace-activation.ts +33 -7
- package/src/input/agent-workspace-basic-command-editor-submission.ts +7 -3
- package/src/input/agent-workspace-basic-command-editors.ts +30 -10
- package/src/input/agent-workspace-categories.ts +276 -64
- package/src/input/agent-workspace-channel-command-editor-submission.ts +34 -0
- package/src/input/agent-workspace-channel-command-editors.ts +23 -5
- package/src/input/agent-workspace-channels.ts +35 -2
- package/src/input/agent-workspace-command-editor.ts +18 -2
- package/src/input/agent-workspace-config-reader.ts +16 -0
- package/src/input/agent-workspace-delegation-editor-submission.ts +1 -1
- package/src/input/agent-workspace-editors.ts +140 -2
- package/src/input/agent-workspace-knowledge-query-editor.ts +1 -1
- package/src/input/agent-workspace-learned-behavior.ts +2 -2
- package/src/input/agent-workspace-library-command-editor-submission.ts +1 -1
- package/src/input/agent-workspace-library-command-editors.ts +2 -2
- package/src/input/agent-workspace-local-operations.ts +218 -0
- package/src/input/agent-workspace-local-selection.ts +75 -0
- package/src/input/agent-workspace-media-command-editor-submission.ts +62 -0
- package/src/input/agent-workspace-media-command-editors.ts +27 -0
- package/src/input/agent-workspace-memory-command-editors.ts +1 -1
- package/src/input/agent-workspace-memory-editor.ts +2 -2
- package/src/input/agent-workspace-navigation.ts +38 -2
- package/src/input/agent-workspace-notify-editor-submission.ts +16 -2
- package/src/input/agent-workspace-operations-command-editor-submission.ts +63 -1
- package/src/input/agent-workspace-operations-command-editors.ts +80 -3
- package/src/input/agent-workspace-panel-route.ts +43 -0
- package/src/input/agent-workspace-reminder-schedule-editor.ts +1 -1
- package/src/input/agent-workspace-routine-schedule-editor.ts +2 -2
- package/src/input/agent-workspace-search.ts +169 -0
- package/src/input/agent-workspace-setup.ts +22 -11
- package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +6 -6
- package/src/input/agent-workspace-skill-bundle-command-editors.ts +10 -10
- package/src/input/agent-workspace-snapshot.ts +112 -13
- package/src/input/agent-workspace-task-command-editors.ts +4 -4
- package/src/input/agent-workspace-token.ts +18 -2
- package/src/input/agent-workspace-types.ts +92 -4
- package/src/input/agent-workspace-voice-media.ts +3 -6
- package/src/input/agent-workspace-web-research-editor.ts +104 -0
- package/src/input/agent-workspace.ts +136 -208
- package/src/input/command-registry.ts +4 -1
- package/src/input/commands/agent-runtime-profile-runtime.ts +45 -41
- package/src/input/commands/agent-skills-runtime.ts +83 -70
- package/src/input/commands/agent-workspace-runtime.ts +23 -7
- package/src/input/commands/brief-runtime.ts +25 -24
- package/src/input/commands/channels-runtime.ts +145 -31
- package/src/input/commands/delegation-runtime.ts +29 -23
- package/src/input/commands/experience-runtime.ts +6 -4
- package/src/input/commands/guidance-runtime.ts +4 -4
- package/src/input/commands/health-runtime.ts +140 -115
- package/src/input/commands/knowledge.ts +57 -56
- package/src/input/commands/local-provider-runtime.ts +36 -18
- package/src/input/commands/local-runtime.ts +138 -16
- package/src/input/commands/local-setup-review.ts +7 -7
- package/src/input/commands/mcp-runtime.ts +56 -35
- package/src/input/commands/notify-runtime.ts +38 -9
- package/src/input/commands/operator-actions-runtime.ts +138 -0
- package/src/input/commands/operator-runtime.ts +6 -17
- package/src/input/commands/personas-runtime.ts +45 -30
- package/src/input/commands/planning-runtime.ts +1 -1
- package/src/input/commands/platform-access-runtime.ts +29 -29
- package/src/input/commands/provider-accounts-runtime.ts +60 -39
- package/src/input/commands/qrcode-runtime.ts +45 -6
- package/src/input/commands/recall-bundle.ts +11 -11
- package/src/input/commands/recall-capture.ts +13 -11
- package/src/input/commands/recall-query.ts +29 -21
- package/src/input/commands/recall-review.ts +2 -1
- package/src/input/commands/routines-runtime.ts +59 -43
- package/src/input/commands/schedule-runtime.ts +33 -20
- package/src/input/commands/security-runtime.ts +4 -4
- package/src/input/commands/session-content.ts +80 -78
- package/src/input/commands/session-workflow.ts +132 -93
- package/src/input/commands/session.ts +3 -174
- package/src/input/commands/shell-core.ts +32 -17
- package/src/input/commands/subscription-runtime.ts +53 -179
- package/src/input/commands/tasks-runtime.ts +20 -20
- package/src/input/commands/work-plan-runtime.ts +33 -8
- package/src/input/commands.ts +2 -2
- package/src/input/feed-context-factory.ts +2 -1
- package/src/input/file-picker.ts +3 -2
- package/src/input/handler-command-route.ts +4 -7
- package/src/input/handler-content-actions.ts +89 -1
- package/src/input/handler-feed-routes.ts +19 -12
- package/src/input/handler-feed.ts +6 -3
- package/src/input/handler-interactions.ts +7 -5
- package/src/input/handler-modal-stack.ts +3 -3
- package/src/input/handler-onboarding.ts +24 -80
- package/src/input/handler-shortcuts.ts +15 -4
- package/src/input/handler.ts +47 -17
- package/src/input/input-history.ts +5 -6
- package/src/input/keybindings.ts +22 -11
- package/src/input/onboarding/onboarding-wizard-apply.ts +13 -0
- package/src/input/onboarding/onboarding-wizard-constants.ts +1 -0
- package/src/input/onboarding/onboarding-wizard-operator-steps.ts +147 -57
- package/src/input/onboarding/onboarding-wizard-steps.ts +46 -29
- package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
- package/src/input/onboarding/onboarding-wizard.ts +68 -0
- package/src/input/profile-picker-modal.ts +31 -12
- package/src/input/session-picker-modal.ts +21 -4
- package/src/input/settings-modal-behavior.ts +0 -3
- package/src/input/settings-modal-subscriptions.ts +3 -3
- package/src/input/settings-modal-types.ts +2 -13
- package/src/input/settings-modal.ts +6 -52
- package/src/input/submission-intent.ts +0 -1
- package/src/input/submission-router.ts +3 -3
- package/src/main.ts +18 -8
- package/src/panels/approval-panel.ts +8 -8
- package/src/panels/automation-control-panel.ts +2 -2
- package/src/panels/base-panel.ts +1 -1
- package/src/panels/builtin/agent.ts +1 -1
- package/src/panels/builtin/operations.ts +1 -10
- package/src/panels/builtin/session.ts +9 -9
- package/src/panels/builtin/shared.ts +2 -2
- package/src/panels/cost-tracker-panel.ts +1 -1
- package/src/panels/docs-panel.ts +5 -3
- package/src/panels/index.ts +0 -1
- package/src/panels/knowledge-panel.ts +6 -5
- package/src/panels/memory-panel.ts +7 -6
- package/src/panels/panel-list-panel.ts +36 -80
- package/src/panels/project-planning-panel.ts +18 -11
- package/src/panels/provider-account-snapshot.ts +51 -25
- package/src/panels/provider-accounts-panel.ts +33 -18
- package/src/panels/provider-health-domains.ts +45 -4
- package/src/panels/provider-health-panel.ts +5 -4
- package/src/panels/qr-panel.ts +1 -1
- package/src/panels/schedule-panel.ts +9 -17
- package/src/panels/security-panel.ts +8 -8
- package/src/panels/session-browser-panel.ts +10 -10
- package/src/panels/subscription-panel.ts +3 -3
- package/src/panels/system-messages-panel.ts +1 -1
- package/src/panels/tasks-panel.ts +20 -13
- package/src/panels/tool-inspector-panel.ts +19 -12
- package/src/panels/work-plan-panel.ts +5 -5
- package/src/planning/project-planning-coordinator.ts +1 -1
- package/src/provider-auth-route-display.ts +9 -0
- package/src/renderer/agent-workspace-style.ts +34 -0
- package/src/renderer/agent-workspace.ts +228 -52
- package/src/renderer/autocomplete-overlay.ts +25 -6
- package/src/renderer/bookmark-modal.ts +19 -4
- package/src/renderer/buffer.ts +4 -2
- package/src/renderer/context-inspector.ts +50 -13
- package/src/renderer/diff.ts +1 -1
- package/src/renderer/file-picker-overlay.ts +19 -6
- package/src/renderer/help-overlay.ts +106 -33
- package/src/renderer/history-search-overlay.ts +19 -4
- package/src/renderer/live-tail-modal.ts +27 -5
- package/src/renderer/mcp-workspace.ts +164 -50
- package/src/renderer/model-picker-overlay.ts +58 -2
- package/src/renderer/model-workspace.ts +104 -20
- package/src/renderer/process-modal.ts +27 -6
- package/src/renderer/profile-picker-modal.ts +20 -4
- package/src/renderer/search-overlay.ts +25 -5
- package/src/renderer/selection-modal-overlay.ts +46 -14
- package/src/renderer/session-picker-modal.ts +18 -1
- package/src/renderer/settings-modal-helpers.ts +2 -40
- package/src/renderer/settings-modal.ts +83 -50
- package/src/renderer/tool-call.ts +20 -11
- package/src/runtime/agent-runtime-events.ts +6 -6
- package/src/runtime/bootstrap-command-context.ts +7 -1
- package/src/runtime/bootstrap-command-parts.ts +9 -7
- package/src/runtime/bootstrap-core.ts +19 -1
- package/src/runtime/bootstrap-hook-bridge.ts +7 -18
- package/src/runtime/bootstrap-shell.ts +4 -4
- package/src/runtime/bootstrap.ts +31 -22
- package/src/runtime/connected-host-auth.ts +7 -2
- package/src/runtime/diagnostics/panels/index.ts +2 -2
- package/src/runtime/diagnostics/panels/policy.ts +7 -7
- package/src/runtime/index.ts +2 -1
- package/src/runtime/onboarding/apply-file-helpers.ts +66 -0
- package/src/runtime/onboarding/apply.ts +80 -79
- package/src/runtime/onboarding/derivation.ts +5 -8
- package/src/runtime/onboarding/snapshot.ts +0 -15
- package/src/runtime/onboarding/types.ts +8 -19
- package/src/runtime/onboarding/verify.ts +32 -10
- package/src/runtime/operator-token-cleanup.ts +1 -1
- package/src/runtime/services.ts +91 -22
- package/src/runtime/store/selectors/index.ts +3 -3
- package/src/runtime/store/state.ts +1 -4
- package/src/runtime/surface-feature-flags.ts +41 -6
- package/src/runtime/ui-read-models.ts +3 -4
- package/src/runtime/ui-services.ts +6 -6
- package/src/shell/blocking-input.ts +2 -1
- package/src/shell/ui-openers.ts +3 -13
- package/src/tools/agent-analysis-registry-policy.ts +0 -1
- package/src/tools/agent-channel-send-tool.ts +133 -0
- package/src/tools/agent-context-policy.ts +1 -1
- package/src/tools/agent-knowledge-ingest-tool.ts +405 -0
- package/src/tools/agent-knowledge-tool.ts +170 -0
- package/src/tools/agent-local-registry-tool.ts +269 -69
- package/src/tools/agent-media-generate-tool.ts +133 -0
- package/src/tools/agent-notify-tool.ts +143 -0
- package/src/tools/agent-operator-action-tool.ts +137 -0
- package/src/tools/agent-operator-briefing-tool.ts +217 -0
- package/src/tools/agent-reminder-schedule-tool.ts +237 -0
- package/src/tools/agent-tool-policy-guard.ts +8 -8
- package/src/tools/agent-work-plan-tool.ts +256 -0
- package/src/version.ts +1 -1
- package/src/input/commands/policy-dispatch.ts +0 -339
- package/src/input/commands/policy.ts +0 -13
- package/src/panels/panel-picker.ts +0 -105
- package/src/panels/policy-panel.ts +0 -308
- package/src/renderer/panel-picker-overlay.ts +0 -202
|
@@ -103,6 +103,10 @@ const PROFILE_CREATED_FILE = 'profile.json';
|
|
|
103
103
|
const PROFILE_SELECTION_FILE = 'profile-selection.json';
|
|
104
104
|
const PROFILE_ID_PATTERN = /^[a-z0-9](?:[a-z0-9._-]{0,62}[a-z0-9])?$/;
|
|
105
105
|
|
|
106
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
107
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
108
|
+
}
|
|
109
|
+
|
|
106
110
|
export function normalizeAgentRuntimeProfileId(value: string): string {
|
|
107
111
|
return value
|
|
108
112
|
.trim()
|
|
@@ -149,12 +153,11 @@ export function readAgentRuntimeProfileSelection(baseHomeDirectory: string): Age
|
|
|
149
153
|
if (!existsSync(path)) return null;
|
|
150
154
|
try {
|
|
151
155
|
const raw: unknown = JSON.parse(readFileSync(path, 'utf-8'));
|
|
152
|
-
if (!
|
|
153
|
-
const
|
|
154
|
-
const profileId = record.profileId;
|
|
156
|
+
if (!isRecord(raw)) return null;
|
|
157
|
+
const profileId = raw.profileId;
|
|
155
158
|
if (typeof profileId !== 'string') return null;
|
|
156
159
|
const resolution = resolveAgentRuntimeProfileHome(baseHomeDirectory, profileId);
|
|
157
|
-
const selectedAt = typeof
|
|
160
|
+
const selectedAt = typeof raw.selectedAt === 'string' ? raw.selectedAt : null;
|
|
158
161
|
return {
|
|
159
162
|
...resolution,
|
|
160
163
|
selectedAt,
|
|
@@ -192,13 +195,11 @@ function readProfileStarterTemplate(homeDirectory: string): Pick<AgentRuntimePro
|
|
|
192
195
|
if (!existsSync(path)) return {};
|
|
193
196
|
try {
|
|
194
197
|
const raw: unknown = JSON.parse(readFileSync(path, 'utf-8'));
|
|
195
|
-
if (!
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const
|
|
200
|
-
const id = starterRecord.id;
|
|
201
|
-
const name = starterRecord.name;
|
|
198
|
+
if (!isRecord(raw)) return {};
|
|
199
|
+
const starter = raw.starterTemplate;
|
|
200
|
+
if (!isRecord(starter)) return {};
|
|
201
|
+
const id = starter.id;
|
|
202
|
+
const name = starter.name;
|
|
202
203
|
if (!isAgentRuntimeProfileTemplateId(id) || typeof name !== 'string') return {};
|
|
203
204
|
return {
|
|
204
205
|
starterTemplateId: id,
|
|
@@ -271,7 +272,7 @@ function selectDiscoveredRecord<T extends { readonly name: string; readonly path
|
|
|
271
272
|
if (!selector?.trim()) return records[0]!;
|
|
272
273
|
const lookup = selector.trim().toLowerCase();
|
|
273
274
|
const match = records.find((record) => normalizedLookupValues(record.name, record.path).includes(lookup));
|
|
274
|
-
if (!match) throw new Error(`Unknown discovered Agent ${label}
|
|
275
|
+
if (!match) throw new Error(`Unknown discovered Agent ${label} ${selector}.`);
|
|
275
276
|
return match;
|
|
276
277
|
}
|
|
277
278
|
|
|
@@ -323,8 +324,8 @@ function readTemplateTextBlock(value: unknown, field: string): string {
|
|
|
323
324
|
}
|
|
324
325
|
|
|
325
326
|
function readTemplateObject(value: unknown, field: string): Record<string, unknown> {
|
|
326
|
-
if (!
|
|
327
|
-
return value
|
|
327
|
+
if (!isRecord(value)) throw new Error(`Starter template ${field} must be an object.`);
|
|
328
|
+
return value;
|
|
328
329
|
}
|
|
329
330
|
|
|
330
331
|
function parseTemplateSkill(value: unknown): AgentRuntimeProfileStarterTemplate['skills'][number] {
|
|
@@ -405,7 +406,7 @@ function resolveAgentRuntimeProfileTemplate(templateId: AgentRuntimeProfileTempl
|
|
|
405
406
|
const local = baseHomeDirectory ? listLocalTemplates(baseHomeDirectory).find((template) => template.id === id) : undefined;
|
|
406
407
|
if (local) return local;
|
|
407
408
|
const suffix = baseHomeDirectory ? ' Use profiles templates to list starters.' : '';
|
|
408
|
-
throw new Error(`Unknown Agent starter profile template
|
|
409
|
+
throw new Error(`Unknown Agent starter profile template ${templateId}.${suffix}`);
|
|
409
410
|
}
|
|
410
411
|
|
|
411
412
|
export function listAgentRuntimeProfileTemplates(baseHomeDirectory?: string): readonly AgentRuntimeProfileTemplateSummary[] {
|
|
@@ -468,7 +469,7 @@ export async function createAgentRuntimeProfileTemplateFromDiscovered(
|
|
|
468
469
|
const selectedRoutines = selectDiscoveredRecords(routines, options.routines, 'routine');
|
|
469
470
|
const target = join(getAgentRuntimeProfileTemplatesRoot(shellPaths.homeDirectory), `${id}.json`);
|
|
470
471
|
if (existsSync(target) && options.replace !== true) {
|
|
471
|
-
throw new Error(`Agent starter template already exists
|
|
472
|
+
throw new Error(`Agent starter template already exists ${id}. Rerun with --replace to overwrite it.`);
|
|
472
473
|
}
|
|
473
474
|
const persona = discoveredPersonaToTemplate(selectedPersona);
|
|
474
475
|
const template: AgentRuntimeProfileStarterTemplate = {
|
|
@@ -621,7 +622,7 @@ export function createAgentRuntimeProfile(baseHomeDirectory: string, profileName
|
|
|
621
622
|
export function setAgentRuntimeProfileSelection(baseHomeDirectory: string, profileName: string): AgentRuntimeProfileSelection {
|
|
622
623
|
const resolution = resolveAgentRuntimeProfileHome(baseHomeDirectory, profileName);
|
|
623
624
|
if (!existsSync(resolution.homeDirectory)) {
|
|
624
|
-
throw new Error(`Agent profile not found
|
|
625
|
+
throw new Error(`Agent profile not found ${resolution.id}`);
|
|
625
626
|
}
|
|
626
627
|
const path = getAgentRuntimeProfileSelectionPath(baseHomeDirectory);
|
|
627
628
|
mkdirSync(dirname(path), { recursive: true });
|
|
@@ -3,6 +3,7 @@ import { delimiter, dirname, join } from 'node:path';
|
|
|
3
3
|
import type { ShellPathService } from '@/runtime/index.ts';
|
|
4
4
|
import { GOODVIBES_AGENT_SURFACE_ROOT } from '../config/surface.ts';
|
|
5
5
|
import { assertNoSecretLikeText } from './persona-registry.ts';
|
|
6
|
+
import { formatAgentRecordReviewState } from './record-labels.ts';
|
|
6
7
|
|
|
7
8
|
export type AgentSkillSource = 'user' | 'agent' | 'imported' | 'system';
|
|
8
9
|
export type AgentSkillReviewState = 'fresh' | 'reviewed' | 'stale';
|
|
@@ -153,10 +154,10 @@ function normalizeList(values: readonly string[] | undefined): string[] {
|
|
|
153
154
|
|
|
154
155
|
function validateRequirementName(requirement: AgentSkillRequirement): void {
|
|
155
156
|
if (requirement.kind === 'env' && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(requirement.name)) {
|
|
156
|
-
throw new Error(`Invalid skill env requirement
|
|
157
|
+
throw new Error(`Invalid skill env requirement ${requirement.name}`);
|
|
157
158
|
}
|
|
158
159
|
if (requirement.kind === 'command' && !/^[A-Za-z0-9._+-]+$/.test(requirement.name)) {
|
|
159
|
-
throw new Error(`Invalid skill command requirement
|
|
160
|
+
throw new Error(`Invalid skill command requirement ${requirement.name}`);
|
|
160
161
|
}
|
|
161
162
|
}
|
|
162
163
|
|
|
@@ -375,9 +376,9 @@ export class AgentSkillRegistry {
|
|
|
375
376
|
const procedure = input.procedure.trim();
|
|
376
377
|
this.validateRequired(name, description, procedure);
|
|
377
378
|
const requirements = normalizeRequirements(input.requirements);
|
|
378
|
-
assertNoSecretLikeText([name, description, procedure, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])]);
|
|
379
|
+
assertNoSecretLikeText([name, description, procedure, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])], 'Skills');
|
|
379
380
|
const duplicate = store.skills.find((skill) => skill.name.toLowerCase() === name.toLowerCase());
|
|
380
|
-
if (duplicate) throw new Error(`Skill already exists
|
|
381
|
+
if (duplicate) throw new Error(`Skill already exists ${duplicate.id}`);
|
|
381
382
|
const timestamp = nowIso();
|
|
382
383
|
const skill: AgentSkillRecord = {
|
|
383
384
|
id: this.nextId(name, store.skills),
|
|
@@ -404,9 +405,9 @@ export class AgentSkillRegistry {
|
|
|
404
405
|
const description = input.description.trim();
|
|
405
406
|
const skillIds = this.normalizeExistingSkillIds(store, input.skillIds);
|
|
406
407
|
this.validateBundleRequired(name, description, skillIds);
|
|
407
|
-
assertNoSecretLikeText([name, description, ...skillIds]);
|
|
408
|
+
assertNoSecretLikeText([name, description, ...skillIds], 'Skill bundles');
|
|
408
409
|
const duplicate = store.bundles.find((bundle) => bundle.name.toLowerCase() === name.toLowerCase());
|
|
409
|
-
if (duplicate) throw new Error(`Skill bundle already exists
|
|
410
|
+
if (duplicate) throw new Error(`Skill bundle already exists ${duplicate.id}`);
|
|
410
411
|
const timestamp = nowIso();
|
|
411
412
|
const bundle: AgentSkillBundleRecord = {
|
|
412
413
|
id: this.nextBundleId(name, store.bundles),
|
|
@@ -427,15 +428,15 @@ export class AgentSkillRegistry {
|
|
|
427
428
|
public update(idOrName: string, input: AgentSkillUpdateInput): AgentSkillRecord {
|
|
428
429
|
const store = this.readStore();
|
|
429
430
|
const existing = this.findInStore(store, idOrName);
|
|
430
|
-
if (!existing) throw new Error(`Unknown skill
|
|
431
|
+
if (!existing) throw new Error(`Unknown skill ${idOrName}`);
|
|
431
432
|
const name = input.name === undefined ? existing.name : normalizeName(input.name);
|
|
432
433
|
const description = input.description === undefined ? existing.description : input.description.trim();
|
|
433
434
|
const procedure = input.procedure === undefined ? existing.procedure : input.procedure.trim();
|
|
434
435
|
this.validateRequired(name, description, procedure);
|
|
435
436
|
const requirements = input.requirements === undefined ? existing.requirements : normalizeRequirements(input.requirements);
|
|
436
|
-
assertNoSecretLikeText([name, description, procedure, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])]);
|
|
437
|
+
assertNoSecretLikeText([name, description, procedure, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])], 'Skills');
|
|
437
438
|
const duplicate = store.skills.find((skill) => skill.id !== existing.id && skill.name.toLowerCase() === name.toLowerCase());
|
|
438
|
-
if (duplicate) throw new Error(`Skill already exists
|
|
439
|
+
if (duplicate) throw new Error(`Skill already exists ${duplicate.id}`);
|
|
439
440
|
const updated: AgentSkillRecord = {
|
|
440
441
|
...existing,
|
|
441
442
|
name,
|
|
@@ -460,14 +461,14 @@ export class AgentSkillRegistry {
|
|
|
460
461
|
public updateBundle(idOrName: string, input: AgentSkillBundleUpdateInput): AgentSkillBundleRecord {
|
|
461
462
|
const store = this.readStore();
|
|
462
463
|
const existing = this.findBundleInStore(store, idOrName);
|
|
463
|
-
if (!existing) throw new Error(`Unknown skill bundle
|
|
464
|
+
if (!existing) throw new Error(`Unknown skill bundle ${idOrName}`);
|
|
464
465
|
const name = input.name === undefined ? existing.name : normalizeName(input.name);
|
|
465
466
|
const description = input.description === undefined ? existing.description : input.description.trim();
|
|
466
467
|
const skillIds = input.skillIds === undefined ? existing.skillIds : this.normalizeExistingSkillIds(store, input.skillIds);
|
|
467
468
|
this.validateBundleRequired(name, description, skillIds);
|
|
468
|
-
assertNoSecretLikeText([name, description, ...skillIds]);
|
|
469
|
+
assertNoSecretLikeText([name, description, ...skillIds], 'Skill bundles');
|
|
469
470
|
const duplicate = store.bundles.find((bundle) => bundle.id !== existing.id && bundle.name.toLowerCase() === name.toLowerCase());
|
|
470
|
-
if (duplicate) throw new Error(`Skill bundle already exists
|
|
471
|
+
if (duplicate) throw new Error(`Skill bundle already exists ${duplicate.id}`);
|
|
471
472
|
const updated: AgentSkillBundleRecord = {
|
|
472
473
|
...existing,
|
|
473
474
|
name,
|
|
@@ -489,7 +490,7 @@ export class AgentSkillRegistry {
|
|
|
489
490
|
public setEnabled(idOrName: string, enabled: boolean): AgentSkillRecord {
|
|
490
491
|
const store = this.readStore();
|
|
491
492
|
const existing = this.findInStore(store, idOrName);
|
|
492
|
-
if (!existing) throw new Error(`Unknown skill
|
|
493
|
+
if (!existing) throw new Error(`Unknown skill ${idOrName}`);
|
|
493
494
|
const updated: AgentSkillRecord = { ...existing, enabled, updatedAt: nowIso() };
|
|
494
495
|
this.writeStore({
|
|
495
496
|
...store,
|
|
@@ -501,7 +502,7 @@ export class AgentSkillRegistry {
|
|
|
501
502
|
public setBundleEnabled(idOrName: string, enabled: boolean): AgentSkillBundleRecord {
|
|
502
503
|
const store = this.readStore();
|
|
503
504
|
const existing = this.findBundleInStore(store, idOrName);
|
|
504
|
-
if (!existing) throw new Error(`Unknown skill bundle
|
|
505
|
+
if (!existing) throw new Error(`Unknown skill bundle ${idOrName}`);
|
|
505
506
|
const updated: AgentSkillBundleRecord = { ...existing, enabled, updatedAt: nowIso() };
|
|
506
507
|
this.writeStore({
|
|
507
508
|
...store,
|
|
@@ -513,7 +514,7 @@ export class AgentSkillRegistry {
|
|
|
513
514
|
public markReviewed(idOrName: string): AgentSkillRecord {
|
|
514
515
|
const store = this.readStore();
|
|
515
516
|
const existing = this.findInStore(store, idOrName);
|
|
516
|
-
if (!existing) throw new Error(`Unknown skill
|
|
517
|
+
if (!existing) throw new Error(`Unknown skill ${idOrName}`);
|
|
517
518
|
const updated: AgentSkillRecord = {
|
|
518
519
|
...existing,
|
|
519
520
|
reviewState: 'reviewed',
|
|
@@ -531,7 +532,7 @@ export class AgentSkillRegistry {
|
|
|
531
532
|
public markBundleReviewed(idOrName: string): AgentSkillBundleRecord {
|
|
532
533
|
const store = this.readStore();
|
|
533
534
|
const existing = this.findBundleInStore(store, idOrName);
|
|
534
|
-
if (!existing) throw new Error(`Unknown skill bundle
|
|
535
|
+
if (!existing) throw new Error(`Unknown skill bundle ${idOrName}`);
|
|
535
536
|
const updated: AgentSkillBundleRecord = {
|
|
536
537
|
...existing,
|
|
537
538
|
reviewState: 'reviewed',
|
|
@@ -549,7 +550,7 @@ export class AgentSkillRegistry {
|
|
|
549
550
|
public markStale(idOrName: string, reason: string): AgentSkillRecord {
|
|
550
551
|
const store = this.readStore();
|
|
551
552
|
const existing = this.findInStore(store, idOrName);
|
|
552
|
-
if (!existing) throw new Error(`Unknown skill
|
|
553
|
+
if (!existing) throw new Error(`Unknown skill ${idOrName}`);
|
|
553
554
|
const updated: AgentSkillRecord = {
|
|
554
555
|
...existing,
|
|
555
556
|
reviewState: 'stale',
|
|
@@ -566,7 +567,7 @@ export class AgentSkillRegistry {
|
|
|
566
567
|
public markBundleStale(idOrName: string, reason: string): AgentSkillBundleRecord {
|
|
567
568
|
const store = this.readStore();
|
|
568
569
|
const existing = this.findBundleInStore(store, idOrName);
|
|
569
|
-
if (!existing) throw new Error(`Unknown skill bundle
|
|
570
|
+
if (!existing) throw new Error(`Unknown skill bundle ${idOrName}`);
|
|
570
571
|
const updated: AgentSkillBundleRecord = {
|
|
571
572
|
...existing,
|
|
572
573
|
reviewState: 'stale',
|
|
@@ -583,7 +584,7 @@ export class AgentSkillRegistry {
|
|
|
583
584
|
public deleteSkill(idOrName: string): AgentSkillRecord {
|
|
584
585
|
const store = this.readStore();
|
|
585
586
|
const existing = this.findInStore(store, idOrName);
|
|
586
|
-
if (!existing) throw new Error(`Unknown skill
|
|
587
|
+
if (!existing) throw new Error(`Unknown skill ${idOrName}`);
|
|
587
588
|
this.writeStore({
|
|
588
589
|
...store,
|
|
589
590
|
skills: store.skills.filter((skill) => skill.id !== existing.id),
|
|
@@ -597,7 +598,7 @@ export class AgentSkillRegistry {
|
|
|
597
598
|
public deleteBundle(idOrName: string): AgentSkillBundleRecord {
|
|
598
599
|
const store = this.readStore();
|
|
599
600
|
const existing = this.findBundleInStore(store, idOrName);
|
|
600
|
-
if (!existing) throw new Error(`Unknown skill bundle
|
|
601
|
+
if (!existing) throw new Error(`Unknown skill bundle ${idOrName}`);
|
|
601
602
|
this.writeStore({
|
|
602
603
|
...store,
|
|
603
604
|
bundles: store.bundles.filter((bundle) => bundle.id !== existing.id),
|
|
@@ -655,7 +656,7 @@ export class AgentSkillRegistry {
|
|
|
655
656
|
const normalized = normalizeList(skillIds).map(slugify);
|
|
656
657
|
const known = new Set(store.skills.map((skill) => skill.id));
|
|
657
658
|
for (const skillId of normalized) {
|
|
658
|
-
if (!known.has(skillId)) throw new Error(`Unknown skill for bundle
|
|
659
|
+
if (!known.has(skillId)) throw new Error(`Unknown skill for bundle ${skillId}`);
|
|
659
660
|
}
|
|
660
661
|
return normalized;
|
|
661
662
|
}
|
|
@@ -665,7 +666,7 @@ export class AgentSkillRegistry {
|
|
|
665
666
|
try {
|
|
666
667
|
return parseStore(readFileSync(this.storePath, 'utf-8'));
|
|
667
668
|
} catch (error) {
|
|
668
|
-
throw new Error(`Could not read Agent skill store
|
|
669
|
+
throw new Error(`Could not read Agent skill store ${error instanceof Error ? error.message : String(error)}`);
|
|
669
670
|
}
|
|
670
671
|
}
|
|
671
672
|
|
|
@@ -768,7 +769,7 @@ export function buildEnabledSkillsPrompt(shellPaths: ShellPathService): string |
|
|
|
768
769
|
return [
|
|
769
770
|
`### Skill Bundle: ${bundle.name}`,
|
|
770
771
|
`Description: ${bundle.description}`,
|
|
771
|
-
`Review
|
|
772
|
+
`Review: ${formatAgentRecordReviewState(bundle.reviewState)}`,
|
|
772
773
|
`Readiness: ${readiness.ready ? 'ready' : `missing ${missing.join(', ')}`}`,
|
|
773
774
|
`Included skills: ${bundle.skillIds.join(', ')}`,
|
|
774
775
|
'',
|
|
@@ -777,7 +778,7 @@ export function buildEnabledSkillsPrompt(shellPaths: ShellPathService): string |
|
|
|
777
778
|
...active.slice(0, 8).flatMap((skill) => [
|
|
778
779
|
`### ${skill.name}`,
|
|
779
780
|
`Description: ${skill.description}`,
|
|
780
|
-
`Review
|
|
781
|
+
`Review: ${formatAgentRecordReviewState(skill.reviewState)}`,
|
|
781
782
|
`Triggers: ${skill.triggers.join(', ') || '(manual)'}`,
|
|
782
783
|
`Readiness: ${evaluateAgentSkillReadiness(skill).ready ? 'ready' : `missing ${evaluateAgentSkillReadiness(skill).missing.map(formatAgentSkillRequirement).join(', ')}`}`,
|
|
783
784
|
skill.procedure,
|
|
@@ -14,7 +14,7 @@ export function commandValues(args: readonly string[]): string[] {
|
|
|
14
14
|
export function delegationTaskValues(args: readonly string[]): string[] {
|
|
15
15
|
const values: string[] = [];
|
|
16
16
|
for (const token of args) {
|
|
17
|
-
if (token === '--wrfc') continue;
|
|
17
|
+
if (token === '--review' || token === '--wrfc') continue;
|
|
18
18
|
if (!token.startsWith('--')) values.push(token);
|
|
19
19
|
}
|
|
20
20
|
return values;
|
|
@@ -79,6 +79,10 @@ export function hasFlag(args: readonly string[], flag: string): boolean {
|
|
|
79
79
|
return args.includes(flag);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
export function hasAnyFlag(args: readonly string[], flags: readonly string[]): boolean {
|
|
83
|
+
return flags.some((flag) => args.includes(flag));
|
|
84
|
+
}
|
|
85
|
+
|
|
82
86
|
export function stripCommandFlag(args: readonly string[], flag: string): { readonly rest: readonly string[]; readonly present: boolean } {
|
|
83
87
|
const rest: string[] = [];
|
|
84
88
|
let present = false;
|
|
@@ -3,6 +3,7 @@ import type { CliCommandRuntime } from './management.ts';
|
|
|
3
3
|
import {
|
|
4
4
|
commandValues,
|
|
5
5
|
delegationTaskValues,
|
|
6
|
+
hasAnyFlag,
|
|
6
7
|
hasFlag,
|
|
7
8
|
parseConnectorInput,
|
|
8
9
|
readFirstStringList,
|
|
@@ -14,6 +15,7 @@ import {
|
|
|
14
15
|
} from './agent-knowledge-args.ts';
|
|
15
16
|
import {
|
|
16
17
|
formatAsk,
|
|
18
|
+
formatAgentKnowledgeFailureKind,
|
|
17
19
|
formatBatchIngest,
|
|
18
20
|
formatConnector,
|
|
19
21
|
formatConnectorDoctor,
|
|
@@ -30,7 +32,7 @@ import {
|
|
|
30
32
|
import { AGENT_KNOWLEDGE_METHODS, DELEGATION_METHOD } from './agent-knowledge-methods.ts';
|
|
31
33
|
import {
|
|
32
34
|
createAgentSdk,
|
|
33
|
-
|
|
35
|
+
fetchConnectedHostStatus,
|
|
34
36
|
findDisallowedKnowledgeScopeFlag,
|
|
35
37
|
formatScopeFlagRejection,
|
|
36
38
|
getAgentKnowledgeJson,
|
|
@@ -38,7 +40,7 @@ import {
|
|
|
38
40
|
postAgentKnowledgeJson,
|
|
39
41
|
readPackageMetadata,
|
|
40
42
|
readString,
|
|
41
|
-
|
|
43
|
+
resolveConnectedHostConnection,
|
|
42
44
|
runKnowledgeCall,
|
|
43
45
|
} from './agent-knowledge-runtime.ts';
|
|
44
46
|
import { formatJsonOrText, yesNo } from './management.ts';
|
|
@@ -47,23 +49,23 @@ interface DelegationResult {
|
|
|
47
49
|
readonly sessionId: string;
|
|
48
50
|
readonly message: unknown;
|
|
49
51
|
readonly task: string;
|
|
50
|
-
readonly
|
|
52
|
+
readonly reviewRequested: boolean;
|
|
51
53
|
}
|
|
52
54
|
|
|
53
|
-
function buildDelegationBody(task: string,
|
|
55
|
+
function buildDelegationBody(task: string, reviewRequested: boolean): string {
|
|
54
56
|
return [
|
|
55
57
|
'GoodVibes Agent explicit build delegation.',
|
|
56
58
|
'',
|
|
57
|
-
'Original user ask
|
|
59
|
+
'Original user ask',
|
|
58
60
|
task,
|
|
59
61
|
'',
|
|
60
|
-
'Agent policy
|
|
62
|
+
'Agent policy',
|
|
61
63
|
'- GoodVibes Agent is not the coding TUI.',
|
|
62
64
|
'- Preserve the full original ask.',
|
|
63
|
-
'- GoodVibes TUI owns file edits, git/worktree flows, execution isolation UX, and any
|
|
64
|
-
|
|
65
|
-
? '-
|
|
66
|
-
: '-
|
|
65
|
+
'- GoodVibes TUI owns file edits, git/worktree flows, execution isolation UX, and any delegated review owner chain.',
|
|
66
|
+
reviewRequested
|
|
67
|
+
? '- Delegated review was explicitly requested by the Agent user for this build/fix/review handoff.'
|
|
68
|
+
: '- Delegated review was not explicitly requested; do not add review solely because this came from Agent.',
|
|
67
69
|
].join('\n');
|
|
68
70
|
}
|
|
69
71
|
|
|
@@ -479,12 +481,12 @@ export async function handleAgentKnowledgeShortcutCommand(
|
|
|
479
481
|
}
|
|
480
482
|
|
|
481
483
|
export async function handleCompatCommand(runtime: CliCommandRuntime): Promise<CliCommandOutput> {
|
|
482
|
-
const connection =
|
|
484
|
+
const connection = resolveConnectedHostConnection(runtime);
|
|
483
485
|
const metadata = readPackageMetadata();
|
|
484
|
-
const
|
|
485
|
-
const
|
|
486
|
-
const
|
|
487
|
-
const versionCompatible =
|
|
486
|
+
const connectedHost = await fetchConnectedHostStatus(connection);
|
|
487
|
+
const connectedHostRecord = isRecord(connectedHost.body) ? connectedHost.body : {};
|
|
488
|
+
const connectedHostVersion = readString(connectedHostRecord, 'version') ?? 'unknown';
|
|
489
|
+
const versionCompatible = connectedHost.ok && connectedHostVersion === metadata.sdkVersion;
|
|
488
490
|
const knowledgeRoute = await runKnowledgeCall(runtime, AGENT_KNOWLEDGE_METHODS.status, async (routeConnection) => (
|
|
489
491
|
await createAgentSdk(routeConnection).knowledge.status()
|
|
490
492
|
));
|
|
@@ -495,9 +497,9 @@ export async function handleCompatCommand(runtime: CliCommandRuntime): Promise<C
|
|
|
495
497
|
sdkPin: metadata.sdkVersion,
|
|
496
498
|
connectedHost: {
|
|
497
499
|
baseUrl: connection.baseUrl,
|
|
498
|
-
status:
|
|
499
|
-
version:
|
|
500
|
-
reachable:
|
|
500
|
+
status: connectedHost.status,
|
|
501
|
+
version: connectedHostVersion,
|
|
502
|
+
reachable: connectedHost.ok,
|
|
501
503
|
compatible: versionCompatible,
|
|
502
504
|
},
|
|
503
505
|
auth: {
|
|
@@ -512,13 +514,13 @@ export async function handleCompatCommand(runtime: CliCommandRuntime): Promise<C
|
|
|
512
514
|
};
|
|
513
515
|
const text = [
|
|
514
516
|
'GoodVibes Agent compatibility',
|
|
515
|
-
` package
|
|
516
|
-
` SDK pin
|
|
517
|
-
`
|
|
518
|
-
` version compatible
|
|
519
|
-
` operator token
|
|
520
|
-
` Agent
|
|
521
|
-
...(versionCompatible ? [] : [' next
|
|
517
|
+
` package ${metadata.version}`,
|
|
518
|
+
` SDK pin ${metadata.sdkVersion}`,
|
|
519
|
+
` connected host ${connectedHostVersion} at ${connection.baseUrl} (${connectedHost.ok ? 'reachable' : 'unreachable'})`,
|
|
520
|
+
` version compatible ${yesNo(versionCompatible)}`,
|
|
521
|
+
` operator token ${connection.token ? 'present' : 'missing'} (${connection.tokenPath})`,
|
|
522
|
+
` Agent Knowledge route ${knowledgeRouteReady ? 'ready' : `not ready (${knowledgeRoute.ok ? 'unknown' : formatAgentKnowledgeFailureKind(knowledgeRoute.kind)})`}`,
|
|
523
|
+
...(versionCompatible ? [] : [' next update the connected GoodVibes host so /status matches the Agent SDK pin.']),
|
|
522
524
|
].join('\n');
|
|
523
525
|
return {
|
|
524
526
|
output: runtime.cli.flags.outputFormat === 'json' ? JSON.stringify(value, null, 2) : text,
|
|
@@ -527,11 +529,11 @@ export async function handleCompatCommand(runtime: CliCommandRuntime): Promise<C
|
|
|
527
529
|
}
|
|
528
530
|
|
|
529
531
|
export async function handleDelegateCommand(runtime: CliCommandRuntime): Promise<CliCommandOutput> {
|
|
530
|
-
const
|
|
532
|
+
const reviewRequested = hasAnyFlag(runtime.cli.commandArgs, ['--review', '--wrfc']);
|
|
531
533
|
const task = delegationTaskValues(runtime.cli.commandArgs).join(' ').trim();
|
|
532
534
|
if (!task) {
|
|
533
535
|
return {
|
|
534
|
-
output: 'Usage: goodvibes-agent delegate [--
|
|
536
|
+
output: 'Usage: goodvibes-agent delegate [--review] <build/fix/review task>',
|
|
535
537
|
exitCode: 2,
|
|
536
538
|
};
|
|
537
539
|
}
|
|
@@ -548,10 +550,14 @@ export async function handleDelegateCommand(runtime: CliCommandRuntime): Promise
|
|
|
548
550
|
if (!sessionId) throw new Error('sessions.create returned no session id.');
|
|
549
551
|
const message = await sdk.operator.invoke('sessions.messages.create', {
|
|
550
552
|
sessionId,
|
|
551
|
-
body: buildDelegationBody(task,
|
|
553
|
+
body: buildDelegationBody(task, reviewRequested),
|
|
552
554
|
surfaceKind: 'goodvibes-agent',
|
|
553
555
|
surfaceId: 'goodvibes-agent-cli',
|
|
554
556
|
kind: 'task',
|
|
557
|
+
metadata: {
|
|
558
|
+
reviewRequested,
|
|
559
|
+
wrfcRequested: reviewRequested,
|
|
560
|
+
},
|
|
555
561
|
routing: {
|
|
556
562
|
executionIntent: {
|
|
557
563
|
riskClass: 'elevated',
|
|
@@ -561,15 +567,15 @@ export async function handleDelegateCommand(runtime: CliCommandRuntime): Promise
|
|
|
561
567
|
},
|
|
562
568
|
},
|
|
563
569
|
});
|
|
564
|
-
return { sessionId, message, task,
|
|
570
|
+
return { sessionId, message, task, reviewRequested };
|
|
565
571
|
});
|
|
566
572
|
if (!result.ok) return { output: formatFailure(result, runtime.cli.flags.outputFormat === 'json'), exitCode: 1 };
|
|
567
573
|
const text = [
|
|
568
574
|
'Delegation submitted to GoodVibes TUI/shared-session routes.',
|
|
569
|
-
` session
|
|
570
|
-
` mode
|
|
571
|
-
` task
|
|
572
|
-
' next
|
|
575
|
+
` session ${result.data.sessionId}`,
|
|
576
|
+
` mode ${result.data.reviewRequested ? 'delegated review requested' : 'direct build delegation'}`,
|
|
577
|
+
` task ${result.data.task}`,
|
|
578
|
+
' next check GoodVibes TUI shared-session/task status for the result.',
|
|
573
579
|
].join('\n');
|
|
574
580
|
return {
|
|
575
581
|
output: formatJsonOrText(runtime.cli)(result, text),
|