@pellux/goodvibes-agent 1.4.4 → 1.5.1
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 +22 -0
- package/README.md +52 -7
- package/dist/package/main.js +7491 -12253
- package/docs/README.md +2 -2
- package/docs/channels-remote-and-api.md +1 -1
- package/docs/getting-started.md +2 -2
- package/docs/release-and-publishing.md +1 -1
- package/docs/tools-and-commands.md +5 -5
- package/package.json +4 -2
- package/release/live-verification/live-verification.json +13 -13
- package/release/live-verification/live-verification.md +15 -15
- package/release/performance-snapshot.json +2 -2
- package/release/release-notes.md +5 -7
- package/release/release-readiness.json +6 -6
- package/src/agent/behavior-discovery-summary.ts +4 -18
- package/src/agent/calendar-registry.ts +322 -0
- package/src/agent/competitive-feature-inventory.ts +268 -130
- package/src/agent/document-registry.ts +5 -1
- package/src/agent/email/email-service.ts +350 -0
- package/src/agent/email/imap-client.ts +596 -0
- package/src/agent/email/smtp-client.ts +453 -0
- package/src/agent/ics-calendar.ts +662 -0
- package/src/agent/ics-timezone.ts +172 -0
- package/src/agent/markdown-frontmatter.ts +31 -0
- package/src/agent/memory-safety.ts +1 -1
- package/src/agent/note-registry.ts +3 -9
- package/src/agent/persona-discovery.ts +3 -15
- package/src/agent/persona-registry.ts +1 -10
- package/src/agent/research-source-registry.ts +5 -1
- package/src/agent/routine-discovery.ts +3 -15
- package/src/agent/runtime-profile.ts +3 -0
- package/src/agent/skill-discovery.ts +3 -15
- package/src/agent/skill-draft-proposer.ts +203 -0
- package/src/agent/skill-draft-runner.ts +201 -0
- package/src/agent/skill-registry.ts +36 -1
- package/src/agent/skill-standard.ts +99 -0
- package/src/agent/vibe-file.ts +7 -22
- package/src/cli/completion.ts +1 -1
- package/src/cli/config-overrides.ts +10 -1
- package/src/cli/redaction.ts +17 -5
- package/src/config/provider-model.ts +2 -1
- package/src/config/secret-config.ts +13 -6
- package/src/core/activity-feed.ts +97 -0
- package/src/core/away-digest.ts +161 -0
- package/src/core/conversation-rendering.ts +7 -3
- package/src/core/conversation.ts +22 -15
- package/src/core/hardware-profile.ts +362 -0
- package/src/core/last-seen-store.ts +78 -0
- package/src/core/plain-language.ts +52 -0
- package/src/core/setup-incomplete-hint.ts +90 -0
- package/src/core/system-message-router.ts +38 -87
- package/src/input/agent-workspace-basic-command-editor-submission.ts +60 -220
- package/src/input/agent-workspace-basic-command-editors.ts +39 -141
- package/src/input/agent-workspace-categories.ts +40 -125
- package/src/input/agent-workspace-command-editor.ts +4 -0
- package/src/input/agent-workspace-host-category.ts +0 -5
- package/src/input/agent-workspace-local-editor-submission.ts +3 -1
- package/src/input/agent-workspace-navigation.ts +10 -3
- package/src/input/agent-workspace-onboarding-actions.ts +132 -0
- package/src/input/agent-workspace-onboarding-categories.ts +29 -26
- package/src/input/agent-workspace-onboarding-finish.ts +11 -4
- package/src/input/agent-workspace-onboarding-state.ts +111 -0
- package/src/input/agent-workspace-profile-editor-submission.ts +260 -0
- package/src/input/agent-workspace-profile-editors.ts +155 -0
- package/src/input/agent-workspace-subscription-editor.ts +7 -0
- package/src/input/agent-workspace-types.ts +5 -1
- package/src/input/agent-workspace.ts +65 -39
- package/src/input/command-registry.ts +18 -5
- package/src/input/commands/agent-runtime-profile-runtime.ts +2 -1
- package/src/input/commands/agent-skills-runtime.ts +60 -3
- package/src/input/commands/calendar-runtime.ts +209 -0
- package/src/input/commands/channels-runtime.ts +1 -0
- package/src/input/commands/command-error.ts +9 -0
- package/src/input/commands/compat-runtime.ts +1 -0
- package/src/input/commands/config.ts +1 -0
- package/src/input/commands/conversation-runtime.ts +1 -1
- package/src/input/commands/delegation-runtime.ts +5 -6
- package/src/input/commands/email-runtime.ts +387 -0
- package/src/input/commands/experience-runtime.ts +17 -4
- package/src/input/commands/guidance-runtime.ts +1 -1
- package/src/input/commands/health-runtime.ts +6 -1
- package/src/input/commands/knowledge-format.ts +73 -0
- package/src/input/commands/knowledge.ts +9 -74
- package/src/input/commands/local-provider-runtime.ts +2 -1
- package/src/input/commands/local-runtime.ts +10 -4
- package/src/input/commands/mcp-runtime.ts +7 -0
- package/src/input/commands/notify-runtime.ts +17 -1
- package/src/input/commands/onboarding-runtime.ts +1 -0
- package/src/input/commands/operator-actions-runtime.ts +1 -0
- package/src/input/commands/operator-runtime.ts +3 -0
- package/src/input/commands/personas-runtime.ts +1 -0
- package/src/input/commands/platform-access-runtime.ts +40 -17
- package/src/input/commands/product-runtime.ts +6 -1
- package/src/input/commands/provider-accounts-runtime.ts +3 -2
- package/src/input/commands/qrcode-runtime.ts +1 -0
- package/src/input/commands/routines-runtime.ts +1 -0
- package/src/input/commands/runtime-services.ts +0 -13
- package/src/input/commands/security-runtime.ts +1 -0
- package/src/input/commands/session-content.ts +1 -0
- package/src/input/commands/shell-core.ts +5 -2
- package/src/input/commands/subscription-runtime.ts +33 -9
- package/src/input/commands/support-bundle-runtime.ts +8 -1
- package/src/input/commands/tasks-runtime.ts +1 -0
- package/src/input/commands/tts-runtime.ts +1 -0
- package/src/input/commands/vibe-runtime.ts +1 -0
- package/src/input/commands.ts +4 -0
- package/src/input/feed-context-factory.ts +3 -12
- package/src/input/handler-command-route.ts +7 -60
- package/src/input/handler-feed-routes.ts +0 -194
- package/src/input/handler-feed.ts +2 -54
- package/src/input/handler-interactions.ts +0 -2
- package/src/input/handler-modal-stack.ts +0 -9
- package/src/input/handler-picker-routes.ts +24 -1
- package/src/input/handler-shortcuts.ts +11 -40
- package/src/input/handler-ui-state.ts +13 -0
- package/src/input/handler.ts +8 -35
- package/src/input/keybindings.ts +40 -17
- package/src/input/model-picker-local-fit.ts +130 -0
- package/src/input/shell-passthrough.ts +58 -0
- package/src/input/submission-router.ts +0 -5
- package/src/main.ts +129 -90
- package/src/renderer/activity-sidebar.ts +186 -0
- package/src/renderer/agent-workspace-context-lines.ts +5 -48
- package/src/renderer/agent-workspace-style.ts +1 -1
- package/src/renderer/agent-workspace.ts +14 -2
- package/src/renderer/compositor.ts +37 -125
- package/src/renderer/conversation-overlays.ts +3 -3
- package/src/renderer/help-overlay.ts +7 -5
- package/src/renderer/model-workspace.ts +101 -86
- package/src/{panels → renderer}/polish.ts +3 -3
- package/src/renderer/shell-surface.ts +4 -5
- package/src/renderer/status-token.ts +31 -11
- package/src/renderer/tab-strip.ts +1 -1
- package/src/renderer/tool-call.ts +7 -8
- package/src/renderer/tool-labels.ts +92 -0
- package/src/renderer/ui-factory.ts +48 -96
- package/src/runtime/bootstrap-command-context.ts +0 -5
- package/src/runtime/bootstrap-command-parts.ts +6 -15
- package/src/runtime/bootstrap-core.ts +34 -13
- package/src/runtime/bootstrap-hook-bridge.ts +3 -1
- package/src/runtime/bootstrap-shell.ts +3 -50
- package/src/runtime/bootstrap.ts +3 -4
- package/src/runtime/context.ts +1 -1
- package/src/runtime/diagnostics/panels/index.ts +0 -1
- package/src/runtime/diagnostics/panels/policy.ts +1 -1
- package/src/runtime/execution-ledger.ts +16 -1
- package/src/runtime/index.ts +6 -1
- package/src/runtime/onboarding/index.ts +1 -0
- package/src/runtime/onboarding/onboarding-state.ts +200 -0
- package/src/{panels → runtime}/provider-account-snapshot.ts +5 -2
- package/src/runtime/services.ts +16 -4
- package/src/runtime/terminal-output-guard.ts +7 -0
- package/src/runtime/tool-permission-safety.ts +1 -1
- package/src/runtime/ui/model-picker/data-provider.ts +1 -1
- package/src/runtime/ui/model-picker/health-enrichment.ts +2 -2
- package/src/runtime/ui/model-picker/types.ts +2 -2
- package/src/runtime/ui/provider-health/data-provider.ts +3 -3
- package/src/runtime/ui/provider-health/types.ts +2 -2
- package/src/runtime/ui-services.ts +0 -2
- package/src/shell/agent-workspace-fullscreen.ts +0 -1
- package/src/shell/autonomy-surfacing.ts +272 -0
- package/src/shell/session-continuity-hints.ts +0 -6
- package/src/shell/startup-wiring.ts +221 -0
- package/src/shell/ui-openers.ts +18 -29
- package/src/tools/agent-context-policy.ts +1 -1
- package/src/tools/agent-harness-background-processes.ts +4 -4
- package/src/tools/agent-harness-browser-cockpit-route.ts +3 -3
- package/src/tools/agent-harness-command-runner.ts +6 -1
- package/src/tools/agent-harness-document-ops.ts +26 -53
- package/src/tools/agent-harness-execution-history.ts +1 -13
- package/src/tools/agent-harness-execution-posture.ts +0 -15
- package/src/tools/agent-harness-keybinding-metadata.ts +28 -29
- package/src/tools/agent-harness-local-model-cookbook.ts +24 -1
- package/src/tools/agent-harness-metadata.ts +16 -0
- package/src/tools/agent-harness-mode-catalog.ts +2 -9
- package/src/tools/agent-harness-provider-account-metadata.ts +2 -2
- package/src/tools/agent-harness-setup-posture-utils.ts +1 -1
- package/src/tools/agent-harness-tool-schema.ts +13 -14
- package/src/tools/agent-harness-tool.ts +27 -23
- package/src/tools/agent-harness-ui-surface-metadata.ts +10 -20
- package/src/tools/agent-harness-workspace-action-runner.ts +3 -4
- package/src/tools/agent-workspace-tool.ts +2 -33
- package/src/utils/terminal-width.ts +9 -3
- package/src/version.ts +1 -1
- package/src/input/agent-workspace-panel-route.ts +0 -44
- package/src/input/panel-integration-actions.ts +0 -26
- package/src/panels/approval-panel.ts +0 -149
- package/src/panels/automation-control-panel.ts +0 -212
- package/src/panels/base-panel.ts +0 -254
- package/src/panels/builtin/agent.ts +0 -58
- package/src/panels/builtin/knowledge.ts +0 -26
- package/src/panels/builtin/operations.ts +0 -121
- package/src/panels/builtin/session.ts +0 -138
- package/src/panels/builtin/shared.ts +0 -275
- package/src/panels/builtin/usage.ts +0 -21
- package/src/panels/builtin-panels.ts +0 -23
- package/src/panels/confirm-state.ts +0 -61
- package/src/panels/context-visualizer-panel.ts +0 -204
- package/src/panels/cost-tracker-panel.ts +0 -444
- package/src/panels/docs-panel.ts +0 -285
- package/src/panels/index.ts +0 -25
- package/src/panels/knowledge-panel.ts +0 -417
- package/src/panels/memory-panel.ts +0 -226
- package/src/panels/panel-list-panel.ts +0 -464
- package/src/panels/panel-manager.ts +0 -570
- package/src/panels/provider-accounts-panel.ts +0 -233
- package/src/panels/provider-health-domains.ts +0 -208
- package/src/panels/provider-health-panel.ts +0 -720
- package/src/panels/provider-health-tracker.ts +0 -115
- package/src/panels/provider-stats-panel.ts +0 -366
- package/src/panels/qr-panel.ts +0 -207
- package/src/panels/schedule-panel.ts +0 -321
- package/src/panels/scrollable-list-panel.ts +0 -491
- package/src/panels/search-focus.ts +0 -32
- package/src/panels/security-panel.ts +0 -295
- package/src/panels/session-browser-panel.ts +0 -395
- package/src/panels/session-maintenance.ts +0 -125
- package/src/panels/subscription-panel.ts +0 -263
- package/src/panels/system-messages-panel.ts +0 -230
- package/src/panels/tasks-panel.ts +0 -344
- package/src/panels/thinking-panel.ts +0 -304
- package/src/panels/token-budget-panel.ts +0 -475
- package/src/panels/tool-inspector-panel.ts +0 -436
- package/src/panels/types.ts +0 -54
- package/src/renderer/panel-composite.ts +0 -158
- package/src/renderer/panel-tab-bar.ts +0 -69
- package/src/renderer/panel-workspace-bar.ts +0 -42
- package/src/runtime/diagnostics/panels/panel-resources.ts +0 -118
- package/src/tools/agent-harness-panel-metadata.ts +0 -211
- /package/src/runtime/perf/{panel-health-monitor.ts → component-health.ts} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const AGENT_HARNESS_MODES = [
|
|
2
|
-
'summary', 'modes', 'mode', 'route_decision', 'cli_commands', 'cli_command',
|
|
2
|
+
'summary', 'modes', 'mode', 'route_decision', 'cli_commands', 'cli_command',
|
|
3
3
|
'ui_surfaces', 'ui_surface', 'open_ui_surface',
|
|
4
4
|
'shortcuts', 'keybindings', 'keybinding', 'run_keybinding', 'set_keybinding', 'reset_keybinding',
|
|
5
5
|
'commands', 'command', 'run_command', 'channels', 'channel', 'channel_setup_guide', 'channel_triage', 'channel_deliveries', 'notifications', 'notification_target',
|
|
@@ -32,6 +32,12 @@ export const AGENT_HARNESS_MODES = [
|
|
|
32
32
|
'service_posture', 'service_endpoint',
|
|
33
33
|
'connected_host', 'connected_host_status', 'connected_host_capability',
|
|
34
34
|
'daemon', 'daemon_status',
|
|
35
|
+
'propose_skill_drafts',
|
|
36
|
+
] as const;
|
|
37
|
+
|
|
38
|
+
const BACKGROUND_PROCESS_ACTION_VALUES = [
|
|
39
|
+
'start', 'spawn', 'run', 'stop', 'kill', 'cancel', 'wait', 'list',
|
|
40
|
+
'status', 'poll', 'log', 'output', 'write', 'capabilities', 'doctor', 'parity',
|
|
35
41
|
] as const;
|
|
36
42
|
|
|
37
43
|
const KEY_COMBO_PARAMETER_SCHEMA = {
|
|
@@ -42,10 +48,12 @@ const KEY_COMBO_PARAMETER_SCHEMA = {
|
|
|
42
48
|
} as const;
|
|
43
49
|
|
|
44
50
|
export const AGENT_HARNESS_PARAMETER_PROPERTIES = {
|
|
51
|
+
// The full mode catalog is intentionally NOT inlined as an enum:
|
|
52
|
+
// it would bloat every model prompt. Dispatch still validates against
|
|
53
|
+
// AGENT_HARNESS_MODES and unknown modes return suggestions.
|
|
45
54
|
mode: {
|
|
46
55
|
type: 'string',
|
|
47
|
-
|
|
48
|
-
description: 'Harness operation. Start with summary or a plural catalog mode.',
|
|
56
|
+
description: 'Operation. Start with "summary"; mode:"modes" lists the catalog.',
|
|
49
57
|
},
|
|
50
58
|
query: {
|
|
51
59
|
type: 'string',
|
|
@@ -131,12 +139,12 @@ export const AGENT_HARNESS_PARAMETER_PROPERTIES = {
|
|
|
131
139
|
},
|
|
132
140
|
processAction: {
|
|
133
141
|
type: 'string',
|
|
134
|
-
enum:
|
|
142
|
+
enum: BACKGROUND_PROCESS_ACTION_VALUES,
|
|
135
143
|
description: 'Background process lifecycle action or process-style alias.',
|
|
136
144
|
},
|
|
137
145
|
action: {
|
|
138
146
|
type: 'string',
|
|
139
|
-
enum:
|
|
147
|
+
enum: BACKGROUND_PROCESS_ACTION_VALUES,
|
|
140
148
|
description: 'Process-style action alias for run_background_process.',
|
|
141
149
|
},
|
|
142
150
|
cwd: {
|
|
@@ -216,10 +224,6 @@ export const AGENT_HARNESS_PARAMETER_PROPERTIES = {
|
|
|
216
224
|
type: 'string',
|
|
217
225
|
description: 'UI surface id for ui_surface or open_ui_surface modes.',
|
|
218
226
|
},
|
|
219
|
-
panelId: {
|
|
220
|
-
type: 'string',
|
|
221
|
-
description: 'Built-in panel id for panel or open_panel modes.',
|
|
222
|
-
},
|
|
223
227
|
actionId: {
|
|
224
228
|
type: 'string',
|
|
225
229
|
description: 'Workspace action id or keybinding action id.',
|
|
@@ -332,11 +336,6 @@ export const AGENT_HARNESS_PARAMETER_PROPERTIES = {
|
|
|
332
336
|
type: 'number',
|
|
333
337
|
description: 'Maximum catalog entries to return.',
|
|
334
338
|
},
|
|
335
|
-
pane: {
|
|
336
|
-
type: 'string',
|
|
337
|
-
enum: ['top', 'bottom'],
|
|
338
|
-
description: 'Preferred pane for open_panel.',
|
|
339
|
-
},
|
|
340
339
|
confirm: {
|
|
341
340
|
type: 'boolean',
|
|
342
341
|
description: 'Required true for confirmed harness effects.',
|
|
@@ -8,11 +8,12 @@ import { channelReadinessCatalogStatus, describeHarnessChannel, describeHarnessC
|
|
|
8
8
|
import { blockedHarnessCliCommandTokens, describeHarnessCliCommand, listHarnessCliCommands, totalHarnessCliCommands } from './agent-harness-cli-metadata.ts';
|
|
9
9
|
import { describeHarnessCommand, listHarnessCommands } from './agent-harness-command-catalog.ts';
|
|
10
10
|
import { describeLearningCandidate, learningCuratorCatalogStatus, learningCuratorSummary } from './agent-harness-learning-curator.ts';
|
|
11
|
+
import { runSkillDraftProposer } from '../agent/skill-draft-runner.ts';
|
|
12
|
+
import { AgentSkillRegistry } from '../agent/skill-registry.ts';
|
|
11
13
|
import { delegationPostureCatalogStatus, delegationPostureSummary, describeHarnessDelegationRoute } from './agent-harness-delegation-posture.ts';
|
|
12
14
|
import { describeHarnessKeybinding, listHarnessKeybindings, listHarnessShortcuts, resetHarnessKeybinding, runHarnessKeybinding, setHarnessKeybinding, totalHarnessKeybindings, totalHarnessShortcuts } from './agent-harness-keybinding-metadata.ts';
|
|
13
15
|
import { describeHarnessMediaProvider, mediaPostureCatalogStatus, mediaPostureSummary } from './agent-harness-media-posture.ts';
|
|
14
16
|
import { describeHarnessNotificationTarget, listHarnessNotificationTargets, notificationTargetCatalogStatus } from './agent-harness-notification-metadata.ts';
|
|
15
|
-
import { describeHarnessPanel, listHarnessPanels, openHarnessPanel, totalHarnessPanels } from './agent-harness-panel-metadata.ts';
|
|
16
17
|
import { connectedHostStatusSummary } from './agent-harness-connected-host-status.ts';
|
|
17
18
|
import { backgroundProcessCatalogStatus, backgroundProcessSummary, describeBackgroundProcess, runBackgroundProcessAction } from './agent-harness-background-processes.ts';
|
|
18
19
|
import { describeDocumentOpsLane, documentOpsCatalogStatus, documentOpsSummary } from './agent-harness-document-ops.ts';
|
|
@@ -81,7 +82,6 @@ function compactHarnessModeGuide(): Record<string, unknown> {
|
|
|
81
82
|
function detailedHarnessModelAccessGuide(): Record<string, string> {
|
|
82
83
|
return {
|
|
83
84
|
cliCommands: 'Prefer workspace action:"cli_commands|cli_command" for CLI discovery. Lower-level cli command modes remain available.',
|
|
84
|
-
panels: 'Prefer workspace action:"panels|panel|open_panel"; visible navigation needs confirm:true and explicitUserRequest.',
|
|
85
85
|
uiSurfaces: 'Prefer workspace action:"surfaces|surface|open" for visible UI and computer action:"browser|open_browser" for browser/PWA. Lower-level UI modes remain available.',
|
|
86
86
|
shortcuts: 'Prefer workspace action:"shortcuts|keybindings|keybinding|run_keybinding|set_keybinding|reset_keybinding"; effects need confirmation.',
|
|
87
87
|
slashCommands: 'Prefer workspace action:"commands|command|run_command"; slash-command execution needs confirmation.',
|
|
@@ -131,7 +131,7 @@ export function createAgentHarnessTool(deps: AgentHarnessToolDeps): Tool {
|
|
|
131
131
|
return {
|
|
132
132
|
definition: {
|
|
133
133
|
name: 'agent_harness',
|
|
134
|
-
description: 'Inspect or operate
|
|
134
|
+
description: 'Inspect or operate Agent harness surfaces; mode:"modes" lists all.',
|
|
135
135
|
parameters: {
|
|
136
136
|
type: 'object',
|
|
137
137
|
properties: AGENT_HARNESS_PARAMETER_PROPERTIES,
|
|
@@ -143,7 +143,13 @@ export function createAgentHarnessTool(deps: AgentHarnessToolDeps): Tool {
|
|
|
143
143
|
},
|
|
144
144
|
execute: async (rawArgs) => {
|
|
145
145
|
const args = rawArgs as AgentHarnessToolArgs;
|
|
146
|
-
if (!isMode(args.mode))
|
|
146
|
+
if (!isMode(args.mode)) {
|
|
147
|
+
const requested = String(args.mode).toLowerCase();
|
|
148
|
+
const suggestions = AGENT_HARNESS_MODES
|
|
149
|
+
.filter((mode) => mode.includes(requested) || requested.includes(mode))
|
|
150
|
+
.slice(0, 5);
|
|
151
|
+
return error(`Unknown agent_harness mode: ${String(args.mode)}. ${suggestions.length > 0 ? `Closest modes: ${suggestions.join(', ')}. ` : ''}Use mode:"modes" to list the full catalog.`);
|
|
152
|
+
}
|
|
147
153
|
try {
|
|
148
154
|
if (args.mode === 'summary') {
|
|
149
155
|
const channelReadiness = channelReadinessCatalogStatus(deps.commandContext);
|
|
@@ -214,7 +220,6 @@ export function createAgentHarnessTool(deps: AgentHarnessToolDeps): Tool {
|
|
|
214
220
|
harnessModes: HARNESS_MODE_DESCRIPTORS.length,
|
|
215
221
|
cliCommands: totalHarnessCliCommands(),
|
|
216
222
|
blockedCliCommandTokens: blockedHarnessCliCommandTokens(),
|
|
217
|
-
panels: totalHarnessPanels(deps.commandContext),
|
|
218
223
|
uiSurfaces: totalHarnessUiSurfaces(),
|
|
219
224
|
shortcuts: totalHarnessShortcuts(deps.commandContext),
|
|
220
225
|
keybindings: totalHarnessKeybindings(deps.commandContext),
|
|
@@ -282,24 +287,6 @@ export function createAgentHarnessTool(deps: AgentHarnessToolDeps): Tool {
|
|
|
282
287
|
if (args.mode === 'cli_command') {
|
|
283
288
|
return output(describeHarnessCliCommand(args));
|
|
284
289
|
}
|
|
285
|
-
if (args.mode === 'panels') {
|
|
286
|
-
const panels = listHarnessPanels(deps.commandContext, args);
|
|
287
|
-
return output({
|
|
288
|
-
panels,
|
|
289
|
-
returned: panels.length,
|
|
290
|
-
total: totalHarnessPanels(deps.commandContext),
|
|
291
|
-
policy: 'Panel modes expose Agent/TUI operator view catalog and open state. open_panel is confirmation-gated and routes through the current Agent operator surface.',
|
|
292
|
-
});
|
|
293
|
-
}
|
|
294
|
-
if (args.mode === 'panel') {
|
|
295
|
-
const panel = describeHarnessPanel(deps.commandContext, args);
|
|
296
|
-
return panel ? output(panel) : error(`Unknown panel ${readString(args.panelId || args.target || args.query) || '<missing>'}.`);
|
|
297
|
-
}
|
|
298
|
-
if (args.mode === 'open_panel') {
|
|
299
|
-
const confirmationError = requireConfirmedAction(args, 'Panel routing');
|
|
300
|
-
if (confirmationError) return error(confirmationError);
|
|
301
|
-
return output(openHarnessPanel(deps.commandContext, args));
|
|
302
|
-
}
|
|
303
290
|
if (args.mode === 'ui_surfaces') {
|
|
304
291
|
const surfaces = listHarnessUiSurfaces(deps.commandContext, args);
|
|
305
292
|
return output({ surfaces, returned: surfaces.length, total: totalHarnessUiSurfaces() });
|
|
@@ -717,6 +704,23 @@ export function createAgentHarnessTool(deps: AgentHarnessToolDeps): Tool {
|
|
|
717
704
|
includeParameters: args.includeParameters === true,
|
|
718
705
|
}));
|
|
719
706
|
}
|
|
707
|
+
if (args.mode === 'propose_skill_drafts') {
|
|
708
|
+
const confirmationError = requireConfirmedAction(args, 'Skill draft proposal');
|
|
709
|
+
if (confirmationError) return error(confirmationError);
|
|
710
|
+
const shellPaths = deps.commandContext.workspace?.shellPaths;
|
|
711
|
+
if (!shellPaths) return error('Skill draft proposal requires an active workspace.');
|
|
712
|
+
const registry = AgentSkillRegistry.fromShellPaths(shellPaths);
|
|
713
|
+
const result = runSkillDraftProposer(deps.commandContext, registry);
|
|
714
|
+
return output({
|
|
715
|
+
proposed: result.proposed,
|
|
716
|
+
skipped: result.skipped,
|
|
717
|
+
skillIds: result.skillIds,
|
|
718
|
+
message: result.proposed > 0
|
|
719
|
+
? `Drafted ${result.proposed} skill${result.proposed !== 1 ? 's' : ''} for review. Find them under Memory > Skills with enabled:false.`
|
|
720
|
+
: 'No new skill drafts this pass.',
|
|
721
|
+
policy: 'Drafted skills are disabled and require review before use. Enable them under Memory > Skills.',
|
|
722
|
+
});
|
|
723
|
+
}
|
|
720
724
|
return error(`Unhandled agent_harness mode: ${args.mode}`);
|
|
721
725
|
} catch (err) {
|
|
722
726
|
return error(formatHarnessError(err));
|
|
@@ -101,16 +101,6 @@ function openPanelWorkspaceSurface(
|
|
|
101
101
|
options.opener();
|
|
102
102
|
return opened(surface, { categoryId: options.categoryId, panelId: options.panelId, route: 'named-opener' });
|
|
103
103
|
}
|
|
104
|
-
if (context.showPanel) {
|
|
105
|
-
const pane = optionalPane(args);
|
|
106
|
-
context.showPanel(options.panelId, pane);
|
|
107
|
-
return opened(surface, {
|
|
108
|
-
categoryId: options.categoryId,
|
|
109
|
-
panelId: options.panelId,
|
|
110
|
-
pane: pane ?? 'default',
|
|
111
|
-
route: 'panel-route',
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
104
|
return routeUnavailable(surface);
|
|
115
105
|
}
|
|
116
106
|
|
|
@@ -183,17 +173,17 @@ const UI_SURFACES: readonly UiSurfaceDefinition[] = [
|
|
|
183
173
|
},
|
|
184
174
|
{
|
|
185
175
|
id: 'panel-picker',
|
|
186
|
-
label: '
|
|
176
|
+
label: 'Workspace Picker',
|
|
187
177
|
kind: 'picker',
|
|
188
|
-
summary: 'Operator
|
|
178
|
+
summary: 'Operator route into the Agent Workspace home.',
|
|
189
179
|
command: 'Ctrl+P',
|
|
190
|
-
preferredModelRoute:
|
|
191
|
-
available: (context) => typeof context.
|
|
180
|
+
preferredModelRoute: 'Use mode:"workspace_actions" for concrete model operation, or mode:"open_ui_surface" for visible routing.',
|
|
181
|
+
available: (context) => typeof context.openWorkspacePicker === 'function' || typeof context.openAgentWorkspace === 'function',
|
|
192
182
|
open: (context) => {
|
|
193
183
|
const surface = findSurfaceById('panel-picker')!;
|
|
194
|
-
if (context.
|
|
195
|
-
context.
|
|
196
|
-
return opened(surface, { categoryId: 'home', route: '
|
|
184
|
+
if (context.openWorkspacePicker) {
|
|
185
|
+
context.openWorkspacePicker();
|
|
186
|
+
return opened(surface, { categoryId: 'home', route: 'workspace-picker' });
|
|
197
187
|
}
|
|
198
188
|
return openAgentWorkspaceCategory(context, surface, 'home');
|
|
199
189
|
},
|
|
@@ -206,7 +196,7 @@ const UI_SURFACES: readonly UiSurfaceDefinition[] = [
|
|
|
206
196
|
command: '/security',
|
|
207
197
|
preferredModelRoute: `Use mode:"workspace_actions" for security review actions or ${agentHarnessModes('run_command')} for confirmed /security review output.`,
|
|
208
198
|
parameters: ['pane'],
|
|
209
|
-
available: (context) => typeof context.openAgentWorkspace === 'function' || typeof context.openSecurityPanel === 'function'
|
|
199
|
+
available: (context) => typeof context.openAgentWorkspace === 'function' || typeof context.openSecurityPanel === 'function',
|
|
210
200
|
open: (context, args) => {
|
|
211
201
|
const surface = findSurfaceById('security-panel')!;
|
|
212
202
|
return openPanelWorkspaceSurface(context, args, surface, {
|
|
@@ -224,7 +214,7 @@ const UI_SURFACES: readonly UiSurfaceDefinition[] = [
|
|
|
224
214
|
command: '/knowledge',
|
|
225
215
|
preferredModelRoute: `Use agent_knowledge, agent_knowledge_ingest, mode:"workspace_actions", or ${agentHarnessModes('run_command')} for confirmed /knowledge operation.`,
|
|
226
216
|
parameters: ['pane'],
|
|
227
|
-
available: (context) => typeof context.openAgentWorkspace === 'function' || typeof context.openKnowledgePanel === 'function'
|
|
217
|
+
available: (context) => typeof context.openAgentWorkspace === 'function' || typeof context.openKnowledgePanel === 'function',
|
|
228
218
|
open: (context, args) => {
|
|
229
219
|
const surface = findSurfaceById('knowledge-panel')!;
|
|
230
220
|
return openPanelWorkspaceSurface(context, args, surface, {
|
|
@@ -242,7 +232,7 @@ const UI_SURFACES: readonly UiSurfaceDefinition[] = [
|
|
|
242
232
|
command: '/subscription',
|
|
243
233
|
preferredModelRoute: `Use mode:"workspace_actions" or ${agentHarnessModes('run_command')} for confirmed /subscription mirrors.`,
|
|
244
234
|
parameters: ['pane'],
|
|
245
|
-
available: (context) => typeof context.openAgentWorkspace === 'function' || typeof context.openSubscriptionPanel === 'function'
|
|
235
|
+
available: (context) => typeof context.openAgentWorkspace === 'function' || typeof context.openSubscriptionPanel === 'function',
|
|
246
236
|
open: (context, args) => {
|
|
247
237
|
const surface = findSurfaceById('subscription-panel')!;
|
|
248
238
|
return openPanelWorkspaceSurface(context, args, surface, {
|
|
@@ -49,7 +49,7 @@ export async function runWorkspaceAction(
|
|
|
49
49
|
if (action.kind === 'settings-import') {
|
|
50
50
|
const preview = previewAgentWorkspaceTuiSettingsImport(deps.commandContext);
|
|
51
51
|
if (!preview) return error('GoodVibes settings import is unavailable in this runtime.');
|
|
52
|
-
if (args.confirm !== true) {
|
|
52
|
+
if (args.confirm !== true && !readString(args.explicitUserRequest)) {
|
|
53
53
|
return output({
|
|
54
54
|
status: 'confirmation_required',
|
|
55
55
|
action: describeWorkspaceAction(category, action, { lookup }),
|
|
@@ -57,10 +57,9 @@ export async function runWorkspaceAction(
|
|
|
57
57
|
next: 'Run with confirm:true and explicitUserRequest after the user asks to import these settings.',
|
|
58
58
|
});
|
|
59
59
|
}
|
|
60
|
+
const confirmationError = requireConfirmedAction(args, 'GoodVibes settings import');
|
|
61
|
+
if (confirmationError) return error(confirmationError);
|
|
60
62
|
const explicitUserRequest = readString(args.explicitUserRequest);
|
|
61
|
-
if (!explicitUserRequest) {
|
|
62
|
-
return error('GoodVibes settings import requires explicitUserRequest when confirm is true.');
|
|
63
|
-
}
|
|
64
63
|
const outcome = await importAgentWorkspaceTuiSettings(deps.commandContext);
|
|
65
64
|
return output({
|
|
66
65
|
status: outcome.status,
|
|
@@ -11,9 +11,6 @@ type AgentWorkspaceToolAction =
|
|
|
11
11
|
| 'surfaces'
|
|
12
12
|
| 'surface'
|
|
13
13
|
| 'open'
|
|
14
|
-
| 'panels'
|
|
15
|
-
| 'panel'
|
|
16
|
-
| 'open_panel'
|
|
17
14
|
| 'shortcuts'
|
|
18
15
|
| 'keybindings'
|
|
19
16
|
| 'keybinding'
|
|
@@ -33,7 +30,6 @@ interface AgentWorkspaceToolArgs {
|
|
|
33
30
|
readonly actionId?: unknown;
|
|
34
31
|
readonly workspaceActionId?: unknown;
|
|
35
32
|
readonly surfaceId?: unknown;
|
|
36
|
-
readonly panelId?: unknown;
|
|
37
33
|
readonly command?: unknown;
|
|
38
34
|
readonly commandName?: unknown;
|
|
39
35
|
readonly args?: unknown;
|
|
@@ -47,7 +43,6 @@ interface AgentWorkspaceToolArgs {
|
|
|
47
43
|
readonly combos?: unknown;
|
|
48
44
|
readonly key?: unknown;
|
|
49
45
|
readonly value?: unknown;
|
|
50
|
-
readonly pane?: unknown;
|
|
51
46
|
readonly includeParameters?: unknown;
|
|
52
47
|
readonly limit?: unknown;
|
|
53
48
|
readonly confirm?: unknown;
|
|
@@ -79,9 +74,6 @@ function normalizeWorkspaceAction(value: unknown): AgentWorkspaceToolAction | nu
|
|
|
79
74
|
if (action === 'surfaces' || action === 'ui_surfaces' || action === 'screens' || action === 'views') return 'surfaces';
|
|
80
75
|
if (action === 'surface' || action === 'ui_surface' || action === 'screen' || action === 'view') return 'surface';
|
|
81
76
|
if (action === 'open' || action === 'navigate' || action === 'open_surface' || action === 'open_ui_surface') return 'open';
|
|
82
|
-
if (action === 'panels' || action === 'panes') return 'panels';
|
|
83
|
-
if (action === 'panel' || action === 'pane') return 'panel';
|
|
84
|
-
if (action === 'open_panel' || action === 'open_pane') return 'open_panel';
|
|
85
77
|
if (action === 'shortcuts' || action === 'shortcut_help' || action === 'help') return 'shortcuts';
|
|
86
78
|
if (action === 'keybindings' || action === 'bindings' || action === 'keys') return 'keybindings';
|
|
87
79
|
if (action === 'keybinding' || action === 'binding' || action === 'key') return 'keybinding';
|
|
@@ -100,7 +92,6 @@ function readAction(args: AgentWorkspaceToolArgs): AgentWorkspaceToolAction {
|
|
|
100
92
|
const explicit = normalizeWorkspaceAction(args.action) ?? normalizeWorkspaceAction(args.mode);
|
|
101
93
|
if (explicit) return explicit;
|
|
102
94
|
if (readString(args.surfaceId)) return 'surface';
|
|
103
|
-
if (readString(args.panelId)) return 'panel';
|
|
104
95
|
if (readString(args.actionId) || readString(args.workspaceActionId)) return 'action';
|
|
105
96
|
if (readString(args.command) || readString(args.commandName)) return 'command';
|
|
106
97
|
if (readString(args.key)) return 'keybinding';
|
|
@@ -120,10 +111,6 @@ function surfaceLookup(args: AgentWorkspaceToolArgs): string {
|
|
|
120
111
|
return readString(args.surfaceId) || readString(args.id);
|
|
121
112
|
}
|
|
122
113
|
|
|
123
|
-
function panelLookup(args: AgentWorkspaceToolArgs): string {
|
|
124
|
-
return readString(args.panelId) || readString(args.id);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
114
|
function confirmedArgs(args: AgentWorkspaceToolArgs): Record<string, unknown> {
|
|
128
115
|
return compactArgs({
|
|
129
116
|
confirm: args.confirm,
|
|
@@ -173,19 +160,6 @@ function surfaceArgs(mode: 'ui_surface' | 'open_ui_surface', args: AgentWorkspac
|
|
|
173
160
|
});
|
|
174
161
|
}
|
|
175
162
|
|
|
176
|
-
function panelArgs(mode: 'panel' | 'open_panel', args: AgentWorkspaceToolArgs): Record<string, unknown> {
|
|
177
|
-
const panelId = panelLookup(args);
|
|
178
|
-
return compactArgs({
|
|
179
|
-
mode,
|
|
180
|
-
panelId,
|
|
181
|
-
target: panelId ? undefined : args.target,
|
|
182
|
-
query: panelId ? undefined : args.query,
|
|
183
|
-
pane: args.pane,
|
|
184
|
-
includeParameters: mode === 'panel' ? args.includeParameters : undefined,
|
|
185
|
-
...(mode === 'open_panel' ? confirmedArgs(args) : {}),
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
|
|
189
163
|
function keybindingArgs(mode: 'keybinding' | 'run_keybinding' | 'set_keybinding' | 'reset_keybinding', args: AgentWorkspaceToolArgs): Record<string, unknown> {
|
|
190
164
|
const actionId = lookupId(args);
|
|
191
165
|
return compactArgs({
|
|
@@ -232,15 +206,14 @@ export function createAgentWorkspaceTool(deps: AgentWorkspaceToolDeps): Tool {
|
|
|
232
206
|
properties: {
|
|
233
207
|
action: {
|
|
234
208
|
type: 'string',
|
|
235
|
-
enum: ['status', 'actions', 'action', 'run', 'surfaces', 'surface', 'open', '
|
|
209
|
+
enum: ['status', 'actions', 'action', 'run', 'surfaces', 'surface', 'open', 'shortcuts', 'keybindings', 'keybinding', 'run_keybinding', 'set_keybinding', 'reset_keybinding', 'commands', 'command', 'run_command', 'cli_commands', 'cli_command'],
|
|
236
210
|
description: 'Inspect catalogs, open UI, or run approved workspace actions.',
|
|
237
211
|
},
|
|
238
212
|
mode: { type: 'string', description: 'Alias for action.' },
|
|
239
|
-
id: { type: 'string', description: 'Generic action, surface,
|
|
213
|
+
id: { type: 'string', description: 'Generic action, surface, or keybinding id.' },
|
|
240
214
|
actionId: { type: 'string', description: 'Workspace action or keybinding action id.' },
|
|
241
215
|
workspaceActionId: { type: 'string', description: 'Workspace action id alias.' },
|
|
242
216
|
surfaceId: { type: 'string', description: 'UI surface id.' },
|
|
243
|
-
panelId: { type: 'string', description: 'Panel id.' },
|
|
244
217
|
command: { type: 'string', description: 'Slash or CLI command string.' },
|
|
245
218
|
commandName: { type: 'string', description: 'Slash or CLI command name.' },
|
|
246
219
|
args: { type: 'array', items: { type: 'string' }, description: 'Command arguments when commandName is used.' },
|
|
@@ -254,7 +227,6 @@ export function createAgentWorkspaceTool(deps: AgentWorkspaceToolDeps): Tool {
|
|
|
254
227
|
combos: { type: 'array', items: { type: 'object' }, description: 'Multiple keybinding combos.' },
|
|
255
228
|
key: { type: 'string', description: 'Keybinding lookup key.' },
|
|
256
229
|
value: { anyOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], description: 'Keybinding value such as Ctrl+G.' },
|
|
257
|
-
pane: { type: 'string', enum: ['top', 'bottom'], description: 'Preferred pane for panel open.' },
|
|
258
230
|
includeParameters: { type: 'boolean', description: 'Include detailed schemas or route metadata.' },
|
|
259
231
|
limit: { type: 'number', description: 'Maximum rows returned for catalog actions.' },
|
|
260
232
|
confirm: { type: 'boolean', description: 'Required true for UI opens and side-effecting actions.' },
|
|
@@ -276,9 +248,6 @@ export function createAgentWorkspaceTool(deps: AgentWorkspaceToolDeps): Tool {
|
|
|
276
248
|
if (action === 'surfaces') return harnessTool.execute(compactArgs({ mode: 'ui_surfaces', target: args.target, query: args.query, limit: args.limit, includeParameters: args.includeParameters }));
|
|
277
249
|
if (action === 'surface') return harnessTool.execute(surfaceArgs('ui_surface', args));
|
|
278
250
|
if (action === 'open') return harnessTool.execute(surfaceArgs('open_ui_surface', args));
|
|
279
|
-
if (action === 'panels') return harnessTool.execute(compactArgs({ mode: 'panels', target: args.target, query: args.query, limit: args.limit, includeParameters: args.includeParameters }));
|
|
280
|
-
if (action === 'panel') return harnessTool.execute(panelArgs('panel', args));
|
|
281
|
-
if (action === 'open_panel') return harnessTool.execute(panelArgs('open_panel', args));
|
|
282
251
|
if (action === 'shortcuts') return harnessTool.execute(compactArgs({ mode: 'shortcuts', target: args.target, query: args.query, limit: args.limit, includeParameters: args.includeParameters }));
|
|
283
252
|
if (action === 'keybindings') return harnessTool.execute(compactArgs({ mode: 'keybindings', target: args.target, query: args.query, limit: args.limit, includeParameters: args.includeParameters }));
|
|
284
253
|
if (action === 'keybinding') return harnessTool.execute(keybindingArgs('keybinding', args));
|
|
@@ -139,14 +139,20 @@ export function wrapText(text: string, width: number): string[] {
|
|
|
139
139
|
if (currentLine) lines.push(currentLine);
|
|
140
140
|
let remaining = word;
|
|
141
141
|
while (getDisplayWidth(remaining) > width) {
|
|
142
|
+
const codePoints = [...remaining];
|
|
142
143
|
let splitIdx = 0;
|
|
143
144
|
let currentWidth = 0;
|
|
144
|
-
for (
|
|
145
|
-
const charWidth = getDisplayWidth(
|
|
145
|
+
for (const cp of codePoints) {
|
|
146
|
+
const charWidth = getDisplayWidth(cp);
|
|
146
147
|
if (currentWidth + charWidth > width) break;
|
|
147
148
|
currentWidth += charWidth;
|
|
148
|
-
splitIdx
|
|
149
|
+
splitIdx += cp.length;
|
|
149
150
|
}
|
|
151
|
+
// Safety: guarantee at least one code point of progress even if a
|
|
152
|
+
// single grapheme is wider than the available width (e.g. CJK/emoji
|
|
153
|
+
// at width===1). Without this guard splitIdx stays 0, remaining
|
|
154
|
+
// never shrinks, and the while-loop hangs.
|
|
155
|
+
if (splitIdx === 0) splitIdx = codePoints[0]!.length;
|
|
150
156
|
lines.push(remaining.slice(0, splitIdx));
|
|
151
157
|
remaining = remaining.slice(splitIdx);
|
|
152
158
|
}
|
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 = '1.
|
|
9
|
+
let _version = '1.5.1';
|
|
10
10
|
try {
|
|
11
11
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8')) as {
|
|
12
12
|
readonly version?: unknown;
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { AgentWorkspaceCategoryId } from './agent-workspace-types.ts';
|
|
2
|
-
|
|
3
|
-
const PANEL_CATEGORY_ROUTES = {
|
|
4
|
-
knowledge: 'knowledge',
|
|
5
|
-
memory: 'memory',
|
|
6
|
-
'work-plan': 'work',
|
|
7
|
-
'project-planning': 'work',
|
|
8
|
-
plan: 'work',
|
|
9
|
-
approval: 'work',
|
|
10
|
-
tasks: 'work',
|
|
11
|
-
'provider-health': 'setup',
|
|
12
|
-
automation: 'automation',
|
|
13
|
-
schedule: 'automation',
|
|
14
|
-
providers: 'setup',
|
|
15
|
-
accounts: 'setup',
|
|
16
|
-
subscription: 'setup',
|
|
17
|
-
cost: 'setup',
|
|
18
|
-
tokens: 'setup',
|
|
19
|
-
security: 'tools',
|
|
20
|
-
policy: 'tools',
|
|
21
|
-
tools: 'tools',
|
|
22
|
-
'qr-code': 'channels',
|
|
23
|
-
sessions: 'conversation',
|
|
24
|
-
context: 'conversation',
|
|
25
|
-
thinking: 'conversation',
|
|
26
|
-
'system-messages': 'conversation',
|
|
27
|
-
docs: 'home',
|
|
28
|
-
'panel-list': 'home',
|
|
29
|
-
} as const satisfies Readonly<Record<string, AgentWorkspaceCategoryId>>;
|
|
30
|
-
|
|
31
|
-
type AgentWorkspacePanelRouteId = keyof typeof PANEL_CATEGORY_ROUTES;
|
|
32
|
-
|
|
33
|
-
function hasPanelCategoryRoute(panelId: string): panelId is AgentWorkspacePanelRouteId {
|
|
34
|
-
return Object.prototype.hasOwnProperty.call(PANEL_CATEGORY_ROUTES, panelId);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function agentWorkspaceCategoryForPanel(panelId: string): AgentWorkspaceCategoryId {
|
|
38
|
-
return hasPanelCategoryRoute(panelId) ? PANEL_CATEGORY_ROUTES[panelId] : 'home';
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function agentWorkspaceCommandForPanel(panelId: string): string {
|
|
42
|
-
const category = agentWorkspaceCategoryForPanel(panelId);
|
|
43
|
-
return category === 'setup' ? '/agent' : `/agent ${category}`;
|
|
44
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { CommandContext } from './command-registry.ts';
|
|
2
|
-
import { logger } from '@pellux/goodvibes-sdk/platform/utils';
|
|
3
|
-
import type { Panel } from '../panels/types.ts';
|
|
4
|
-
import type { PanelManager } from '../panels/panel-manager.ts';
|
|
5
|
-
import { ApprovalPanel } from '../panels/approval-panel.ts';
|
|
6
|
-
|
|
7
|
-
export function handlePanelIntegrationAction(
|
|
8
|
-
panelManager: PanelManager,
|
|
9
|
-
activePanel: Panel | null,
|
|
10
|
-
key: string,
|
|
11
|
-
commandContext?: CommandContext,
|
|
12
|
-
): boolean {
|
|
13
|
-
if (!activePanel) return false;
|
|
14
|
-
|
|
15
|
-
if ((key === 'enter' || key === 'return') && activePanel instanceof ApprovalPanel) {
|
|
16
|
-
const command = activePanel.getSelectedCommand();
|
|
17
|
-
if (!command || !commandContext?.executeCommand) return false;
|
|
18
|
-
const parts = command.replace(/^\//, '').split(/\s+/).filter(Boolean);
|
|
19
|
-
const [name, ...args] = parts;
|
|
20
|
-
if (!name) return false;
|
|
21
|
-
void commandContext.executeCommand(name, args).catch((err) => { logger.debug('approval panel command dispatch failed', { err }); });
|
|
22
|
-
return true;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import type { Line } from '../types/grid.ts';
|
|
2
|
-
import { ScrollableListPanel } from './scrollable-list-panel.ts';
|
|
3
|
-
import {
|
|
4
|
-
buildGuidanceLine,
|
|
5
|
-
buildKeyValueLine,
|
|
6
|
-
buildPanelLine,
|
|
7
|
-
DEFAULT_PANEL_PALETTE,
|
|
8
|
-
} from './polish.ts';
|
|
9
|
-
import type { PolicyRuntimeState } from '@/runtime/index.ts';
|
|
10
|
-
import { buildPermissionRuleSuggestions } from '@/runtime/index.ts';
|
|
11
|
-
|
|
12
|
-
const C = {
|
|
13
|
-
...DEFAULT_PANEL_PALETTE,
|
|
14
|
-
headerBg: '#111827',
|
|
15
|
-
} as const;
|
|
16
|
-
|
|
17
|
-
const APPROVAL_ROWS = [
|
|
18
|
-
['shell', 'why prompted: side effects, destructive ops, secret exposure, escalation', 'review via /approval review shell'],
|
|
19
|
-
['file', 'why prompted: config mutation, notebook edits, secret-bearing paths', 'review via /approval review file'],
|
|
20
|
-
['network', 'why prompted: external hosts, fetch scope, egress policy', 'review via /approval review network'],
|
|
21
|
-
['delegate', 'why prompted: explicit TUI build handoff, write scope, result ownership', 'review via /approval review delegate'],
|
|
22
|
-
['mcp', 'why prompted: trust escalation, host scope, path scope, coherence mismatch', 'review via /approval review mcp'],
|
|
23
|
-
['remote', 'why prompted: build-host trust, remote write scope, artifact requirements', 'review via /approval review remote'],
|
|
24
|
-
['hook', 'why prompted: deny/mutate authority, blocking behavior, execution provenance', 'review via /approval review hook'],
|
|
25
|
-
['plugin', 'why prompted: install/update lifecycle, provenance, capability grants', 'review via /approval review plugin'],
|
|
26
|
-
] as const;
|
|
27
|
-
|
|
28
|
-
type ApprovalRow = (typeof APPROVAL_ROWS)[number];
|
|
29
|
-
|
|
30
|
-
export class ApprovalPanel extends ScrollableListPanel<ApprovalRow> {
|
|
31
|
-
private readonly policyRuntimeState: Pick<PolicyRuntimeState, 'getSnapshot'>;
|
|
32
|
-
|
|
33
|
-
public constructor(policyRuntimeState: Pick<PolicyRuntimeState, 'getSnapshot'>) {
|
|
34
|
-
super('approval', 'Approval', 'A', 'monitoring');
|
|
35
|
-
this.showSelectionGutter = true; // I5: non-color selection affordance
|
|
36
|
-
this.policyRuntimeState = policyRuntimeState;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
protected override getPalette() { return C; }
|
|
40
|
-
protected override getEmptyStateMessage() { return ' No approval lanes defined.'; }
|
|
41
|
-
|
|
42
|
-
protected getItems(): readonly ApprovalRow[] {
|
|
43
|
-
return APPROVAL_ROWS;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
protected renderItem(row: ApprovalRow, index: number, selected: boolean, width: number): Line {
|
|
47
|
-
const bg = selected ? C.selectBg : undefined;
|
|
48
|
-
return buildPanelLine(width, [
|
|
49
|
-
[' ', C.label],
|
|
50
|
-
[row[0].padEnd(10), C.info, bg],
|
|
51
|
-
[row[1].slice(0, Math.max(0, width - 18)), C.value, bg],
|
|
52
|
-
]);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
public handleInput(key: string): boolean {
|
|
56
|
-
if (key === 'home') {
|
|
57
|
-
this.selectedIndex = 0;
|
|
58
|
-
this.markDirty();
|
|
59
|
-
return true;
|
|
60
|
-
}
|
|
61
|
-
if (key === 'end') {
|
|
62
|
-
this.selectedIndex = APPROVAL_ROWS.length - 1;
|
|
63
|
-
this.markDirty();
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
if (key === 'enter' || key === 'return') {
|
|
67
|
-
return true;
|
|
68
|
-
}
|
|
69
|
-
return super.handleInput(key);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
public getSelectedCommand(): string | null {
|
|
73
|
-
const selected = APPROVAL_ROWS[this.selectedIndex] ?? null;
|
|
74
|
-
return selected ? selected[2].replace('review via ', '').trim() : null;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
public render(width: number, height: number): Line[] {
|
|
78
|
-
this.clampSelection();
|
|
79
|
-
const policySnapshot = this.policyRuntimeState.getSnapshot();
|
|
80
|
-
const approvalCount = policySnapshot.recentPermissionAudit.filter((e) => e.approved === true).length;
|
|
81
|
-
const denialCount = policySnapshot.recentPermissionAudit.filter((e) => e.approved === false).length;
|
|
82
|
-
const pendingCount = policySnapshot.recentPermissionAudit.filter((e) => e.approved === undefined).length;
|
|
83
|
-
|
|
84
|
-
const selected = APPROVAL_ROWS[this.selectedIndex] ?? null;
|
|
85
|
-
const detailLines: Line[] = [];
|
|
86
|
-
if (selected) {
|
|
87
|
-
detailLines.push(buildPanelLine(width, [[' Selected Lane', C.label]]));
|
|
88
|
-
detailLines.push(buildKeyValueLine(width, [
|
|
89
|
-
{ label: 'lane', value: selected[0], valueColor: C.info },
|
|
90
|
-
{ label: 'next review', value: selected[2], valueColor: C.dim },
|
|
91
|
-
], C));
|
|
92
|
-
detailLines.push(buildPanelLine(width, [[` ${selected[1]}`, C.value]]));
|
|
93
|
-
detailLines.push(buildGuidanceLine(width, selected[2].replace('review via ', ''), `open the ${selected[0]} review path`, C));
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const recentAuditLines: Line[] = [];
|
|
97
|
-
for (const entry of policySnapshot.recentPermissionAudit.slice(0, 5)) {
|
|
98
|
-
const decision = entry.approved === undefined ? 'pending' : entry.approved ? 'approved' : 'denied';
|
|
99
|
-
const decisionColor = entry.approved === undefined ? C.info : entry.approved ? C.good : C.bad;
|
|
100
|
-
recentAuditLines.push(buildPanelLine(width, [
|
|
101
|
-
[` ${decision.padEnd(8)}`, decisionColor],
|
|
102
|
-
[`${entry.tool}`.padEnd(14), C.label],
|
|
103
|
-
[entry.summary.slice(0, Math.max(0, width - 28)), C.value],
|
|
104
|
-
]));
|
|
105
|
-
if (entry.reasons[0]) {
|
|
106
|
-
recentAuditLines.push(buildPanelLine(width, [[` ${entry.reasons[0]}`, C.dim]]));
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
if (recentAuditLines.length === 0) {
|
|
110
|
-
recentAuditLines.push(buildPanelLine(width, [[` No recent approval pressure. Live requests and decisions will appear here.`, C.dim]]));
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const ruleSuggestionLines: Line[] = [];
|
|
114
|
-
for (const suggestion of buildPermissionRuleSuggestions(policySnapshot.recentPermissionAudit).slice(0, 3)) {
|
|
115
|
-
ruleSuggestionLines.push(buildPanelLine(width, [[` ${suggestion.summary}`, C.info]]));
|
|
116
|
-
ruleSuggestionLines.push(buildGuidanceLine(width, suggestion.command, suggestion.reason, C));
|
|
117
|
-
}
|
|
118
|
-
if (ruleSuggestionLines.length === 0) {
|
|
119
|
-
ruleSuggestionLines.push(buildPanelLine(width, [[` No repeated denials currently suggest a durable rule.`, C.dim]]));
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const headerLines: Line[] = [
|
|
123
|
-
buildPanelLine(width, [[' Approval posture', C.label]]),
|
|
124
|
-
buildKeyValueLine(width, [
|
|
125
|
-
{ label: 'why prompted', value: 'risk summary', valueColor: C.value },
|
|
126
|
-
{ label: 'what-if', value: '/security review', valueColor: C.info },
|
|
127
|
-
{ label: 'operator', value: '/approval + /security', valueColor: C.good },
|
|
128
|
-
], C),
|
|
129
|
-
buildPanelLine(width, [
|
|
130
|
-
[' \u2713 ', C.good],
|
|
131
|
-
[`approvals (${approvalCount}) `, C.good],
|
|
132
|
-
['\u2715 ', C.bad],
|
|
133
|
-
[`denials (${denialCount}) `, C.bad],
|
|
134
|
-
['\u25cb ', C.info],
|
|
135
|
-
[`pending (${pendingCount})`, C.info],
|
|
136
|
-
]),
|
|
137
|
-
buildGuidanceLine(width, '/approval review shell', 'inspect the highest-risk approval lane and refine scoped review posture', C),
|
|
138
|
-
...detailLines,
|
|
139
|
-
...recentAuditLines,
|
|
140
|
-
...ruleSuggestionLines,
|
|
141
|
-
];
|
|
142
|
-
|
|
143
|
-
return this.renderList(width, height, {
|
|
144
|
-
title: 'Approval Control Room',
|
|
145
|
-
header: headerLines,
|
|
146
|
-
footer: [buildPanelLine(width, [[` Up/Down move Home/End jump selected lane opens the next command path`, C.dim]])],
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
}
|