@pellux/goodvibes-agent 1.0.3 → 1.0.5

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.
@@ -18,9 +18,12 @@ import type {
18
18
  } from '../input/agent-workspace-types.ts';
19
19
  import { parseSlashCommand } from '../input/slash-command-parser.ts';
20
20
  import { blockedHarnessCliCommandTokens, describeHarnessCliCommand, listHarnessCliCommands, totalHarnessCliCommands } from './agent-harness-cli-metadata.ts';
21
+ import { describeHarnessKeybinding, listHarnessKeybindings, listHarnessShortcuts, resetHarnessKeybinding, setHarnessKeybinding, totalHarnessKeybindings, totalHarnessShortcuts } from './agent-harness-keybinding-metadata.ts';
21
22
  import { describeHarnessPanel, listHarnessPanels, openHarnessPanel, totalHarnessPanels } from './agent-harness-panel-metadata.ts';
22
23
  import { describeLocalWorkspaceModelExecution, runLocalWorkspaceAction } from './agent-harness-local-operations.ts';
23
24
  import { listHarnessModelTools } from './agent-harness-model-tool-catalog.ts';
25
+ import { AGENT_HARNESS_MODES, AGENT_HARNESS_PARAMETER_PROPERTIES } from './agent-harness-tool-schema.ts';
26
+ import { describeHarnessUiSurface, listHarnessUiSurfaces, openHarnessUiSurface, totalHarnessUiSurfaces } from './agent-harness-ui-surface-metadata.ts';
24
27
  import {
25
28
  connectedHostSummary,
26
29
  describeCommandPolicy,
@@ -34,27 +37,7 @@ import {
34
37
  setHarnessSetting,
35
38
  } from '../agent/harness-control.ts';
36
39
 
37
- type AgentHarnessMode =
38
- | 'summary'
39
- | 'cli_commands'
40
- | 'cli_command'
41
- | 'panels'
42
- | 'panel'
43
- | 'open_panel'
44
- | 'commands'
45
- | 'command'
46
- | 'run_command'
47
- | 'settings'
48
- | 'get_setting'
49
- | 'set_setting'
50
- | 'reset_setting'
51
- | 'workspace'
52
- | 'workspace_categories'
53
- | 'workspace_actions'
54
- | 'workspace_action'
55
- | 'run_workspace_action'
56
- | 'tools'
57
- | 'connected_host';
40
+ type AgentHarnessMode = typeof AGENT_HARNESS_MODES[number];
58
41
 
59
42
  interface AgentHarnessToolArgs {
60
43
  readonly mode?: unknown;
@@ -68,8 +51,12 @@ interface AgentHarnessToolArgs {
68
51
  readonly actionId?: unknown;
69
52
  readonly recordId?: unknown;
70
53
  readonly fields?: unknown;
54
+ readonly combo?: unknown;
55
+ readonly combos?: unknown;
56
+ readonly surfaceId?: unknown;
71
57
  readonly key?: unknown;
72
58
  readonly value?: unknown;
59
+ readonly target?: unknown;
73
60
  readonly category?: unknown;
74
61
  readonly prefix?: unknown;
75
62
  readonly includeHidden?: unknown;
@@ -91,31 +78,8 @@ interface WorkspaceEditorContext {
91
78
  readonly selectedRoutine: AgentWorkspaceLocalLibraryItem | null;
92
79
  }
93
80
 
94
- const MODES: readonly AgentHarnessMode[] = [
95
- 'summary',
96
- 'cli_commands',
97
- 'cli_command',
98
- 'panels',
99
- 'panel',
100
- 'open_panel',
101
- 'commands',
102
- 'command',
103
- 'run_command',
104
- 'settings',
105
- 'get_setting',
106
- 'set_setting',
107
- 'reset_setting',
108
- 'workspace',
109
- 'workspace_categories',
110
- 'workspace_actions',
111
- 'workspace_action',
112
- 'run_workspace_action',
113
- 'tools',
114
- 'connected_host',
115
- ];
116
-
117
81
  function isMode(value: unknown): value is AgentHarnessMode {
118
- return typeof value === 'string' && MODES.includes(value as AgentHarnessMode);
82
+ return typeof value === 'string' && AGENT_HARNESS_MODES.includes(value as AgentHarnessMode);
119
83
  }
120
84
 
121
85
  function readString(value: unknown): string {
@@ -145,9 +109,7 @@ function output(value: unknown): { readonly success: true; readonly output: stri
145
109
  };
146
110
  }
147
111
 
148
- function error(message: string): { readonly success: false; readonly error: string } {
149
- return { success: false, error: message };
150
- }
112
+ function error(message: string): { readonly success: false; readonly error: string } { return { success: false, error: message }; }
151
113
 
152
114
  function commandMatches(command: SlashCommand, query: string): boolean {
153
115
  if (!query) return true;
@@ -541,106 +503,13 @@ export function createAgentHarnessTool(deps: AgentHarnessToolDeps): Tool {
541
503
  name: 'agent_harness',
542
504
  description: [
543
505
  'Discover and operate the GoodVibes Agent harness from the main conversation.',
544
- 'Use this tool to inspect Agent workspace actions, built-in panels, top-level CLI mirrors, slash commands with policy metadata, model tools, connected-host capabilities, and Agent settings, or to invoke a workspace action/command through the same in-process command registry the user uses in the TUI.',
545
- 'Discovery modes are read-only. Setting writes, resets, slash command invocation, and workspace action invocation require confirm:true plus explicitUserRequest.',
506
+ 'Use this tool to inspect Agent workspace actions, built-in panels, top-level CLI mirrors, UI surfaces, keybindings, slash commands with policy metadata, model tools, connected-host capabilities, and Agent settings, or to invoke a workspace action/command through the same in-process command registry the user uses in the TUI.',
507
+ 'Discovery modes are read-only. Setting/keybinding writes, resets, UI routing, slash command invocation, and workspace action invocation require confirm:true plus explicitUserRequest.',
546
508
  'This tool preserves Agent product boundaries: connected-host lifecycle and listener posture stay externally owned, connected-host mode reports allowed and blocked route families, and secret-backed settings store raw values through the secret manager while config receives only a secret reference.',
547
509
  ].join(' '),
548
510
  parameters: {
549
511
  type: 'object',
550
- properties: {
551
- mode: {
552
- type: 'string',
553
- enum: MODES,
554
- description: 'Harness operation to perform.',
555
- },
556
- query: {
557
- type: 'string',
558
- description: 'Search text for command, setting, or tool catalogs.',
559
- },
560
- command: {
561
- type: 'string',
562
- description: 'Full slash command string for mode run_command, for example "/settings get provider.model". In cli_command mode this may also hold a top-level CLI string such as "goodvibes-agent status --json".',
563
- },
564
- cliCommand: {
565
- type: 'string',
566
- description: 'Top-level CLI command string for mode cli_command, for example "goodvibes-agent status --json" or "profiles list". This mode is read-only metadata/parse inspection.',
567
- },
568
- commandName: {
569
- type: 'string',
570
- description: 'Slash command root without the leading slash for mode command or run_command, or a top-level CLI command token for cli_command.',
571
- },
572
- args: {
573
- type: 'array',
574
- items: { type: 'string' },
575
- description: 'Slash command argument tokens for mode run_command when commandName is used.',
576
- },
577
- categoryId: {
578
- type: 'string',
579
- description: 'Agent workspace category id for workspace action filtering.',
580
- },
581
- panelId: {
582
- type: 'string',
583
- description: 'Built-in panel id for panel or open_panel modes.',
584
- },
585
- actionId: {
586
- type: 'string',
587
- description: 'Agent workspace action id for workspace_action or run_workspace_action.',
588
- },
589
- fields: {
590
- type: 'object',
591
- additionalProperties: { type: 'string' },
592
- description: 'Field values for run_workspace_action when the workspace action opens an editor form.',
593
- },
594
- recordId: {
595
- type: 'string',
596
- description: 'Selected Agent-local record id for selection-based local workspace operations.',
597
- },
598
- key: {
599
- type: 'string',
600
- description: 'Agent setting key for get_setting, set_setting, or reset_setting.',
601
- },
602
- value: {
603
- anyOf: [
604
- { type: 'string' },
605
- { type: 'number' },
606
- { type: 'boolean' },
607
- ],
608
- description: 'Setting value for set_setting. Strings, booleans, numbers, and enum strings are accepted.',
609
- },
610
- category: {
611
- type: 'string',
612
- description: 'Setting category filter such as provider, behavior, tools, ui, tts, permissions, automation, or surfaces.',
613
- },
614
- prefix: {
615
- type: 'string',
616
- description: 'Setting key prefix filter such as surfaces.slack.',
617
- },
618
- includeHidden: {
619
- type: 'boolean',
620
- description: 'Include settings hidden from the Agent workspace because they are host-owned or non-Agent lifecycle settings.',
621
- },
622
- includeParameters: {
623
- type: 'boolean',
624
- description: 'Include model tool JSON schemas in tools mode, or workspace editor field schemas in workspace_actions mode.',
625
- },
626
- limit: {
627
- type: 'number',
628
- description: 'Maximum catalog entries to return.',
629
- },
630
- pane: {
631
- type: 'string',
632
- enum: ['top', 'bottom'],
633
- description: 'Preferred panel pane for open_panel when the current shell supports panel routing.',
634
- },
635
- confirm: {
636
- type: 'boolean',
637
- description: 'Required true for set_setting, reset_setting, run_command, open_panel, and mutating run_workspace_action calls after an explicit user request.',
638
- },
639
- explicitUserRequest: {
640
- type: 'string',
641
- description: 'Exact user request or faithful short summary authorizing a setting mutation or slash command invocation.',
642
- },
643
- },
512
+ properties: AGENT_HARNESS_PARAMETER_PROPERTIES,
644
513
  required: ['mode'],
645
514
  additionalProperties: false,
646
515
  },
@@ -656,6 +525,9 @@ export function createAgentHarnessTool(deps: AgentHarnessToolDeps): Tool {
656
525
  cliCommands: totalHarnessCliCommands(),
657
526
  blockedCliCommandTokens: blockedHarnessCliCommandTokens(),
658
527
  panels: totalHarnessPanels(deps.commandContext),
528
+ uiSurfaces: totalHarnessUiSurfaces(),
529
+ shortcuts: totalHarnessShortcuts(deps.commandContext),
530
+ keybindings: totalHarnessKeybindings(deps.commandContext),
659
531
  commands: deps.commandRegistry.list().length,
660
532
  settings: deps.commandContext.platform.configManager.getSchema().length,
661
533
  workspaceCategories: AGENT_WORKSPACE_CATEGORIES.length,
@@ -664,6 +536,8 @@ export function createAgentHarnessTool(deps: AgentHarnessToolDeps): Tool {
664
536
  modelAccess: {
665
537
  cliCommands: 'Use mode:"cli_commands" and mode:"cli_command" to inspect package CLI mirrors and their preferred in-process model routes. CLI modes are discovery-only.',
666
538
  panels: 'Use mode:"panels" and mode:"panel" to inspect built-in panel catalog/open state; use mode:"open_panel" with confirm:true plus explicitUserRequest to route a visible panel/workspace change.',
539
+ uiSurfaces: 'Use mode:"ui_surfaces" and mode:"ui_surface" to inspect modal/overlay/picker/workspace surfaces; use mode:"open_ui_surface" with confirm:true plus explicitUserRequest to route visible UI navigation.',
540
+ shortcuts: 'Use mode:"shortcuts" to inspect fixed shortcuts plus configurable keybindings. Use mode:"set_keybinding" and mode:"reset_keybinding" with confirm:true plus explicitUserRequest to edit the same config file the user edits.',
667
541
  slashCommands: 'Use mode:"commands" and mode:"command" to inspect; use mode:"run_command" with confirm:true plus explicitUserRequest to execute.',
668
542
  workspace: 'Use mode:"workspace_actions" to list and mode:"workspace_action" for editor schemas; set includeParameters:true on workspace_actions to inline editor schemas.',
669
543
  settings: 'Use mode:"settings", mode:"get_setting", mode:"set_setting", and mode:"reset_setting".',
@@ -705,6 +579,35 @@ export function createAgentHarnessTool(deps: AgentHarnessToolDeps): Tool {
705
579
  if (confirmationError) return error(confirmationError);
706
580
  return output(openHarnessPanel(deps.commandContext, args));
707
581
  }
582
+ if (args.mode === 'ui_surfaces') {
583
+ const surfaces = listHarnessUiSurfaces(deps.commandContext, args);
584
+ return output({ surfaces, returned: surfaces.length, total: totalHarnessUiSurfaces() });
585
+ }
586
+ if (args.mode === 'ui_surface') {
587
+ const surface = describeHarnessUiSurface(deps.commandContext, args);
588
+ return surface ? output(surface) : error(`Unknown UI surface ${readString(args.surfaceId || args.query) || '<missing>'}.`);
589
+ }
590
+ if (args.mode === 'open_ui_surface') {
591
+ const confirmationError = requireConfirmedAction(args, 'UI surface routing');
592
+ if (confirmationError) return error(confirmationError);
593
+ return output(openHarnessUiSurface(deps.commandContext, args));
594
+ }
595
+ if (args.mode === 'shortcuts') return output(listHarnessShortcuts(deps.commandContext, args));
596
+ if (args.mode === 'keybindings') return output(listHarnessKeybindings(deps.commandContext, args));
597
+ if (args.mode === 'keybinding') {
598
+ const binding = describeHarnessKeybinding(deps.commandContext, args);
599
+ return binding ? output(binding) : error(`Unknown keybinding action ${readString(args.actionId || args.key || args.query) || '<missing>'}.`);
600
+ }
601
+ if (args.mode === 'set_keybinding') {
602
+ const confirmationError = requireConfirmedAction(args, 'Keybinding mutation');
603
+ if (confirmationError) return error(confirmationError);
604
+ return output(setHarnessKeybinding(deps.commandContext, args));
605
+ }
606
+ if (args.mode === 'reset_keybinding') {
607
+ const confirmationError = requireConfirmedAction(args, 'Keybinding reset');
608
+ if (confirmationError) return error(confirmationError);
609
+ return output(resetHarnessKeybinding(deps.commandContext, args));
610
+ }
708
611
  if (args.mode === 'commands') {
709
612
  const commands = listCommands(deps.commandRegistry, args);
710
613
  return output({ commands, returned: commands.length, total: deps.commandRegistry.list().length });
@@ -0,0 +1,343 @@
1
+ import type { CommandContext } from '../input/command-registry.ts';
2
+
3
+ type UiSurfaceKind = 'overlay' | 'modal' | 'workspace' | 'picker';
4
+
5
+ export interface AgentHarnessUiSurfaceArgs {
6
+ readonly query?: unknown;
7
+ readonly surfaceId?: unknown;
8
+ readonly categoryId?: unknown;
9
+ readonly category?: unknown;
10
+ readonly target?: unknown;
11
+ readonly key?: unknown;
12
+ readonly prefix?: unknown;
13
+ readonly limit?: unknown;
14
+ }
15
+
16
+ interface UiSurfaceDefinition {
17
+ readonly id: string;
18
+ readonly label: string;
19
+ readonly kind: UiSurfaceKind;
20
+ readonly summary: string;
21
+ readonly command: string;
22
+ readonly preferredModelRoute: string;
23
+ readonly parameters?: readonly string[];
24
+ readonly available: (context: CommandContext) => boolean;
25
+ readonly open: (context: CommandContext, args: AgentHarnessUiSurfaceArgs) => Record<string, unknown>;
26
+ }
27
+
28
+ function readString(value: unknown): string {
29
+ return typeof value === 'string' ? value.trim() : '';
30
+ }
31
+
32
+ function readLimit(value: unknown, fallback: number): number {
33
+ const parsed = typeof value === 'string' && value.trim() ? Number(value) : value;
34
+ if (typeof parsed !== 'number' || !Number.isFinite(parsed)) return fallback;
35
+ return Math.max(1, Math.min(500, Math.trunc(parsed)));
36
+ }
37
+
38
+ function routeUnavailable(surface: UiSurfaceDefinition): Record<string, unknown> {
39
+ return {
40
+ status: 'route_unavailable',
41
+ surface: surface.id,
42
+ note: 'The current runtime did not provide the shell opener for this UI surface.',
43
+ };
44
+ }
45
+
46
+ function opened(surface: UiSurfaceDefinition, extra: Record<string, unknown> = {}): Record<string, unknown> {
47
+ return {
48
+ status: 'opened',
49
+ surface: surface.id,
50
+ kind: surface.kind,
51
+ ...extra,
52
+ note: 'UI routing was handed to the current Agent shell bridge.',
53
+ };
54
+ }
55
+
56
+ function optionalModelTarget(args: AgentHarnessUiSurfaceArgs): 'main' | 'helper' | 'tool' | 'tts' | undefined {
57
+ const target = readString(args.target);
58
+ return target === 'main' || target === 'helper' || target === 'tool' || target === 'tts' ? target : undefined;
59
+ }
60
+
61
+ function optionalOnboardingMode(args: AgentHarnessUiSurfaceArgs): 'new' | 'edit' | 'reopen' | undefined {
62
+ const target = readString(args.target);
63
+ return target === 'new' || target === 'edit' || target === 'reopen' ? target : undefined;
64
+ }
65
+
66
+ function workspaceCategory(args: AgentHarnessUiSurfaceArgs): string | undefined {
67
+ return readString(args.categoryId || args.category || args.target) || undefined;
68
+ }
69
+
70
+ function settingsTarget(args: AgentHarnessUiSurfaceArgs): string | undefined {
71
+ return readString(args.target || args.key || args.prefix) || undefined;
72
+ }
73
+
74
+ const UI_SURFACES: readonly UiSurfaceDefinition[] = [
75
+ {
76
+ id: 'agent-workspace',
77
+ label: 'Agent Workspace',
78
+ kind: 'workspace',
79
+ summary: 'Fullscreen operator workspace with setup, knowledge, local state, channels, automation, and delegation routes.',
80
+ command: '/agent',
81
+ preferredModelRoute: 'Use workspace_actions/workspace_action/run_workspace_action for model operation; use open_ui_surface only to visibly navigate.',
82
+ parameters: ['categoryId'],
83
+ available: (context) => typeof context.openAgentWorkspace === 'function',
84
+ open: (context, args) => {
85
+ const surface = findSurfaceById('agent-workspace')!;
86
+ if (!context.openAgentWorkspace) return routeUnavailable(surface);
87
+ const categoryId = workspaceCategory(args);
88
+ context.openAgentWorkspace(categoryId);
89
+ return opened(surface, { categoryId: categoryId ?? 'default' });
90
+ },
91
+ },
92
+ {
93
+ id: 'settings',
94
+ label: 'Settings',
95
+ kind: 'modal',
96
+ summary: 'Fullscreen settings workspace for Agent-owned configuration, subscriptions, secrets, MCP, tools, and surface settings.',
97
+ command: '/settings',
98
+ preferredModelRoute: 'Use settings/get_setting/set_setting/reset_setting for model operation; use open_ui_surface only to visibly navigate.',
99
+ parameters: ['target', 'key', 'prefix'],
100
+ available: (context) => typeof context.openSettingsModal === 'function',
101
+ open: (context, args) => {
102
+ const surface = findSurfaceById('settings')!;
103
+ if (!context.openSettingsModal) return routeUnavailable(surface);
104
+ const target = settingsTarget(args);
105
+ context.openSettingsModal(target);
106
+ return opened(surface, { target: target ?? 'default' });
107
+ },
108
+ },
109
+ {
110
+ id: 'mcp-workspace',
111
+ label: 'MCP Workspace',
112
+ kind: 'workspace',
113
+ summary: 'MCP server setup, trust posture, and tool inventory workspace.',
114
+ command: '/mcp',
115
+ preferredModelRoute: 'Use workspace_actions, tools, and settings modes for model operation.',
116
+ available: (context) => typeof context.openMcpWorkspace === 'function',
117
+ open: (context) => {
118
+ const surface = findSurfaceById('mcp-workspace')!;
119
+ if (!context.openMcpWorkspace) return routeUnavailable(surface);
120
+ context.openMcpWorkspace();
121
+ return opened(surface);
122
+ },
123
+ },
124
+ {
125
+ id: 'model-picker',
126
+ label: 'Model Picker',
127
+ kind: 'picker',
128
+ summary: 'Interactive model picker for main, helper, tool, and TTS model routes.',
129
+ command: '/model',
130
+ preferredModelRoute: 'Use settings mode for direct provider.model changes, or run_command /model with confirmation when a concrete model id is known.',
131
+ parameters: ['target'],
132
+ available: (context) => typeof context.openModelPicker === 'function' || typeof context.openModelPickerWithTarget === 'function',
133
+ open: (context, args) => {
134
+ const surface = findSurfaceById('model-picker')!;
135
+ const target = optionalModelTarget(args);
136
+ if (target && context.openModelPickerWithTarget) {
137
+ const openedForTarget = context.openModelPickerWithTarget(target);
138
+ return opened(surface, { target, openedForTarget });
139
+ }
140
+ if (!context.openModelPicker) return routeUnavailable(surface);
141
+ context.openModelPicker();
142
+ return opened(surface, { target: 'main' });
143
+ },
144
+ },
145
+ {
146
+ id: 'provider-picker',
147
+ label: 'Provider Picker',
148
+ kind: 'picker',
149
+ summary: 'Interactive provider picker for model route setup.',
150
+ command: '/provider',
151
+ preferredModelRoute: 'Use settings mode for direct provider routing changes, or run confirmed slash-command mirrors for concrete provider changes.',
152
+ parameters: ['target'],
153
+ available: (context) => typeof context.openProviderPicker === 'function' || typeof context.openProviderModelPickerWithTarget === 'function',
154
+ open: (context, args) => {
155
+ const surface = findSurfaceById('provider-picker')!;
156
+ const target = optionalModelTarget(args);
157
+ if (target && context.openProviderModelPickerWithTarget) {
158
+ context.openProviderModelPickerWithTarget(target);
159
+ return opened(surface, { target });
160
+ }
161
+ if (!context.openProviderPicker) return routeUnavailable(surface);
162
+ context.openProviderPicker();
163
+ return opened(surface, { target: 'main' });
164
+ },
165
+ },
166
+ {
167
+ id: 'session-picker',
168
+ label: 'Session Picker',
169
+ kind: 'picker',
170
+ summary: 'Saved session browser and loader.',
171
+ command: '/sessions',
172
+ preferredModelRoute: 'Use session slash-command mirrors with confirmation for concrete save/load/export actions.',
173
+ available: (context) => typeof context.openSessionPicker === 'function',
174
+ open: (context) => {
175
+ const surface = findSurfaceById('session-picker')!;
176
+ if (!context.openSessionPicker) return routeUnavailable(surface);
177
+ context.openSessionPicker();
178
+ return opened(surface);
179
+ },
180
+ },
181
+ {
182
+ id: 'profile-picker',
183
+ label: 'Profile Picker',
184
+ kind: 'picker',
185
+ summary: 'Agent profile picker for local isolated profile selection.',
186
+ command: '/agent-profile',
187
+ preferredModelRoute: 'Use workspace profile actions or profile slash-command mirrors for concrete model operation.',
188
+ available: (context) => typeof context.openProfilePicker === 'function',
189
+ open: (context) => {
190
+ const surface = findSurfaceById('profile-picker')!;
191
+ if (!context.openProfilePicker) return routeUnavailable(surface);
192
+ context.openProfilePicker();
193
+ return opened(surface);
194
+ },
195
+ },
196
+ {
197
+ id: 'bookmark-modal',
198
+ label: 'Bookmarks',
199
+ kind: 'modal',
200
+ summary: 'Transcript bookmark browser.',
201
+ command: '/bookmarks',
202
+ preferredModelRoute: 'Use slash-command mirrors for concrete bookmark inspection; opening is visible navigation only.',
203
+ available: (context) => typeof context.openBookmarkModal === 'function',
204
+ open: (context) => {
205
+ const surface = findSurfaceById('bookmark-modal')!;
206
+ if (!context.openBookmarkModal) return routeUnavailable(surface);
207
+ context.openBookmarkModal();
208
+ return opened(surface);
209
+ },
210
+ },
211
+ {
212
+ id: 'context-inspector',
213
+ label: 'Context Inspector',
214
+ kind: 'modal',
215
+ summary: 'Context-window usage and token breakdown inspector.',
216
+ command: '/context',
217
+ preferredModelRoute: 'Use slash-command mirrors for text output; opening is visible navigation only.',
218
+ available: (context) => typeof context.openContextInspector === 'function',
219
+ open: (context) => {
220
+ const surface = findSurfaceById('context-inspector')!;
221
+ if (!context.openContextInspector) return routeUnavailable(surface);
222
+ context.openContextInspector();
223
+ return opened(surface);
224
+ },
225
+ },
226
+ {
227
+ id: 'help-overlay',
228
+ label: 'Help Overlay',
229
+ kind: 'overlay',
230
+ summary: 'Registry-driven command and shortcut help overlay.',
231
+ command: '/help',
232
+ preferredModelRoute: 'Use commands/command and shortcuts modes for model-readable discovery.',
233
+ available: (context) => typeof context.openHelpOverlay === 'function',
234
+ open: (context) => {
235
+ const surface = findSurfaceById('help-overlay')!;
236
+ if (!context.openHelpOverlay) return routeUnavailable(surface);
237
+ context.openHelpOverlay();
238
+ return opened(surface);
239
+ },
240
+ },
241
+ {
242
+ id: 'shortcuts-overlay',
243
+ label: 'Shortcuts Overlay',
244
+ kind: 'overlay',
245
+ summary: 'Keyboard shortcut reference overlay.',
246
+ command: '/shortcuts',
247
+ preferredModelRoute: 'Use shortcuts/keybindings modes for model-readable discovery and confirmed keybinding edits.',
248
+ available: (context) => typeof context.openShortcutsOverlay === 'function',
249
+ open: (context) => {
250
+ const surface = findSurfaceById('shortcuts-overlay')!;
251
+ if (!context.openShortcutsOverlay) return routeUnavailable(surface);
252
+ context.openShortcutsOverlay();
253
+ return opened(surface);
254
+ },
255
+ },
256
+ {
257
+ id: 'onboarding',
258
+ label: 'Onboarding Wizard',
259
+ kind: 'modal',
260
+ summary: 'First-run and setup review wizard for Agent readiness.',
261
+ command: '/setup',
262
+ preferredModelRoute: 'Use workspace setup actions and settings modes for concrete model operation.',
263
+ parameters: ['target=new|edit|reopen'],
264
+ available: (context) => typeof context.openOnboardingWizard === 'function',
265
+ open: (context, args) => {
266
+ const surface = findSurfaceById('onboarding')!;
267
+ if (!context.openOnboardingWizard) return routeUnavailable(surface);
268
+ const mode = optionalOnboardingMode(args);
269
+ context.openOnboardingWizard(mode);
270
+ return opened(surface, { mode: mode ?? 'default' });
271
+ },
272
+ },
273
+ ];
274
+
275
+ function findSurfaceById(surfaceId: string): UiSurfaceDefinition | undefined {
276
+ return UI_SURFACES.find((surface) => surface.id === surfaceId);
277
+ }
278
+
279
+ function surfaceMatches(surface: Record<string, unknown>, query: string): boolean {
280
+ if (!query) return true;
281
+ return [
282
+ surface.id,
283
+ surface.label,
284
+ surface.kind,
285
+ surface.summary,
286
+ surface.command,
287
+ surface.preferredModelRoute,
288
+ ].map((value) => String(value ?? '')).join('\n').toLowerCase().includes(query.toLowerCase());
289
+ }
290
+
291
+ function describeSurface(context: CommandContext, surface: UiSurfaceDefinition): Record<string, unknown> {
292
+ return {
293
+ id: surface.id,
294
+ label: surface.label,
295
+ kind: surface.kind,
296
+ summary: surface.summary,
297
+ command: surface.command,
298
+ preferredModelRoute: surface.preferredModelRoute,
299
+ parameters: surface.parameters ?? [],
300
+ available: surface.available(context),
301
+ policy: {
302
+ effect: 'visible-ui-navigation',
303
+ confirmation: 'agent_harness mode:"open_ui_surface" requires confirm:true and explicitUserRequest.',
304
+ boundary: 'UI surface routing opens the same visible Agent shell surface the user can open. Use first-class model tools, settings modes, workspace actions, or confirmed slash-command mirrors for actual operations.',
305
+ },
306
+ };
307
+ }
308
+
309
+ export function totalHarnessUiSurfaces(): number {
310
+ return UI_SURFACES.length;
311
+ }
312
+
313
+ export function listHarnessUiSurfaces(context: CommandContext, args: AgentHarnessUiSurfaceArgs): readonly Record<string, unknown>[] {
314
+ const query = readString(args.query);
315
+ const limit = readLimit(args.limit, 200);
316
+ return UI_SURFACES
317
+ .map((surface) => describeSurface(context, surface))
318
+ .filter((surface) => surfaceMatches(surface, query))
319
+ .slice(0, limit);
320
+ }
321
+
322
+ export function describeHarnessUiSurface(context: CommandContext, args: AgentHarnessUiSurfaceArgs): Record<string, unknown> | null {
323
+ const surfaceId = readString(args.surfaceId || args.query);
324
+ if (!surfaceId) return null;
325
+ const surface = UI_SURFACES.find((entry) => entry.id === surfaceId || entry.label.toLowerCase() === surfaceId.toLowerCase());
326
+ return surface ? describeSurface(context, surface) : null;
327
+ }
328
+
329
+ export function openHarnessUiSurface(context: CommandContext, args: AgentHarnessUiSurfaceArgs): Record<string, unknown> {
330
+ const surfaceId = readString(args.surfaceId || args.query);
331
+ const surface = UI_SURFACES.find((entry) => entry.id === surfaceId || entry.label.toLowerCase() === surfaceId.toLowerCase());
332
+ if (!surface) {
333
+ return {
334
+ status: 'unknown_ui_surface',
335
+ surfaceId: surfaceId || '<missing>',
336
+ availableSurfaces: UI_SURFACES.map((entry) => entry.id),
337
+ };
338
+ }
339
+ return {
340
+ ...surface.open(context, args),
341
+ descriptor: describeSurface(context, surface),
342
+ };
343
+ }
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.0.3';
9
+ let _version = '1.0.5';
10
10
  let _sdkVersion = '0.33.35';
11
11
  try {
12
12
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8')) as {