@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
|
@@ -1,18 +1,34 @@
|
|
|
1
|
-
import type { CommandRegistry } from '../command-registry.ts';
|
|
1
|
+
import type { CommandContext, CommandRegistry } from '../command-registry.ts';
|
|
2
|
+
import { AGENT_WORKSPACE_CATEGORY_IDS } from '../agent-workspace-types.ts';
|
|
3
|
+
|
|
4
|
+
const AGENT_WORKSPACE_ARGS_HINT = `${AGENT_WORKSPACE_CATEGORY_IDS.join('|')}|connected-host`;
|
|
2
5
|
|
|
3
6
|
export function registerAgentWorkspaceRuntimeCommands(registry: CommandRegistry): void {
|
|
7
|
+
function openAgentWorkspace(ctx: CommandContext, categoryId: string | undefined): void {
|
|
8
|
+
if (!ctx.openAgentWorkspace) {
|
|
9
|
+
ctx.print('Agent operator workspace is not available in this runtime.');
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
ctx.openAgentWorkspace(categoryId);
|
|
13
|
+
}
|
|
14
|
+
|
|
4
15
|
registry.register({
|
|
5
16
|
name: 'agent',
|
|
6
17
|
aliases: ['home', 'operator'],
|
|
7
18
|
description: 'Open the GoodVibes Agent operator workspace',
|
|
8
19
|
usage: '[category]',
|
|
9
|
-
argsHint:
|
|
20
|
+
argsHint: AGENT_WORKSPACE_ARGS_HINT,
|
|
10
21
|
handler(args, ctx) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
22
|
+
openAgentWorkspace(ctx, args[0]);
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
registry.register({
|
|
26
|
+
name: 'notes',
|
|
27
|
+
aliases: ['scratchpad'],
|
|
28
|
+
description: 'Open Agent-local scratchpad notes in the operator workspace',
|
|
29
|
+
usage: '',
|
|
30
|
+
handler(_args, ctx) {
|
|
31
|
+
openAgentWorkspace(ctx, 'notes');
|
|
16
32
|
},
|
|
17
33
|
});
|
|
18
34
|
}
|
|
@@ -39,7 +39,7 @@ function countSetupGaps(items: readonly { readonly missingRequirementCount?: num
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
function formatWorkPlanLine(total: number, counts: StatusCounts): string {
|
|
42
|
-
if (total === 0) return ' work plan
|
|
42
|
+
if (total === 0) return ' work plan empty';
|
|
43
43
|
const active = counts.pending + counts.in_progress + counts.blocked;
|
|
44
44
|
return [
|
|
45
45
|
` work plan: ${plural(total, 'item')}`,
|
|
@@ -52,8 +52,8 @@ function formatWorkPlanLine(total: number, counts: StatusCounts): string {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
function formatWarnings(warnings: readonly string[]): readonly string[] {
|
|
55
|
-
if (warnings.length === 0) return [' warnings
|
|
56
|
-
return [' warnings
|
|
55
|
+
if (warnings.length === 0) return [' warnings none'];
|
|
56
|
+
return [' warnings', ...warnings.slice(0, 4).map((warning) => ` - ${warning}`)];
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
export function formatAgentOperatorBriefing(ctx: CommandContext): string {
|
|
@@ -76,63 +76,64 @@ export function formatAgentOperatorBriefing(ctx: CommandContext): string {
|
|
|
76
76
|
|
|
77
77
|
const nextActions = [
|
|
78
78
|
snapshot.provider === 'unknown' || snapshot.model === 'unknown'
|
|
79
|
-
? 'Choose the assistant model
|
|
79
|
+
? 'Choose the assistant model from Agent Workspace -> Home -> Choose model.'
|
|
80
80
|
: '',
|
|
81
81
|
snapshot.localMemoryCount === 0
|
|
82
|
-
? 'Store durable non-secret facts
|
|
82
|
+
? 'Store durable non-secret facts from Agent Workspace -> Memory -> Add memory.'
|
|
83
83
|
: snapshot.localMemoryReviewQueueCount > 0
|
|
84
|
-
? `Review ${plural(snapshot.localMemoryReviewQueueCount, 'memory record')}
|
|
84
|
+
? `Review ${plural(snapshot.localMemoryReviewQueueCount, 'memory record')} from Agent Workspace -> Memory -> Review queue.`
|
|
85
85
|
: '',
|
|
86
86
|
!hasLocalSkillBehavior
|
|
87
|
-
? 'Create reusable procedures
|
|
87
|
+
? 'Create reusable procedures from Agent Workspace -> Skills or import reviewed skill files.'
|
|
88
88
|
: '',
|
|
89
89
|
skillSetupGaps > 0
|
|
90
|
-
? `Resolve ${plural(skillSetupGaps, 'skill')} with setup gaps from
|
|
90
|
+
? `Resolve ${plural(skillSetupGaps, 'skill')} with setup gaps from Agent Workspace -> Skills.`
|
|
91
91
|
: '',
|
|
92
92
|
skillBundleSetupGaps > 0
|
|
93
|
-
? `Resolve ${plural(skillBundleSetupGaps, 'skill bundle')} with setup gaps from
|
|
93
|
+
? `Resolve ${plural(skillBundleSetupGaps, 'skill bundle')} with setup gaps from Agent Workspace -> Skills.`
|
|
94
94
|
: '',
|
|
95
95
|
hasLocalSkillBehavior && skillSetupGaps === 0 && skillBundleSetupGaps === 0 && snapshot.activeSkillCount === 0
|
|
96
|
-
? 'Enable reviewed skills or bundles
|
|
96
|
+
? 'Enable reviewed skills or bundles from Agent Workspace -> Skills.'
|
|
97
97
|
: '',
|
|
98
98
|
snapshot.localRoutineCount === 0
|
|
99
|
-
? 'Create repeatable workflows
|
|
99
|
+
? 'Create repeatable workflows from Agent Workspace -> Routines; promote schedules only with explicit confirmation.'
|
|
100
100
|
: routineSetupGaps > 0
|
|
101
|
-
? `Resolve ${plural(routineSetupGaps, 'routine')} with setup gaps from
|
|
101
|
+
? `Resolve ${plural(routineSetupGaps, 'routine')} with setup gaps from Agent Workspace -> Routines.`
|
|
102
102
|
: snapshot.enabledRoutineCount === 0
|
|
103
|
-
? 'Enable reviewed routines
|
|
103
|
+
? 'Enable reviewed routines from Agent Workspace -> Routines.'
|
|
104
104
|
: '',
|
|
105
105
|
channelSetupGaps > 0
|
|
106
|
-
? `Review ${plural(channelSetupGaps, 'enabled channel')} needing setup from
|
|
106
|
+
? `Review ${plural(channelSetupGaps, 'enabled channel')} needing setup from Agent Workspace -> Channels.`
|
|
107
107
|
: '',
|
|
108
108
|
voiceSetupNeedsReview
|
|
109
|
-
? 'Review voice setup
|
|
109
|
+
? 'Review voice setup from Agent Workspace -> Voice & Media before relying on spoken replies.'
|
|
110
110
|
: '',
|
|
111
111
|
mediaSetupNeedsReview
|
|
112
|
-
? 'Review media provider setup
|
|
112
|
+
? 'Review media provider setup from Agent Workspace -> Voice & Media before relying on image or media workflows.'
|
|
113
113
|
: '',
|
|
114
|
-
'Use
|
|
115
|
-
'Use
|
|
114
|
+
'Use Agent Workspace -> Knowledge for Agent Knowledge status, search, and explicit ingest forms.',
|
|
115
|
+
'Use Agent Workspace -> Build Delegation only for explicit build, fix, implementation, or review handoff to GoodVibes TUI.',
|
|
116
116
|
].filter((line): line is string => line.length > 0);
|
|
117
117
|
|
|
118
118
|
return [
|
|
119
119
|
'Agent Briefing',
|
|
120
120
|
` chat route: ${snapshot.provider} / ${snapshot.modelDisplayName}`,
|
|
121
|
-
` session
|
|
122
|
-
` profile
|
|
123
|
-
` policy
|
|
124
|
-
` knowledge
|
|
121
|
+
` session ${snapshot.sessionId}`,
|
|
122
|
+
` profile ${snapshot.activeRuntimeProfile}`,
|
|
123
|
+
` policy ${snapshot.executionPolicy}; delegated review ${snapshot.delegatedReviewPolicy}`,
|
|
124
|
+
` knowledge ${snapshot.knowledgeRoute} (${snapshot.knowledgeIsolation}; no fallback)`,
|
|
125
125
|
'',
|
|
126
126
|
'Readiness',
|
|
127
|
-
` setup
|
|
127
|
+
` setup ${setupReady}/${snapshot.setupChecklist.length} ready; ${setupRecommended} recommended; ${setupBlocked} blocked`,
|
|
128
128
|
` local memory: ${plural(snapshot.localMemoryCount, 'record')}; prompt-active ${snapshot.localMemoryPromptActiveCount}; review queue ${snapshot.localMemoryReviewQueueCount}`,
|
|
129
|
+
` scratchpad notes: ${plural(snapshot.localNoteCount, 'note')}; review queue ${snapshot.localNoteReviewQueueCount}`,
|
|
129
130
|
` personas: ${plural(snapshot.localPersonaCount, 'persona')}; active ${snapshot.activePersonaName}`,
|
|
130
131
|
` skills: ${snapshot.enabledSkillCount}/${snapshot.localSkillCount} enabled; bundles ${snapshot.enabledSkillBundleCount}/${snapshot.localSkillBundleCount}; active ${snapshot.activeSkillCount}; setup gaps ${skillSetupGaps} skill, ${skillBundleSetupGaps} bundle`,
|
|
131
132
|
` routines: ${snapshot.enabledRoutineCount}/${snapshot.localRoutineCount} enabled; setup gaps ${routineSetupGaps}`,
|
|
132
133
|
` channels: ${readyChannels}/${snapshot.channels.length} ready; ${enabledChannels} enabled; setup gaps ${channelSetupGaps}`,
|
|
133
134
|
` voice/media: ${snapshot.voiceProviderCount} voice, ${snapshot.mediaProviderCount} media; browser tools ${snapshot.voiceMediaReadiness.browserToolState}`,
|
|
134
135
|
formatWorkPlanLine(workPlan.total, workPlan.counts),
|
|
135
|
-
` schedules
|
|
136
|
+
` schedules ${enabledJobs}/${jobs.length} visible jobs enabled`,
|
|
136
137
|
'',
|
|
137
138
|
'Next Actions',
|
|
138
139
|
...nextActions.map((line) => ` - ${line}`),
|
|
@@ -4,11 +4,29 @@ import type { CommandRegistry } from '../command-registry.ts';
|
|
|
4
4
|
import type { CommandContext } from '../command-registry.ts';
|
|
5
5
|
import type { AgentWorkspaceChannelStatus } from '../agent-workspace-channels.ts';
|
|
6
6
|
import { buildAgentWorkspaceChannels } from '../agent-workspace-channels.ts';
|
|
7
|
+
import {
|
|
8
|
+
buildAgentChannelDeliveryPreview,
|
|
9
|
+
deliverAgentChannelMessage,
|
|
10
|
+
formatAgentChannelDeliveryPreview,
|
|
11
|
+
formatAgentChannelDeliveryResult,
|
|
12
|
+
} from '../../agent/channel-delivery.ts';
|
|
13
|
+
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
7
14
|
|
|
8
15
|
type ChannelFilter = 'all' | 'ready' | 'attention';
|
|
9
16
|
type JsonRecord = Record<string, unknown>;
|
|
10
17
|
|
|
11
|
-
interface
|
|
18
|
+
interface ChannelSendArgs {
|
|
19
|
+
readonly message: string;
|
|
20
|
+
readonly title?: string;
|
|
21
|
+
readonly channel?: string;
|
|
22
|
+
readonly route?: string;
|
|
23
|
+
readonly webhook?: string;
|
|
24
|
+
readonly link?: string;
|
|
25
|
+
readonly yes: boolean;
|
|
26
|
+
readonly errors: readonly string[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface ChannelConnectedHostConnection {
|
|
12
30
|
readonly baseUrl: string;
|
|
13
31
|
readonly token: string | null;
|
|
14
32
|
readonly tokenPath: string;
|
|
@@ -23,12 +41,20 @@ interface ChannelRouteSuccess {
|
|
|
23
41
|
interface ChannelRouteFailure {
|
|
24
42
|
readonly ok: false;
|
|
25
43
|
readonly route: string;
|
|
26
|
-
readonly kind: 'auth_required' | '
|
|
44
|
+
readonly kind: 'auth_required' | 'connected_host_unavailable' | 'connected_host_route_unavailable' | 'connected_host_error';
|
|
45
|
+
readonly baseUrl: string;
|
|
27
46
|
readonly message: string;
|
|
28
47
|
}
|
|
29
48
|
|
|
30
49
|
type ChannelRouteResult = ChannelRouteSuccess | ChannelRouteFailure;
|
|
31
50
|
|
|
51
|
+
const CHANNEL_FAILURE_LABELS: Record<ChannelRouteFailure['kind'], string> = {
|
|
52
|
+
auth_required: 'auth required',
|
|
53
|
+
connected_host_unavailable: 'connected host unavailable',
|
|
54
|
+
connected_host_route_unavailable: 'connected host route unavailable',
|
|
55
|
+
connected_host_error: 'connected host error',
|
|
56
|
+
};
|
|
57
|
+
|
|
32
58
|
function isRecord(value: unknown): value is JsonRecord {
|
|
33
59
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
34
60
|
}
|
|
@@ -48,7 +74,59 @@ function readRecordArray(record: JsonRecord, key: string): readonly JsonRecord[]
|
|
|
48
74
|
return Array.isArray(value) ? value.filter(isRecord) : [];
|
|
49
75
|
}
|
|
50
76
|
|
|
51
|
-
function
|
|
77
|
+
function readFlagValue(args: readonly string[], index: number, flag: string, errors: string[]): string | null {
|
|
78
|
+
const value = args[index + 1];
|
|
79
|
+
if (!value || value.startsWith('--')) {
|
|
80
|
+
errors.push(`${flag} requires a value.`);
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function parseChannelSendArgs(args: readonly string[]): ChannelSendArgs {
|
|
87
|
+
const parsed = stripYesFlag([...args]);
|
|
88
|
+
const errors: string[] = [];
|
|
89
|
+
const messageParts: string[] = [];
|
|
90
|
+
let title: string | undefined;
|
|
91
|
+
let channel: string | undefined;
|
|
92
|
+
let route: string | undefined;
|
|
93
|
+
let webhook: string | undefined;
|
|
94
|
+
let link: string | undefined;
|
|
95
|
+
|
|
96
|
+
for (let index = 0; index < parsed.rest.length; index += 1) {
|
|
97
|
+
const arg = parsed.rest[index];
|
|
98
|
+
if (arg === '--title' || arg === '--message' || arg === '--channel' || arg === '--route' || arg === '--webhook' || arg === '--link') {
|
|
99
|
+
const value = readFlagValue(parsed.rest, index, arg, errors);
|
|
100
|
+
index += 1;
|
|
101
|
+
if (!value) continue;
|
|
102
|
+
if (arg === '--title') title = value;
|
|
103
|
+
else if (arg === '--message') messageParts.push(value);
|
|
104
|
+
else if (arg === '--channel') channel = value;
|
|
105
|
+
else if (arg === '--route') route = value;
|
|
106
|
+
else if (arg === '--webhook') webhook = value;
|
|
107
|
+
else link = value;
|
|
108
|
+
} else if (arg?.startsWith('--')) {
|
|
109
|
+
errors.push(`Unknown channel send flag ${arg}`);
|
|
110
|
+
} else if (arg) {
|
|
111
|
+
messageParts.push(arg);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const message = messageParts.join(' ').trim();
|
|
116
|
+
if (!message) errors.push('Channel send message is required.');
|
|
117
|
+
return {
|
|
118
|
+
message,
|
|
119
|
+
...(title ? { title } : {}),
|
|
120
|
+
...(channel ? { channel } : {}),
|
|
121
|
+
...(route ? { route } : {}),
|
|
122
|
+
...(webhook ? { webhook } : {}),
|
|
123
|
+
...(link ? { link } : {}),
|
|
124
|
+
yes: parsed.yes,
|
|
125
|
+
errors,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function resolveChannelConnectedHostConnection(context: CommandContext): ChannelConnectedHostConnection {
|
|
52
130
|
const hostValue = context.platform?.configManager?.get('controlPlane.host');
|
|
53
131
|
const portValue = context.platform?.configManager?.get('controlPlane.port');
|
|
54
132
|
const host = typeof hostValue === 'string' && hostValue.trim().length > 0 ? hostValue.trim() : '127.0.0.1';
|
|
@@ -66,13 +144,14 @@ function resolveChannelDaemonConnection(context: CommandContext): ChannelDaemonC
|
|
|
66
144
|
}
|
|
67
145
|
|
|
68
146
|
async function fetchChannelRoute(context: CommandContext, route: string): Promise<ChannelRouteResult> {
|
|
69
|
-
const connection =
|
|
147
|
+
const connection = resolveChannelConnectedHostConnection(context);
|
|
70
148
|
if (!connection.token) {
|
|
71
149
|
return {
|
|
72
150
|
ok: false,
|
|
73
151
|
route,
|
|
74
152
|
kind: 'auth_required',
|
|
75
|
-
|
|
153
|
+
baseUrl: connection.baseUrl,
|
|
154
|
+
message: `No connected-host operator token found at ${connection.tokenPath}`,
|
|
76
155
|
};
|
|
77
156
|
}
|
|
78
157
|
|
|
@@ -97,8 +176,9 @@ async function fetchChannelRoute(context: CommandContext, route: string): Promis
|
|
|
97
176
|
kind: response.status === 401 || response.status === 403
|
|
98
177
|
? 'auth_required'
|
|
99
178
|
: response.status === 404
|
|
100
|
-
? '
|
|
101
|
-
: '
|
|
179
|
+
? 'connected_host_route_unavailable'
|
|
180
|
+
: 'connected_host_error',
|
|
181
|
+
baseUrl: connection.baseUrl,
|
|
102
182
|
message: `HTTP ${response.status}${detail ? `: ${detail}` : ''}`,
|
|
103
183
|
};
|
|
104
184
|
}
|
|
@@ -107,7 +187,8 @@ async function fetchChannelRoute(context: CommandContext, route: string): Promis
|
|
|
107
187
|
return {
|
|
108
188
|
ok: false,
|
|
109
189
|
route,
|
|
110
|
-
kind: '
|
|
190
|
+
kind: 'connected_host_unavailable',
|
|
191
|
+
baseUrl: connection.baseUrl,
|
|
111
192
|
message: error instanceof Error ? error.message : String(error),
|
|
112
193
|
};
|
|
113
194
|
}
|
|
@@ -116,7 +197,9 @@ async function fetchChannelRoute(context: CommandContext, route: string): Promis
|
|
|
116
197
|
function formatChannelRouteFailure(title: string, failure: ChannelRouteFailure): string {
|
|
117
198
|
return [
|
|
118
199
|
`${title}: unavailable`,
|
|
200
|
+
` status: ${CHANNEL_FAILURE_LABELS[failure.kind]}`,
|
|
119
201
|
` kind: ${failure.kind}`,
|
|
202
|
+
` connected host: ${failure.baseUrl}`,
|
|
120
203
|
` route: ${failure.route}`,
|
|
121
204
|
` error: ${failure.message}`,
|
|
122
205
|
' policy: read-only; no channel send/action route was called',
|
|
@@ -207,8 +290,8 @@ function formatChannelAccounts(body: unknown): string {
|
|
|
207
290
|
const accounts = readRecordArray(root, 'accounts');
|
|
208
291
|
const lines = [
|
|
209
292
|
'Channel Accounts',
|
|
210
|
-
` accounts
|
|
211
|
-
' policy
|
|
293
|
+
` accounts ${accounts.length}`,
|
|
294
|
+
' policy read-only account posture; secret values are never shown',
|
|
212
295
|
'',
|
|
213
296
|
];
|
|
214
297
|
if (accounts.length === 0) return [...lines, ' No channel accounts reported by connected host.'].join('\n');
|
|
@@ -218,10 +301,10 @@ function formatChannelAccounts(body: unknown): string {
|
|
|
218
301
|
const authState = readString(account, 'authState', readString(account, 'state', 'unknown'));
|
|
219
302
|
const configured = readBoolean(account, 'configured') ? 'configured' : 'not-configured';
|
|
220
303
|
const linked = readBoolean(account, 'linked') ? 'linked' : 'not-linked';
|
|
221
|
-
const
|
|
222
|
-
.map((secret) =>
|
|
304
|
+
const secretFields = readRecordArray(account, 'secrets')
|
|
305
|
+
.map((secret) => readString(secret, 'field', 'secret'))
|
|
223
306
|
.join(', ') || 'none';
|
|
224
|
-
lines.push(` ${surface}${accountId ? `/${accountId}` : ''}: ${configured}; ${linked}; auth=${authState}; secret refs=${
|
|
307
|
+
lines.push(` ${surface}${accountId ? `/${accountId}` : ''}: ${configured}; ${linked}; auth=${authState}; secret refs=${secretFields === 'none' ? 'none' : secretFields.split(', ').map((field) => `${field}:config`).join(',')}`);
|
|
225
308
|
}
|
|
226
309
|
if (accounts.length > 20) lines.push(` ${accounts.length - 20} more account(s) omitted.`);
|
|
227
310
|
return lines.join('\n');
|
|
@@ -232,8 +315,8 @@ function formatChannelPolicies(body: unknown): string {
|
|
|
232
315
|
const policies = readRecordArray(root, 'policies');
|
|
233
316
|
const lines = [
|
|
234
317
|
'Channel Policies',
|
|
235
|
-
` policies
|
|
236
|
-
' policy
|
|
318
|
+
` policies ${policies.length}`,
|
|
319
|
+
' policy read-only policy posture; use exact confirmed commands for changes',
|
|
237
320
|
'',
|
|
238
321
|
];
|
|
239
322
|
if (policies.length === 0) return [...lines, ' No channel policies reported by connected host.'].join('\n');
|
|
@@ -254,8 +337,8 @@ function formatChannelStatus(body: unknown): string {
|
|
|
254
337
|
const channels = readRecordArray(root, 'channels');
|
|
255
338
|
const lines = [
|
|
256
339
|
'Connected Channel Status',
|
|
257
|
-
` channels
|
|
258
|
-
' policy
|
|
340
|
+
` channels ${channels.length}`,
|
|
341
|
+
' policy read-only connected-host status',
|
|
259
342
|
'',
|
|
260
343
|
];
|
|
261
344
|
if (channels.length === 0) return [...lines, ' No connected channel status reported.'].join('\n');
|
|
@@ -276,9 +359,9 @@ function formatChannelDoctor(surface: string, body: unknown): string {
|
|
|
276
359
|
const repairActions = readRecordArray(root, 'repairActions');
|
|
277
360
|
const lines = [
|
|
278
361
|
`Channel Doctor: ${readString(root, 'surface', surface)}`,
|
|
279
|
-
` checks
|
|
280
|
-
` repair actions
|
|
281
|
-
' policy
|
|
362
|
+
` checks ${checks.length}`,
|
|
363
|
+
` repair actions ${repairActions.length}`,
|
|
364
|
+
' policy read-only doctor report; repair actions are not run here',
|
|
282
365
|
'',
|
|
283
366
|
];
|
|
284
367
|
if (checks.length === 0) lines.push(' No doctor checks reported.');
|
|
@@ -298,18 +381,18 @@ function formatChannelSetup(surface: string, body: unknown): string {
|
|
|
298
381
|
const secretTargets = readRecordArray(root, 'secretTargets');
|
|
299
382
|
const lines = [
|
|
300
383
|
`Channel Setup Schema: ${readString(root, 'surface', surface)}`,
|
|
301
|
-
` version
|
|
302
|
-
` fields
|
|
303
|
-
` secret targets
|
|
304
|
-
' policy
|
|
384
|
+
` version ${typeof root.version === 'number' ? root.version : 'unknown'}`,
|
|
385
|
+
` fields ${fields.length}`,
|
|
386
|
+
` secret targets ${secretTargets.length}`,
|
|
387
|
+
' policy read-only setup form; no credentials or values are printed',
|
|
305
388
|
'',
|
|
306
389
|
];
|
|
307
390
|
if (fields.length > 0) {
|
|
308
|
-
lines.push(' Fields
|
|
391
|
+
lines.push(' Fields');
|
|
309
392
|
for (const field of fields.slice(0, 20)) lines.push(` - ${readString(field, 'id', 'field')}`);
|
|
310
393
|
}
|
|
311
394
|
if (secretTargets.length > 0) {
|
|
312
|
-
lines.push('', ' Secret targets
|
|
395
|
+
lines.push('', ' Secret targets');
|
|
313
396
|
for (const target of secretTargets.slice(0, 12)) {
|
|
314
397
|
const required = readBoolean(target, 'required') ? 'required' : 'optional';
|
|
315
398
|
lines.push(` - ${readString(target, 'id', 'secret')} (${required})`);
|
|
@@ -336,9 +419,9 @@ export function registerChannelsRuntimeCommands(registry: CommandRegistry): void
|
|
|
336
419
|
registry.register({
|
|
337
420
|
name: 'channels',
|
|
338
421
|
aliases: ['channel'],
|
|
339
|
-
description: 'Inspect Agent channel readiness
|
|
340
|
-
usage: '[list|readiness|ready|attention|show <id>|accounts|policies|status|doctor <id>|setup <id>]',
|
|
341
|
-
argsHint: 'list|readiness|ready|attention|show|accounts|policies|status|doctor|setup',
|
|
422
|
+
description: 'Inspect Agent channel readiness or send an explicitly confirmed delivery message',
|
|
423
|
+
usage: '[list|readiness|ready|attention|show <id>|send --channel <id> --message <text> --yes|accounts|policies|status|doctor <id>|setup <id>]',
|
|
424
|
+
argsHint: 'list|readiness|ready|attention|show|send|accounts|policies|status|doctor|setup',
|
|
342
425
|
async handler(args, ctx) {
|
|
343
426
|
const channels = buildAgentWorkspaceChannels(ctx);
|
|
344
427
|
const subcommand = (args[0] ?? 'readiness').trim().toLowerCase();
|
|
@@ -366,13 +449,44 @@ export function registerChannelsRuntimeCommands(registry: CommandRegistry): void
|
|
|
366
449
|
}
|
|
367
450
|
const channel = channels.find((entry) => entry.id.toLowerCase() === channelId || entry.label.toLowerCase() === channelId);
|
|
368
451
|
if (!channel) {
|
|
369
|
-
ctx.print(`Unknown channel
|
|
452
|
+
ctx.print(`Unknown channel ${channelId}\nUse /channels list to see available channel ids.`);
|
|
370
453
|
return;
|
|
371
454
|
}
|
|
372
455
|
printChannelDetail(ctx.print, channel);
|
|
373
456
|
return;
|
|
374
457
|
}
|
|
375
458
|
|
|
459
|
+
if (subcommand === 'send') {
|
|
460
|
+
const router = ctx.platform.channelDeliveryRouter;
|
|
461
|
+
if (!router) {
|
|
462
|
+
ctx.print('Channel delivery is not available in this Agent runtime.');
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
const parsed = parseChannelSendArgs(args.slice(1));
|
|
466
|
+
if (parsed.errors.length > 0) {
|
|
467
|
+
ctx.print(`[channels] ${parsed.errors.join('\n[channels] ')}`);
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
let preview: ReturnType<typeof buildAgentChannelDeliveryPreview>;
|
|
471
|
+
try {
|
|
472
|
+
preview = buildAgentChannelDeliveryPreview(parsed);
|
|
473
|
+
} catch (error) {
|
|
474
|
+
ctx.print(`[channels] ${error instanceof Error ? error.message : String(error)}`);
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
if (!parsed.yes) {
|
|
478
|
+
ctx.print(formatAgentChannelDeliveryPreview(preview, router.listStrategies().length));
|
|
479
|
+
requireYesFlag(ctx, 'send a channel delivery message', '/channels send --channel <surface[:route[:label]]> --message <text> --yes');
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
try {
|
|
483
|
+
ctx.print(formatAgentChannelDeliveryResult(await deliverAgentChannelMessage(router, parsed)));
|
|
484
|
+
} catch (error) {
|
|
485
|
+
ctx.print(`[channels] Send failed ${error instanceof Error ? error.message : String(error)}`);
|
|
486
|
+
}
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
|
|
376
490
|
if (subcommand === 'accounts') {
|
|
377
491
|
await printReadOnlyChannelRoute(ctx, 'Channel accounts', '/api/channels/accounts', formatChannelAccounts);
|
|
378
492
|
return;
|
|
@@ -403,7 +517,7 @@ export function registerChannelsRuntimeCommands(registry: CommandRegistry): void
|
|
|
403
517
|
return;
|
|
404
518
|
}
|
|
405
519
|
|
|
406
|
-
ctx.print('Usage: /channels [list|readiness|ready|attention|show <id>|accounts|policies|status|doctor <id>|setup <id>]');
|
|
520
|
+
ctx.print('Usage: /channels [list|readiness|ready|attention|show <id>|send --channel <id> --message <text> --yes|accounts|policies|status|doctor <id>|setup <id>]');
|
|
407
521
|
},
|
|
408
522
|
});
|
|
409
523
|
}
|
|
@@ -6,11 +6,15 @@ function hasFlag(args: readonly string[], flag: string): boolean {
|
|
|
6
6
|
return args.includes(flag);
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
function hasAnyFlag(args: readonly string[], flags: readonly string[]): boolean {
|
|
10
|
+
return flags.some((flag) => hasFlag(args, flag));
|
|
11
|
+
}
|
|
12
|
+
|
|
9
13
|
function delegationTaskValues(args: readonly string[]): string[] {
|
|
10
14
|
const values: string[] = [];
|
|
11
15
|
for (let index = 0; index < args.length; index += 1) {
|
|
12
16
|
const token = args[index]!;
|
|
13
|
-
if (token === '--wrfc') continue;
|
|
17
|
+
if (token === '--review' || token === '--wrfc') continue;
|
|
14
18
|
if (!token.startsWith('--')) {
|
|
15
19
|
values.push(token);
|
|
16
20
|
continue;
|
|
@@ -19,35 +23,35 @@ function delegationTaskValues(args: readonly string[]): string[] {
|
|
|
19
23
|
return values;
|
|
20
24
|
}
|
|
21
25
|
|
|
22
|
-
function buildDelegationBody(task: string,
|
|
26
|
+
function buildDelegationBody(task: string, reviewRequested: boolean): string {
|
|
23
27
|
return [
|
|
24
28
|
'GoodVibes Agent explicit build delegation.',
|
|
25
29
|
'',
|
|
26
|
-
'Original user ask
|
|
30
|
+
'Original user ask',
|
|
27
31
|
task,
|
|
28
32
|
'',
|
|
29
|
-
'Agent policy
|
|
33
|
+
'Agent policy',
|
|
30
34
|
'- GoodVibes Agent is not the coding TUI.',
|
|
31
35
|
'- Preserve the full original ask.',
|
|
32
|
-
'- GoodVibes TUI owns file edits, git/worktree flows, execution isolation UX, and any
|
|
33
|
-
|
|
34
|
-
? '-
|
|
35
|
-
: '-
|
|
36
|
+
'- GoodVibes TUI owns file edits, git/worktree flows, execution isolation UX, and any delegated review owner chain.',
|
|
37
|
+
reviewRequested
|
|
38
|
+
? '- Delegated review was explicitly requested by the Agent user for this build/fix/review handoff.'
|
|
39
|
+
: '- Delegated review was not explicitly requested; do not add review solely because this came from Agent.',
|
|
36
40
|
].join('\n');
|
|
37
41
|
}
|
|
38
42
|
|
|
39
43
|
export function registerDelegationRuntimeCommands(registry: CommandRegistry): void {
|
|
40
|
-
const makeHandler = (
|
|
41
|
-
const
|
|
44
|
+
const makeHandler = (defaultReview: boolean) => async (args: string[], ctx: CommandContext): Promise<void> => {
|
|
45
|
+
const reviewRequested = defaultReview || hasAnyFlag(args, ['--review', '--wrfc']);
|
|
42
46
|
const task = delegationTaskValues(args).join(' ').trim();
|
|
43
47
|
if (!task) {
|
|
44
|
-
ctx.print(
|
|
48
|
+
ctx.print(defaultReview ? 'Usage: /delegate --review <build/fix/review task>' : 'Usage: /delegate [--review] <build/fix/review task>');
|
|
45
49
|
return;
|
|
46
50
|
}
|
|
47
51
|
const operator = ctx.clients?.operator;
|
|
48
52
|
if (!operator) {
|
|
49
53
|
ctx.print([
|
|
50
|
-
'Delegation unavailable
|
|
54
|
+
'Delegation unavailable. No operator client is attached.',
|
|
51
55
|
'Use the shared-session route from a configured Agent runtime, or open GoodVibes TUI in the target workspace.',
|
|
52
56
|
].join('\n'));
|
|
53
57
|
return;
|
|
@@ -67,12 +71,13 @@ export function registerDelegationRuntimeCommands(registry: CommandRegistry): vo
|
|
|
67
71
|
originSurface: 'goodvibes-agent',
|
|
68
72
|
sourceSessionId: ctx.session.runtime.sessionId,
|
|
69
73
|
task,
|
|
70
|
-
|
|
74
|
+
reviewRequested,
|
|
75
|
+
wrfcRequested: reviewRequested,
|
|
71
76
|
},
|
|
72
77
|
});
|
|
73
78
|
await operator.sessions.submitMessage({
|
|
74
79
|
sessionId: session.id,
|
|
75
|
-
body: buildDelegationBody(task,
|
|
80
|
+
body: buildDelegationBody(task, reviewRequested),
|
|
76
81
|
surfaceKind: participant.surfaceKind,
|
|
77
82
|
surfaceId: participant.surfaceId,
|
|
78
83
|
externalId: participant.externalId,
|
|
@@ -83,7 +88,8 @@ export function registerDelegationRuntimeCommands(registry: CommandRegistry): vo
|
|
|
83
88
|
sourceSessionId: ctx.session.runtime.sessionId,
|
|
84
89
|
kind: 'task',
|
|
85
90
|
task,
|
|
86
|
-
|
|
91
|
+
reviewRequested,
|
|
92
|
+
wrfcRequested: reviewRequested,
|
|
87
93
|
},
|
|
88
94
|
routing: {
|
|
89
95
|
executionIntent: {
|
|
@@ -96,16 +102,16 @@ export function registerDelegationRuntimeCommands(registry: CommandRegistry): vo
|
|
|
96
102
|
});
|
|
97
103
|
ctx.print([
|
|
98
104
|
'Delegation submitted to GoodVibes TUI/shared-session routes.',
|
|
99
|
-
` session
|
|
100
|
-
` mode
|
|
101
|
-
` task
|
|
102
|
-
' next
|
|
105
|
+
` session ${session.id}`,
|
|
106
|
+
` mode ${reviewRequested ? 'delegated review requested' : 'direct build delegation'}`,
|
|
107
|
+
` task ${task}`,
|
|
108
|
+
' next check GoodVibes TUI shared-session/task status for the result.',
|
|
103
109
|
].join('\n'));
|
|
104
110
|
} catch (error) {
|
|
105
111
|
ctx.print([
|
|
106
112
|
'Delegation failed.',
|
|
107
|
-
` error
|
|
108
|
-
' fallback
|
|
113
|
+
` error ${summarizeError(error)}`,
|
|
114
|
+
' fallback open GoodVibes TUI in the target workspace and paste the original task there.',
|
|
109
115
|
].join('\n'));
|
|
110
116
|
}
|
|
111
117
|
};
|
|
@@ -114,8 +120,8 @@ export function registerDelegationRuntimeCommands(registry: CommandRegistry): vo
|
|
|
114
120
|
name: 'delegate',
|
|
115
121
|
aliases: ['build'],
|
|
116
122
|
description: 'Explicitly delegate build/fix/review work to GoodVibes TUI through shared-session routes',
|
|
117
|
-
usage: '[--
|
|
118
|
-
argsHint: '[--
|
|
123
|
+
usage: '[--review] <task>',
|
|
124
|
+
argsHint: '[--review] <task>',
|
|
119
125
|
handler: makeHandler(false),
|
|
120
126
|
});
|
|
121
127
|
}
|
|
@@ -3,6 +3,7 @@ import { dirname, resolve } from 'node:path';
|
|
|
3
3
|
import type { CommandRegistry } from '../command-registry.ts';
|
|
4
4
|
import { requireShellPaths } from './runtime-services.ts';
|
|
5
5
|
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
6
|
+
import { handleApprovalOperatorAction } from './operator-actions-runtime.ts';
|
|
6
7
|
|
|
7
8
|
interface VoiceBundle {
|
|
8
9
|
readonly version: 1;
|
|
@@ -25,18 +26,19 @@ export function registerExperienceRuntimeCommands(registry: CommandRegistry): vo
|
|
|
25
26
|
name: 'approval',
|
|
26
27
|
aliases: ['approvals'],
|
|
27
28
|
description: 'Review action-specific approval classes and the specialized security UX matrix',
|
|
28
|
-
usage: '[matrix|review <kind>]',
|
|
29
|
+
usage: '[matrix|review <kind>|approve <id> --yes|deny <id> --yes|cancel <id> --yes]',
|
|
29
30
|
async handler(args, ctx) {
|
|
30
31
|
const sub = (args[0] ?? 'matrix').toLowerCase();
|
|
32
|
+
if (await handleApprovalOperatorAction(args, ctx)) return;
|
|
31
33
|
if (sub === 'open' || sub === 'panel') {
|
|
32
|
-
ctx.print('
|
|
34
|
+
ctx.print('Open Agent Workspace -> Work -> Review approvals for the workspace view, or run /approval matrix for the compact command output.');
|
|
33
35
|
return;
|
|
34
36
|
}
|
|
35
37
|
const matrix = [
|
|
36
38
|
['shell', 'Shell execution approval with side-effect and credential review.'],
|
|
37
39
|
['file', 'File mutation approval with config/notebook differentiation.'],
|
|
38
40
|
['network', 'Network access approval with host/scope review.'],
|
|
39
|
-
['delegate', 'Explicit GoodVibes TUI build delegation approval;
|
|
41
|
+
['delegate', 'Explicit GoodVibes TUI build delegation approval; Agent-owned job fanout is blocked.'],
|
|
40
42
|
['mcp', 'MCP trust escalation approval with host/path review.'],
|
|
41
43
|
['remote', 'Remote dispatch approval with trust/artifact review.'],
|
|
42
44
|
['hook', 'Hook execution approval with deny/mutate authority review.'],
|
|
@@ -63,7 +65,7 @@ export function registerExperienceRuntimeCommands(registry: CommandRegistry): vo
|
|
|
63
65
|
].join('\n'));
|
|
64
66
|
return;
|
|
65
67
|
}
|
|
66
|
-
ctx.print('Usage: /approval [matrix|review <kind>]');
|
|
68
|
+
ctx.print('Usage: /approval [matrix|review <kind>|approve <id> --yes|deny <id> --yes|cancel <id> --yes]');
|
|
67
69
|
},
|
|
68
70
|
});
|
|
69
71
|
|
|
@@ -22,10 +22,10 @@ export function registerGuidanceRuntimeCommands(registry: CommandRegistry): void
|
|
|
22
22
|
' /setup - open Agent setup with current settings preloaded',
|
|
23
23
|
' /agent - open the Agent operator workspace',
|
|
24
24
|
' /knowledge - inspect isolated Agent Knowledge status, ask, and search',
|
|
25
|
-
' /memory - manage local
|
|
26
|
-
' /personas - manage local
|
|
27
|
-
' /
|
|
28
|
-
' /routines - manage local
|
|
25
|
+
' /memory - manage Agent-local memory',
|
|
26
|
+
' /personas - manage Agent-local personas',
|
|
27
|
+
' /skills - manage reusable Agent-local skills',
|
|
28
|
+
' /routines - manage Agent-local routines',
|
|
29
29
|
' /delegate - explicitly hand build/fix/review work to GoodVibes TUI',
|
|
30
30
|
].join('\n'));
|
|
31
31
|
return;
|