Test Report – SDK Bridge

Generated: 2026-03-14T13:31:04.474Z | Exit code: 0 | Duration: 35 ms

100
Total
100
Passed
0
Failed
0
Skipped
All tests passed
Status

Tests by file

gateway starts and GET /health returns 200

WebSocket ping returns ok

WebSocket getCapabilities returns supportedMethods and capabilityRegistry

WebSocket executePlan dry-run returns results and success

WebSocket auditListRecent returns actions array

WebSocket automationList returns automations array

WebSocket getDesktopState returns object

WebSocket emergencyStop and getDeniedPermissions

WebSocket invalid method returns error

checkPlan with low-risk steps returns allowed

checkPlan with high-risk steps sets requiresApproval

checkPlan with missing steps returns error

setPermission deny then checkPlan denies plan

setPermission then getDeniedPermissions lists denied

executePlan with denied capability returns PERMISSION_DENIED

executePlan with empty steps returns success

executePlan without steps param returns error

auditListRecent with limit 3 returns at most 3

auditRevert with non-existent id returns error

automationCreate returns id and automationList includes it

automationUpdate then automationList reflects change

automationPause then automation has enabled false

automationDelete removes automation

getDesktopState returns object with expected keys or empty

emergencyStop then emergencyResume restores execution

executePlan dry-run with multiple capabilities returns one result per step

getPlan without message returns error

setPermission without capabilityId returns error

auditRevert without actionId returns error

automationCreate without trigger returns error

getPlan with real message returns plan when E2E_GETPLAN=1

getDesktopState returns live desktop data (windows/panels when available)

logExecution returns action id and listRecent includes it

listRecent respects limit and returns newest first

revert returns error for unknown actionId

revert returns reverted true for known action (no snapshots to restore)

listCapabilityIds returns non-empty array

execute with unknown capabilityId returns ok false

notification.show dry-run returns ok true

window.list dry-run returns ok true

power.lock_screen dry-run returns ok true

display.brightness.set dry-run returns ok true

audio.volume.set dry-run returns ok true

app.launch dry-run returns ok true

panel.get_layout dry-run returns ok true

theme.preview dry-run returns ok true

getList returns array with expected capability ids

each entry has id and schema

register adds or updates capability

executePlan with dryRun returns success and results without running adapters

executePlan with unknown capability returns ok false for that step

executePlan when emergency paused returns empty results and success false

executePlan with empty steps returns success true

getRiskLevel returns high for settings.write and theme.apply

getRiskLevel returns medium for window.move and app.launch

getRiskLevel returns low for notification.show and window.list

checkPlan allows plan when no capability denied

checkPlan denies when capability is denied

checkPlan sets requiresApproval for high-risk steps

getDeniedList and setPermission

emergency pause state

SUPPORTED_METHODS includes expected methods

validateRequest rejects missing method

validateRequest rejects unknown method

validateRequest accepts chat with message

validateRequest rejects chat without message

validateRequest accepts health and ping with no params

validateRequest accepts executePlan with steps array

validateRequest rejects executePlan without steps

validateRequest accepts getPlan with message

validateRequest accepts setPermission with capabilityId and allow

validateRequest accepts auditRevert with actionId

validateRequest accepts automationCreate with trigger and steps

validateRequest accepts getDesktopState, emergencyStop, emergencyResume, getDeniedPermissions with no params

sendResponse and sendError produce valid envelope shape

sendEvent produces event envelope

route returns provider opencode for any intent

route returns provider for short quick-desktop style intent

route returns provider for long intent

route handles empty intent

initScheduler with null dir and list returns array

create returns id and list includes new automation

update changes steps and enabled

setPaused pauses and resumes

remove deletes automation

remove throws for unknown id

validateWithSchema accepts valid chat params

validateWithSchema rejects chat without message

validateWithSchema accepts valid generateCode params

validateWithSchema accepts valid editCode params

validateWithSchema accepts valid writeFile params

validateWithSchema passes through unknown method with ok true

getDesktopState returns an object

getDesktopState may have windows, panels, theme keys

in-memory setSnapshot and getSnapshot

getSnapshot returns null for missing key

deleteSnapshot removes key

restoreSnapshot returns error for non-restorable snapshot

restoreSnapshot returns error for missing snapshot

file-backed persistence when stateDir is set

All tests

StatusTestFile
passed gateway starts and GET /health returns 200 gateway starts and GET /health returns 200
passed WebSocket ping returns ok WebSocket ping returns ok
passed WebSocket getCapabilities returns supportedMethods and capabilityRegistry WebSocket getCapabilities returns supportedMethods and capabilityRegistry
passed WebSocket executePlan dry-run returns results and success WebSocket executePlan dry-run returns results and success
passed WebSocket auditListRecent returns actions array WebSocket auditListRecent returns actions array
passed WebSocket automationList returns automations array WebSocket automationList returns automations array
passed WebSocket getDesktopState returns object WebSocket getDesktopState returns object
passed WebSocket emergencyStop and getDeniedPermissions WebSocket emergencyStop and getDeniedPermissions
passed WebSocket invalid method returns error WebSocket invalid method returns error
passed checkPlan with low-risk steps returns allowed checkPlan with low-risk steps returns allowed
passed checkPlan with high-risk steps sets requiresApproval checkPlan with high-risk steps sets requiresApproval
passed checkPlan with missing steps returns error checkPlan with missing steps returns error
passed setPermission deny then checkPlan denies plan setPermission deny then checkPlan denies plan
passed setPermission then getDeniedPermissions lists denied setPermission then getDeniedPermissions lists denied
passed executePlan with denied capability returns PERMISSION_DENIED executePlan with denied capability returns PERMISSION_DENIED
passed executePlan with empty steps returns success executePlan with empty steps returns success
passed executePlan without steps param returns error executePlan without steps param returns error
passed auditListRecent with limit 3 returns at most 3 auditListRecent with limit 3 returns at most 3
passed auditRevert with non-existent id returns error auditRevert with non-existent id returns error
passed automationCreate returns id and automationList includes it automationCreate returns id and automationList includes it
passed automationUpdate then automationList reflects change automationUpdate then automationList reflects change
passed automationPause then automation has enabled false automationPause then automation has enabled false
passed automationDelete removes automation automationDelete removes automation
passed getDesktopState returns object with expected keys or empty getDesktopState returns object with expected keys or empty
passed emergencyStop then emergencyResume restores execution emergencyStop then emergencyResume restores execution
passed executePlan dry-run with multiple capabilities returns one result per step executePlan dry-run with multiple capabilities returns one result per step
passed getPlan without message returns error getPlan without message returns error
passed setPermission without capabilityId returns error setPermission without capabilityId returns error
passed auditRevert without actionId returns error auditRevert without actionId returns error
passed automationCreate without trigger returns error automationCreate without trigger returns error
passed getPlan with real message returns plan when E2E_GETPLAN=1 getPlan with real message returns plan when E2E_GETPLAN=1
passed getDesktopState returns live desktop data (windows/panels when available) getDesktopState returns live desktop data (windows/panels when available)
passed logExecution returns action id and listRecent includes it logExecution returns action id and listRecent includes it
passed listRecent respects limit and returns newest first listRecent respects limit and returns newest first
passed revert returns error for unknown actionId revert returns error for unknown actionId
passed revert returns reverted true for known action (no snapshots to restore) revert returns reverted true for known action (no snapshots to restore)
passed listCapabilityIds returns non-empty array listCapabilityIds returns non-empty array
passed execute with unknown capabilityId returns ok false execute with unknown capabilityId returns ok false
passed notification.show dry-run returns ok true notification.show dry-run returns ok true
passed window.list dry-run returns ok true window.list dry-run returns ok true
passed power.lock_screen dry-run returns ok true power.lock_screen dry-run returns ok true
passed display.brightness.set dry-run returns ok true display.brightness.set dry-run returns ok true
passed audio.volume.set dry-run returns ok true audio.volume.set dry-run returns ok true
passed app.launch dry-run returns ok true app.launch dry-run returns ok true
passed panel.get_layout dry-run returns ok true panel.get_layout dry-run returns ok true
passed theme.preview dry-run returns ok true theme.preview dry-run returns ok true
passed getList returns array with expected capability ids getList returns array with expected capability ids
passed each entry has id and schema each entry has id and schema
passed register adds or updates capability register adds or updates capability
passed executePlan with dryRun returns success and results without running adapters executePlan with dryRun returns success and results without running adapters
passed executePlan with unknown capability returns ok false for that step executePlan with unknown capability returns ok false for that step
passed executePlan when emergency paused returns empty results and success false executePlan when emergency paused returns empty results and success false
passed executePlan with empty steps returns success true executePlan with empty steps returns success true
passed getRiskLevel returns high for settings.write and theme.apply getRiskLevel returns high for settings.write and theme.apply
passed getRiskLevel returns medium for window.move and app.launch getRiskLevel returns medium for window.move and app.launch
passed getRiskLevel returns low for notification.show and window.list getRiskLevel returns low for notification.show and window.list
passed checkPlan allows plan when no capability denied checkPlan allows plan when no capability denied
passed checkPlan denies when capability is denied checkPlan denies when capability is denied
passed checkPlan sets requiresApproval for high-risk steps checkPlan sets requiresApproval for high-risk steps
passed getDeniedList and setPermission getDeniedList and setPermission
passed emergency pause state emergency pause state
passed SUPPORTED_METHODS includes expected methods SUPPORTED_METHODS includes expected methods
passed validateRequest rejects missing method validateRequest rejects missing method
passed validateRequest rejects unknown method validateRequest rejects unknown method
passed validateRequest accepts chat with message validateRequest accepts chat with message
passed validateRequest rejects chat without message validateRequest rejects chat without message
passed validateRequest accepts health and ping with no params validateRequest accepts health and ping with no params
passed validateRequest accepts executePlan with steps array validateRequest accepts executePlan with steps array
passed validateRequest rejects executePlan without steps validateRequest rejects executePlan without steps
passed validateRequest accepts getPlan with message validateRequest accepts getPlan with message
passed validateRequest accepts setPermission with capabilityId and allow validateRequest accepts setPermission with capabilityId and allow
passed validateRequest accepts auditRevert with actionId validateRequest accepts auditRevert with actionId
passed validateRequest accepts automationCreate with trigger and steps validateRequest accepts automationCreate with trigger and steps
passed validateRequest accepts getDesktopState, emergencyStop, emergencyResume, getDeniedPermissions with no params validateRequest accepts getDesktopState, emergencyStop, emergencyResume, getDeniedPermissions with no params
passed sendResponse and sendError produce valid envelope shape sendResponse and sendError produce valid envelope shape
passed sendEvent produces event envelope sendEvent produces event envelope
passed route returns provider opencode for any intent route returns provider opencode for any intent
passed route returns provider for short quick-desktop style intent route returns provider for short quick-desktop style intent
passed route returns provider for long intent route returns provider for long intent
passed route handles empty intent route handles empty intent
passed initScheduler with null dir and list returns array initScheduler with null dir and list returns array
passed create returns id and list includes new automation create returns id and list includes new automation
passed update changes steps and enabled update changes steps and enabled
passed setPaused pauses and resumes setPaused pauses and resumes
passed remove deletes automation remove deletes automation
passed remove throws for unknown id remove throws for unknown id
passed validateWithSchema accepts valid chat params validateWithSchema accepts valid chat params
passed validateWithSchema rejects chat without message validateWithSchema rejects chat without message
passed validateWithSchema accepts valid generateCode params validateWithSchema accepts valid generateCode params
passed validateWithSchema accepts valid editCode params validateWithSchema accepts valid editCode params
passed validateWithSchema accepts valid writeFile params validateWithSchema accepts valid writeFile params
passed validateWithSchema passes through unknown method with ok true validateWithSchema passes through unknown method with ok true
passed getDesktopState returns an object getDesktopState returns an object
passed getDesktopState may have windows, panels, theme keys getDesktopState may have windows, panels, theme keys
passed in-memory setSnapshot and getSnapshot in-memory setSnapshot and getSnapshot
passed getSnapshot returns null for missing key getSnapshot returns null for missing key
passed deleteSnapshot removes key deleteSnapshot removes key
passed restoreSnapshot returns error for non-restorable snapshot restoreSnapshot returns error for non-restorable snapshot
passed restoreSnapshot returns error for missing snapshot restoreSnapshot returns error for missing snapshot
passed file-backed persistence when stateDir is set file-backed persistence when stateDir is set

Raw output (TAP)

TAP version 13
# {"timestamp":1773495064310,"level":"info","event":"server.listening","port":18770,"sdkInitialized":false}
# Subtest: gateway starts and GET /health returns 200
# {"timestamp":1773495064337,"level":"info","event":"server.listening","port":18770,"sdkInitialized":false}
ok 1 - gateway starts and GET /health returns 200
  ---
  duration_ms: 35.262801
  ...
# {"timestamp":1773495064347,"level":"info","event":"ws.connected","sdkInitialized":false}
# Subtest: WebSocket ping returns ok
# {"timestamp":1773495064354,"level":"info","event":"server.listening","port":18770,"sdkInitialized":false}
ok 2 - WebSocket ping returns ok
  ---
  duration_ms: 17.017428
  ...
# {"timestamp":1773495064356,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064358,"level":"info","event":"ws.closed"}
# Subtest: WebSocket getCapabilities returns supportedMethods and capabilityRegistry
# {"timestamp":1773495064360,"level":"info","event":"server.listening","port":18770,"sdkInitialized":false}
ok 3 - WebSocket getCapabilities returns supportedMethods and capabilityRegistry
  ---
  duration_ms: 6.600831
  ...
# {"timestamp":1773495064363,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064364,"level":"info","event":"ws.closed"}
# {"timestamp":1773495064365,"level":"info","event":"executor.step","capabilityId":"notification.show","ok":true,"dryRun":true}
# Subtest: WebSocket executePlan dry-run returns results and success
# {"timestamp":1773495064366,"level":"info","event":"server.listening","port":18770,"sdkInitialized":false}
ok 4 - WebSocket executePlan dry-run returns results and success
  ---
  duration_ms: 5.651215
  ...
# {"timestamp":1773495064368,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064369,"level":"info","event":"ws.closed"}
# Subtest: WebSocket auditListRecent returns actions array
# {"timestamp":1773495064370,"level":"info","event":"server.listening","port":18770,"sdkInitialized":false}
ok 5 - WebSocket auditListRecent returns actions array
  ---
  duration_ms: 4.000918
  ...
# {"timestamp":1773495064372,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064373,"level":"info","event":"ws.closed"}
# Subtest: WebSocket automationList returns automations array
# {"timestamp":1773495064375,"level":"info","event":"server.listening","port":18770,"sdkInitialized":false}
ok 6 - WebSocket automationList returns automations array
  ---
  duration_ms: 4.229713
  ...
# {"timestamp":1773495064376,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064377,"level":"info","event":"ws.closed"}
# Subtest: WebSocket getDesktopState returns object
# {"timestamp":1773495064385,"level":"info","event":"server.listening","port":18770,"sdkInitialized":false}
ok 7 - WebSocket getDesktopState returns object
  ---
  duration_ms: 9.957598
  ...
# {"timestamp":1773495064387,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064388,"level":"info","event":"ws.closed"}
# Subtest: WebSocket emergencyStop and getDeniedPermissions
# {"timestamp":1773495064390,"level":"info","event":"server.listening","port":18770,"sdkInitialized":false}
ok 8 - WebSocket emergencyStop and getDeniedPermissions
  ---
  duration_ms: 4.705925
  ...
# {"timestamp":1773495064392,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064393,"level":"info","event":"ws.closed"}
# Subtest: WebSocket invalid method returns error
ok 9 - WebSocket invalid method returns error
  ---
  duration_ms: 3.990377
  ...
# {"timestamp":1773495064394,"level":"info","event":"ws.closed"}
# {"timestamp":1773495064306,"level":"info","event":"server.listening","port":18771,"sdkInitialized":false}
# {"timestamp":1773495064313,"level":"info","event":"ws.connected","sdkInitialized":false}
# Subtest: checkPlan with low-risk steps returns allowed
# {"timestamp":1773495064320,"level":"info","event":"server.listening","port":18771,"sdkInitialized":false}
ok 10 - checkPlan with low-risk steps returns allowed
  ---
  duration_ms: 23.079922
  ...
# {"timestamp":1773495064322,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064323,"level":"info","event":"ws.closed"}
# Subtest: checkPlan with high-risk steps sets requiresApproval
# {"timestamp":1773495064326,"level":"info","event":"server.listening","port":18771,"sdkInitialized":false}
ok 11 - checkPlan with high-risk steps sets requiresApproval
  ---
  duration_ms: 5.921204
  ...
# {"timestamp":1773495064328,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064328,"level":"info","event":"ws.closed"}
# Subtest: checkPlan with missing steps returns error
# {"timestamp":1773495064330,"level":"info","event":"server.listening","port":18771,"sdkInitialized":false}
ok 12 - checkPlan with missing steps returns error
  ---
  duration_ms: 4.217445
  ...
# {"timestamp":1773495064331,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064332,"level":"info","event":"ws.closed"}
# Subtest: setPermission deny then checkPlan denies plan
# {"timestamp":1773495064334,"level":"info","event":"server.listening","port":18771,"sdkInitialized":false}
ok 13 - setPermission deny then checkPlan denies plan
  ---
  duration_ms: 4.129147
  ...
# {"timestamp":1773495064336,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064337,"level":"info","event":"ws.closed"}
# Subtest: setPermission then getDeniedPermissions lists denied
# {"timestamp":1773495064339,"level":"info","event":"server.listening","port":18771,"sdkInitialized":false}
ok 14 - setPermission then getDeniedPermissions lists denied
  ---
  duration_ms: 4.829458
  ...
# {"timestamp":1773495064343,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064344,"level":"info","event":"ws.closed"}
# Subtest: executePlan with denied capability returns PERMISSION_DENIED
# {"timestamp":1773495064346,"level":"info","event":"server.listening","port":18771,"sdkInitialized":false}
ok 15 - executePlan with denied capability returns PERMISSION_DENIED
  ---
  duration_ms: 6.209081
  ...
# {"timestamp":1773495064347,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064348,"level":"info","event":"ws.closed"}
# Subtest: executePlan with empty steps returns success
# {"timestamp":1773495064350,"level":"info","event":"server.listening","port":18771,"sdkInitialized":false}
ok 16 - executePlan with empty steps returns success
  ---
  duration_ms: 3.928414
  ...
# {"timestamp":1773495064351,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064351,"level":"info","event":"ws.closed"}
# Subtest: executePlan without steps param returns error
# {"timestamp":1773495064353,"level":"info","event":"server.listening","port":18771,"sdkInitialized":false}
ok 17 - executePlan without steps param returns error
  ---
  duration_ms: 2.749635
  ...
# {"timestamp":1773495064354,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064355,"level":"info","event":"ws.closed"}
# Subtest: auditListRecent with limit 3 returns at most 3
# {"timestamp":1773495064356,"level":"info","event":"server.listening","port":18771,"sdkInitialized":false}
ok 18 - auditListRecent with limit 3 returns at most 3
  ---
  duration_ms: 3.497624
  ...
# {"timestamp":1773495064357,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064358,"level":"info","event":"ws.closed"}
# Subtest: auditRevert with non-existent id returns error
# {"timestamp":1773495064360,"level":"info","event":"server.listening","port":18771,"sdkInitialized":false}
ok 19 - auditRevert with non-existent id returns error
  ---
  duration_ms: 3.644918
  ...
# {"timestamp":1773495064362,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064363,"level":"info","event":"ws.closed"}
# Subtest: automationCreate returns id and automationList includes it
# {"timestamp":1773495064365,"level":"info","event":"server.listening","port":18771,"sdkInitialized":false}
ok 20 - automationCreate returns id and automationList includes it
  ---
  duration_ms: 4.873618
  ...
# {"timestamp":1773495064367,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064368,"level":"info","event":"ws.closed"}
# Subtest: automationUpdate then automationList reflects change
# {"timestamp":1773495064369,"level":"info","event":"server.listening","port":18771,"sdkInitialized":false}
ok 21 - automationUpdate then automationList reflects change
  ---
  duration_ms: 4.140781
  ...
# {"timestamp":1773495064371,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064372,"level":"info","event":"ws.closed"}
# Subtest: automationPause then automation has enabled false
# {"timestamp":1773495064374,"level":"info","event":"server.listening","port":18771,"sdkInitialized":false}
ok 22 - automationPause then automation has enabled false
  ---
  duration_ms: 4.301412
  ...
# {"timestamp":1773495064376,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064376,"level":"info","event":"ws.closed"}
# Subtest: automationDelete removes automation
# {"timestamp":1773495064378,"level":"info","event":"server.listening","port":18771,"sdkInitialized":false}
ok 23 - automationDelete removes automation
  ---
  duration_ms: 3.951736
  ...
# {"timestamp":1773495064379,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064380,"level":"info","event":"ws.closed"}
# Subtest: getDesktopState returns object with expected keys or empty
# {"timestamp":1773495064386,"level":"info","event":"server.listening","port":18771,"sdkInitialized":false}
ok 24 - getDesktopState returns object with expected keys or empty
  ---
  duration_ms: 7.281329
  ...
# {"timestamp":1773495064387,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064388,"level":"info","event":"ws.closed"}
# Subtest: emergencyStop then emergencyResume restores execution
# {"timestamp":1773495064390,"level":"info","event":"server.listening","port":18771,"sdkInitialized":false}
ok 25 - emergencyStop then emergencyResume restores execution
  ---
  duration_ms: 3.81353
  ...
# {"timestamp":1773495064391,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064392,"level":"info","event":"ws.closed"}
# {"timestamp":1773495064396,"level":"info","event":"executor.step","capabilityId":"window.list","ok":true,"dryRun":true}
# {"timestamp":1773495064396,"level":"info","event":"executor.step","capabilityId":"notification.show","ok":true,"dryRun":true}
# Subtest: executePlan dry-run with multiple capabilities returns one result per step
# {"timestamp":1773495064397,"level":"info","event":"server.listening","port":18771,"sdkInitialized":false}
ok 26 - executePlan dry-run with multiple capabilities returns one result per step
  ---
  duration_ms: 7.000131
  ...
# {"timestamp":1773495064398,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064399,"level":"info","event":"ws.closed"}
# Subtest: getPlan without message returns error
# {"timestamp":1773495064401,"level":"info","event":"server.listening","port":18771,"sdkInitialized":false}
ok 27 - getPlan without message returns error
  ---
  duration_ms: 3.68112
  ...
# {"timestamp":1773495064403,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064404,"level":"info","event":"ws.closed"}
# Subtest: setPermission without capabilityId returns error
# {"timestamp":1773495064405,"level":"info","event":"server.listening","port":18771,"sdkInitialized":false}
ok 28 - setPermission without capabilityId returns error
  ---
  duration_ms: 4.414768
  ...
# {"timestamp":1773495064406,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064407,"level":"info","event":"ws.closed"}
# Subtest: auditRevert without actionId returns error
# {"timestamp":1773495064408,"level":"info","event":"server.listening","port":18771,"sdkInitialized":false}
ok 29 - auditRevert without actionId returns error
  ---
  duration_ms: 2.814115
  ...
# {"timestamp":1773495064410,"level":"info","event":"ws.connected","sdkInitialized":false}
# {"timestamp":1773495064411,"level":"info","event":"ws.closed"}
# Subtest: automationCreate without trigger returns error
ok 30 - automationCreate without trigger returns error
  ---
  duration_ms: 3.371327
  ...
# {"timestamp":1773495064412,"level":"info","event":"ws.closed"}
# Subtest: getPlan with real message returns plan when E2E_GETPLAN=1
ok 31 - getPlan with real message returns plan when E2E_GETPLAN=1 # SKIP
  ---
  duration_ms: 1.07672
  ...
# {"timestamp":1773495064321,"level":"info","event":"server.listening","port":18772,"sdkInitialized":false}
# {"timestamp":1773495064331,"level":"info","event":"ws.connected","sdkInitialized":false}
# Subtest: getDesktopState returns live desktop data (windows/panels when available)
ok 32 - getDesktopState returns live desktop data (windows/panels when available)
  ---
  duration_ms: 31.184032
  ...
# {"timestamp":1773495064343,"level":"info","event":"ws.closed"}
# {"timestamp":1773495064180,"level":"info","event":"audit.logged","id":"action-1-1773495064180","steps":1}
# {"timestamp":1773495064187,"level":"info","event":"audit.logged","id":"action-2-1773495064187","steps":0}
# {"timestamp":1773495064188,"level":"info","event":"audit.reverted","actionId":"action-2-1773495064187"}
# Subtest: logExecution returns action id and listRecent includes it
ok 33 - logExecution returns action id and listRecent includes it
  ---
  duration_ms: 4.989089
  ...
# Subtest: listRecent respects limit and returns newest first
ok 34 - listRecent respects limit and returns newest first
  ---
  duration_ms: 0.19342
  ...
# Subtest: revert returns error for unknown actionId
ok 35 - revert returns error for unknown actionId
  ---
  duration_ms: 0.282002
  ...
# Subtest: revert returns reverted true for known action (no snapshots to restore)
ok 36 - revert returns reverted true for known action (no snapshots to restore)
  ---
  duration_ms: 0.779934
  ...
# Subtest: listCapabilityIds returns non-empty array
ok 37 - listCapabilityIds returns non-empty array
  ---
  duration_ms: 2.20314
  ...
# Subtest: execute with unknown capabilityId returns ok false
ok 38 - execute with unknown capabilityId returns ok false
  ---
  duration_ms: 0.287942
  ...
# Subtest: notification.show dry-run returns ok true
ok 39 - notification.show dry-run returns ok true
  ---
  duration_ms: 0.323658
  ...
# Subtest: window.list dry-run returns ok true
ok 40 - window.list dry-run returns ok true
  ---
  duration_ms: 7.745331
  ...
# Subtest: power.lock_screen dry-run returns ok true
ok 41 - power.lock_screen dry-run returns ok true
  ---
  duration_ms: 0.362939
  ...
# Subtest: display.brightness.set dry-run returns ok true
ok 42 - display.brightness.set dry-run returns ok true
  ---
  duration_ms: 0.301082
  ...
# Subtest: audio.volume.set dry-run returns ok true
ok 43 - audio.volume.set dry-run returns ok true
  ---
  duration_ms: 0.248977
  ...
# Subtest: app.launch dry-run returns ok true
ok 44 - app.launch dry-run returns ok true
  ---
  duration_ms: 0.271499
  ...
# Subtest: panel.get_layout dry-run returns ok true
ok 45 - panel.get_layout dry-run returns ok true
  ---
  duration_ms: 0.340813
  ...
# Subtest: theme.preview dry-run returns ok true
ok 46 - theme.preview dry-run returns ok true
  ---
  duration_ms: 0.446982
  ...
# Subtest: getList returns array with expected capability ids
ok 47 - getList returns array with expected capability ids
  ---
  duration_ms: 3.662524
  ...
# Subtest: each entry has id and schema
ok 48 - each entry has id and schema
  ---
  duration_ms: 0.228857
  ...
# Subtest: register adds or updates capability
ok 49 - register adds or updates capability
  ---
  duration_ms: 0.286184
  ...
# {"timestamp":1773495064206,"level":"info","event":"executor.step","capabilityId":"notification.show","ok":true,"dryRun":true}
# {"timestamp":1773495064215,"level":"info","event":"executor.step","capabilityId":"window.list","ok":true,"dryRun":true}
# {"timestamp":1773495064222,"level":"info","event":"executor.step","capabilityId":"unknown.capability","ok":false,"dryRun":true}
# Subtest: executePlan with dryRun returns success and results without running adapters
ok 50 - executePlan with dryRun returns success and results without running adapters
  ---
  duration_ms: 16.30432
  ...
# Subtest: executePlan with unknown capability returns ok false for that step
ok 51 - executePlan with unknown capability returns ok false for that step
  ---
  duration_ms: 0.519796
  ...
# Subtest: executePlan when emergency paused returns empty results and success false
ok 52 - executePlan when emergency paused returns empty results and success false
  ---
  duration_ms: 0.21024
  ...
# Subtest: executePlan with empty steps returns success true
ok 53 - executePlan with empty steps returns success true
  ---
  duration_ms: 0.170262
  ...
# Subtest: getRiskLevel returns high for settings.write and theme.apply
ok 54 - getRiskLevel returns high for settings.write and theme.apply
  ---
  duration_ms: 2.855703
  ...
# Subtest: getRiskLevel returns medium for window.move and app.launch
ok 55 - getRiskLevel returns medium for window.move and app.launch
  ---
  duration_ms: 0.209329
  ...
# Subtest: getRiskLevel returns low for notification.show and window.list
ok 56 - getRiskLevel returns low for notification.show and window.list
  ---
  duration_ms: 0.168686
  ...
# Subtest: checkPlan allows plan when no capability denied
ok 57 - checkPlan allows plan when no capability denied
  ---
  duration_ms: 0.542366
  ...
# Subtest: checkPlan denies when capability is denied
ok 58 - checkPlan denies when capability is denied
  ---
  duration_ms: 0.324301
  ...
# Subtest: checkPlan sets requiresApproval for high-risk steps
ok 59 - checkPlan sets requiresApproval for high-risk steps
  ---
  duration_ms: 0.169279
  ...
# Subtest: getDeniedList and setPermission
ok 60 - getDeniedList and setPermission
  ---
  duration_ms: 0.179424
  ...
# Subtest: emergency pause state
ok 61 - emergency pause state
  ---
  duration_ms: 0.337992
  ...
# Subtest: SUPPORTED_METHODS includes expected methods
ok 62 - SUPPORTED_METHODS includes expected methods
  ---
  duration_ms: 2.951497
  ...
# Subtest: validateRequest rejects missing method
ok 63 - validateRequest rejects missing method
  ---
  duration_ms: 0.298851
  ...
# Subtest: validateRequest rejects unknown method
ok 64 - validateRequest rejects unknown method
  ---
  duration_ms: 0.144912
  ...
# Subtest: validateRequest accepts chat with message
ok 65 - validateRequest accepts chat with message
  ---
  duration_ms: 0.83139
  ...
# Subtest: validateRequest rejects chat without message
ok 66 - validateRequest rejects chat without message
  ---
  duration_ms: 0.150713
  ...
# Subtest: validateRequest accepts health and ping with no params
ok 67 - validateRequest accepts health and ping with no params
  ---
  duration_ms: 0.124677
  ...
# Subtest: validateRequest accepts executePlan with steps array
ok 68 - validateRequest accepts executePlan with steps array
  ---
  duration_ms: 0.136428
  ...
# Subtest: validateRequest rejects executePlan without steps
ok 69 - validateRequest rejects executePlan without steps
  ---
  duration_ms: 0.153538
  ...
# Subtest: validateRequest accepts getPlan with message
ok 70 - validateRequest accepts getPlan with message
  ---
  duration_ms: 0.155742
  ...
# Subtest: validateRequest accepts setPermission with capabilityId and allow
ok 71 - validateRequest accepts setPermission with capabilityId and allow
  ---
  duration_ms: 0.393757
  ...
# Subtest: validateRequest accepts auditRevert with actionId
ok 72 - validateRequest accepts auditRevert with actionId
  ---
  duration_ms: 0.189929
  ...
# Subtest: validateRequest accepts automationCreate with trigger and steps
ok 73 - validateRequest accepts automationCreate with trigger and steps
  ---
  duration_ms: 0.182084
  ...
# Subtest: validateRequest accepts getDesktopState, emergencyStop, emergencyResume, getDeniedPermissions with no params
ok 74 - validateRequest accepts getDesktopState, emergencyStop, emergencyResume, getDeniedPermissions with no params
  ---
  duration_ms: 0.339719
  ...
# Subtest: sendResponse and sendError produce valid envelope shape
ok 75 - sendResponse and sendError produce valid envelope shape
  ---
  duration_ms: 0.426361
  ...
# Subtest: sendEvent produces event envelope
ok 76 - sendEvent produces event envelope
  ---
  duration_ms: 0.291482
  ...
# {"timestamp":1773495064205,"level":"info","event":"provider_router.routed","provider":"opencode","reason":"quick_desktop_fallback"}
# {"timestamp":1773495064211,"level":"info","event":"provider_router.routed","provider":"opencode","reason":"quick_desktop_fallback"}
# {"timestamp":1773495064212,"level":"info","event":"provider_router.routed","provider":"opencode"}
# {"timestamp":1773495064213,"level":"info","event":"provider_router.routed","provider":"opencode"}
# Subtest: route returns provider opencode for any intent
ok 77 - route returns provider opencode for any intent
  ---
  duration_ms: 5.483607
  ...
# Subtest: route returns provider for short quick-desktop style intent
ok 78 - route returns provider for short quick-desktop style intent
  ---
  duration_ms: 0.523157
  ...
# Subtest: route returns provider for long intent
ok 79 - route returns provider for long intent
  ---
  duration_ms: 0.609656
  ...
# Subtest: route handles empty intent
ok 80 - route handles empty intent
  ---
  duration_ms: 1.539466
  ...
# Subtest: initScheduler with null dir and list returns array
ok 81 - initScheduler with null dir and list returns array
  ---
  duration_ms: 1.964995
  ...
# Subtest: create returns id and list includes new automation
ok 82 - create returns id and list includes new automation
  ---
  duration_ms: 0.315259
  ...
# Subtest: update changes steps and enabled
ok 83 - update changes steps and enabled
  ---
  duration_ms: 0.209924
  ...
# Subtest: setPaused pauses and resumes
ok 84 - setPaused pauses and resumes
  ---
  duration_ms: 0.29249
  ...
# Subtest: remove deletes automation
ok 85 - remove deletes automation
  ---
  duration_ms: 0.182173
  ...
# Subtest: remove throws for unknown id
ok 86 - remove throws for unknown id
  ---
  duration_ms: 0.410789
  ...
# Subtest: validateWithSchema accepts valid chat params
ok 87 - validateWithSchema accepts valid chat params
  ---
  duration_ms: 3.40644
  ...
# Subtest: validateWithSchema rejects chat without message
ok 88 - validateWithSchema rejects chat without message
  ---
  duration_ms: 1.056674
  ...
# Subtest: validateWithSchema accepts valid generateCode params
ok 89 - validateWithSchema accepts valid generateCode params
  ---
  duration_ms: 0.258408
  ...
# Subtest: validateWithSchema accepts valid editCode params
ok 90 - validateWithSchema accepts valid editCode params
  ---
  duration_ms: 0.399495
  ...
# Subtest: validateWithSchema accepts valid writeFile params
ok 91 - validateWithSchema accepts valid writeFile params
  ---
  duration_ms: 0.228806
  ...
# Subtest: validateWithSchema passes through unknown method with ok true
ok 92 - validateWithSchema passes through unknown method with ok true
  ---
  duration_ms: 1.057124
  ...
# Subtest: getDesktopState returns an object
ok 93 - getDesktopState returns an object
  ---
  duration_ms: 9.150338
  ...
# Subtest: getDesktopState may have windows, panels, theme keys
ok 94 - getDesktopState may have windows, panels, theme keys
  ---
  duration_ms: 3.428324
  ...
# Subtest: in-memory setSnapshot and getSnapshot
ok 95 - in-memory setSnapshot and getSnapshot
  ---
  duration_ms: 1.679295
  ...
# Subtest: getSnapshot returns null for missing key
ok 96 - getSnapshot returns null for missing key
  ---
  duration_ms: 0.119843
  ...
# Subtest: deleteSnapshot removes key
ok 97 - deleteSnapshot removes key
  ---
  duration_ms: 0.15862
  ...
# Subtest: restoreSnapshot returns error for non-restorable snapshot
ok 98 - restoreSnapshot returns error for non-restorable snapshot
  ---
  duration_ms: 0.432374
  ...
# Subtest: restoreSnapshot returns error for missing snapshot
ok 99 - restoreSnapshot returns error for missing snapshot
  ---
  duration_ms: 0.10687
  ...
# Subtest: file-backed persistence when stateDir is set
ok 100 - file-backed persistence when stateDir is set
  ---
  duration_ms: 2.049011
  ...
1..100
# tests 100
# suites 0
# pass 99
# fail 0
# cancelled 0
# skipped 1
# todo 0
# duration_ms 443.358115