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