Harden model-backed device commands and status checks - #71
Conversation
Summary: - Map select labels to canonical typed model values. - Reject response-only params in command requests. - Add opt-in exact-ID status polling to the device command flow. - Keep endpoint metadata, documentation, and the shipped skill in sync. Rationale: - Honor model metadata generically. - Stop on ambiguous option mappings before sending. - Expose queue status without treating it as device execution proof. Tests: - npm run lint (0 errors; existing warnings only) - npm test (1,079 passed, 2 skipped) - npm run build - 219 focused command, flow, catalog, and skill tests Co-authored-by: Codex <codex@openai.com>
There was a problem hiding this comment.
Pull request overview
This PR tightens correctness for device command execution by (1) mapping model-backed select inputs to the model’s canonical values before sending, (2) preventing misuse of response-only params in sendCommand requests (favoring extra_params), and (3) adding optional, exact-command-id status polling to the flow.device-command workflow. These changes strengthen deterministic behavior for both CLI users and agent-driven flows.
Changes:
- Add model command select-option normalization/matching and enforce unambiguous mappings before
sendCommand. - Reject request bodies that include response-only
params(CLI-level guard) and documentextra_paramsas the request surface. - Add opt-in
command.polltask and wire it intoflow.device-commandto pollgetCommandsby the exact returned command id.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| tests/model-command-options.test.ts | Adds unit coverage for select-option extraction and matching behavior. |
| tests/flow-runner.test.ts | Extends flow runner tests for select mapping, ambiguity blocking, and optional command status polling. |
| tests/endpoints.test.ts | Validates endpoint catalog docs separate request extra_params from response params. |
| tests/command-poll.test.ts | Adds unit coverage for extracting a single command id and polling by exact id to terminal status/timeout. |
| tests/cli.test.ts | Updates CLI expectations for sendCommand request shape and ensures params is rejected pre-API-call. |
| src/workflows/model-command-options.ts | Implements select-option normalization from model metadata and robust input matching (label/value). |
| src/workflows/flow-runner.ts | Integrates select mapping into model validation and adds command.poll task execution + config validation. |
| src/workflows/flow-catalog.ts | Registers command.poll task type and adds an optional polling step to flow.device-command. |
| src/workflows/command-poll.ts | Implements exact-id polling via organization.commands.getCommands with bounded timeout/interval. |
| src/cli/commands/api.ts | Adds a guard to reject response-only params in raw sendCommand request bodies. |
| src/api-catalog/public-endpoints.json | Updates sendCommand docs example to use extra_params and clarifies params as response/history-only. |
| skills/xyte-cli/SKILL.md | Updates shipped skill guidance for select mapping, extra_params, and optional polling behavior. |
| skills/xyte-cli/references/flow-recipes.md | Documents optional polling knobs and select mapping for flow.device-command. |
| skills/xyte-cli/references/endpoints.md | Clarifies request vs response fields and adds polling usage notes/examples. |
| skills/xyte-cli/data/public-endpoints.json | Mirrors endpoint catalog changes in shipped skill data. |
| README.md | Updates command-send guidance: select mapping, extra_params usage, and optional polling semantics. |
| docs/reference/commands.html | Updates reference docs to reflect select mapping and extra_params/params distinction + polling example. |
| docs/reference/built-in-flows.html | Updates built-in flow descriptions to include select mapping and optional polling. |
| docs/flows/agent-ops.md | Updates operator guidance for flow.device-command, including polling and select mapping constraints. |
| docs/commands.md | Updates raw API examples and documents extra_params/polling semantics for command operations. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary: - send the model command with the API's command selector - map single and multi choices to canonical model values - harden malformed metadata, request bodies, and exact-id polling - update docs, shipped skill, schemas, catalog, workflows, and tests Rationale: - model metadata names and send request fields have different contracts - unavailable or ambiguous choices must never reach the send request Tests: - npm run typecheck - npm run lint - npm test - npm run build - npm run smoke:pack-install - live Playground reboot flow and command-history check Co-authored-by: Codex <codex@openai.com>
Summary: - Treat mapped option keys as canonical values and reject conflicts. - Fail closed when a value also matches another option label. - Document the behavior and skill refresh guidance. Rationale: - Follow the server model metadata contract without numeric heuristics. - Prevent ambiguous model options from sending the wrong value. Tests: - npm test -- --maxWorkers=1 (1163 passed, 2 skipped) - npm run typecheck - npm run build - npm run smoke:pack-install - Packaged Playground reboot accepted and exact ID found in history Co-authored-by: Codex <codex@openai.com>
Summary: - reject malformed string and incomplete command definitions before sending - accept enum cardinality from model typeName across generic field types - isolate command validation and polling from the generic flow runner - share raw send-command request shape checks and cover selector edge cases Rationale: - command mapping must follow model metadata without assuming select-only fields - malformed or ambiguous metadata must fail before the command request Tests: - npm run lint - focused command and flow tests - npm run release:check - built CLI reboot smoke against the Playground tenant Co-authored-by: Codex <codex@openai.com>
Summary: - Validate shipped raw command examples against the send request contract. - Verify skill refresh copies the complete bundle and repairs stale installs. - Clarify generic option mapping for model-defined field cardinality. Rationale: - Prevent outdated selector and parameter guidance from reaching users. - Keep the published skill aligned with fail-closed command validation. Tests: - npm run release:check - ESLint on the changed test files (zero errors) Co-authored-by: Codex <codex@openai.com>
Summary: - Give the filesystem-heavy skill refresh integration test a local 20-second timeout. Rationale: - The test completed in about eight seconds on Windows and exceeded Vitest's default five-second ceiling. - Keep the larger parity assertions without weakening production behavior. Tests: - Targeted skill refresh integration test - npm run release:check Co-authored-by: Codex <codex@openai.com>
Summary: - share model validation across command send paths, including declared types, static choices, required files, and exact poll provenance - preserve the exact command request around uncertain delivery so resume cannot automatically send it again - align documentation, shipped skill data, catalog notes, and edge tests Rationale: - command labels and canonical values must follow each device model without hardcoded vendor values or ambiguous coercion - production retries must not duplicate a non-idempotent command when the original send may already have been accepted Tests: - npm run typecheck - targeted Vitest command, flow, docs, and skill suites - npm run lint - env -u XYTE_CLI_KEY npm run release:check - npm pack --dry-run --json Co-authored-by: Codex <codex@openai.com>
Windows CI hit the same config integration test's five-second limit on two consecutive runs, while the affected command and flow tests passed. Give this filesystem-heavy test the existing twenty-second allowance used by other integration coverage; production behavior is unchanged.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 40 out of 40 changed files in this pull request and generated no new comments.
Suppressed comments (2)
src/workflows/model-command-options.ts:42
- isOptionValue treats any non-empty string as valid even when it contains leading/trailing whitespace. That can allow whitespace-padded canonical option values (including map keys) to propagate into the value that gets sent, which is hard to notice and inconsistent with other command metadata handling that trims or rejects whitespace.
function isOptionValue(value: unknown): value is ModelCommandOptionValue {
return (
(typeof value === 'string' && value.trim().length > 0) ||
typeof value === 'boolean' ||
(typeof value === 'number' && Number.isFinite(value))
src/cli/commands/api.ts:111
- validateSendCommandRequestBody currently catches "name"/"params"/invalid extra_params, but it still allows sendCommand bodies that omit both selectors (command/friendly_name) or provide an empty selector value. Since this command is being hardened to fail fast, it would be better to reject these cases locally with a clear error instead of sending a guaranteed-bad request to the API.
function validateSendCommandRequestBody(key: string, body: unknown): void {
if (key !== 'organization.commands.sendCommand') return;
const inspection = inspectSendCommandRequestBody(body);
if (!inspection) return;
Summary: - bump package metadata and lockfile to 0.14.0 - cut the 0.14.0 changelog and restore the 0.13.0 release entry Rationale: - prepare the tag-driven npm and GitHub release after approval and merge - use a minor release for optional command polling plus command safety fixes Tests: - env -u XYTE_CLI_KEY npm run release:check (clean temporary snapshot) Co-authored-by: Codex <codex@openai.com>
What changed
Device commands now follow the device model consistently wherever the CLI guides a send:
command. Raw or custom calls may usefriendly_name; request fieldnameis rejected.extra_params. Required fields, declared value types, and one-versus-many shape are checked before sending.select.Examples
"33"to HDMI 1, entering HDMI 1 sends"33"."33"also sends"33".["Lobby", "Auditorium"]to that model's exact values.5and rejects"5".This is CLI-only. There are no server changes, new endpoints, or new workflows.
Also updated
The documentation, shipped CLI skill, endpoint guidance, and edge-case coverage now describe the same behavior.
The package and changelog are prepared for version
0.14.0. This PR does not publish anything. Only after approval and merge should the exactmaincommit be taggedv0.14.0; the existing release workflows will then publish the npm package and GitHub release assets.Validation
done.