@pellux/goodvibes-agent 1.0.2 → 1.0.4

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.
@@ -0,0 +1,36 @@
1
+ import type { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
2
+
3
+ export interface AgentHarnessModelToolCatalogArgs {
4
+ readonly query?: unknown;
5
+ readonly includeParameters?: unknown;
6
+ readonly limit?: unknown;
7
+ }
8
+
9
+ function readString(value: unknown): string {
10
+ return typeof value === 'string' ? value.trim() : '';
11
+ }
12
+
13
+ function readLimit(value: unknown, fallback: number): number {
14
+ const parsed = typeof value === 'string' && value.trim() ? Number(value) : value;
15
+ if (typeof parsed !== 'number' || !Number.isFinite(parsed)) return fallback;
16
+ return Math.max(1, Math.min(500, Math.trunc(parsed)));
17
+ }
18
+
19
+ export function listHarnessModelTools(toolRegistry: ToolRegistry, args: AgentHarnessModelToolCatalogArgs): readonly Record<string, unknown>[] {
20
+ const query = readString(args.query).toLowerCase();
21
+ const includeParameters = args.includeParameters === true;
22
+ const limit = readLimit(args.limit, 200);
23
+ return toolRegistry.getToolDefinitions()
24
+ .filter((tool) => !query || [tool.name, tool.description, ...(tool.sideEffects ?? [])].join('\n').toLowerCase().includes(query))
25
+ .sort((a, b) => a.name.localeCompare(b.name))
26
+ .slice(0, limit)
27
+ .map((tool) => ({
28
+ name: tool.name,
29
+ description: tool.description,
30
+ sideEffects: tool.sideEffects ?? [],
31
+ concurrency: tool.concurrency ?? 'parallel',
32
+ supportsProgress: tool.supportsProgress ?? false,
33
+ supportsStreamingOutput: tool.supportsStreamingOutput ?? false,
34
+ ...(includeParameters ? { parameters: tool.parameters } : {}),
35
+ }));
36
+ }
@@ -0,0 +1,121 @@
1
+ import type { CommandContext } from '../input/command-registry.ts';
2
+ import { agentWorkspaceCategoryForPanel, agentWorkspaceCommandForPanel } from '../input/agent-workspace-panel-route.ts';
3
+ import type { PanelRegistration } from '../panels/types.ts';
4
+
5
+ export interface AgentHarnessPanelArgs {
6
+ readonly query?: unknown;
7
+ readonly panelId?: unknown;
8
+ readonly category?: unknown;
9
+ readonly limit?: unknown;
10
+ readonly pane?: unknown;
11
+ }
12
+
13
+ function readString(value: unknown): string {
14
+ return typeof value === 'string' ? value.trim() : '';
15
+ }
16
+
17
+ function readLimit(value: unknown, fallback: number): number {
18
+ const parsed = typeof value === 'string' && value.trim() ? Number(value) : value;
19
+ if (typeof parsed !== 'number' || !Number.isFinite(parsed)) return fallback;
20
+ return Math.max(1, Math.min(500, Math.trunc(parsed)));
21
+ }
22
+
23
+ function panelManager(context: CommandContext) {
24
+ return context.workspace.panelManager ?? null;
25
+ }
26
+
27
+ function panelMatches(panel: Record<string, unknown>, query: string): boolean {
28
+ if (!query) return true;
29
+ return [
30
+ panel.id,
31
+ panel.name,
32
+ panel.category,
33
+ panel.description,
34
+ panel.workspaceRoute,
35
+ ].map((value) => String(value ?? '')).join('\n').toLowerCase().includes(query.toLowerCase());
36
+ }
37
+
38
+ function describePanelRegistration(context: CommandContext, registration: PanelRegistration): Record<string, unknown> {
39
+ const manager = panelManager(context);
40
+ const openPanel = manager?.getPanel(registration.id) ?? null;
41
+ const pane = manager?.getPaneOf(registration.id) ?? null;
42
+ const activePanel = manager?.getActivePanel() ?? null;
43
+ return {
44
+ id: registration.id,
45
+ name: registration.name,
46
+ icon: registration.icon,
47
+ category: registration.category,
48
+ description: registration.description,
49
+ preload: registration.preload === true,
50
+ open: openPanel !== null,
51
+ pane,
52
+ active: activePanel?.id === registration.id,
53
+ focused: activePanel?.id === registration.id,
54
+ workspaceRoute: {
55
+ categoryId: agentWorkspaceCategoryForPanel(registration.id),
56
+ command: agentWorkspaceCommandForPanel(registration.id),
57
+ },
58
+ policy: {
59
+ effect: 'ui-navigation',
60
+ confirmation: 'agent_harness mode:"open_panel" requires confirm:true and explicitUserRequest.',
61
+ boundary: 'Panels are Agent/TUI operator views. The model can inspect panel catalog/open state; panel routing uses the existing Agent workspace bridge and does not mutate connected-host lifecycle.',
62
+ },
63
+ };
64
+ }
65
+
66
+ export function totalHarnessPanels(context: CommandContext): number {
67
+ return panelManager(context)?.getRegisteredTypes().length ?? 0;
68
+ }
69
+
70
+ export function listHarnessPanels(context: CommandContext, args: AgentHarnessPanelArgs): readonly Record<string, unknown>[] {
71
+ const manager = panelManager(context);
72
+ if (!manager) return [];
73
+ const query = readString(args.query);
74
+ const category = readString(args.category);
75
+ const limit = readLimit(args.limit, 200);
76
+ return manager.getRegisteredTypes()
77
+ .map((registration) => describePanelRegistration(context, registration))
78
+ .filter((panel) => !category || panel.category === category)
79
+ .filter((panel) => panelMatches(panel, query))
80
+ .sort((a, b) => String(a.id).localeCompare(String(b.id)))
81
+ .slice(0, limit);
82
+ }
83
+
84
+ export function describeHarnessPanel(context: CommandContext, args: AgentHarnessPanelArgs): Record<string, unknown> | null {
85
+ const manager = panelManager(context);
86
+ if (!manager) return null;
87
+ const panelId = readString(args.panelId || args.query);
88
+ if (!panelId) return null;
89
+ const registration = manager.getRegisteredTypes().find((panel) => (
90
+ panel.id === panelId
91
+ || panel.name.toLowerCase() === panelId.toLowerCase()
92
+ ));
93
+ return registration ? describePanelRegistration(context, registration) : null;
94
+ }
95
+
96
+ export function openHarnessPanel(context: CommandContext, args: AgentHarnessPanelArgs): Record<string, unknown> {
97
+ const panel = describeHarnessPanel(context, args);
98
+ if (!panel) {
99
+ return {
100
+ status: 'unknown_panel',
101
+ panelId: readString(args.panelId || args.query) || '<missing>',
102
+ availablePanels: listHarnessPanels(context, { limit: 50 }).map((entry) => entry.id),
103
+ };
104
+ }
105
+ const requestedPane = readString(args.pane);
106
+ const pane = requestedPane === 'bottom' || requestedPane === 'top' ? requestedPane : undefined;
107
+ if (!context.showPanel) {
108
+ return {
109
+ status: 'route_unavailable',
110
+ panel,
111
+ note: 'The current runtime did not provide showPanel. Use the returned workspaceRoute from the TUI.',
112
+ };
113
+ }
114
+ context.showPanel(String(panel.id), pane);
115
+ return {
116
+ status: 'routed',
117
+ panel,
118
+ pane: pane ?? 'default',
119
+ note: 'Panel routing was handed to the current Agent shell bridge.',
120
+ };
121
+ }
@@ -17,11 +17,13 @@ import type {
17
17
  AgentWorkspaceRuntimeSnapshot,
18
18
  } from '../input/agent-workspace-types.ts';
19
19
  import { parseSlashCommand } from '../input/slash-command-parser.ts';
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';
22
+ import { describeHarnessPanel, listHarnessPanels, openHarnessPanel, totalHarnessPanels } from './agent-harness-panel-metadata.ts';
20
23
  import { describeLocalWorkspaceModelExecution, runLocalWorkspaceAction } from './agent-harness-local-operations.ts';
24
+ import { listHarnessModelTools } from './agent-harness-model-tool-catalog.ts';
21
25
  import {
22
- blockedConnectedHostCapabilities,
23
- connectedHostCapabilityMap,
24
- connectedHostRouteFamilies,
26
+ connectedHostSummary,
25
27
  describeCommandPolicy,
26
28
  settingsPolicySummary,
27
29
  } from './agent-harness-metadata.ts';
@@ -32,35 +34,31 @@ import {
32
34
  resetHarnessSetting,
33
35
  setHarnessSetting,
34
36
  } from '../agent/harness-control.ts';
35
- import { resolveAgentConnectedHostConnection } from '../agent/routine-schedule-promotion.ts';
36
-
37
- type AgentHarnessMode =
38
- | 'summary'
39
- | 'commands'
40
- | 'command'
41
- | 'run_command'
42
- | 'settings'
43
- | 'get_setting'
44
- | 'set_setting'
45
- | 'reset_setting'
46
- | 'workspace'
47
- | 'workspace_categories'
48
- | 'workspace_actions'
49
- | 'workspace_action'
50
- | 'run_workspace_action'
51
- | 'tools'
52
- | 'connected_host';
37
+
38
+ const MODES = [
39
+ 'summary', 'cli_commands', 'cli_command', 'panels', 'panel', 'open_panel',
40
+ 'shortcuts', 'keybindings', 'keybinding', 'set_keybinding', 'reset_keybinding',
41
+ 'commands', 'command', 'run_command', 'settings', 'get_setting', 'set_setting',
42
+ 'reset_setting', 'workspace', 'workspace_categories', 'workspace_actions',
43
+ 'workspace_action', 'run_workspace_action', 'tools', 'connected_host',
44
+ ] as const;
45
+
46
+ type AgentHarnessMode = typeof MODES[number];
53
47
 
54
48
  interface AgentHarnessToolArgs {
55
49
  readonly mode?: unknown;
56
50
  readonly query?: unknown;
57
51
  readonly command?: unknown;
52
+ readonly cliCommand?: unknown;
58
53
  readonly commandName?: unknown;
59
54
  readonly args?: unknown;
60
55
  readonly categoryId?: unknown;
56
+ readonly panelId?: unknown;
61
57
  readonly actionId?: unknown;
62
58
  readonly recordId?: unknown;
63
59
  readonly fields?: unknown;
60
+ readonly combo?: unknown;
61
+ readonly combos?: unknown;
64
62
  readonly key?: unknown;
65
63
  readonly value?: unknown;
66
64
  readonly category?: unknown;
@@ -68,6 +66,7 @@ interface AgentHarnessToolArgs {
68
66
  readonly includeHidden?: unknown;
69
67
  readonly includeParameters?: unknown;
70
68
  readonly limit?: unknown;
69
+ readonly pane?: unknown;
71
70
  readonly confirm?: unknown;
72
71
  readonly explicitUserRequest?: unknown;
73
72
  }
@@ -83,24 +82,6 @@ interface WorkspaceEditorContext {
83
82
  readonly selectedRoutine: AgentWorkspaceLocalLibraryItem | null;
84
83
  }
85
84
 
86
- const MODES: readonly AgentHarnessMode[] = [
87
- 'summary',
88
- 'commands',
89
- 'command',
90
- 'run_command',
91
- 'settings',
92
- 'get_setting',
93
- 'set_setting',
94
- 'reset_setting',
95
- 'workspace',
96
- 'workspace_categories',
97
- 'workspace_actions',
98
- 'workspace_action',
99
- 'run_workspace_action',
100
- 'tools',
101
- 'connected_host',
102
- ];
103
-
104
85
  function isMode(value: unknown): value is AgentHarnessMode {
105
86
  return typeof value === 'string' && MODES.includes(value as AgentHarnessMode);
106
87
  }
@@ -132,9 +113,14 @@ function output(value: unknown): { readonly success: true; readonly output: stri
132
113
  };
133
114
  }
134
115
 
135
- function error(message: string): { readonly success: false; readonly error: string } {
136
- return { success: false, error: message };
137
- }
116
+ function error(message: string): { readonly success: false; readonly error: string } { return { success: false, error: message }; }
117
+
118
+ const KEY_COMBO_PARAMETER_SCHEMA = {
119
+ type: 'object',
120
+ properties: { key: { type: 'string' }, ctrl: { type: 'boolean' }, shift: { type: 'boolean' }, alt: { type: 'boolean' } },
121
+ required: ['key'],
122
+ additionalProperties: false,
123
+ } as const;
138
124
 
139
125
  function commandMatches(command: SlashCommand, query: string): boolean {
140
126
  if (!query) return true;
@@ -307,28 +293,6 @@ function listCommands(commandRegistry: CommandRegistry, args: AgentHarnessToolAr
307
293
  .map(describeCommand);
308
294
  }
309
295
 
310
- function listTools(toolRegistry: ToolRegistry, args: AgentHarnessToolArgs): readonly Record<string, unknown>[] {
311
- const query = readString(args.query).toLowerCase();
312
- const includeParameters = args.includeParameters === true;
313
- const limit = readLimit(args.limit, 200);
314
- return toolRegistry.getToolDefinitions()
315
- .filter((tool) => {
316
- if (!query) return true;
317
- return [tool.name, tool.description, ...(tool.sideEffects ?? [])].join('\n').toLowerCase().includes(query);
318
- })
319
- .sort((a, b) => a.name.localeCompare(b.name))
320
- .slice(0, limit)
321
- .map((tool) => ({
322
- name: tool.name,
323
- description: tool.description,
324
- sideEffects: tool.sideEffects ?? [],
325
- concurrency: tool.concurrency ?? 'parallel',
326
- supportsProgress: tool.supportsProgress ?? false,
327
- supportsStreamingOutput: tool.supportsStreamingOutput ?? false,
328
- ...(includeParameters ? { parameters: tool.parameters } : {}),
329
- }));
330
- }
331
-
332
296
  function requireConfirmedAction(args: AgentHarnessToolArgs, action: string): string | null {
333
297
  const explicitUserRequest = readString(args.explicitUserRequest);
334
298
  if (!explicitUserRequest) return `${action} requires explicitUserRequest with the user's exact request or a short faithful summary.`;
@@ -544,30 +508,14 @@ async function runWorkspaceAction(
544
508
  });
545
509
  }
546
510
 
547
- function connectedHostSummary(context: CommandContext, toolRegistry: ToolRegistry): Record<string, unknown> {
548
- const shellPaths = context.workspace.shellPaths;
549
- const homeDirectory = shellPaths?.homeDirectory ?? context.platform.configManager.getHomeDirectory() ?? '';
550
- const connection = resolveAgentConnectedHostConnection(context.platform.configManager, homeDirectory);
551
- return {
552
- baseUrl: connection.baseUrl,
553
- operatorToken: connection.token ? 'configured' : 'missing',
554
- tokenPath: connection.tokenPath,
555
- ownership: 'external-connected-host',
556
- lifecycle: 'GoodVibes Agent can use public connected-host operator routes, but does not start, stop, restart, install, expose, or mutate the host listener.',
557
- routeFamilies: connectedHostRouteFamilies(),
558
- capabilities: connectedHostCapabilityMap(toolRegistry),
559
- blockedCapabilities: blockedConnectedHostCapabilities(),
560
- };
561
- }
562
-
563
511
  export function createAgentHarnessTool(deps: AgentHarnessToolDeps): Tool {
564
512
  return {
565
513
  definition: {
566
514
  name: 'agent_harness',
567
515
  description: [
568
516
  'Discover and operate the GoodVibes Agent harness from the main conversation.',
569
- 'Use this tool to inspect Agent workspace actions, 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.',
570
- 'Discovery modes are read-only. Setting writes, resets, slash command invocation, and workspace action invocation require confirm:true plus explicitUserRequest.',
517
+ 'Use this tool to inspect Agent workspace actions, built-in panels, top-level CLI mirrors, 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.',
518
+ 'Discovery modes are read-only. Setting/keybinding writes, resets, slash command invocation, and workspace action invocation require confirm:true plus explicitUserRequest.',
571
519
  '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.',
572
520
  ].join(' '),
573
521
  parameters: {
@@ -584,11 +532,15 @@ export function createAgentHarnessTool(deps: AgentHarnessToolDeps): Tool {
584
532
  },
585
533
  command: {
586
534
  type: 'string',
587
- description: 'Full slash command string for mode run_command, for example "/settings get provider.model".',
535
+ 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".',
536
+ },
537
+ cliCommand: {
538
+ type: 'string',
539
+ 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.',
588
540
  },
589
541
  commandName: {
590
542
  type: 'string',
591
- description: 'Slash command root without the leading slash for mode command or run_command.',
543
+ description: 'Slash command root without the leading slash for mode command or run_command, or a top-level CLI command token for cli_command.',
592
544
  },
593
545
  args: {
594
546
  type: 'array',
@@ -599,15 +551,28 @@ export function createAgentHarnessTool(deps: AgentHarnessToolDeps): Tool {
599
551
  type: 'string',
600
552
  description: 'Agent workspace category id for workspace action filtering.',
601
553
  },
554
+ panelId: {
555
+ type: 'string',
556
+ description: 'Built-in panel id for panel or open_panel modes.',
557
+ },
602
558
  actionId: {
603
559
  type: 'string',
604
- description: 'Agent workspace action id for workspace_action or run_workspace_action.',
560
+ description: 'Agent workspace action id for workspace_action or run_workspace_action, or keybinding action id for keybinding/set_keybinding/reset_keybinding.',
605
561
  },
606
562
  fields: {
607
563
  type: 'object',
608
564
  additionalProperties: { type: 'string' },
609
565
  description: 'Field values for run_workspace_action when the workspace action opens an editor form.',
610
566
  },
567
+ combo: {
568
+ ...KEY_COMBO_PARAMETER_SCHEMA,
569
+ description: 'Single key combo for set_keybinding, for example { "key": "g", "ctrl": true }.',
570
+ },
571
+ combos: {
572
+ type: 'array',
573
+ items: KEY_COMBO_PARAMETER_SCHEMA,
574
+ description: 'Multiple key combos for set_keybinding.',
575
+ },
611
576
  recordId: {
612
577
  type: 'string',
613
578
  description: 'Selected Agent-local record id for selection-based local workspace operations.',
@@ -644,9 +609,14 @@ export function createAgentHarnessTool(deps: AgentHarnessToolDeps): Tool {
644
609
  type: 'number',
645
610
  description: 'Maximum catalog entries to return.',
646
611
  },
612
+ pane: {
613
+ type: 'string',
614
+ enum: ['top', 'bottom'],
615
+ description: 'Preferred panel pane for open_panel when the current shell supports panel routing.',
616
+ },
647
617
  confirm: {
648
618
  type: 'boolean',
649
- description: 'Required true for set_setting, reset_setting, run_command, and mutating run_workspace_action calls after an explicit user request.',
619
+ description: 'Required true for set_setting, reset_setting, run_command, open_panel, and mutating run_workspace_action calls after an explicit user request.',
650
620
  },
651
621
  explicitUserRequest: {
652
622
  type: 'string',
@@ -665,12 +635,20 @@ export function createAgentHarnessTool(deps: AgentHarnessToolDeps): Tool {
665
635
  try {
666
636
  if (args.mode === 'summary') {
667
637
  return output({
638
+ cliCommands: totalHarnessCliCommands(),
639
+ blockedCliCommandTokens: blockedHarnessCliCommandTokens(),
640
+ panels: totalHarnessPanels(deps.commandContext),
641
+ shortcuts: totalHarnessShortcuts(deps.commandContext),
642
+ keybindings: totalHarnessKeybindings(deps.commandContext),
668
643
  commands: deps.commandRegistry.list().length,
669
644
  settings: deps.commandContext.platform.configManager.getSchema().length,
670
645
  workspaceCategories: AGENT_WORKSPACE_CATEGORIES.length,
671
646
  workspaceActions: allWorkspaceActions().length,
672
647
  tools: deps.toolRegistry.getToolDefinitions().length,
673
648
  modelAccess: {
649
+ 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.',
650
+ 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.',
651
+ 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.',
674
652
  slashCommands: 'Use mode:"commands" and mode:"command" to inspect; use mode:"run_command" with confirm:true plus explicitUserRequest to execute.',
675
653
  workspace: 'Use mode:"workspace_actions" to list and mode:"workspace_action" for editor schemas; set includeParameters:true on workspace_actions to inline editor schemas.',
676
654
  settings: 'Use mode:"settings", mode:"get_setting", mode:"set_setting", and mode:"reset_setting".',
@@ -681,6 +659,53 @@ export function createAgentHarnessTool(deps: AgentHarnessToolDeps): Tool {
681
659
  connectedHost: connectedHostSummary(deps.commandContext, deps.toolRegistry),
682
660
  });
683
661
  }
662
+ if (args.mode === 'cli_commands') {
663
+ const commands = listHarnessCliCommands(args);
664
+ return output({
665
+ commands,
666
+ returned: commands.length,
667
+ total: totalHarnessCliCommands(),
668
+ blockedTokens: blockedHarnessCliCommandTokens(),
669
+ policy: 'CLI modes are read-only discovery. Use first-class model tools, workspace actions, settings modes, or confirmed slash-command mirrors for in-process operation.',
670
+ });
671
+ }
672
+ if (args.mode === 'cli_command') {
673
+ return output(describeHarnessCliCommand(args));
674
+ }
675
+ if (args.mode === 'panels') {
676
+ const panels = listHarnessPanels(deps.commandContext, args);
677
+ return output({
678
+ panels,
679
+ returned: panels.length,
680
+ total: totalHarnessPanels(deps.commandContext),
681
+ policy: 'Panel modes expose Agent/TUI operator view catalog and open state. open_panel is confirmation-gated and routes through the current Agent shell bridge.',
682
+ });
683
+ }
684
+ if (args.mode === 'panel') {
685
+ const panel = describeHarnessPanel(deps.commandContext, args);
686
+ return panel ? output(panel) : error(`Unknown panel ${readString(args.panelId || args.query) || '<missing>'}.`);
687
+ }
688
+ if (args.mode === 'open_panel') {
689
+ const confirmationError = requireConfirmedAction(args, 'Panel routing');
690
+ if (confirmationError) return error(confirmationError);
691
+ return output(openHarnessPanel(deps.commandContext, args));
692
+ }
693
+ if (args.mode === 'shortcuts') return output(listHarnessShortcuts(deps.commandContext, args));
694
+ if (args.mode === 'keybindings') return output(listHarnessKeybindings(deps.commandContext, args));
695
+ if (args.mode === 'keybinding') {
696
+ const binding = describeHarnessKeybinding(deps.commandContext, args);
697
+ return binding ? output(binding) : error(`Unknown keybinding action ${readString(args.actionId || args.key || args.query) || '<missing>'}.`);
698
+ }
699
+ if (args.mode === 'set_keybinding') {
700
+ const confirmationError = requireConfirmedAction(args, 'Keybinding mutation');
701
+ if (confirmationError) return error(confirmationError);
702
+ return output(setHarnessKeybinding(deps.commandContext, args));
703
+ }
704
+ if (args.mode === 'reset_keybinding') {
705
+ const confirmationError = requireConfirmedAction(args, 'Keybinding reset');
706
+ if (confirmationError) return error(confirmationError);
707
+ return output(resetHarnessKeybinding(deps.commandContext, args));
708
+ }
684
709
  if (args.mode === 'commands') {
685
710
  const commands = listCommands(deps.commandRegistry, args);
686
711
  return output({ commands, returned: commands.length, total: deps.commandRegistry.list().length });
@@ -749,7 +774,7 @@ export function createAgentHarnessTool(deps: AgentHarnessToolDeps): Tool {
749
774
  }
750
775
  if (args.mode === 'run_workspace_action') return runWorkspaceAction(deps, args);
751
776
  if (args.mode === 'tools') {
752
- const tools = listTools(deps.toolRegistry, args);
777
+ const tools = listHarnessModelTools(deps.toolRegistry, args);
753
778
  return output({ tools, returned: tools.length, total: deps.toolRegistry.getToolDefinitions().length });
754
779
  }
755
780
  if (args.mode === 'connected_host') return output(connectedHostSummary(deps.commandContext, deps.toolRegistry));
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.2';
9
+ let _version = '1.0.4';
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 {