@pellux/goodvibes-agent 1.1.7 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (274) hide show
  1. package/CHANGELOG.md +329 -0
  2. package/README.md +46 -30
  3. package/dist/package/{ast-grep-napi.linux-x64-gnu-mkk8xwww.node → ast-grep-napi.linux-x64-gnu-swtppvy9.node} +0 -0
  4. package/dist/package/{ast-grep-napi.linux-x64-musl-ryqtgdv6.node → ast-grep-napi.linux-x64-musl-ttfcdtap.node} +0 -0
  5. package/dist/package/main.js +138903 -91477
  6. package/docs/README.md +14 -7
  7. package/docs/channels-remote-and-api.md +8 -5
  8. package/docs/connected-host.md +14 -12
  9. package/docs/getting-started.md +58 -30
  10. package/docs/knowledge-artifacts-and-multimodal.md +7 -5
  11. package/docs/project-planning.md +2 -2
  12. package/docs/providers-and-routing.md +11 -3
  13. package/docs/tools-and-commands.md +116 -45
  14. package/docs/voice-and-live-tts.md +3 -1
  15. package/package.json +3 -2
  16. package/release/release-notes.md +142 -5
  17. package/release/release-readiness.json +124 -94
  18. package/src/agent/assistant-cockpit.ts +247 -0
  19. package/src/agent/autonomy-schedule-format.ts +96 -0
  20. package/src/agent/autonomy-schedule.ts +514 -0
  21. package/src/agent/channel-delivery-receipts.ts +292 -0
  22. package/src/agent/competitive-feature-inventory.ts +294 -0
  23. package/src/agent/document-registry-types.ts +106 -0
  24. package/src/agent/document-registry.ts +734 -0
  25. package/src/agent/harness-control.ts +2 -2
  26. package/src/agent/memory-prompt.ts +2 -2
  27. package/src/agent/operator-actions.ts +50 -6
  28. package/src/agent/persona-registry.ts +8 -0
  29. package/src/agent/project-context-files.ts +273 -0
  30. package/src/agent/prompt-context-receipts.ts +502 -0
  31. package/src/agent/reminder-schedule-format.ts +16 -2
  32. package/src/agent/research-run-registry.ts +582 -0
  33. package/src/agent/research-source-registry.ts +441 -0
  34. package/src/agent/routine-registry.ts +19 -3
  35. package/src/agent/routine-schedule-format.ts +27 -2
  36. package/src/agent/runtime-profile-starters.ts +7 -0
  37. package/src/agent/runtime-profile.ts +106 -4
  38. package/src/agent/schedule-edit-format.ts +129 -0
  39. package/src/agent/schedule-edit.ts +496 -0
  40. package/src/agent/schedule-next-routes.ts +42 -0
  41. package/src/agent/setup-wizard-artifact-receipts.ts +366 -0
  42. package/src/agent/setup-wizard-checkpoint.ts +140 -0
  43. package/src/agent/setup-wizard.ts +683 -0
  44. package/src/agent/skill-registry-types.ts +102 -0
  45. package/src/agent/skill-registry.ts +81 -107
  46. package/src/agent/vibe-confirmation-routes.ts +62 -0
  47. package/src/agent/vibe-file.ts +285 -0
  48. package/src/cli/agent-knowledge-command.ts +45 -2
  49. package/src/cli/help.ts +3 -0
  50. package/src/cli/profiles-command.ts +25 -11
  51. package/src/config/agent-settings-policy.ts +1 -18
  52. package/src/input/agent-workspace-activation.ts +53 -0
  53. package/src/input/agent-workspace-artifact-browser-editor.ts +494 -0
  54. package/src/input/agent-workspace-artifact-metadata.ts +17 -0
  55. package/src/input/agent-workspace-basic-command-editors.ts +5 -1
  56. package/src/input/agent-workspace-categories.ts +153 -155
  57. package/src/input/agent-workspace-channel-triage.ts +481 -0
  58. package/src/input/agent-workspace-channels.ts +263 -0
  59. package/src/input/agent-workspace-command-editor.ts +152 -0
  60. package/src/input/agent-workspace-context-snapshot.ts +349 -0
  61. package/src/input/agent-workspace-delegation-editor-submission.ts +8 -0
  62. package/src/input/agent-workspace-document-editor.ts +502 -0
  63. package/src/input/agent-workspace-document-ops-editor.ts +523 -0
  64. package/src/input/agent-workspace-host-category.ts +1 -1
  65. package/src/input/agent-workspace-local-library-snapshot.ts +167 -0
  66. package/src/input/agent-workspace-model-compare-editor.ts +376 -0
  67. package/src/input/agent-workspace-model-compare-prompt-submission.ts +552 -0
  68. package/src/input/agent-workspace-model-compare-types.ts +82 -0
  69. package/src/input/agent-workspace-model-compare-utils.ts +35 -0
  70. package/src/input/agent-workspace-onboarding-categories.ts +141 -0
  71. package/src/input/agent-workspace-operations-command-editor-submission.ts +53 -0
  72. package/src/input/agent-workspace-operations-command-editors.ts +22 -0
  73. package/src/input/agent-workspace-research-report-editor.ts +212 -0
  74. package/src/input/agent-workspace-research-run-editor.ts +144 -0
  75. package/src/input/agent-workspace-research-source-editor.ts +167 -0
  76. package/src/input/agent-workspace-review-packet-snapshot.ts +593 -0
  77. package/src/input/agent-workspace-review-packet-utils.ts +265 -0
  78. package/src/input/agent-workspace-settings.ts +313 -30
  79. package/src/input/agent-workspace-setup-checkpoint-action.ts +127 -0
  80. package/src/input/agent-workspace-setup-snapshot.ts +217 -0
  81. package/src/input/agent-workspace-setup.ts +61 -1
  82. package/src/input/agent-workspace-snapshot.ts +184 -149
  83. package/src/input/agent-workspace-types.ts +287 -2
  84. package/src/input/agent-workspace.ts +23 -1
  85. package/src/input/command-registry.ts +9 -2
  86. package/src/input/commands/agent-runtime-profile-runtime.ts +28 -13
  87. package/src/input/commands/channels-runtime.ts +102 -114
  88. package/src/input/commands/delegation-runtime.ts +87 -24
  89. package/src/input/commands/knowledge-browser-flags.ts +12 -0
  90. package/src/input/commands/knowledge.ts +28 -14
  91. package/src/input/commands/operator-actions-runtime.ts +6 -3
  92. package/src/input/commands/schedule-runtime.ts +71 -14
  93. package/src/input/commands/session-content.ts +2 -2
  94. package/src/input/commands/shell-core.ts +2 -0
  95. package/src/input/commands/vibe-runtime.ts +140 -0
  96. package/src/input/commands.ts +2 -0
  97. package/src/input/connected-host-routes.ts +123 -0
  98. package/src/input/settings-modal-types.ts +14 -0
  99. package/src/input/setup-wizard-live-receipts.ts +76 -0
  100. package/src/main.ts +15 -15
  101. package/src/panels/qr-panel.ts +2 -2
  102. package/src/renderer/agent-workspace-context-lines.ts +701 -0
  103. package/src/renderer/agent-workspace.ts +68 -421
  104. package/src/renderer/settings-modal-helpers.ts +12 -0
  105. package/src/renderer/settings-modal.ts +12 -0
  106. package/src/runtime/bootstrap-command-context.ts +11 -1
  107. package/src/runtime/bootstrap-command-parts.ts +14 -3
  108. package/src/runtime/bootstrap-core.ts +39 -1
  109. package/src/runtime/bootstrap-shell.ts +6 -0
  110. package/src/runtime/bootstrap.ts +113 -30
  111. package/src/runtime/connected-host-auth.ts +3 -2
  112. package/src/runtime/execution-ledger.ts +231 -0
  113. package/src/runtime/services.ts +88 -10
  114. package/src/runtime/tool-permission-safety.ts +199 -5
  115. package/src/shell/session-continuity-hints.ts +39 -0
  116. package/src/tools/agent-artifacts-tool.ts +606 -0
  117. package/src/tools/agent-audit-tool.ts +155 -0
  118. package/src/tools/agent-autonomy-schedule-tool.ts +238 -0
  119. package/src/tools/agent-autonomy-tool.ts +140 -0
  120. package/src/tools/agent-channel-send-tool.ts +24 -2
  121. package/src/tools/agent-channels-tool.ts +140 -0
  122. package/src/tools/agent-computer-tool.ts +168 -0
  123. package/src/tools/agent-context-policy.ts +137 -7
  124. package/src/tools/agent-context-tool.ts +143 -0
  125. package/src/tools/agent-delegation-tool.ts +128 -0
  126. package/src/tools/agent-device-tool.ts +240 -0
  127. package/src/tools/agent-documents-tool.ts +684 -0
  128. package/src/tools/agent-execution-tool.ts +230 -0
  129. package/src/tools/agent-harness-agent-orchestration-policy.ts +75 -0
  130. package/src/tools/agent-harness-agent-orchestration.ts +791 -0
  131. package/src/tools/agent-harness-autonomy-intake.ts +785 -0
  132. package/src/tools/agent-harness-autonomy-live-records.ts +742 -0
  133. package/src/tools/agent-harness-autonomy-queue-types.ts +88 -0
  134. package/src/tools/agent-harness-autonomy-queue.ts +571 -0
  135. package/src/tools/agent-harness-autonomy-watcher-read-models.ts +509 -0
  136. package/src/tools/agent-harness-background-processes-types.ts +35 -0
  137. package/src/tools/agent-harness-background-processes.ts +798 -0
  138. package/src/tools/agent-harness-browser-cockpit-route.ts +373 -0
  139. package/src/tools/agent-harness-browser-control.ts +346 -0
  140. package/src/tools/agent-harness-browser-pwa-read-models.ts +622 -0
  141. package/src/tools/agent-harness-channel-metadata.ts +168 -10
  142. package/src/tools/agent-harness-cli-command-policy.ts +110 -0
  143. package/src/tools/agent-harness-command-catalog.ts +4 -2
  144. package/src/tools/agent-harness-command-runner.ts +42 -0
  145. package/src/tools/agent-harness-connected-host-status.ts +7 -4
  146. package/src/tools/agent-harness-delegation-posture.ts +234 -19
  147. package/src/tools/agent-harness-device-live-read-models.ts +366 -0
  148. package/src/tools/agent-harness-document-ops-reviewer-readiness.ts +234 -0
  149. package/src/tools/agent-harness-document-ops-types.ts +72 -0
  150. package/src/tools/agent-harness-document-ops.ts +671 -0
  151. package/src/tools/agent-harness-execution-history.ts +489 -0
  152. package/src/tools/agent-harness-execution-posture.ts +385 -0
  153. package/src/tools/agent-harness-file-recovery.ts +135 -0
  154. package/src/tools/agent-harness-interactive-runtime-records.ts +421 -0
  155. package/src/tools/agent-harness-keybinding-metadata.ts +16 -12
  156. package/src/tools/agent-harness-learning-curator-common.ts +102 -0
  157. package/src/tools/agent-harness-learning-curator-consolidation.ts +295 -0
  158. package/src/tools/agent-harness-learning-curator-proposals.ts +606 -0
  159. package/src/tools/agent-harness-learning-curator-types.ts +151 -0
  160. package/src/tools/agent-harness-learning-curator.ts +417 -0
  161. package/src/tools/agent-harness-local-model-benchmarks.ts +269 -0
  162. package/src/tools/agent-harness-local-model-cookbook.ts +238 -0
  163. package/src/tools/agent-harness-local-model-endpoints.ts +788 -0
  164. package/src/tools/agent-harness-local-model-smoke.ts +277 -0
  165. package/src/tools/agent-harness-local-model-url.ts +78 -0
  166. package/src/tools/agent-harness-media-posture.ts +223 -2
  167. package/src/tools/agent-harness-memory-external-providers.ts +796 -0
  168. package/src/tools/agent-harness-memory-posture.ts +672 -0
  169. package/src/tools/agent-harness-memory-provider-certification.ts +219 -0
  170. package/src/tools/agent-harness-memory-refinement.ts +340 -0
  171. package/src/tools/agent-harness-metadata.ts +100 -168
  172. package/src/tools/agent-harness-mode-catalog.ts +86 -33
  173. package/src/tools/agent-harness-model-catalog.ts +162 -0
  174. package/src/tools/agent-harness-model-provider-health.ts +283 -0
  175. package/src/tools/agent-harness-model-readiness.ts +432 -0
  176. package/src/tools/agent-harness-model-routing-types.ts +327 -0
  177. package/src/tools/agent-harness-model-routing-utils.ts +30 -0
  178. package/src/tools/agent-harness-model-routing.ts +165 -193
  179. package/src/tools/agent-harness-operator-methods.ts +115 -133
  180. package/src/tools/agent-harness-pairing-posture.ts +452 -18
  181. package/src/tools/agent-harness-personal-ops-certification.ts +116 -0
  182. package/src/tools/agent-harness-personal-ops-discovery.ts +533 -0
  183. package/src/tools/agent-harness-personal-ops-intake.ts +385 -0
  184. package/src/tools/agent-harness-personal-ops-lanes.ts +270 -0
  185. package/src/tools/agent-harness-personal-ops-operations.ts +225 -0
  186. package/src/tools/agent-harness-personal-ops-provider-records.ts +358 -0
  187. package/src/tools/agent-harness-personal-ops-provider-task-records.ts +321 -0
  188. package/src/tools/agent-harness-personal-ops-records.ts +684 -0
  189. package/src/tools/agent-harness-personal-ops-runner.ts +637 -0
  190. package/src/tools/agent-harness-personal-ops-types.ts +240 -0
  191. package/src/tools/agent-harness-personal-ops.ts +613 -0
  192. package/src/tools/agent-harness-project-context.ts +144 -0
  193. package/src/tools/agent-harness-prompt-context.ts +589 -0
  194. package/src/tools/agent-harness-provider-account-metadata.ts +2 -2
  195. package/src/tools/agent-harness-release-evidence.ts +9 -7
  196. package/src/tools/agent-harness-release-readiness.ts +9 -7
  197. package/src/tools/agent-harness-remote-read-models.ts +541 -0
  198. package/src/tools/agent-harness-research-briefing.ts +446 -0
  199. package/src/tools/agent-harness-research-live-read-models.ts +500 -0
  200. package/src/tools/agent-harness-research-queue.ts +250 -0
  201. package/src/tools/agent-harness-research-runs.ts +307 -0
  202. package/src/tools/agent-harness-research-workflow.ts +395 -0
  203. package/src/tools/agent-harness-security-posture.ts +2 -2
  204. package/src/tools/agent-harness-service-posture.ts +9 -9
  205. package/src/tools/agent-harness-session-metadata.ts +3 -3
  206. package/src/tools/agent-harness-setup-connected-host.ts +501 -0
  207. package/src/tools/agent-harness-setup-describe.ts +557 -0
  208. package/src/tools/agent-harness-setup-handoffs.ts +502 -0
  209. package/src/tools/agent-harness-setup-model-helpers.ts +102 -0
  210. package/src/tools/agent-harness-setup-plan.ts +253 -0
  211. package/src/tools/agent-harness-setup-posture-types.ts +218 -0
  212. package/src/tools/agent-harness-setup-posture-utils.ts +173 -0
  213. package/src/tools/agent-harness-setup-posture.ts +449 -221
  214. package/src/tools/agent-harness-setup-smoke.ts +533 -0
  215. package/src/tools/agent-harness-sudo-posture.ts +114 -0
  216. package/src/tools/agent-harness-tool-schema.ts +142 -7
  217. package/src/tools/agent-harness-tool-types.ts +83 -0
  218. package/src/tools/agent-harness-tool-utils.ts +43 -0
  219. package/src/tools/agent-harness-tool.ts +319 -370
  220. package/src/tools/agent-harness-ui-surface-metadata.ts +76 -69
  221. package/src/tools/agent-harness-ui-surface-types.ts +46 -0
  222. package/src/tools/agent-harness-vibe-health.ts +105 -0
  223. package/src/tools/agent-harness-workspace-action-runner.ts +149 -0
  224. package/src/tools/agent-harness-workspace-actions.ts +114 -8
  225. package/src/tools/agent-harness-workspace-editor-execution.ts +214 -0
  226. package/src/tools/agent-harness-workspace-editor-runner.ts +755 -0
  227. package/src/tools/agent-host-tool.ts +159 -0
  228. package/src/tools/agent-knowledge-ingest-tool.ts +34 -1
  229. package/src/tools/agent-learning-consolidation-core.ts +327 -0
  230. package/src/tools/agent-learning-consolidation-tool.ts +758 -0
  231. package/src/tools/agent-memory-tool.ts +253 -0
  232. package/src/tools/agent-model-compare-export.ts +315 -0
  233. package/src/tools/agent-model-compare-handoff.ts +592 -0
  234. package/src/tools/agent-model-compare-judgment.ts +633 -0
  235. package/src/tools/agent-model-compare-run.ts +735 -0
  236. package/src/tools/agent-model-compare-tool.ts +698 -0
  237. package/src/tools/agent-model-compare-types.ts +191 -0
  238. package/src/tools/agent-model-compare-utils.ts +93 -0
  239. package/src/tools/agent-models-tool.ts +208 -0
  240. package/src/tools/agent-operator-action-tool.ts +1 -1
  241. package/src/tools/agent-operator-method-tool.ts +643 -0
  242. package/src/tools/agent-personal-ops-tool.ts +197 -0
  243. package/src/tools/agent-policy-explanation.ts +415 -0
  244. package/src/tools/agent-research-report-tool.ts +608 -0
  245. package/src/tools/agent-research-runner.ts +367 -0
  246. package/src/tools/agent-research-runs-tool.ts +434 -0
  247. package/src/tools/agent-research-sources-tool.ts +443 -0
  248. package/src/tools/agent-research-tool.ts +515 -0
  249. package/src/tools/agent-review-packet-presets-core.ts +757 -0
  250. package/src/tools/agent-review-packet-presets-tool.ts +466 -0
  251. package/src/tools/agent-review-packet-share-tool.ts +305 -0
  252. package/src/tools/agent-route-planner-candidates-setup.ts +411 -0
  253. package/src/tools/agent-route-planner-candidates-surfaces.ts +432 -0
  254. package/src/tools/agent-route-planner-candidates-work.ts +251 -0
  255. package/src/tools/agent-route-planner-helpers.ts +667 -0
  256. package/src/tools/agent-route-planner.ts +185 -0
  257. package/src/tools/agent-route-tool.ts +105 -0
  258. package/src/tools/agent-schedule-edit-tool.ts +210 -0
  259. package/src/tools/agent-schedule-tool.ts +282 -0
  260. package/src/tools/agent-security-tool.ts +145 -0
  261. package/src/tools/agent-sessions-tool.ts +122 -0
  262. package/src/tools/agent-settings-import-tool.ts +104 -0
  263. package/src/tools/agent-settings-tool.ts +176 -0
  264. package/src/tools/agent-setup-tool.ts +226 -0
  265. package/src/tools/agent-support-tool.ts +122 -0
  266. package/src/tools/agent-terminal-process-tools.ts +167 -0
  267. package/src/tools/agent-tool-policy-guard-types.ts +77 -0
  268. package/src/tools/agent-tool-policy-guard.ts +106 -85
  269. package/src/tools/agent-vibe-tool.ts +297 -0
  270. package/src/tools/agent-work-plan-tool.ts +265 -6
  271. package/src/tools/agent-workspace-tool.ts +305 -0
  272. package/src/tools/artifact-archive.ts +169 -0
  273. package/src/tools/tool-definition-compaction.ts +36 -0
  274. package/src/version.ts +1 -1
@@ -3,7 +3,11 @@ import type { CommandContext } from '../input/command-registry.ts';
3
3
  import { GOODVIBES_AGENT_PAIRING_SURFACE } from '../config/surface.ts';
4
4
  import { resolveRuntimeEndpointBinding } from '../cli/endpoints.ts';
5
5
  import { connectedHostOperatorTokenFingerprint, readConnectedHostOperatorToken } from '../runtime/connected-host-auth.ts';
6
+ import { buildAgentWorkspaceChannels } from '../input/agent-workspace-channels.ts';
7
+ import { buildAgentWorkspaceVoiceMediaReadiness } from '../input/agent-workspace-voice-media.ts';
6
8
  import { requirePlatform, requireShellPaths } from '../input/commands/runtime-services.ts';
9
+ import { browserControlPosture } from './agent-harness-browser-control.ts';
10
+ import { certifiedDeviceLiveRecords, deviceLiveReadModelSnapshot } from './agent-harness-device-live-read-models.ts';
7
11
  import { previewHarnessText } from './agent-harness-text.ts';
8
12
 
9
13
  export interface AgentHarnessPairingArgs {
@@ -25,13 +29,44 @@ interface PairingRoute {
25
29
  readonly id: string;
26
30
  readonly label: string;
27
31
  readonly detail: string;
28
- readonly effect: 'read-only' | 'visible-navigation' | 'external-network' | 'confirmation-gated-secret-display';
32
+ readonly effect: 'read-only' | 'visible-navigation' | 'external-network' | 'confirmation-gated-secret-display' | 'confirmed-local-auth-provisioning';
29
33
  readonly command?: string;
30
34
  readonly harnessRoute?: string;
31
35
  readonly capabilityIds?: readonly string[];
32
36
  readonly requiresConfirmation?: boolean;
33
37
  }
34
38
 
39
+ type DeviceCapabilityStatus = 'ready' | 'attention' | 'setup-needed' | 'not-published';
40
+
41
+ interface DeviceCapability {
42
+ readonly id: string;
43
+ readonly label: string;
44
+ readonly domain: 'companion' | 'browser' | 'voice' | 'delivery' | 'desktop' | 'device';
45
+ readonly status: DeviceCapabilityStatus;
46
+ readonly userOutcome: string;
47
+ readonly summary: string;
48
+ readonly nextStep: string;
49
+ readonly capabilities: readonly string[];
50
+ readonly modelRoute: string;
51
+ readonly userRoute?: string;
52
+ readonly setupRoutes?: readonly string[];
53
+ readonly evidence?: Record<string, unknown>;
54
+ readonly policy: string;
55
+ }
56
+
57
+ interface DeviceCapabilityMap {
58
+ readonly summary: {
59
+ readonly total: number;
60
+ readonly ready: number;
61
+ readonly attention: number;
62
+ readonly setupNeeded: number;
63
+ readonly notPublished: number;
64
+ readonly primaryNextStep: string;
65
+ };
66
+ readonly capabilities: readonly DeviceCapability[];
67
+ readonly policy: string;
68
+ }
69
+
35
70
  function readString(value: unknown): string {
36
71
  return typeof value === 'string' ? value.trim() : '';
37
72
  }
@@ -42,6 +77,32 @@ function readLimit(value: unknown, fallback: number): number {
42
77
  return Math.max(1, Math.min(500, Math.trunc(parsed)));
43
78
  }
44
79
 
80
+ function readConfigBoolean(context: CommandContext, key: string, fallback: boolean): boolean {
81
+ try {
82
+ const value = (context.platform.configManager as { get(settingKey: string): unknown }).get(key);
83
+ if (typeof value === 'boolean') return value;
84
+ if (typeof value === 'string') {
85
+ const normalized = value.trim().toLowerCase();
86
+ if (normalized === 'true') return true;
87
+ if (normalized === 'false') return false;
88
+ }
89
+ return fallback;
90
+ } catch {
91
+ return fallback;
92
+ }
93
+ }
94
+
95
+ function readConfigStringArray(context: CommandContext, key: string): readonly string[] {
96
+ try {
97
+ const value = (context.platform.configManager as { get(settingKey: string): unknown }).get(key);
98
+ if (Array.isArray(value)) return value.filter((entry): entry is string => typeof entry === 'string' && entry.trim().length > 0);
99
+ if (typeof value === 'string' && value.trim().length > 0) return [value.trim()];
100
+ return [];
101
+ } catch {
102
+ return [];
103
+ }
104
+ }
105
+
45
106
  function localNetworkIp(): string {
46
107
  try {
47
108
  const nets = networkInterfaces();
@@ -99,12 +160,21 @@ function pairingRoutes(): readonly PairingRoute[] {
99
160
  harnessRoute: 'agent_harness mode:"workspace_action" target:"pair"',
100
161
  capabilityIds: ['companion-pairing', 'mobile-command-routing'],
101
162
  },
163
+ {
164
+ id: 'connected-host-token-provisioning',
165
+ label: 'Connected-host token provisioning',
166
+ detail: 'Confirmed SDK-backed route that creates or repairs the local canonical connected-host token file without printing the raw token.',
167
+ effect: 'confirmed-local-auth-provisioning',
168
+ harnessRoute: 'setup action:"token" setupItemId:"connected-host-auth" confirm:true explicitUserRequest:"..."',
169
+ capabilityIds: ['connected-host-status', 'companion-pairing'],
170
+ requiresConfirmation: true,
171
+ },
102
172
  {
103
173
  id: 'connected-host-status',
104
174
  label: 'Connected host live posture',
105
175
  detail: 'Read-only reachability, token posture, and route readiness used before companion setup.',
106
176
  effect: 'read-only',
107
- harnessRoute: 'agent_harness mode:"connected_host_status"',
177
+ harnessRoute: 'host action:"status"',
108
178
  capabilityIds: ['connected-host-status'],
109
179
  },
110
180
  {
@@ -112,7 +182,7 @@ function pairingRoutes(): readonly PairingRoute[] {
112
182
  label: 'Companion capability map',
113
183
  detail: 'Allowed and blocked companion route families for pairing, shared sessions, tasks, approvals, provider/model changes, attachments, and mobile command surfaces.',
114
184
  effect: 'read-only',
115
- harnessRoute: 'agent_harness mode:"connected_host"',
185
+ harnessRoute: 'host action:"capabilities"',
116
186
  capabilityIds: [
117
187
  'companion-pairing',
118
188
  'shared-session',
@@ -123,13 +193,30 @@ function pairingRoutes(): readonly PairingRoute[] {
123
193
  'mobile-command-routing',
124
194
  ],
125
195
  },
196
+ {
197
+ id: 'device-capability-map',
198
+ label: 'Device capability map',
199
+ detail: 'Read-only mobile, browser/PWA, voice, notification, browser/desktop, camera, screen, location, and device-command readiness map with honest setup and not-published states.',
200
+ effect: 'read-only',
201
+ harnessRoute: 'agent_harness mode:"pairing_posture" query:"device" includeParameters:true',
202
+ capabilityIds: [
203
+ 'companion-pairing',
204
+ 'mobile-command-routing',
205
+ 'browser-cockpit-pwa',
206
+ 'voice-controls',
207
+ 'tts-spoken-responses',
208
+ 'notifications',
209
+ 'browser-desktop-control',
210
+ 'camera-location-sensors',
211
+ ],
212
+ },
126
213
  {
127
214
  id: 'channels-readiness',
128
215
  label: 'Channel readiness',
129
216
  detail: 'Read-only channel setup and delivery posture used after pairing when messages or reminders need an explicit delivery target.',
130
217
  effect: 'read-only',
131
218
  command: '/channels',
132
- harnessRoute: 'agent_harness mode:"channels"',
219
+ harnessRoute: 'channels action:"status"',
133
220
  capabilityIds: ['channels', 'notifications'],
134
221
  },
135
222
  ];
@@ -147,6 +234,14 @@ function routeSearchText(route: PairingRoute): string {
147
234
  ].join('\n').toLowerCase();
148
235
  }
149
236
 
237
+ function routeMatches(route: PairingRoute, query: string): boolean {
238
+ if (!query) return true;
239
+ const text = routeSearchText(route);
240
+ if (text.includes(query)) return true;
241
+ const tokens = query.split(/\s+/).map((token) => token.trim()).filter(Boolean);
242
+ return tokens.length > 0 && tokens.every((token) => text.includes(token));
243
+ }
244
+
150
245
  function describeCandidate(route: PairingRoute): Record<string, unknown> {
151
246
  return {
152
247
  pairingRouteId: route.id,
@@ -157,7 +252,11 @@ function describeCandidate(route: PairingRoute): Record<string, unknown> {
157
252
  };
158
253
  }
159
254
 
160
- function describeRoute(route: PairingRoute, options: { readonly includeParameters?: boolean; readonly lookup?: Record<string, unknown> } = {}): Record<string, unknown> {
255
+ function describeRoute(route: PairingRoute, options: {
256
+ readonly includeParameters?: boolean;
257
+ readonly lookup?: Record<string, unknown>;
258
+ readonly context?: CommandContext;
259
+ } = {}): Record<string, unknown> {
161
260
  return {
162
261
  pairingRouteId: route.id,
163
262
  label: route.label,
@@ -180,11 +279,14 @@ function describeRoute(route: PairingRoute, options: { readonly includeParameter
180
279
  modelAccess: {
181
280
  inspectPairing: 'agent_harness mode:"pairing_posture"',
182
281
  inspectRoute: 'agent_harness mode:"pairing_route"',
183
- connectedHostStatus: 'agent_harness mode:"connected_host_status"',
184
- connectedHostCapabilities: 'agent_harness mode:"connected_host"',
185
- channels: 'agent_harness mode:"channels"',
282
+ connectedHostStatus: 'host action:"status"',
283
+ connectedHostCapabilities: 'host action:"capabilities"',
284
+ channels: 'channels action:"status"',
186
285
  },
187
286
  } : {}),
287
+ ...(options.includeParameters && route.id === 'device-capability-map' && options.context ? {
288
+ deviceCapabilityMap: buildDeviceCapabilityMap(options.context),
289
+ } : {}),
188
290
  };
189
291
  }
190
292
 
@@ -205,6 +307,7 @@ function pairingState(context: CommandContext): Record<string, unknown> {
205
307
  surface: GOODVIBES_AGENT_PAIRING_SURFACE,
206
308
  endpoint: {
207
309
  endpointId: 'controlPlane',
310
+ enabled: readConfigBoolean(context, 'controlPlane.enabled', false),
208
311
  bindHost: binding.host,
209
312
  advertisedHost: host,
210
313
  port: binding.port,
@@ -220,12 +323,325 @@ function pairingState(context: CommandContext): Record<string, unknown> {
220
323
  };
221
324
  }
222
325
 
326
+ function tokenPresent(state: Record<string, unknown>): boolean {
327
+ const token = state.token as { readonly present?: boolean } | undefined;
328
+ return token?.present === true;
329
+ }
330
+
331
+ function endpointEnabled(state: Record<string, unknown>): boolean {
332
+ const endpoint = state.endpoint as { readonly enabled?: boolean } | undefined;
333
+ return endpoint?.enabled === true;
334
+ }
335
+
336
+ function deviceStatusRank(status: DeviceCapabilityStatus): number {
337
+ if (status === 'attention') return 0;
338
+ if (status === 'setup-needed') return 1;
339
+ if (status === 'not-published') return 2;
340
+ return 3;
341
+ }
342
+
343
+ function deviceCapabilitySearchText(capability: DeviceCapability): string {
344
+ return [
345
+ capability.id,
346
+ capability.label,
347
+ capability.domain,
348
+ capability.status,
349
+ capability.userOutcome,
350
+ capability.summary,
351
+ capability.nextStep,
352
+ capability.modelRoute,
353
+ capability.userRoute ?? '',
354
+ ...(capability.setupRoutes ?? []),
355
+ ...capability.capabilities,
356
+ ].join('\n').toLowerCase();
357
+ }
358
+
359
+ function deviceCapabilityMatches(capability: DeviceCapability, query: string): boolean {
360
+ if (!query) return true;
361
+ const text = deviceCapabilitySearchText(capability);
362
+ if (text.includes(query)) return true;
363
+ const tokens = query.split(/\s+/).map((token) => token.trim()).filter(Boolean);
364
+ return tokens.length > 0 && tokens.every((token) => text.includes(token));
365
+ }
366
+
367
+ function queryRequestsWholeDeviceMap(query: string): boolean {
368
+ if (!query) return true;
369
+ const tokens = query.split(/\s+/).map((token) => token.trim()).filter(Boolean);
370
+ if (query === 'device' || query === 'mobile' || query === 'phone') return true;
371
+ return tokens.includes('device') && (tokens.includes('capability') || tokens.includes('capabilities') || tokens.includes('map'));
372
+ }
373
+
374
+ function liveDeviceEvidence(records: ReturnType<typeof certifiedDeviceLiveRecords>): Record<string, unknown> | undefined {
375
+ return records.length > 0 ? { certifiedLiveRecords: records.slice(0, 5) } : undefined;
376
+ }
377
+
378
+ function buildDeviceCapabilityMap(context: CommandContext): DeviceCapabilityMap {
379
+ const state = pairingState(context);
380
+ const hasToken = tokenPresent(state);
381
+ const controlPlaneReady = endpointEnabled(state) && hasToken;
382
+ const webEnabled = readConfigBoolean(context, 'web.enabled', false);
383
+ const webBinding = resolveRuntimeEndpointBinding(requirePlatform(context).configManager, 'web');
384
+ const webHost = urlHostForBindHost(webBinding.host);
385
+ const webUrl = `http://${webHost}:${webBinding.port}`;
386
+ const voiceEnabled = readConfigBoolean(context, 'ui.voiceEnabled', false);
387
+ const voiceReadiness = buildAgentWorkspaceVoiceMediaReadiness({
388
+ context,
389
+ voiceProviders: context.platform.voiceProviderRegistry?.list() ?? [],
390
+ mediaProviders: context.platform.mediaProviderRegistry?.list() ?? [],
391
+ });
392
+ const channels = buildAgentWorkspaceChannels(context);
393
+ const readyChannels = channels.filter((channel) => channel.ready);
394
+ const enabledChannels = channels.filter((channel) => channel.enabled);
395
+ const notificationWebhookCount = readConfigStringArray(context, 'notifications.webhookUrls').length;
396
+ const browserPosture = browserControlPosture(context);
397
+ const liveDevice = deviceLiveReadModelSnapshot(context);
398
+ const sensorRecords = certifiedDeviceLiveRecords(liveDevice, 'camera-location-sensors', ['camera', 'location', 'screen', 'device command', 'sensor']);
399
+ const sensorReady = sensorRecords.length > 0;
400
+ const ttsStatus: DeviceCapabilityStatus = voiceReadiness.selectedTtsProviderStatus === 'ready' && voiceReadiness.ttsVoiceConfigured
401
+ ? 'ready'
402
+ : voiceReadiness.selectedTtsProviderStatus === 'ready' || voiceReadiness.selectedTtsProviderStatus === 'registered'
403
+ ? 'attention'
404
+ : 'setup-needed';
405
+ const notificationStatus: DeviceCapabilityStatus = readyChannels.length > 0 || notificationWebhookCount > 0
406
+ ? 'ready'
407
+ : enabledChannels.length > 0
408
+ ? 'attention'
409
+ : 'setup-needed';
410
+ const capabilities: DeviceCapability[] = [
411
+ {
412
+ id: 'companion-pairing',
413
+ label: 'Companion pairing',
414
+ domain: 'companion',
415
+ status: hasToken ? 'ready' : 'setup-needed',
416
+ userOutcome: 'Pair a phone or companion surface without exposing the raw operator token in chat.',
417
+ summary: hasToken
418
+ ? 'A connected-host operator token is present; QR and manual setup still require visible confirmed routes.'
419
+ : 'No connected-host operator token is present, so companion pairing needs local token provisioning first.',
420
+ nextStep: hasToken
421
+ ? 'Use /pair or inspect the QR/manual routes when the user asks to pair a companion.'
422
+ : 'Provision connected-host auth, then return to QR pairing.',
423
+ capabilities: ['QR pairing', 'manual setup fallback', 'token-safe handoff'],
424
+ modelRoute: 'agent_harness mode:"pairing_route" pairingRouteId:"qr-pairing"',
425
+ userRoute: '/pair',
426
+ setupRoutes: [
427
+ 'setup action:"item" setupItemId:"connected-host-auth"',
428
+ 'setup action:"token" setupItemId:"connected-host-auth" confirm:true explicitUserRequest:"..."',
429
+ ],
430
+ evidence: {
431
+ tokenPresent: hasToken,
432
+ rawTokenReturned: false,
433
+ },
434
+ policy: 'Pairing display and raw-token fallback are visible, confirmation-gated user flows.',
435
+ },
436
+ {
437
+ id: 'mobile-command-routing',
438
+ label: 'Mobile command routing',
439
+ domain: 'companion',
440
+ status: controlPlaneReady ? 'ready' : endpointEnabled(state) ? 'attention' : 'setup-needed',
441
+ userOutcome: 'Send commands from a companion surface into the same visible Agent control plane.',
442
+ summary: controlPlaneReady
443
+ ? 'Control-plane routing and operator auth are both configured.'
444
+ : endpointEnabled(state)
445
+ ? 'The control plane is enabled, but companion auth is not ready.'
446
+ : 'The control plane is disabled, so mobile command routing is not reachable yet.',
447
+ nextStep: controlPlaneReady
448
+ ? 'Use connected-host status before accepting companion-originated work.'
449
+ : 'Enable and authenticate the control plane before relying on mobile command routing.',
450
+ capabilities: ['shared session routing', 'task handoff', 'approval handoff', 'provider/model routing'],
451
+ modelRoute: 'host action:"status"',
452
+ setupRoutes: [
453
+ 'host action:"service" endpointId:"controlPlane"',
454
+ 'setup action:"item" setupItemId:"connected-host-auth"',
455
+ ],
456
+ evidence: {
457
+ controlPlaneEnabled: endpointEnabled(state),
458
+ tokenPresent: hasToken,
459
+ },
460
+ policy: 'Companion-originated tasks, approvals, sends, and provider/model changes remain explicit visible routes.',
461
+ },
462
+ {
463
+ id: 'browser-cockpit-pwa',
464
+ label: 'Browser cockpit/PWA',
465
+ domain: 'browser',
466
+ status: webEnabled ? 'ready' : 'setup-needed',
467
+ userOutcome: 'Use the connected-host browser cockpit from desktop or mobile without returning to the terminal.',
468
+ summary: webEnabled
469
+ ? `The connected-host web endpoint is enabled at ${webUrl}.`
470
+ : 'The connected-host web endpoint is disabled, so the browser cockpit/PWA cannot open yet.',
471
+ nextStep: webEnabled
472
+ ? 'Open the connected browser cockpit when the user confirms a browser handoff.'
473
+ : 'Inspect the web service endpoint and enable the host web surface only when the user wants browser access.',
474
+ capabilities: ['responsive browser workspace', 'PWA route', 'mobile-friendly cockpit'],
475
+ modelRoute: 'agent_harness mode:"open_ui_surface" surfaceId:"connected-browser-cockpit" confirm:true explicitUserRequest:"..."',
476
+ setupRoutes: [
477
+ 'agent_harness mode:"ui_surface" surfaceId:"connected-browser-cockpit"',
478
+ 'host action:"service" endpointId:"web"',
479
+ ],
480
+ evidence: {
481
+ webEnabled,
482
+ webEndpoint: webEnabled ? webUrl : null,
483
+ },
484
+ policy: 'Opening external browser surfaces is confirmation-gated and never mutates host state by inspection alone.',
485
+ },
486
+ {
487
+ id: 'voice-controls',
488
+ label: 'Voice controls',
489
+ domain: 'voice',
490
+ status: voiceEnabled && voiceReadiness.readyVoiceProviderCount > 0 ? 'ready' : voiceEnabled ? 'attention' : 'setup-needed',
491
+ userOutcome: 'Talk to the assistant through an intentionally enabled voice surface.',
492
+ summary: voiceEnabled
493
+ ? `${voiceReadiness.readyVoiceProviderCount}/${voiceReadiness.voiceProviders.length} voice provider(s) are ready.`
494
+ : 'The voice surface is disabled.',
495
+ nextStep: voiceEnabled
496
+ ? 'Review media posture for provider or voice setup blockers before claiming hands-free use.'
497
+ : 'Enable voice only after the user asks for spoken or push-to-talk interaction.',
498
+ capabilities: ['voice surface toggle', 'push-to-talk setup posture', 'provider readiness'],
499
+ modelRoute: 'agent_harness mode:"media_posture" query:"voice" includeParameters:true',
500
+ setupRoutes: [
501
+ 'settings action:"get" query:"ui.voiceEnabled" includeParameters:true',
502
+ 'agent_harness mode:"media_posture" query:"voice" includeParameters:true',
503
+ ],
504
+ evidence: {
505
+ voiceSurfaceEnabled: voiceEnabled,
506
+ readyVoiceProviders: voiceReadiness.readyVoiceProviderCount,
507
+ totalVoiceProviders: voiceReadiness.voiceProviders.length,
508
+ },
509
+ policy: 'Voice enablement and provider changes stay visible settings or media-provider flows.',
510
+ },
511
+ {
512
+ id: 'tts-spoken-responses',
513
+ label: 'Spoken responses',
514
+ domain: 'voice',
515
+ status: ttsStatus,
516
+ userOutcome: 'Hear assistant responses with a predictable provider and voice.',
517
+ summary: `${voiceReadiness.selectedTtsProviderLabel} is ${voiceReadiness.selectedTtsProviderStatus}; voice configured=${voiceReadiness.ttsVoiceConfigured ? 'yes' : 'no'}.`,
518
+ nextStep: ttsStatus === 'ready'
519
+ ? 'Use explicit TTS routes when the user asks for spoken output.'
520
+ : 'Choose a ready TTS provider and voice before presenting spoken replies as ready.',
521
+ capabilities: ['TTS provider selection', 'voice picker', 'spoken reply route'],
522
+ modelRoute: 'agent_harness mode:"media_posture" query:"tts" includeParameters:true',
523
+ setupRoutes: [
524
+ 'agent_harness mode:"open_ui_surface" surfaceId:"tts-provider-picker" confirm:true explicitUserRequest:"..."',
525
+ 'agent_harness mode:"open_ui_surface" surfaceId:"tts-voice-picker" confirm:true explicitUserRequest:"..."',
526
+ ],
527
+ evidence: {
528
+ selectedTtsProviderStatus: voiceReadiness.selectedTtsProviderStatus,
529
+ ttsVoiceConfigured: voiceReadiness.ttsVoiceConfigured,
530
+ },
531
+ policy: 'Spoken replies and TTS setting changes require explicit user-visible routes.',
532
+ },
533
+ {
534
+ id: 'notifications',
535
+ label: 'Notifications and delivery',
536
+ domain: 'delivery',
537
+ status: notificationStatus,
538
+ userOutcome: 'Receive completion/status messages through a chosen channel without hidden sends.',
539
+ summary: `${readyChannels.length}/${channels.length} channel(s) ready; ${notificationWebhookCount} notification webhook target(s) configured.`,
540
+ nextStep: notificationStatus === 'ready'
541
+ ? 'Use explicit confirmed send/test routes only for the channel the user chose.'
542
+ : enabledChannels.length > 0
543
+ ? 'Finish setup for enabled channels before relying on notifications.'
544
+ : 'Choose one notification channel intentionally, then configure only that surface.',
545
+ capabilities: ['channel delivery', 'desktop notification posture', 'webhook notification posture'],
546
+ modelRoute: 'channels action:"setup"',
547
+ userRoute: '/channels',
548
+ setupRoutes: [
549
+ 'channels action:"status" includeParameters:true',
550
+ 'channels action:"setup"',
551
+ 'agent_harness mode:"notifications"',
552
+ ],
553
+ evidence: {
554
+ readyChannels: readyChannels.length,
555
+ enabledChannels: enabledChannels.length,
556
+ notificationWebhookTargets: notificationWebhookCount,
557
+ },
558
+ policy: 'Delivery tests and outbound messages require explicit confirmed user action.',
559
+ },
560
+ {
561
+ id: 'browser-desktop-control',
562
+ label: 'Browser and desktop control',
563
+ domain: 'desktop',
564
+ status: browserPosture.status,
565
+ userOutcome: 'Control browser, screen, or desktop surfaces only through trusted configured tools.',
566
+ summary: browserPosture.configured
567
+ ? 'A browser/desktop control route is configured.'
568
+ : browserPosture.needsReview
569
+ ? 'Browser/desktop tooling exists but needs trust or schema review.'
570
+ : 'No trusted browser/desktop control route is configured.',
571
+ nextStep: browserPosture.recommendedRoute,
572
+ capabilities: ['browser navigation', 'screenshot/screen observation', 'desktop control'],
573
+ modelRoute: browserPosture.recommendedRoute,
574
+ setupRoutes: [
575
+ browserPosture.setupRoute,
576
+ 'execution action:"status" query:"browser desktop" includeParameters:true',
577
+ ],
578
+ evidence: {
579
+ toolMatches: browserPosture.toolMatches,
580
+ mcpServers: browserPosture.mcpServers.length,
581
+ },
582
+ policy: browserPosture.policy,
583
+ },
584
+ {
585
+ id: 'camera-location-sensors',
586
+ label: 'Camera, screen, and location sensors',
587
+ domain: 'device',
588
+ status: sensorReady ? 'ready' : 'not-published',
589
+ userOutcome: 'Use phone camera, screen, location, or local device commands only after the daemon/companion publishes permission-scoped records.',
590
+ summary: sensorReady
591
+ ? 'The SDK/daemon published certified permission-scoped camera, screen, location, or device-command capability records.'
592
+ : 'Camera, screen, location, and device-command adapters are not published by the current Agent-visible SDK/daemon contract.',
593
+ nextStep: sensorReady
594
+ ? 'Inspect the certified device record and use only its returned confirmed permission/control routes.'
595
+ : 'Inspect operator methods for newly published camera/location/screen contracts before claiming device access.',
596
+ capabilities: [
597
+ 'camera permission posture',
598
+ 'screen capture permission posture',
599
+ 'location permission posture',
600
+ 'local device command routes',
601
+ ...sensorRecords.flatMap((record) => record.capabilities),
602
+ ],
603
+ modelRoute: sensorRecords[0]?.modelRoute ?? 'host action:"methods" query:"camera location device"',
604
+ setupRoutes: [
605
+ ...sensorRecords.slice(0, 3).map((record) => record.modelRoute),
606
+ 'host action:"methods" query:"camera location device"',
607
+ 'host action:"capabilities" includeParameters:true',
608
+ ],
609
+ evidence: {
610
+ publishedByCurrentAgentContract: sensorReady,
611
+ liveDeviceRecords: liveDevice.capabilities.length,
612
+ ...liveDeviceEvidence(sensorRecords),
613
+ },
614
+ policy: 'Agent reports unpublished device sensor APIs honestly and counts device access only when certified SDK/daemon permission-scoped records publish exact routes without raw payloads.',
615
+ },
616
+ ];
617
+ capabilities.sort((left, right) => deviceStatusRank(left.status) - deviceStatusRank(right.status) || left.label.localeCompare(right.label));
618
+ const counts = {
619
+ ready: capabilities.filter((capability) => capability.status === 'ready').length,
620
+ attention: capabilities.filter((capability) => capability.status === 'attention').length,
621
+ setupNeeded: capabilities.filter((capability) => capability.status === 'setup-needed').length,
622
+ notPublished: capabilities.filter((capability) => capability.status === 'not-published').length,
623
+ };
624
+ const primaryNextStep = capabilities.find((capability) => capability.status !== 'ready')?.nextStep
625
+ ?? 'All published companion device capabilities are ready; use explicit confirmed routes for effects.';
626
+ return {
627
+ summary: {
628
+ total: capabilities.length,
629
+ ...counts,
630
+ primaryNextStep,
631
+ },
632
+ capabilities,
633
+ policy: 'Read-only device capability map. It does not pair devices, send notifications, open browsers, change voice settings, capture screens, or claim unpublished sensor APIs.',
634
+ };
635
+ }
636
+
223
637
  export function pairingPostureCatalogStatus(context: CommandContext): Record<string, unknown> {
224
638
  const state = pairingState(context);
639
+ const deviceMap = buildDeviceCapabilityMap(context);
225
640
  return {
226
641
  modes: ['pairing_posture', 'pairing_route'],
227
642
  routes: pairingRoutes().length,
228
- tokenPresent: (state.token as { readonly present?: boolean }).present === true,
643
+ tokenPresent: tokenPresent(state),
644
+ deviceCapabilitySummary: deviceMap.summary,
229
645
  readOnly: true,
230
646
  };
231
647
  }
@@ -234,20 +650,38 @@ export function pairingPostureSummary(context: CommandContext, args: AgentHarnes
234
650
  const query = readString(args.query).toLowerCase();
235
651
  const includeParameters = args.includeParameters === true;
236
652
  const routes = pairingRoutes();
653
+ const deviceMap = buildDeviceCapabilityMap(context);
654
+ const limit = readLimit(args.limit, 100);
237
655
  const filtered = routes
238
- .filter((route) => !query || routeSearchText(route).includes(query))
239
- .slice(0, readLimit(args.limit, 100));
656
+ .filter((route) => routeMatches(route, query))
657
+ .slice(0, limit);
658
+ const includeWholeDeviceMap = queryRequestsWholeDeviceMap(query);
659
+ const filteredCapabilities = deviceMap.capabilities
660
+ .filter((capability) => includeWholeDeviceMap || deviceCapabilityMatches(capability, query))
661
+ .slice(0, limit);
240
662
  return {
241
663
  status: 'available',
664
+ summary: {
665
+ deviceCapabilities: deviceMap.summary,
666
+ returnedDeviceCapabilities: filteredCapabilities.length,
667
+ },
242
668
  pairing: pairingState(context),
243
- routes: filtered.map((route) => describeRoute(route, { includeParameters })),
669
+ deviceCapabilities: filteredCapabilities.map((capability) => includeParameters ? capability : {
670
+ capabilityId: capability.id,
671
+ label: capability.label,
672
+ domain: capability.domain,
673
+ status: capability.status,
674
+ summary: previewHarnessText(capability.summary),
675
+ modelRoute: capability.modelRoute,
676
+ }),
677
+ routes: filtered.map((route) => describeRoute(route, { includeParameters, context })),
244
678
  returned: filtered.length,
245
679
  total: routes.length,
246
- policy: 'Read-only pairing posture. QR display, manual token display, companion connection, channel delivery, task, approval, provider/model, and attachment actions remain explicit visible user flows.',
680
+ policy: `${deviceMap.policy} QR display, manual token display, companion connection, channel delivery, task, approval, provider/model, and attachment actions remain explicit visible user flows.`,
247
681
  };
248
682
  }
249
683
 
250
- export function describeHarnessPairingRoute(_context: CommandContext, args: AgentHarnessPairingArgs): PairingResolution {
684
+ export function describeHarnessPairingRoute(context: CommandContext, args: AgentHarnessPairingArgs): PairingResolution {
251
685
  const lookup = lookupFromArgs(args);
252
686
  if (!lookup) {
253
687
  return {
@@ -258,11 +692,11 @@ export function describeHarnessPairingRoute(_context: CommandContext, args: Agen
258
692
  const routes = pairingRoutes();
259
693
  const normalized = lookup.input.toLowerCase();
260
694
  const exact = routes.find((route) => route.id === lookup.input);
261
- if (exact) return { status: 'found', route: describeRoute(exact, { includeParameters: true, lookup: { ...lookup, resolvedBy: 'id' } }) };
695
+ if (exact) return { status: 'found', route: describeRoute(exact, { includeParameters: true, lookup: { ...lookup, resolvedBy: 'id' }, context }) };
262
696
  const insensitive = routes.find((route) => route.id.toLowerCase() === normalized);
263
- if (insensitive) return { status: 'found', route: describeRoute(insensitive, { includeParameters: true, lookup: { ...lookup, resolvedBy: 'case-insensitive-id' } }) };
264
- const searched = routes.filter((route) => routeSearchText(route).includes(normalized));
265
- if (searched.length === 1) return { status: 'found', route: describeRoute(searched[0]!, { includeParameters: true, lookup: { ...lookup, resolvedBy: 'search' } }) };
697
+ if (insensitive) return { status: 'found', route: describeRoute(insensitive, { includeParameters: true, lookup: { ...lookup, resolvedBy: 'case-insensitive-id' }, context }) };
698
+ const searched = routes.filter((route) => routeMatches(route, normalized));
699
+ if (searched.length === 1) return { status: 'found', route: describeRoute(searched[0]!, { includeParameters: true, lookup: { ...lookup, resolvedBy: 'search' }, context }) };
266
700
  if (searched.length > 1) {
267
701
  return {
268
702
  status: 'ambiguous',