@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.
- package/CHANGELOG.md +329 -0
- package/README.md +46 -30
- package/dist/package/{ast-grep-napi.linux-x64-gnu-mkk8xwww.node → ast-grep-napi.linux-x64-gnu-swtppvy9.node} +0 -0
- package/dist/package/{ast-grep-napi.linux-x64-musl-ryqtgdv6.node → ast-grep-napi.linux-x64-musl-ttfcdtap.node} +0 -0
- package/dist/package/main.js +138903 -91477
- package/docs/README.md +14 -7
- package/docs/channels-remote-and-api.md +8 -5
- package/docs/connected-host.md +14 -12
- package/docs/getting-started.md +58 -30
- package/docs/knowledge-artifacts-and-multimodal.md +7 -5
- package/docs/project-planning.md +2 -2
- package/docs/providers-and-routing.md +11 -3
- package/docs/tools-and-commands.md +116 -45
- package/docs/voice-and-live-tts.md +3 -1
- package/package.json +3 -2
- package/release/release-notes.md +142 -5
- package/release/release-readiness.json +124 -94
- package/src/agent/assistant-cockpit.ts +247 -0
- package/src/agent/autonomy-schedule-format.ts +96 -0
- package/src/agent/autonomy-schedule.ts +514 -0
- package/src/agent/channel-delivery-receipts.ts +292 -0
- package/src/agent/competitive-feature-inventory.ts +294 -0
- package/src/agent/document-registry-types.ts +106 -0
- package/src/agent/document-registry.ts +734 -0
- package/src/agent/harness-control.ts +2 -2
- package/src/agent/memory-prompt.ts +2 -2
- package/src/agent/operator-actions.ts +50 -6
- package/src/agent/persona-registry.ts +8 -0
- package/src/agent/project-context-files.ts +273 -0
- package/src/agent/prompt-context-receipts.ts +502 -0
- package/src/agent/reminder-schedule-format.ts +16 -2
- package/src/agent/research-run-registry.ts +582 -0
- package/src/agent/research-source-registry.ts +441 -0
- package/src/agent/routine-registry.ts +19 -3
- package/src/agent/routine-schedule-format.ts +27 -2
- package/src/agent/runtime-profile-starters.ts +7 -0
- package/src/agent/runtime-profile.ts +106 -4
- package/src/agent/schedule-edit-format.ts +129 -0
- package/src/agent/schedule-edit.ts +496 -0
- package/src/agent/schedule-next-routes.ts +42 -0
- package/src/agent/setup-wizard-artifact-receipts.ts +366 -0
- package/src/agent/setup-wizard-checkpoint.ts +140 -0
- package/src/agent/setup-wizard.ts +683 -0
- package/src/agent/skill-registry-types.ts +102 -0
- package/src/agent/skill-registry.ts +81 -107
- package/src/agent/vibe-confirmation-routes.ts +62 -0
- package/src/agent/vibe-file.ts +285 -0
- package/src/cli/agent-knowledge-command.ts +45 -2
- package/src/cli/help.ts +3 -0
- package/src/cli/profiles-command.ts +25 -11
- package/src/config/agent-settings-policy.ts +1 -18
- package/src/input/agent-workspace-activation.ts +53 -0
- package/src/input/agent-workspace-artifact-browser-editor.ts +494 -0
- package/src/input/agent-workspace-artifact-metadata.ts +17 -0
- package/src/input/agent-workspace-basic-command-editors.ts +5 -1
- package/src/input/agent-workspace-categories.ts +153 -155
- package/src/input/agent-workspace-channel-triage.ts +481 -0
- package/src/input/agent-workspace-channels.ts +263 -0
- package/src/input/agent-workspace-command-editor.ts +152 -0
- package/src/input/agent-workspace-context-snapshot.ts +349 -0
- package/src/input/agent-workspace-delegation-editor-submission.ts +8 -0
- package/src/input/agent-workspace-document-editor.ts +502 -0
- package/src/input/agent-workspace-document-ops-editor.ts +523 -0
- package/src/input/agent-workspace-host-category.ts +1 -1
- package/src/input/agent-workspace-local-library-snapshot.ts +167 -0
- package/src/input/agent-workspace-model-compare-editor.ts +376 -0
- package/src/input/agent-workspace-model-compare-prompt-submission.ts +552 -0
- package/src/input/agent-workspace-model-compare-types.ts +82 -0
- package/src/input/agent-workspace-model-compare-utils.ts +35 -0
- package/src/input/agent-workspace-onboarding-categories.ts +141 -0
- package/src/input/agent-workspace-operations-command-editor-submission.ts +53 -0
- package/src/input/agent-workspace-operations-command-editors.ts +22 -0
- package/src/input/agent-workspace-research-report-editor.ts +212 -0
- package/src/input/agent-workspace-research-run-editor.ts +144 -0
- package/src/input/agent-workspace-research-source-editor.ts +167 -0
- package/src/input/agent-workspace-review-packet-snapshot.ts +593 -0
- package/src/input/agent-workspace-review-packet-utils.ts +265 -0
- package/src/input/agent-workspace-settings.ts +313 -30
- package/src/input/agent-workspace-setup-checkpoint-action.ts +127 -0
- package/src/input/agent-workspace-setup-snapshot.ts +217 -0
- package/src/input/agent-workspace-setup.ts +61 -1
- package/src/input/agent-workspace-snapshot.ts +184 -149
- package/src/input/agent-workspace-types.ts +287 -2
- package/src/input/agent-workspace.ts +23 -1
- package/src/input/command-registry.ts +9 -2
- package/src/input/commands/agent-runtime-profile-runtime.ts +28 -13
- package/src/input/commands/channels-runtime.ts +102 -114
- package/src/input/commands/delegation-runtime.ts +87 -24
- package/src/input/commands/knowledge-browser-flags.ts +12 -0
- package/src/input/commands/knowledge.ts +28 -14
- package/src/input/commands/operator-actions-runtime.ts +6 -3
- package/src/input/commands/schedule-runtime.ts +71 -14
- package/src/input/commands/session-content.ts +2 -2
- package/src/input/commands/shell-core.ts +2 -0
- package/src/input/commands/vibe-runtime.ts +140 -0
- package/src/input/commands.ts +2 -0
- package/src/input/connected-host-routes.ts +123 -0
- package/src/input/settings-modal-types.ts +14 -0
- package/src/input/setup-wizard-live-receipts.ts +76 -0
- package/src/main.ts +15 -15
- package/src/panels/qr-panel.ts +2 -2
- package/src/renderer/agent-workspace-context-lines.ts +701 -0
- package/src/renderer/agent-workspace.ts +68 -421
- package/src/renderer/settings-modal-helpers.ts +12 -0
- package/src/renderer/settings-modal.ts +12 -0
- package/src/runtime/bootstrap-command-context.ts +11 -1
- package/src/runtime/bootstrap-command-parts.ts +14 -3
- package/src/runtime/bootstrap-core.ts +39 -1
- package/src/runtime/bootstrap-shell.ts +6 -0
- package/src/runtime/bootstrap.ts +113 -30
- package/src/runtime/connected-host-auth.ts +3 -2
- package/src/runtime/execution-ledger.ts +231 -0
- package/src/runtime/services.ts +88 -10
- package/src/runtime/tool-permission-safety.ts +199 -5
- package/src/shell/session-continuity-hints.ts +39 -0
- package/src/tools/agent-artifacts-tool.ts +606 -0
- package/src/tools/agent-audit-tool.ts +155 -0
- package/src/tools/agent-autonomy-schedule-tool.ts +238 -0
- package/src/tools/agent-autonomy-tool.ts +140 -0
- package/src/tools/agent-channel-send-tool.ts +24 -2
- package/src/tools/agent-channels-tool.ts +140 -0
- package/src/tools/agent-computer-tool.ts +168 -0
- package/src/tools/agent-context-policy.ts +137 -7
- package/src/tools/agent-context-tool.ts +143 -0
- package/src/tools/agent-delegation-tool.ts +128 -0
- package/src/tools/agent-device-tool.ts +240 -0
- package/src/tools/agent-documents-tool.ts +684 -0
- package/src/tools/agent-execution-tool.ts +230 -0
- package/src/tools/agent-harness-agent-orchestration-policy.ts +75 -0
- package/src/tools/agent-harness-agent-orchestration.ts +791 -0
- package/src/tools/agent-harness-autonomy-intake.ts +785 -0
- package/src/tools/agent-harness-autonomy-live-records.ts +742 -0
- package/src/tools/agent-harness-autonomy-queue-types.ts +88 -0
- package/src/tools/agent-harness-autonomy-queue.ts +571 -0
- package/src/tools/agent-harness-autonomy-watcher-read-models.ts +509 -0
- package/src/tools/agent-harness-background-processes-types.ts +35 -0
- package/src/tools/agent-harness-background-processes.ts +798 -0
- package/src/tools/agent-harness-browser-cockpit-route.ts +373 -0
- package/src/tools/agent-harness-browser-control.ts +346 -0
- package/src/tools/agent-harness-browser-pwa-read-models.ts +622 -0
- package/src/tools/agent-harness-channel-metadata.ts +168 -10
- package/src/tools/agent-harness-cli-command-policy.ts +110 -0
- package/src/tools/agent-harness-command-catalog.ts +4 -2
- package/src/tools/agent-harness-command-runner.ts +42 -0
- package/src/tools/agent-harness-connected-host-status.ts +7 -4
- package/src/tools/agent-harness-delegation-posture.ts +234 -19
- package/src/tools/agent-harness-device-live-read-models.ts +366 -0
- package/src/tools/agent-harness-document-ops-reviewer-readiness.ts +234 -0
- package/src/tools/agent-harness-document-ops-types.ts +72 -0
- package/src/tools/agent-harness-document-ops.ts +671 -0
- package/src/tools/agent-harness-execution-history.ts +489 -0
- package/src/tools/agent-harness-execution-posture.ts +385 -0
- package/src/tools/agent-harness-file-recovery.ts +135 -0
- package/src/tools/agent-harness-interactive-runtime-records.ts +421 -0
- package/src/tools/agent-harness-keybinding-metadata.ts +16 -12
- package/src/tools/agent-harness-learning-curator-common.ts +102 -0
- package/src/tools/agent-harness-learning-curator-consolidation.ts +295 -0
- package/src/tools/agent-harness-learning-curator-proposals.ts +606 -0
- package/src/tools/agent-harness-learning-curator-types.ts +151 -0
- package/src/tools/agent-harness-learning-curator.ts +417 -0
- package/src/tools/agent-harness-local-model-benchmarks.ts +269 -0
- package/src/tools/agent-harness-local-model-cookbook.ts +238 -0
- package/src/tools/agent-harness-local-model-endpoints.ts +788 -0
- package/src/tools/agent-harness-local-model-smoke.ts +277 -0
- package/src/tools/agent-harness-local-model-url.ts +78 -0
- package/src/tools/agent-harness-media-posture.ts +223 -2
- package/src/tools/agent-harness-memory-external-providers.ts +796 -0
- package/src/tools/agent-harness-memory-posture.ts +672 -0
- package/src/tools/agent-harness-memory-provider-certification.ts +219 -0
- package/src/tools/agent-harness-memory-refinement.ts +340 -0
- package/src/tools/agent-harness-metadata.ts +100 -168
- package/src/tools/agent-harness-mode-catalog.ts +86 -33
- package/src/tools/agent-harness-model-catalog.ts +162 -0
- package/src/tools/agent-harness-model-provider-health.ts +283 -0
- package/src/tools/agent-harness-model-readiness.ts +432 -0
- package/src/tools/agent-harness-model-routing-types.ts +327 -0
- package/src/tools/agent-harness-model-routing-utils.ts +30 -0
- package/src/tools/agent-harness-model-routing.ts +165 -193
- package/src/tools/agent-harness-operator-methods.ts +115 -133
- package/src/tools/agent-harness-pairing-posture.ts +452 -18
- package/src/tools/agent-harness-personal-ops-certification.ts +116 -0
- package/src/tools/agent-harness-personal-ops-discovery.ts +533 -0
- package/src/tools/agent-harness-personal-ops-intake.ts +385 -0
- package/src/tools/agent-harness-personal-ops-lanes.ts +270 -0
- package/src/tools/agent-harness-personal-ops-operations.ts +225 -0
- package/src/tools/agent-harness-personal-ops-provider-records.ts +358 -0
- package/src/tools/agent-harness-personal-ops-provider-task-records.ts +321 -0
- package/src/tools/agent-harness-personal-ops-records.ts +684 -0
- package/src/tools/agent-harness-personal-ops-runner.ts +637 -0
- package/src/tools/agent-harness-personal-ops-types.ts +240 -0
- package/src/tools/agent-harness-personal-ops.ts +613 -0
- package/src/tools/agent-harness-project-context.ts +144 -0
- package/src/tools/agent-harness-prompt-context.ts +589 -0
- package/src/tools/agent-harness-provider-account-metadata.ts +2 -2
- package/src/tools/agent-harness-release-evidence.ts +9 -7
- package/src/tools/agent-harness-release-readiness.ts +9 -7
- package/src/tools/agent-harness-remote-read-models.ts +541 -0
- package/src/tools/agent-harness-research-briefing.ts +446 -0
- package/src/tools/agent-harness-research-live-read-models.ts +500 -0
- package/src/tools/agent-harness-research-queue.ts +250 -0
- package/src/tools/agent-harness-research-runs.ts +307 -0
- package/src/tools/agent-harness-research-workflow.ts +395 -0
- package/src/tools/agent-harness-security-posture.ts +2 -2
- package/src/tools/agent-harness-service-posture.ts +9 -9
- package/src/tools/agent-harness-session-metadata.ts +3 -3
- package/src/tools/agent-harness-setup-connected-host.ts +501 -0
- package/src/tools/agent-harness-setup-describe.ts +557 -0
- package/src/tools/agent-harness-setup-handoffs.ts +502 -0
- package/src/tools/agent-harness-setup-model-helpers.ts +102 -0
- package/src/tools/agent-harness-setup-plan.ts +253 -0
- package/src/tools/agent-harness-setup-posture-types.ts +218 -0
- package/src/tools/agent-harness-setup-posture-utils.ts +173 -0
- package/src/tools/agent-harness-setup-posture.ts +449 -221
- package/src/tools/agent-harness-setup-smoke.ts +533 -0
- package/src/tools/agent-harness-sudo-posture.ts +114 -0
- package/src/tools/agent-harness-tool-schema.ts +142 -7
- package/src/tools/agent-harness-tool-types.ts +83 -0
- package/src/tools/agent-harness-tool-utils.ts +43 -0
- package/src/tools/agent-harness-tool.ts +319 -370
- package/src/tools/agent-harness-ui-surface-metadata.ts +76 -69
- package/src/tools/agent-harness-ui-surface-types.ts +46 -0
- package/src/tools/agent-harness-vibe-health.ts +105 -0
- package/src/tools/agent-harness-workspace-action-runner.ts +149 -0
- package/src/tools/agent-harness-workspace-actions.ts +114 -8
- package/src/tools/agent-harness-workspace-editor-execution.ts +214 -0
- package/src/tools/agent-harness-workspace-editor-runner.ts +755 -0
- package/src/tools/agent-host-tool.ts +159 -0
- package/src/tools/agent-knowledge-ingest-tool.ts +34 -1
- package/src/tools/agent-learning-consolidation-core.ts +327 -0
- package/src/tools/agent-learning-consolidation-tool.ts +758 -0
- package/src/tools/agent-memory-tool.ts +253 -0
- package/src/tools/agent-model-compare-export.ts +315 -0
- package/src/tools/agent-model-compare-handoff.ts +592 -0
- package/src/tools/agent-model-compare-judgment.ts +633 -0
- package/src/tools/agent-model-compare-run.ts +735 -0
- package/src/tools/agent-model-compare-tool.ts +698 -0
- package/src/tools/agent-model-compare-types.ts +191 -0
- package/src/tools/agent-model-compare-utils.ts +93 -0
- package/src/tools/agent-models-tool.ts +208 -0
- package/src/tools/agent-operator-action-tool.ts +1 -1
- package/src/tools/agent-operator-method-tool.ts +643 -0
- package/src/tools/agent-personal-ops-tool.ts +197 -0
- package/src/tools/agent-policy-explanation.ts +415 -0
- package/src/tools/agent-research-report-tool.ts +608 -0
- package/src/tools/agent-research-runner.ts +367 -0
- package/src/tools/agent-research-runs-tool.ts +434 -0
- package/src/tools/agent-research-sources-tool.ts +443 -0
- package/src/tools/agent-research-tool.ts +515 -0
- package/src/tools/agent-review-packet-presets-core.ts +757 -0
- package/src/tools/agent-review-packet-presets-tool.ts +466 -0
- package/src/tools/agent-review-packet-share-tool.ts +305 -0
- package/src/tools/agent-route-planner-candidates-setup.ts +411 -0
- package/src/tools/agent-route-planner-candidates-surfaces.ts +432 -0
- package/src/tools/agent-route-planner-candidates-work.ts +251 -0
- package/src/tools/agent-route-planner-helpers.ts +667 -0
- package/src/tools/agent-route-planner.ts +185 -0
- package/src/tools/agent-route-tool.ts +105 -0
- package/src/tools/agent-schedule-edit-tool.ts +210 -0
- package/src/tools/agent-schedule-tool.ts +282 -0
- package/src/tools/agent-security-tool.ts +145 -0
- package/src/tools/agent-sessions-tool.ts +122 -0
- package/src/tools/agent-settings-import-tool.ts +104 -0
- package/src/tools/agent-settings-tool.ts +176 -0
- package/src/tools/agent-setup-tool.ts +226 -0
- package/src/tools/agent-support-tool.ts +122 -0
- package/src/tools/agent-terminal-process-tools.ts +167 -0
- package/src/tools/agent-tool-policy-guard-types.ts +77 -0
- package/src/tools/agent-tool-policy-guard.ts +106 -85
- package/src/tools/agent-vibe-tool.ts +297 -0
- package/src/tools/agent-work-plan-tool.ts +265 -6
- package/src/tools/agent-workspace-tool.ts +305 -0
- package/src/tools/artifact-archive.ts +169 -0
- package/src/tools/tool-definition-compaction.ts +36 -0
- 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: '
|
|
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: '
|
|
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: '
|
|
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: {
|
|
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: '
|
|
184
|
-
connectedHostCapabilities: '
|
|
185
|
-
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
|
|
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) =>
|
|
239
|
-
.slice(0,
|
|
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
|
-
|
|
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:
|
|
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(
|
|
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) =>
|
|
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',
|