@pellux/goodvibes-agent 1.0.35 → 1.0.37

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.
Files changed (89) hide show
  1. package/CHANGELOG.md +20 -9
  2. package/README.md +8 -7
  3. package/dist/package/{ast-grep-napi.linux-x64-gnu-swtppvy9.node → ast-grep-napi.linux-x64-gnu-mkk8xwww.node} +0 -0
  4. package/dist/package/{ast-grep-napi.linux-x64-musl-ttfcdtap.node → ast-grep-napi.linux-x64-musl-ryqtgdv6.node} +0 -0
  5. package/dist/package/main.js +51495 -49329
  6. package/docs/README.md +6 -5
  7. package/docs/channels-remote-and-api.md +3 -3
  8. package/docs/connected-host.md +3 -3
  9. package/docs/getting-started.md +12 -7
  10. package/docs/knowledge-artifacts-and-multimodal.md +9 -4
  11. package/docs/project-planning.md +3 -3
  12. package/docs/providers-and-routing.md +2 -2
  13. package/docs/release-and-publishing.md +8 -8
  14. package/docs/tools-and-commands.md +31 -18
  15. package/docs/voice-and-live-tts.md +1 -1
  16. package/package.json +1 -1
  17. package/release/live-verification/live-verification.json +3 -3
  18. package/release/live-verification/live-verification.md +2 -4
  19. package/release/release-notes.md +4 -4
  20. package/release/release-readiness.json +46 -46
  21. package/src/agent/harness-control.ts +35 -9
  22. package/src/agent/reminder-schedule-format.ts +2 -5
  23. package/src/agent/reminder-schedule.ts +4 -11
  24. package/src/agent/routine-schedule-format.ts +3 -6
  25. package/src/agent/routine-schedule-promotion.ts +5 -14
  26. package/src/agent/routine-schedule-receipts.ts +4 -9
  27. package/src/cli/agent-knowledge-command.ts +6 -11
  28. package/src/cli/agent-knowledge-format.ts +4 -9
  29. package/src/cli/agent-knowledge-runtime.ts +7 -14
  30. package/src/cli/external-runtime.ts +3 -46
  31. package/src/cli/help.ts +2 -2
  32. package/src/cli/status.ts +6 -7
  33. package/src/input/agent-workspace-categories.ts +23 -23
  34. package/src/input/agent-workspace-channels.ts +3 -3
  35. package/src/input/agent-workspace-operations-command-editors.ts +1 -1
  36. package/src/input/command-registry.ts +1 -1
  37. package/src/input/commands/compat-runtime.ts +1 -1
  38. package/src/input/commands/planning-runtime.ts +1 -1
  39. package/src/input/commands/runtime-services.ts +1 -1
  40. package/src/input/mcp-workspace.ts +1 -1
  41. package/src/input/submission-router.ts +1 -1
  42. package/src/panels/builtin/shared.ts +1 -1
  43. package/src/panels/project-planning-panel.ts +4 -4
  44. package/src/panels/qr-panel.ts +2 -2
  45. package/src/renderer/conversation-overlays.ts +1 -0
  46. package/src/renderer/onboarding/onboarding-wizard.ts +1 -2
  47. package/src/runtime/bootstrap-command-parts.ts +1 -1
  48. package/src/runtime/bootstrap-core.ts +1 -1
  49. package/src/runtime/bootstrap-hook-bridge.ts +1 -1
  50. package/src/runtime/bootstrap-shell.ts +1 -1
  51. package/src/runtime/bootstrap.ts +5 -4
  52. package/src/runtime/context.ts +2 -2
  53. package/src/runtime/index.ts +88 -5
  54. package/src/runtime/store/state.ts +1 -1
  55. package/src/tools/agent-channel-send-tool.ts +1 -1
  56. package/src/tools/agent-harness-channel-metadata.ts +36 -25
  57. package/src/tools/agent-harness-cli-metadata.ts +19 -1
  58. package/src/tools/agent-harness-command-catalog.ts +15 -4
  59. package/src/tools/agent-harness-connected-host-status.ts +7 -6
  60. package/src/tools/agent-harness-delegation-posture.ts +15 -4
  61. package/src/tools/agent-harness-keybinding-metadata.ts +67 -16
  62. package/src/tools/agent-harness-mcp-metadata.ts +11 -3
  63. package/src/tools/agent-harness-media-posture.ts +25 -14
  64. package/src/tools/agent-harness-metadata.ts +97 -6
  65. package/src/tools/agent-harness-mode-catalog.ts +81 -28
  66. package/src/tools/agent-harness-model-routing.ts +18 -3
  67. package/src/tools/agent-harness-model-tool-catalog.ts +85 -7
  68. package/src/tools/agent-harness-notification-metadata.ts +30 -19
  69. package/src/tools/agent-harness-operator-methods.ts +13 -8
  70. package/src/tools/agent-harness-pairing-posture.ts +17 -5
  71. package/src/tools/agent-harness-panel-metadata.ts +10 -4
  72. package/src/tools/agent-harness-provider-account-metadata.ts +46 -35
  73. package/src/tools/agent-harness-release-evidence.ts +31 -3
  74. package/src/tools/agent-harness-release-readiness.ts +30 -0
  75. package/src/tools/agent-harness-security-posture.ts +18 -4
  76. package/src/tools/agent-harness-service-posture.ts +8 -1
  77. package/src/tools/agent-harness-session-metadata.ts +24 -8
  78. package/src/tools/agent-harness-setup-posture.ts +32 -21
  79. package/src/tools/agent-harness-text.ts +6 -0
  80. package/src/tools/agent-harness-tool.ts +26 -34
  81. package/src/tools/agent-harness-ui-surface-metadata.ts +84 -84
  82. package/src/tools/agent-harness-workspace-actions.ts +64 -3
  83. package/src/tools/agent-knowledge-ingest-tool.ts +1 -1
  84. package/src/tools/agent-knowledge-tool.ts +1 -1
  85. package/src/tools/agent-media-generate-tool.ts +2 -2
  86. package/src/tools/agent-notify-tool.ts +1 -1
  87. package/src/tools/agent-reminder-schedule-tool.ts +1 -1
  88. package/src/tools/tool-definition-compaction.ts +35 -8
  89. package/src/version.ts +2 -8
@@ -222,6 +222,8 @@ function bindingCandidate(manager: KeybindingsManager | null, entry: KeybindingE
222
222
  action: entry.action,
223
223
  description: entry.description,
224
224
  labels: entry.combos.map((combo) => formatCombo(manager, combo)),
225
+ modelRoute: keybindingModelRoute(entry.action),
226
+ inspectRoute: `agent_harness mode:"keybinding" actionId:"${entry.action}"`,
225
227
  };
226
228
  }
227
229
 
@@ -246,9 +248,30 @@ function resolveHarnessKeybinding(context: CommandContext, args: HarnessKeybindi
246
248
  return null;
247
249
  }
248
250
 
251
+ function keybindingModelRoute(action: KeyAction): string {
252
+ const route = keybindingOperationRoute(action);
253
+ if (route.preferredMode === 'run_keybinding') return `agent_harness mode:"run_keybinding" actionId:"${action}"`;
254
+ if (route.preferredMode === 'open_ui_surface' && route.surfaceId) return `agent_harness mode:"open_ui_surface" surfaceId:"${route.surfaceId}"`;
255
+ if (route.preferredMode === 'open_ui_surface') return 'agent_harness mode:"open_ui_surface"';
256
+ if (route.preferredMode === 'run_command' && route.command) return `agent_harness mode:"run_command" command:"${route.command}"`;
257
+ if (route.preferredMode === 'run_command') return 'agent_harness mode:"run_command"';
258
+ return 'direct-user-interaction';
259
+ }
260
+
261
+ function keybindingModelAccess(action: KeyAction, operation: KeybindingOperationRoute): Record<string, unknown> {
262
+ return {
263
+ inspect: `agent_harness mode:"keybinding" actionId:"${action}"`,
264
+ run: operation.supported ? `agent_harness mode:"run_keybinding" actionId:"${action}" confirm:true explicitUserRequest:"..."` : 'not exposed',
265
+ set: `agent_harness mode:"set_keybinding" actionId:"${action}" combo:{...} confirm:true explicitUserRequest:"..."`,
266
+ reset: `agent_harness mode:"reset_keybinding" actionId:"${action}" confirm:true explicitUserRequest:"..."`,
267
+ preferred: keybindingModelRoute(action),
268
+ };
269
+ }
270
+
249
271
  function describeBinding(manager: KeybindingsManager | null, action: KeyAction, combos: KeyCombo[], lookup?: KeybindingLookup): Record<string, unknown> {
250
272
  const defaults = DEFAULT_KEYBINDINGS[action];
251
273
  const customized = !combosEqual(combos, defaults);
274
+ const modelOperation = keybindingOperationRoute(action);
252
275
  return {
253
276
  action,
254
277
  description: ACTION_DESCRIPTIONS[action],
@@ -258,7 +281,10 @@ function describeBinding(manager: KeybindingsManager | null, action: KeyAction,
258
281
  defaultBindings: defaults.map((combo) => describeCombo(manager, combo)),
259
282
  customized,
260
283
  source: manager ? (customized ? 'custom' : 'default') : 'default-fallback',
261
- modelOperation: keybindingOperationRoute(action),
284
+ modelRoute: keybindingModelRoute(action),
285
+ inspectRoute: `agent_harness mode:"keybinding" actionId:"${action}"`,
286
+ modelAccess: keybindingModelAccess(action, modelOperation),
287
+ modelOperation,
262
288
  };
263
289
  }
264
290
 
@@ -270,7 +296,7 @@ function keybindingOperationRoute(action: KeyAction): KeybindingOperationRoute {
270
296
  effect: 'shell-action',
271
297
  preferredMode: 'run_keybinding',
272
298
  confirmation: 'agent_harness mode:"run_keybinding" requires confirm:true and explicitUserRequest.',
273
- note: 'Routes the cancel-generation side of this shortcut through commandContext.cancelGeneration. Prompt clearing and double-press exit remain direct user interaction.',
299
+ note: 'Runs the available cancel-generation route. Prompt clearing and double-press exit remain direct user interaction.',
274
300
  };
275
301
  case 'screen-clear':
276
302
  return {
@@ -278,7 +304,7 @@ function keybindingOperationRoute(action: KeyAction): KeybindingOperationRoute {
278
304
  effect: 'shell-action',
279
305
  preferredMode: 'run_keybinding',
280
306
  confirmation: 'agent_harness mode:"run_keybinding" requires confirm:true and explicitUserRequest.',
281
- note: 'Routes through commandContext.clearScreen.',
307
+ note: 'Runs the available clear-screen route.',
282
308
  };
283
309
  case 'panel-picker':
284
310
  return {
@@ -295,7 +321,7 @@ function keybindingOperationRoute(action: KeyAction): KeybindingOperationRoute {
295
321
  effect: 'visible-ui-navigation',
296
322
  preferredMode: 'run_keybinding',
297
323
  confirmation: 'agent_harness mode:"run_keybinding" requires confirm:true and explicitUserRequest.',
298
- note: 'Dismisses the active Agent workspace first. If no Agent workspace is active, closes the active legacy panel if present, then focuses the prompt when the shell bridge exposes focusPrompt.',
324
+ note: 'Dismisses Agent workspace first. If it is not active, closes the active panel and focuses the prompt when focus is available.',
299
325
  };
300
326
  case 'panel-close-all':
301
327
  return {
@@ -303,7 +329,7 @@ function keybindingOperationRoute(action: KeyAction): KeybindingOperationRoute {
303
329
  effect: 'visible-ui-navigation',
304
330
  preferredMode: 'run_keybinding',
305
331
  confirmation: 'agent_harness mode:"run_keybinding" requires confirm:true and explicitUserRequest.',
306
- note: 'Dismisses the active Agent workspace first. If no Agent workspace is active, closes all legacy panels if present, hides the panel manager, then focuses the prompt when the shell bridge exposes focusPrompt.',
332
+ note: 'Dismisses Agent workspace first. If it is not active, closes open panels and focuses the prompt when focus is available.',
307
333
  };
308
334
  case 'history-search':
309
335
  return {
@@ -330,7 +356,7 @@ function keybindingOperationRoute(action: KeyAction): KeybindingOperationRoute {
330
356
  preferredMode: 'run_keybinding',
331
357
  command: '/paste',
332
358
  confirmation: 'agent_harness mode:"run_keybinding" requires confirm:true and explicitUserRequest.',
333
- note: 'Routes through commandContext.pasteFromClipboard and reports whether text, image, or nothing was pasted.',
359
+ note: 'Runs the paste route and reports whether text, image, or nothing was pasted.',
334
360
  };
335
361
  case 'block-copy':
336
362
  case 'bookmark':
@@ -408,13 +434,38 @@ export function totalHarnessShortcuts(context: CommandContext): number {
408
434
  return totalHarnessKeybindings(context) + FIXED_SHORTCUTS.length;
409
435
  }
410
436
 
437
+ function fixedShortcutModelRoute(shortcut: Record<string, string>): string {
438
+ if (shortcut.key.includes('/shortcuts')) return 'agent_harness mode:"shortcuts"';
439
+ if (shortcut.key.includes('/keybindings')) return 'agent_harness mode:"keybindings"';
440
+ if (shortcut.key.includes('?') || shortcut.key.includes('F1')) return 'agent_harness mode:"open_ui_surface" surfaceId:"help-overlay"';
441
+ if (shortcut.key.includes('F2')) return 'agent_harness mode:"open_ui_surface" surfaceId:"process-monitor"';
442
+ if (shortcut.key.includes('Esc')) return 'direct-user-interaction';
443
+ if (shortcut.key.includes('Tab')) return 'agent_harness mode:"commands"';
444
+ if (shortcut.key.includes('Enter')) return 'main conversation';
445
+ return 'direct-user-interaction';
446
+ }
447
+
448
+ function describeFixedShortcut(shortcut: Record<string, string>): Record<string, unknown> {
449
+ return {
450
+ ...shortcut,
451
+ source: 'fixed',
452
+ userEditable: false,
453
+ modelRoute: fixedShortcutModelRoute(shortcut),
454
+ modelAccess: {
455
+ inspectShortcuts: 'agent_harness mode:"shortcuts"',
456
+ inspectKeybindings: 'agent_harness mode:"keybindings"',
457
+ preferred: fixedShortcutModelRoute(shortcut),
458
+ },
459
+ };
460
+ }
461
+
411
462
  export function listHarnessShortcuts(context: CommandContext, args: HarnessKeybindingArgs): Record<string, unknown> {
412
463
  const keybindings = listHarnessKeybindings(context, args);
413
464
  const query = readString(args.query).toLowerCase();
414
465
  const fixed = FIXED_SHORTCUTS
415
466
  .filter((shortcut) => !query || `${shortcut.key}\n${shortcut.description}`.toLowerCase().includes(query))
416
467
  .slice(0, readLimit(args.limit, 200))
417
- .map((shortcut) => ({ ...shortcut, source: 'fixed', userEditable: false }));
468
+ .map(describeFixedShortcut);
418
469
  const degraded = keybindings.status === 'degraded';
419
470
  return {
420
471
  status: degraded ? 'degraded' : 'available',
@@ -496,11 +547,11 @@ export function runHarnessKeybinding(context: CommandContext, args: HarnessKeybi
496
547
 
497
548
  switch (resolved.action) {
498
549
  case 'clear-cancel':
499
- if (!context.cancelGeneration) return runUnavailable(resolved.action, route, 'commandContext.cancelGeneration is unavailable.');
550
+ if (!context.cancelGeneration) return runUnavailable(resolved.action, route, 'Cancel-generation route is unavailable.');
500
551
  context.cancelGeneration();
501
552
  return { status: 'executed', action: resolved.action, effect: 'cancel-generation', keybinding: descriptor };
502
553
  case 'screen-clear':
503
- if (!context.clearScreen) return runUnavailable(resolved.action, route, 'commandContext.clearScreen is unavailable.');
554
+ if (!context.clearScreen) return runUnavailable(resolved.action, route, 'Clear-screen route is unavailable.');
504
555
  context.clearScreen();
505
556
  return { status: 'executed', action: resolved.action, effect: 'screen-clear', keybinding: descriptor };
506
557
  case 'panel-picker':
@@ -512,7 +563,7 @@ export function runHarnessKeybinding(context: CommandContext, args: HarnessKeybi
512
563
  context.openAgentWorkspace('home');
513
564
  return { status: 'executed', action: resolved.action, effect: 'agent-workspace-opened', route: 'openAgentWorkspace', categoryId: 'home', keybinding: descriptor };
514
565
  }
515
- return runUnavailable(resolved.action, route, 'No panel picker or Agent workspace opener is available.');
566
+ return runUnavailable(resolved.action, route, 'No panel picker or Agent workspace route is available.');
516
567
  case 'panel-close': {
517
568
  const dismissedAgentWorkspace = context.dismissAgentWorkspace?.() ?? false;
518
569
  if (dismissedAgentWorkspace) {
@@ -526,7 +577,7 @@ export function runHarnessKeybinding(context: CommandContext, args: HarnessKeybi
526
577
  }
527
578
  const active = context.workspace.panelManager?.getActivePanel() ?? null;
528
579
  if (active) context.workspace.panelManager?.close(active.id);
529
- if (!active && !context.focusPrompt) return runUnavailable(resolved.action, route, 'No active Agent workspace, active legacy panel, or prompt focus route is available.');
580
+ if (!active && !context.focusPrompt) return runUnavailable(resolved.action, route, 'No active Agent workspace, active panel, or prompt focus route is available.');
530
581
  if (context.focusPrompt) context.focusPrompt();
531
582
  context.renderRequest();
532
583
  return {
@@ -552,7 +603,7 @@ export function runHarnessKeybinding(context: CommandContext, args: HarnessKeybi
552
603
  const openPanels = managerPanel?.getAllOpen() ?? [];
553
604
  for (const panel of openPanels) managerPanel?.close(panel.id);
554
605
  managerPanel?.hide();
555
- if (openPanels.length === 0 && !context.focusPrompt) return runUnavailable(resolved.action, route, 'No active Agent workspace, open legacy panels, or prompt focus route is available.');
606
+ if (openPanels.length === 0 && !context.focusPrompt) return runUnavailable(resolved.action, route, 'No active Agent workspace, open panels, or prompt focus route is available.');
556
607
  if (context.focusPrompt) context.focusPrompt();
557
608
  context.renderRequest();
558
609
  return {
@@ -564,26 +615,26 @@ export function runHarnessKeybinding(context: CommandContext, args: HarnessKeybi
564
615
  };
565
616
  }
566
617
  case 'history-search': {
567
- if (!context.openPromptHistorySearch) return runUnavailable(resolved.action, route, 'commandContext.openPromptHistorySearch is unavailable.');
618
+ if (!context.openPromptHistorySearch) return runUnavailable(resolved.action, route, 'Prompt history search route is unavailable.');
568
619
  const query = readString(args.value);
569
620
  context.openPromptHistorySearch(query || undefined);
570
621
  return { status: 'executed', action: resolved.action, effect: 'prompt-history-search-opened', query, keybinding: descriptor };
571
622
  }
572
623
  case 'search': {
573
- if (!context.openConversationSearch) return runUnavailable(resolved.action, route, 'commandContext.openConversationSearch is unavailable.');
624
+ if (!context.openConversationSearch) return runUnavailable(resolved.action, route, 'Conversation search route is unavailable.');
574
625
  const query = readString(args.value);
575
626
  context.openConversationSearch(query || undefined);
576
627
  return { status: 'executed', action: resolved.action, effect: 'conversation-search-opened', query, keybinding: descriptor };
577
628
  }
578
629
  case 'paste': {
579
- if (!context.pasteFromClipboard) return runUnavailable(resolved.action, route, 'commandContext.pasteFromClipboard is unavailable.');
630
+ if (!context.pasteFromClipboard) return runUnavailable(resolved.action, route, 'Paste route is unavailable.');
580
631
  const pasted = context.pasteFromClipboard();
581
632
  return { status: 'executed', action: resolved.action, effect: 'paste-from-clipboard', pasted, keybinding: descriptor };
582
633
  }
583
634
  case 'block-copy':
584
635
  case 'bookmark':
585
636
  case 'block-save': {
586
- if (!context.openBlockActions) return runUnavailable(resolved.action, route, 'commandContext.openBlockActions is unavailable.');
637
+ if (!context.openBlockActions) return runUnavailable(resolved.action, route, 'Block action surface route is unavailable.');
587
638
  const opened = context.openBlockActions();
588
639
  return opened
589
640
  ? {
@@ -1,4 +1,5 @@
1
1
  import type { CommandContext } from '../input/command-registry.ts';
2
+ import { previewHarnessText } from './agent-harness-text.ts';
2
3
 
3
4
  export interface AgentHarnessMcpArgs {
4
5
  readonly mcpServerId?: unknown;
@@ -76,9 +77,14 @@ function describeCandidate(server: McpServerRecord): Record<string, unknown> {
76
77
  trustMode: server.trustMode,
77
78
  role: server.role,
78
79
  schemaFreshness: server.schemaFreshness,
80
+ modelRoute: mcpServerModelRoute(),
79
81
  };
80
82
  }
81
83
 
84
+ function mcpServerModelRoute(): string {
85
+ return 'agent_harness mode:"mcp_server" or mode:"run_command"';
86
+ }
87
+
82
88
  function toolsByServer(tools: readonly McpToolRecord[]): ReadonlyMap<string, readonly McpToolRecord[]> {
83
89
  const grouped = new Map<string, McpToolRecord[]>();
84
90
  for (const tool of tools) {
@@ -108,6 +114,7 @@ function describeServer(
108
114
  allowedHostCount: server.allowedHosts.length,
109
115
  ...(server.quarantineReason ? { quarantineReason: server.quarantineReason } : {}),
110
116
  ...(server.quarantineDetail ? { quarantineDetail: server.quarantineDetail } : {}),
117
+ modelRoute: mcpServerModelRoute(),
111
118
  ...(options.lookup ? { lookup: options.lookup } : {}),
112
119
  ...(options.includeParameters ? {
113
120
  tools: tools.map((tool) => ({
@@ -115,8 +122,6 @@ function describeServer(
115
122
  ...(tool.description ? { description: tool.description } : {}),
116
123
  })),
117
124
  toolCount: tools.length,
118
- } : { toolCount: tools.length }),
119
- ...(options.includeParameters ? {
120
125
  policy: {
121
126
  effect: 'read-only',
122
127
  values: 'Server posture returns trust, role, connection, quarantine, and tool metadata; env values and secret config values are never returned.',
@@ -145,7 +150,10 @@ function describeServer(
145
150
  `/mcp remove ${server.name} --yes`,
146
151
  ],
147
152
  },
148
- } : {}),
153
+ } : {
154
+ toolCount: tools.length,
155
+ summary: previewHarnessText(`${server.connected ? 'connected' : 'disconnected'} ${server.trustMode} ${server.schemaFreshness}`),
156
+ }),
149
157
  };
150
158
  }
151
159
 
@@ -1,6 +1,7 @@
1
1
  import { buildAgentWorkspaceVoiceMediaReadiness } from '../input/agent-workspace-voice-media.ts';
2
2
  import type { AgentWorkspaceVoiceMediaProviderStatus } from '../input/agent-workspace-voice-media.ts';
3
3
  import type { CommandContext } from '../input/command-registry.ts';
4
+ import { previewHarnessText } from './agent-harness-text.ts';
4
5
 
5
6
  export interface AgentHarnessMediaArgs {
6
7
  readonly mediaProviderId?: unknown;
@@ -77,9 +78,14 @@ function describeProviderCandidate(provider: AgentWorkspaceVoiceMediaProviderSta
77
78
  label: provider.label,
78
79
  setupState: provider.setupState,
79
80
  selected: provider.selected,
81
+ modelRoute: mediaProviderModelRoute(),
80
82
  };
81
83
  }
82
84
 
85
+ function mediaProviderModelRoute(): string {
86
+ return 'agent_media_generate or agent_harness mode:"media_provider"';
87
+ }
88
+
83
89
  function statusMap(statuses: readonly RuntimeProviderStatus[]): ReadonlyMap<string, RuntimeProviderStatus> {
84
90
  return new Map(statuses.map((status) => [status.id, status]));
85
91
  }
@@ -103,27 +109,32 @@ function describeProvider(
103
109
  configuredSecretKeyNames: provider.configuredSecretKeys,
104
110
  missingSecretKeyNames: provider.missingSecretKeyOptions,
105
111
  nextStep: provider.nextStep,
112
+ modelRoute: mediaProviderModelRoute(),
106
113
  ...(runtimeStatus ? {
107
114
  runtimeStatus: {
108
115
  state: runtimeStatus.state,
109
116
  configured: runtimeStatus.configured,
110
- ...(runtimeStatus.detail ? { detail: runtimeStatus.detail } : {}),
117
+ ...(options.includeParameters && runtimeStatus.detail ? { detail: runtimeStatus.detail } : {}),
111
118
  },
112
119
  } : {}),
113
120
  ...(options.lookup ? { lookup: options.lookup } : {}),
114
- ...(options.includeParameters ? {
115
- modelRoutes: {
116
- inspectPosture: 'agent_harness mode:"media_posture"',
117
- inspectProvider: 'agent_harness mode:"media_provider"',
118
- generateMedia: 'agent_media_generate with confirm:true and explicitUserRequest',
119
- ttsSettings: 'agent_harness mode:"settings", mode:"get_setting", mode:"set_setting" for tts.provider, tts.voice, tts.llmProvider, and tts.llmModel',
120
- },
121
- policy: {
122
- effect: 'read-only',
123
- values: 'Provider posture returns capability, setup, selected, health, and safe environment key names only; secret values and media payloads are never returned.',
124
- mutation: 'Media generation, voice enable/disable, TTS setting changes, and bundle export stay explicit confirmation-gated tool, setting, workspace, or slash-command flows.',
125
- },
126
- } : {}),
121
+ ...(options.includeParameters
122
+ ? {
123
+ modelRoutes: {
124
+ inspectPosture: 'agent_harness mode:"media_posture"',
125
+ inspectProvider: 'agent_harness mode:"media_provider"',
126
+ generateMedia: 'agent_media_generate with confirm:true and explicitUserRequest',
127
+ ttsSettings: 'agent_harness mode:"settings", mode:"get_setting", mode:"set_setting" for tts.provider, tts.voice, tts.llmProvider, and tts.llmModel',
128
+ },
129
+ policy: {
130
+ effect: 'read-only',
131
+ values: 'Provider posture returns capability, setup, selected, health, and safe environment key names only; secret values and media payloads are never returned.',
132
+ mutation: 'Media generation, voice enable/disable, TTS setting changes, and bundle export stay explicit confirmation-gated tool, setting, workspace, or slash-command flows.',
133
+ },
134
+ }
135
+ : {
136
+ summary: previewHarnessText(provider.nextStep || `${provider.domain} provider ${provider.setupState}`),
137
+ }),
127
138
  };
128
139
  }
129
140
 
@@ -1,6 +1,7 @@
1
1
  import type { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
2
2
  import type { CommandContext } from '../input/command-registry.ts';
3
3
  import { resolveAgentConnectedHostConnection } from '../agent/routine-schedule-promotion.ts';
4
+ import { previewHarnessText } from './agent-harness-text.ts';
4
5
 
5
6
  export interface CommandExecutionPolicy {
6
7
  readonly effect: 'read-only' | 'local-state' | 'connected-host-state' | 'external-network' | 'ui-navigation' | 'session-lifecycle' | 'delegated-work' | 'mixed' | 'unknown';
@@ -269,7 +270,7 @@ export function describeCommandPolicy(commandName: string): CommandExecutionPoli
269
270
  return {
270
271
  effect: 'ui-navigation',
271
272
  confirmation,
272
- preferredModelTool: root === 'bookmarks' ? agentHarnessModes('open_ui_surface') : agentHarnessModes('run_command'),
273
+ preferredModelTool: root === 'bookmarks' ? `${agentHarnessModes('open_ui_surface')} or ${agentHarnessModes('run_command')}` : agentHarnessModes('run_command'),
273
274
  boundary: 'Conversation display navigation mutates only the visible transcript view or scroll position.',
274
275
  };
275
276
  }
@@ -331,7 +332,7 @@ export function describeCliCommandPolicy(commandName: string): CommandExecutionP
331
332
  return {
332
333
  effect: 'unknown',
333
334
  confirmation,
334
- boundary: 'Blocked package CLI token. Agent can launch its own TUI and use public connected-host routes, but it does not manage connected-host lifecycle, listeners, servers, bridges, remotes, web surfaces, or webhook listeners.',
335
+ boundary: 'Blocked package CLI token. Agent can launch its own TUI and use public connected-host routes, but it does not manage connected-host lifecycle, listeners, servers, route relays, remotes, web surfaces, or webhook listeners.',
335
336
  };
336
337
  }
337
338
  if (root === 'tui' || root === 'onboarding' || root === 'help' || root === 'version' || root === 'completion') {
@@ -341,7 +342,7 @@ export function describeCliCommandPolicy(commandName: string): CommandExecutionP
341
342
  preferredModelTool: root === 'onboarding' || root === 'tui'
342
343
  ? agentHarnessModes('workspace', 'workspace_actions', 'workspace_action', 'run_workspace_action')
343
344
  : agentHarnessModes('cli_commands', 'cli_command'),
344
- boundary: 'Top-level CLI launch, setup, help, version, and completion commands are package entrypoint surfaces; use in-process workspace and slash-command bridges from the model when operating inside the TUI.',
345
+ boundary: 'Top-level CLI launch, setup, help, version, and completion commands are package entrypoint surfaces; use in-process workspace and slash-command routes from the model when operating inside the TUI.',
345
346
  };
346
347
  }
347
348
  if (root === 'run') {
@@ -405,9 +406,30 @@ function toolIsAvailable(toolRegistry: ToolRegistry, toolName: string): boolean
405
406
  return toolRegistry.getToolDefinitions().some((tool) => tool.name === toolName);
406
407
  }
407
408
 
409
+ function connectedHostCapabilityModelRoute(capability: Record<string, unknown>): string {
410
+ const modelTools = Array.isArray(capability.modelTools)
411
+ ? capability.modelTools.filter((tool): tool is string => typeof tool === 'string')
412
+ : [];
413
+ if (modelTools.length > 0) return previewHarnessText(modelTools.join(' or '));
414
+ return 'agent_harness mode:"connected_host_capability"';
415
+ }
416
+
417
+ function blockedConnectedHostModelRoute(): string {
418
+ return 'agent_harness mode:"service_posture" or mode:"connected_host_status"';
419
+ }
420
+
408
421
  export function connectedHostCapabilityMap(toolRegistry: ToolRegistry): readonly Record<string, unknown>[] {
409
422
  const withAvailability = (capability: Record<string, unknown> & { readonly modelTools: readonly string[] }): Record<string, unknown> => ({
410
423
  ...capability,
424
+ modelRoute: connectedHostCapabilityModelRoute(capability),
425
+ harnessRoute: 'agent_harness mode:"connected_host_capability"',
426
+ modelAccess: {
427
+ inspectCapability: 'agent_harness mode:"connected_host_capability"',
428
+ liveStatus: 'agent_harness mode:"connected_host_status"',
429
+ endpointPosture: 'agent_harness mode:"service_posture"',
430
+ routeFamilies: 'agent_harness mode:"connected_host" includeParameters:true',
431
+ operate: connectedHostCapabilityModelRoute(capability),
432
+ },
411
433
  available: capability.modelTools.every((toolName) => toolIsAvailable(toolRegistry, toolName)),
412
434
  });
413
435
  return [
@@ -510,6 +532,7 @@ export function connectedHostRouteFamilies(): readonly Record<string, unknown>[]
510
532
  id: 'agent-knowledge',
511
533
  prefixes: ['/api/goodvibes-agent/knowledge/*'],
512
534
  modelTools: ['agent_knowledge', 'agent_knowledge_ingest'],
535
+ modelRoute: 'agent_knowledge or agent_knowledge_ingest',
513
536
  boundary: 'Agent-owned knowledge segment only; no fallback to default or non-Agent knowledge.',
514
537
  },
515
538
  {
@@ -522,24 +545,28 @@ export function connectedHostRouteFamilies(): readonly Record<string, unknown>[]
522
545
  '/api/runtime/scheduler',
523
546
  ],
524
547
  modelTools: ['agent_operator_briefing'],
548
+ modelRoute: 'agent_operator_briefing',
525
549
  boundary: 'Read-only public operator briefing routes.',
526
550
  },
527
551
  {
528
552
  id: 'operator-actions',
529
553
  routes: ['public operator action methods for approvals, automation jobs, automation runs, and schedules'],
530
554
  modelTools: ['agent_operator_action'],
555
+ modelRoute: 'agent_operator_action',
531
556
  boundary: 'Allowlisted confirmed mutations only; no arbitrary route invocation.',
532
557
  },
533
558
  {
534
559
  id: 'delivery',
535
560
  routes: ['configured channel, notification, and delivery targets'],
536
561
  modelTools: ['agent_channel_send', 'agent_notify'],
562
+ modelRoute: 'agent_channel_send or agent_notify',
537
563
  boundary: 'Explicit user-approved delivery only; no route/account creation.',
538
564
  },
539
565
  {
540
566
  id: 'connected-schedules',
541
567
  routes: ['public schedule creation/run routes'],
542
568
  modelTools: ['agent_reminder_schedule', 'agent_operator_action'],
569
+ modelRoute: 'agent_reminder_schedule or agent_operator_action',
543
570
  boundary: 'Connected schedules only; no hidden local scheduler or separate Agent job.',
544
571
  },
545
572
  ];
@@ -582,6 +609,9 @@ function describeConnectedHostCapabilityCandidates(
582
609
  capabilityId: typeof entry.capability.id === 'string' ? entry.capability.id : '',
583
610
  purpose: typeof entry.capability.purpose === 'string' ? entry.capability.purpose : undefined,
584
611
  reason: typeof entry.capability.reason === 'string' ? entry.capability.reason : undefined,
612
+ modelRoute: entry.status === 'allowed'
613
+ ? connectedHostCapabilityModelRoute(entry.capability)
614
+ : blockedConnectedHostModelRoute(),
585
615
  }));
586
616
  }
587
617
 
@@ -590,18 +620,36 @@ function connectedHostCapabilityDetail(entry: { readonly status: 'allowed' | 'bl
590
620
  return {
591
621
  status: 'allowed',
592
622
  capability: entry.capability,
623
+ modelRoute: connectedHostCapabilityModelRoute(entry.capability),
624
+ harnessRoute: 'agent_harness mode:"connected_host_capability"',
593
625
  relatedRouteFamilies: relatedConnectedHostRouteFamilies(entry.capability),
626
+ modelAccess: {
627
+ inspectCapability: 'agent_harness mode:"connected_host_capability"',
628
+ connectedHostInventory: 'agent_harness mode:"connected_host" includeParameters:true',
629
+ liveStatus: 'agent_harness mode:"connected_host_status"',
630
+ endpointPosture: 'agent_harness mode:"service_posture"',
631
+ operate: connectedHostCapabilityModelRoute(entry.capability),
632
+ },
594
633
  operatorMethodMode: 'Use agent_harness mode:"operator_methods" for the public operator and Agent Knowledge method catalog. Use mode:"operator_method" for one method.',
595
634
  servicePostureMode: 'Use agent_harness mode:"service_posture" for endpoint binding, network-facing posture, issue, and redacted-log diagnostics. Use mode:"service_endpoint" for one endpoint.',
596
- statusMode: 'Use agent_harness mode:"connected_host_status" for live read-only reachability, SDK compatibility, token posture, and Agent Knowledge route readiness.',
597
- boundary: 'Use only the listed first-class model tools, slash-command families, and workspace categories. Mutations still require explicit confirmation through those tools or command bridges.',
635
+ statusMode: 'Use agent_harness mode:"connected_host_status" for live read-only reachability, token posture, and Agent Knowledge route readiness.',
636
+ boundary: 'Use only the listed first-class model tools, slash-command families, and workspace categories. Mutations still require explicit confirmation through those tools or command routes.',
598
637
  };
599
638
  }
600
639
  return {
601
640
  status: 'blocked',
602
641
  capability: entry.capability,
642
+ modelRoute: blockedConnectedHostModelRoute(),
643
+ harnessRoute: 'agent_harness mode:"connected_host_capability"',
603
644
  allowed: false,
604
645
  available: false,
646
+ modelAccess: {
647
+ inspectCapability: 'agent_harness mode:"connected_host_capability"',
648
+ connectedHostInventory: 'agent_harness mode:"connected_host" includeParameters:true',
649
+ liveStatus: 'agent_harness mode:"connected_host_status"',
650
+ endpointPosture: 'agent_harness mode:"service_posture"',
651
+ operate: 'not exposed',
652
+ },
605
653
  boundary: 'This connected-host surface is intentionally not exposed to the model as an Agent operation.',
606
654
  servicePostureMode: 'Use agent_harness mode:"service_posture" or mode:"service_endpoint" only for read-only endpoint diagnostics.',
607
655
  statusMode: 'Use agent_harness mode:"connected_host_status" only for read-only readiness diagnostics.',
@@ -652,6 +700,17 @@ export function connectedHostSummary(
652
700
  operatorMethods: agentHarnessModes('operator_methods', 'operator_method'),
653
701
  liveStatus: agentHarnessModes('connected_host_status'),
654
702
  capabilityDetail: agentHarnessModes('connected_host_capability'),
703
+ daemonAliases: agentHarnessModes('daemon', 'daemon_status'),
704
+ },
705
+ modelRoute: 'agent_harness mode:"connected_host" or mode:"connected_host_capability"',
706
+ modelAccess: {
707
+ inventory: 'agent_harness mode:"connected_host" includeParameters:true',
708
+ liveStatus: 'agent_harness mode:"connected_host_status"',
709
+ capabilityDetail: 'agent_harness mode:"connected_host_capability" capabilityId:"..."',
710
+ servicePosture: 'agent_harness mode:"service_posture"',
711
+ daemonAliases: 'agent_harness mode:"daemon" or mode:"daemon_status"',
712
+ daemonStatusAlias: 'agent_harness mode:"daemon_status"',
713
+ lifecycleBlocked: 'start, stop, restart, install, upgrade, expose-listener, and listener mutation are not exposed through Agent.',
655
714
  },
656
715
  counts: {
657
716
  routeFamilies: routeFamilies.length,
@@ -672,22 +731,54 @@ export function blockedConnectedHostCapabilities(): readonly Record<string, unkn
672
731
  {
673
732
  id: 'connected-host-lifecycle',
674
733
  blocked: ['start', 'stop', 'restart', 'install', 'upgrade', 'expose-listener', 'mutate-listener'],
734
+ modelRoute: blockedConnectedHostModelRoute(),
735
+ harnessRoute: 'agent_harness mode:"connected_host_capability"',
736
+ modelAccess: {
737
+ inspectCapability: 'agent_harness mode:"connected_host_capability"',
738
+ liveStatus: 'agent_harness mode:"connected_host_status"',
739
+ endpointPosture: 'agent_harness mode:"service_posture"',
740
+ operate: 'not exposed',
741
+ },
675
742
  reason: 'The connected host and listener are externally owned by GoodVibes; Agent can use public operator routes but not manage hosting.',
676
743
  },
677
744
  {
678
745
  id: 'non-agent-knowledge',
679
746
  blocked: ['default-knowledge', 'non-agent-knowledge-segments', 'fallback-knowledge'],
747
+ modelRoute: blockedConnectedHostModelRoute(),
748
+ harnessRoute: 'agent_harness mode:"connected_host_capability"',
749
+ modelAccess: {
750
+ inspectCapability: 'agent_harness mode:"connected_host_capability"',
751
+ allowedAgentKnowledge: 'agent_knowledge or agent_knowledge_ingest',
752
+ liveStatus: 'agent_harness mode:"connected_host_status"',
753
+ operate: 'not exposed',
754
+ },
680
755
  reason: 'Agent model tools must stay inside the isolated Agent Knowledge route family.',
681
756
  },
682
757
  {
683
758
  id: 'hidden-background-work',
684
759
  blocked: ['separate-agent-jobs', 'implicit-delegated-review', 'local-schedulers'],
760
+ modelRoute: blockedConnectedHostModelRoute(),
761
+ harnessRoute: 'agent_harness mode:"connected_host_capability"',
762
+ modelAccess: {
763
+ inspectCapability: 'agent_harness mode:"connected_host_capability"',
764
+ allowedDelegation: 'agent_harness mode:"delegation_posture"',
765
+ liveStatus: 'agent_harness mode:"connected_host_status"',
766
+ operate: 'not exposed',
767
+ },
685
768
  reason: 'All model work is serial and visible unless the user explicitly requests delegation through an exposed surface.',
686
769
  },
687
770
  {
688
771
  id: 'arbitrary-connected-host-mutations',
689
772
  blocked: ['route-discovery-mutation', 'account-creation', 'automation-definition-creation'],
690
- reason: 'Only the documented allowlisted model tools and slash-command bridges are exposed.',
773
+ modelRoute: blockedConnectedHostModelRoute(),
774
+ harnessRoute: 'agent_harness mode:"connected_host_capability"',
775
+ modelAccess: {
776
+ inspectCapability: 'agent_harness mode:"connected_host_capability"',
777
+ allowedActions: 'agent_operator_action for documented allowlisted mutations only',
778
+ liveStatus: 'agent_harness mode:"connected_host_status"',
779
+ operate: 'not exposed',
780
+ },
781
+ reason: 'Only the documented allowlisted model tools and slash-command routes are exposed.',
691
782
  },
692
783
  ];
693
784
  }