@moretea-labs/forge 1.7.0 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/AGENTS.md +2 -0
  2. package/CHANGELOG.md +13 -0
  3. package/CLAUDE.md +2 -0
  4. package/THIRD_PARTY_NOTICES.md +1 -1
  5. package/agents/openai.yaml +1 -1
  6. package/assets/partials-agents/02-operating-mode.partial.md +2 -0
  7. package/assets/plugin-registry.v1.json +3 -3
  8. package/assets/skill-version.json +2 -2
  9. package/assets/templates/helpers/check-architecture-sync.sh +17 -0
  10. package/assets/templates/helpers/inspect-project-state.ts +8 -6
  11. package/assets/templates/helpers/migrate-workflow-docs.ts +9 -7
  12. package/assets/templates/helpers/workflow-contract.ts +5 -1
  13. package/docs/architecture/CURRENT.md +12 -4
  14. package/docs/forge-plugin-management.md +1 -1
  15. package/docs/operations/controller-performance-and-502.md +19 -0
  16. package/docs/operations/homebrew.md +2 -2
  17. package/package.json +5 -2
  18. package/scripts/architecture-event.ts +2 -820
  19. package/scripts/architecture-queue.sh +2 -645
  20. package/scripts/archive-architecture-request.sh +2 -242
  21. package/scripts/archive-workflow.sh +1 -291
  22. package/scripts/benchmark-work-efficiency.ts +0 -0
  23. package/scripts/capability-config.ts +2 -392
  24. package/scripts/capability-resolver.ts +2 -503
  25. package/scripts/capture-plan.sh +1 -332
  26. package/scripts/check-agent-tooling.sh +2 -1531
  27. package/scripts/check-architecture-sync.sh +2 -302
  28. package/scripts/check-brain-manifest.sh +2 -253
  29. package/scripts/check-context-files.sh +2 -47
  30. package/scripts/check-deploy-sql-order.sh +2 -115
  31. package/scripts/check-runtime-architecture.mjs +243 -16
  32. package/scripts/check-skill-version.ts +3 -191
  33. package/scripts/check-source-duplication.mjs +61 -0
  34. package/scripts/check-stable-baseline.ts +0 -0
  35. package/scripts/check-task-sync.sh +2 -63
  36. package/scripts/check-task-workflow.sh +2 -1000
  37. package/scripts/codex-handoff-resume.sh +2 -267
  38. package/scripts/context-contract-sync.sh +2 -514
  39. package/scripts/contract-run.ts +2 -463
  40. package/scripts/contract-worktree.sh +1 -702
  41. package/scripts/ensure-task-workflow.sh +1 -1192
  42. package/scripts/inspect-project-state.ts +4 -279
  43. package/scripts/lib/project-init-lib.sh +7 -2
  44. package/scripts/lib/run-forge-helper.sh +19 -0
  45. package/scripts/lib/run-forge-helper.ts +26 -0
  46. package/scripts/migrate-project-template.sh +1 -33
  47. package/scripts/migrate-workflow-docs.ts +4 -412
  48. package/scripts/new-plan.sh +1 -180
  49. package/scripts/new-spec.sh +1 -47
  50. package/scripts/new-sprint.sh +1 -46
  51. package/scripts/plan-to-todo.sh +1 -771
  52. package/scripts/prepare-codex-handoff.sh +2 -201
  53. package/scripts/prepare-handoff.sh +1 -78
  54. package/scripts/refresh-current-status.sh +2 -467
  55. package/scripts/run-governed-gate.ts +3 -0
  56. package/scripts/select-agent-context-blocks.sh +2 -64
  57. package/scripts/ship-worktrees.sh +1 -531
  58. package/scripts/sprint-backlog.sh +1 -834
  59. package/scripts/stage-runtime-release.ts +2 -1
  60. package/scripts/summarize-failures.sh +2 -123
  61. package/scripts/switch-plan.sh +1 -150
  62. package/scripts/sync-brain-docs.sh +2 -379
  63. package/scripts/sync-generated-authority.mjs +175 -0
  64. package/scripts/verify-contract.sh +2 -593
  65. package/scripts/verify-sprint.sh +1 -500
  66. package/scripts/workflow-contract.ts +4 -201
  67. package/scripts/workstream-sync.sh +2 -261
  68. package/src/cli/agent-jobs/executor-health.ts +1 -1
  69. package/src/cli/commands/plugin.ts +12 -3
  70. package/src/cli/controller/check-runner.ts +7 -2
  71. package/src/cli/controller/context-pack.ts +46 -21
  72. package/src/cli/github/contracts.ts +16 -0
  73. package/src/cli/github/github.ts +2 -16
  74. package/src/cli/github/session.ts +2 -1
  75. package/src/cli/local-bridge/facade-api.ts +37 -47
  76. package/src/cli/local-bridge/server.ts +2 -2
  77. package/src/cli/mcp/legacy-tool-service.ts +10 -33
  78. package/src/cli/mcp/post-finalize-work-attribution.ts +150 -0
  79. package/src/cli/mcp/repository-tools.ts +85 -89
  80. package/src/cli/mcp/server.ts +57 -13
  81. package/src/cli/mcp/tool-contract.ts +14 -0
  82. package/src/cli/mcp/toolset.ts +2 -2
  83. package/src/cli/mcp/transports/http.ts +23 -0
  84. package/src/cli/repositories/command-classifier.ts +2 -2
  85. package/src/cli/repositories/command-executor.ts +24 -6
  86. package/src/cli/repositories/repository-snapshot.ts +5 -2
  87. package/src/cli/repositories/structured-git.ts +57 -3
  88. package/src/cli/repository/inspector.ts +11 -3
  89. package/src/runtime/context/generic-lsp-provider.ts +1 -1
  90. package/src/runtime/context/semantic-navigation-contract.ts +61 -0
  91. package/src/runtime/context/semantic-navigation.ts +9 -61
  92. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +18 -25
  93. package/src/runtime/control-plane/execution/repository-work-attribution.ts +106 -0
  94. package/src/runtime/control-plane/execution/retained-work-resume.ts +203 -0
  95. package/src/runtime/control-plane/execution/work-completion-authority.ts +58 -0
  96. package/src/runtime/control-plane/execution/work-evidence-policy.ts +200 -0
  97. package/src/runtime/control-plane/execution/work-execution-support.ts +386 -0
  98. package/src/runtime/control-plane/execution/work-finalization-service.ts +1913 -0
  99. package/src/runtime/control-plane/execution/work-handle-authority.ts +96 -0
  100. package/src/runtime/control-plane/execution/work-handle-store.ts +1 -1
  101. package/src/runtime/control-plane/execution/work-operation-service.ts +468 -0
  102. package/src/runtime/control-plane/execution/work-path-scope.ts +37 -0
  103. package/src/runtime/control-plane/execution/work-preparation-service.ts +479 -0
  104. package/src/runtime/control-plane/execution/work-process-evidence.ts +129 -0
  105. package/src/runtime/control-plane/execution/work-task-receipt.ts +4 -43
  106. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +13 -3
  107. package/src/runtime/control-plane/execution/work-verification-context.ts +56 -0
  108. package/src/runtime/control-plane/execution/work-verification-service.ts +388 -0
  109. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +5 -1
  110. package/src/runtime/control-plane/facade/controller-round-relay.ts +164 -37
  111. package/src/runtime/control-plane/facade/controller-session-store.ts +183 -24
  112. package/src/runtime/control-plane/facade/goal-workloop-access.ts +15 -1
  113. package/src/runtime/control-plane/facade/goal-workloop.ts +329 -148
  114. package/src/runtime/control-plane/facade/index.ts +2 -0
  115. package/src/runtime/control-plane/facade/plan-contract-store.ts +90 -22
  116. package/src/runtime/control-plane/facade/repository-work-admission.ts +147 -0
  117. package/src/runtime/control-plane/facade/requirement-authority.ts +83 -0
  118. package/src/runtime/control-plane/facade/types.ts +68 -5
  119. package/src/runtime/control-plane/facade/work-contract-store.ts +3 -209
  120. package/src/runtime/control-plane/facade/work-state-machine.ts +250 -0
  121. package/src/runtime/control-plane/global-scheduler/maintenance.ts +14 -7
  122. package/src/runtime/control-plane/global-scheduler/scheduler.ts +5 -1
  123. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +13 -0
  124. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +15 -6
  125. package/src/runtime/control-plane/launcher/launch-reservation-store.ts +54 -5
  126. package/src/runtime/control-plane/launcher/thin-launcher.ts +56 -7
  127. package/src/runtime/control-plane/persistence/requirement-store.ts +0 -12
  128. package/src/runtime/control-plane/repo-actor/registry.ts +4 -2
  129. package/src/runtime/control-plane/routing/route-policy.ts +18 -6
  130. package/src/runtime/control-plane/routing/workspace-admission.ts +70 -0
  131. package/src/runtime/evidence/process-check-execution.ts +18 -0
  132. package/src/runtime/execution/jobs/child-reference.ts +6 -2
  133. package/src/runtime/execution/process-runtime/check-result.ts +53 -18
  134. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +7 -1
  135. package/src/runtime/execution/process-runtime/command-facade.ts +39 -17
  136. package/src/runtime/execution/process-runtime/lightweight-managed.ts +20 -3
  137. package/src/runtime/execution/process-runtime/persisted-check.ts +4 -1
  138. package/src/runtime/execution/process-runtime/runtime.ts +10 -0
  139. package/src/runtime/execution/process-runtime/types.ts +2 -12
  140. package/src/runtime/execution/work-activity.ts +21 -0
  141. package/src/runtime/execution/workers/executor.ts +18 -13
  142. package/src/runtime/gateway/mcp/execution-tools.ts +35 -2930
  143. package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +61 -0
  144. package/src/runtime/gateway/mcp/router.ts +2 -2
  145. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +21 -2
  146. package/src/runtime/gateway/mcp/runtime-tools.ts +805 -1009
  147. package/src/runtime/plugins/app-store-connect-adapter.ts +93 -28
  148. package/src/runtime/plugins/browser-adapter.ts +256 -1083
  149. package/src/runtime/plugins/browser-handoff-host.ts +2 -25
  150. package/src/runtime/plugins/browser-handoff.ts +18 -3
  151. package/src/runtime/plugins/browser-macos-bridge.ts +121 -16
  152. package/src/runtime/plugins/browser-manifest-surface.ts +603 -0
  153. package/src/runtime/plugins/browser-native-session-service.ts +222 -0
  154. package/src/runtime/plugins/browser-runtime-contract.ts +2 -0
  155. package/src/runtime/plugins/browser-runtime.ts +10 -0
  156. package/src/runtime/plugins/browser-session-store.ts +121 -0
  157. package/src/runtime/plugins/browser-session-types.ts +98 -0
  158. package/src/runtime/plugins/desktop-operator-registration.ts +4 -4
  159. package/src/runtime/plugins/external-adapter.ts +92 -58
  160. package/src/runtime/plugins/macos-capability-broker.ts +0 -8
  161. package/src/runtime/plugins/store.ts +66 -9
  162. package/src/runtime/plugins/types.ts +3 -1
  163. package/src/runtime/recovery/maintenance-executor.ts +61 -25
  164. package/src/runtime/root/package-connector-service.ts +46 -4
  165. package/src/runtime/root/package-runtime-release.ts +1 -1
  166. package/src/runtime/root/package-runtime-service.ts +33 -2
  167. package/src/runtime/root/release-manifest.ts +137 -16
  168. package/src/runtime/root/release-materialize.ts +109 -35
  169. package/src/runtime/root/types.ts +10 -0
  170. package/src/runtime/standalone-recovery/core.ts +425 -194
  171. package/src/runtime/standalone-recovery/entry.ts +1 -1
  172. package/src/runtime/standalone-recovery/installer.ts +1 -1
  173. package/src/runtime/workflow/schedules/engine.ts +113 -87
  174. package/src/runtime/workflow/schedules/settlement.ts +26 -23
  175. package/src/runtime/workflow/schedules/store.ts +79 -10
@@ -0,0 +1,603 @@
1
+ import type {
2
+ AssistantPluginActionDescriptor,
3
+ AssistantPluginCapability,
4
+ AssistantPluginPermissionScope,
5
+ } from './types';
6
+
7
+ import {
8
+ DEFAULT_BROWSER_SESSION_LIST_LIMIT,
9
+ MAX_BROWSER_SESSION_LIST_LIMIT,
10
+ } from './browser-session-authority';
11
+ import { MAX_BROWSER_CDP_ENDPOINT_CANDIDATES } from './browser-runtime-contract';
12
+
13
+ export function browserPermissions(ready: boolean): AssistantPluginPermissionScope[] {
14
+ return [
15
+ {
16
+ scope: 'browser.read',
17
+ mode: 'read',
18
+ description: 'Open pages, extract text, and capture screenshots.',
19
+ granted: ready,
20
+ required: true,
21
+ },
22
+ {
23
+ scope: 'browser.interact',
24
+ mode: 'write',
25
+ description: 'Click, type, press keys, and wait on allowed browser pages after explicit authorization.',
26
+ granted: ready,
27
+ required: true,
28
+ },
29
+ {
30
+ scope: 'browser.profile',
31
+ mode: 'write',
32
+ description: 'Persist the dedicated local browser profile, session metadata, and screenshots.',
33
+ granted: ready,
34
+ required: true,
35
+ },
36
+ ];
37
+ }
38
+
39
+ export function browserCapabilities(): AssistantPluginCapability[] {
40
+ return [
41
+ {
42
+ capabilityId: 'browser-session',
43
+ title: 'Browser Sessions',
44
+ description: 'Create, list, reuse, and close persistent browser sessions.',
45
+ scopes: ['browser.read', 'browser.profile'],
46
+ actions: ['create_session', 'list_sessions', 'reconcile_sessions', 'close_session', 'close_page', 'clear_session'],
47
+ },
48
+ {
49
+ capabilityId: 'browser-human-handoff',
50
+ title: 'Browser Human Handoff',
51
+ description: 'Present the persistent browser in the foreground, keep it open for manual verification, and resume safely.',
52
+ scopes: ['browser.read', 'browser.profile'],
53
+ actions: ['request_human_handoff', 'get_handoff_status', 'resolve_handoff'],
54
+ },
55
+ {
56
+ capabilityId: 'browser-readonly',
57
+ title: 'Read-only Browser',
58
+ description: 'Navigate allowed pages, extract DOM/text, capture screenshots, and collect diagnostics.',
59
+ scopes: ['browser.read', 'browser.profile'],
60
+ actions: [
61
+ 'open_page', 'navigate', 'reload', 'go_back', 'wait_for_load_state',
62
+ 'get_text', 'get_html', 'query_selector', 'query_all', 'get_attribute', 'list_frames', 'verify_state',
63
+ 'screenshot', 'extract_links', 'extract_tables', 'extract_forms', 'snapshot_interactive',
64
+ 'get_console_errors', 'get_failed_requests',
65
+ ],
66
+ },
67
+ {
68
+ capabilityId: 'browser-interaction',
69
+ title: 'Browser Interaction',
70
+ description: 'Perform explicit form and pointer interactions on HTTP(S) pages through the persistent Playwright profile.',
71
+ scopes: ['browser.interact', 'browser.profile'],
72
+ actions: [
73
+ 'activate_page', 'click', 'click_text', 'double_click', 'hover', 'focus', 'type', 'fill', 'select_option',
74
+ 'check', 'uncheck', 'press', 'keyboard_shortcut', 'trusted_input', 'dispatch_event', 'wait_for_selector', 'attach_local_file', 'await_file_transfer',
75
+ ],
76
+ },
77
+ ];
78
+ }
79
+
80
+ function sessionTargetSchema(extra: Record<string, unknown> = {}, required: string[] = []): Record<string, unknown> {
81
+ return {
82
+ type: 'object',
83
+ properties: {
84
+ session_id: { type: 'string' },
85
+ url: { type: 'string' },
86
+ wait_until: { type: 'string', enum: ['load', 'domcontentloaded', 'networkidle'] },
87
+ timeout_ms: { type: 'number' },
88
+ retries: { type: 'number' },
89
+ browser_mode: { type: 'string', enum: ['attach_preferred', 'managed_persistent', 'isolated'] },
90
+ cdp_attach_fallback: { type: 'string', enum: ['managed_persistent', 'fail_closed'] },
91
+ native_attach_mode: { type: 'string', enum: ['auto', 'disabled'] },
92
+ native_browser_candidates: { type: 'array', items: { type: 'string', enum: ['vivaldi', 'chrome'] }, maxItems: 2 },
93
+ ...extra,
94
+ },
95
+ ...(required.length > 0 ? { required } : {}),
96
+ additionalProperties: false,
97
+ };
98
+ }
99
+
100
+ const frameScopeProperties = {
101
+ frame_url: { type: 'string' },
102
+ frame_name: { type: 'string' },
103
+ };
104
+
105
+ function interactSchema(extra: Record<string, unknown>, required: string[]): Record<string, unknown> {
106
+ return sessionTargetSchema({
107
+ post_action_wait_ms: { type: 'number' },
108
+ ...extra,
109
+ }, required);
110
+ }
111
+
112
+ export function browserActions(): AssistantPluginActionDescriptor[] {
113
+ const readRemote = [
114
+ { resource: 'remote' as const, mode: 'read' as const },
115
+ { resource: 'repo-state' as const, mode: 'write' as const },
116
+ ];
117
+ const writeRemote = [
118
+ { resource: 'remote' as const, mode: 'exclusive' as const },
119
+ { resource: 'repo-state' as const, mode: 'write' as const },
120
+ ];
121
+ const descriptors: AssistantPluginActionDescriptor[] = [
122
+ {
123
+ actionId: 'configure',
124
+ title: 'Configure browser plugin',
125
+ description: 'Enable or update the local browser plugin configuration.',
126
+ readOnly: false,
127
+ risk: 'workspace_write',
128
+ confirmation: 'authorization',
129
+ defaultTimeoutMs: 30_000,
130
+ cancellable: true,
131
+ idempotent: true,
132
+ scopes: ['browser.profile'],
133
+ resourceClaims: [{ resource: 'repo-state', mode: 'write' }],
134
+ argumentsSchema: {
135
+ type: 'object',
136
+ properties: {
137
+ enabled: { type: 'boolean' },
138
+ browser_mode: { type: 'string', enum: ['attach_preferred', 'managed_persistent', 'isolated'] },
139
+ profile_mode: { type: 'string', enum: ['repo_local', 'custom'] },
140
+ profile_dir: { type: 'string' },
141
+ profile_directory: { type: 'string' },
142
+ clear_profile_dir: { type: 'boolean' },
143
+ clear_profile_directory: { type: 'boolean' },
144
+ browser_channel: { type: 'string', enum: ['chromium', 'chrome', 'chrome-beta', 'chrome-dev', 'chrome-canary'] },
145
+ clear_browser_channel: { type: 'boolean' },
146
+ browser_executable_path: { type: 'string' },
147
+ clear_browser_executable_path: { type: 'boolean' },
148
+ cdp_endpoint: { type: 'string' },
149
+ clear_cdp_endpoint: { type: 'boolean' },
150
+ cdp_endpoint_candidates: { type: 'array', items: { type: 'string' }, maxItems: MAX_BROWSER_CDP_ENDPOINT_CANDIDATES },
151
+ clear_cdp_endpoint_candidates: { type: 'boolean' },
152
+ cdp_discovery_timeout_ms: { type: 'number' },
153
+ cdp_attach_fallback: { type: 'string', enum: ['managed_persistent', 'fail_closed'] },
154
+ native_attach_mode: { type: 'string', enum: ['auto', 'disabled'] },
155
+ native_browser_candidates: { type: 'array', items: { type: 'string', enum: ['vivaldi', 'chrome'] }, maxItems: 2 },
156
+ default_timeout_ms: { type: 'number' },
157
+ },
158
+ additionalProperties: false,
159
+ },
160
+ },
161
+ {
162
+ actionId: 'create_session',
163
+ title: 'Create browser session',
164
+ description: 'Open an HTTP(S) URL or explicitly adopt the matching frontmost native tab, then persist a reusable session id.',
165
+ readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 60_000, cancellable: true, idempotent: false,
166
+ scopes: ['browser.read', 'browser.profile'], resourceClaims: [{ resource: 'repo-state', mode: 'write' }, ...readRemote],
167
+ argumentsSchema: sessionTargetSchema({
168
+ extract_text: { type: 'boolean' },
169
+ max_chars: { type: 'number' },
170
+ native_browser_product: { type: 'string', enum: ['chrome', 'vivaldi'] },
171
+ native_window_id: { type: 'string' },
172
+ native_tab_id: { type: 'string' },
173
+ native_active_tab: { type: 'boolean' },
174
+ }, ['url']),
175
+ },
176
+ {
177
+ actionId: 'list_sessions',
178
+ title: 'List browser sessions',
179
+ description: 'List saved browser session metadata without secrets or cookies.',
180
+ readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 10_000, cancellable: true, idempotent: true,
181
+ scopes: ['browser.read', 'browser.profile'], resourceClaims: [{ resource: 'repo-state', mode: 'read' }],
182
+ argumentsSchema: {
183
+ type: 'object',
184
+ properties: {
185
+ limit: { type: 'number', minimum: 1, maximum: MAX_BROWSER_SESSION_LIST_LIMIT, default: DEFAULT_BROWSER_SESSION_LIST_LIMIT },
186
+ cursor: { type: 'string' },
187
+ },
188
+ additionalProperties: false,
189
+ },
190
+ },
191
+ {
192
+ actionId: 'reconcile_sessions',
193
+ title: 'Reconcile browser sessions',
194
+ description: 'Prune only saved managed-session metadata whose exact Runtime-bound page is positively proven gone. Never removes profiles, cookies, or unverified native sessions.',
195
+ readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 15_000, cancellable: true, idempotent: true,
196
+ scopes: ['browser.read', 'browser.profile'], resourceClaims: [{ resource: 'repo-state', mode: 'write' }],
197
+ argumentsSchema: { type: 'object', properties: {}, additionalProperties: false },
198
+ },
199
+ {
200
+ actionId: 'close_session',
201
+ title: 'Close browser session',
202
+ description: 'Remove one saved session metadata record.',
203
+ readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 15_000, cancellable: true, idempotent: true,
204
+ scopes: ['browser.read', 'browser.profile'], resourceClaims: [{ resource: 'repo-state', mode: 'write' }],
205
+ argumentsSchema: { type: 'object', properties: { session_id: { type: 'string' } }, required: ['session_id'], additionalProperties: false },
206
+ },
207
+ {
208
+ actionId: 'clear_session',
209
+ title: 'Clear all browser sessions',
210
+ description: 'Remove all saved session metadata while keeping the profile.',
211
+ readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 15_000, cancellable: true, idempotent: true,
212
+ scopes: ['browser.profile'], resourceClaims: [{ resource: 'repo-state', mode: 'write' }],
213
+ argumentsSchema: { type: 'object', properties: {}, additionalProperties: false },
214
+ },
215
+ {
216
+ actionId: 'request_human_handoff',
217
+ title: 'Request human browser handoff',
218
+ description: 'Keep a saved browser session open for CAPTCHA, login, two-factor authentication, or another manual step.',
219
+ readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 30_000, cancellable: true, idempotent: false,
220
+ scopes: ['browser.read', 'browser.profile'], resourceClaims: readRemote,
221
+ argumentsSchema: sessionTargetSchema({
222
+ reason: { type: 'string', enum: ['captcha', 'login', 'two_factor', 'manual_review', 'sensitive_confirmation'] },
223
+ instructions: { type: 'string' },
224
+ handoff_timeout_ms: { type: 'number' },
225
+ }, ['session_id']),
226
+ },
227
+ {
228
+ actionId: 'get_handoff_status',
229
+ title: 'Get browser handoff status',
230
+ description: 'Read durable browser handoff status and reconcile a stale or crashed host.',
231
+ readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 10_000, cancellable: true, idempotent: true,
232
+ scopes: ['browser.read', 'browser.profile'], resourceClaims: [{ resource: 'repo-state', mode: 'read' }],
233
+ argumentsSchema: { type: 'object', properties: { interaction_id: { type: 'string' } }, required: ['interaction_id'], additionalProperties: false },
234
+ },
235
+ {
236
+ actionId: 'resolve_handoff',
237
+ title: 'Resolve browser handoff',
238
+ description: 'Resume or cancel a foreground browser handoff.',
239
+ readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 15_000, cancellable: true, idempotent: true,
240
+ scopes: ['browser.read', 'browser.profile'], resourceClaims: [{ resource: 'repo-state', mode: 'write' }],
241
+ argumentsSchema: {
242
+ type: 'object',
243
+ properties: {
244
+ interaction_id: { type: 'string' },
245
+ resolution: { type: 'string', enum: ['resume', 'cancel'] },
246
+ },
247
+ required: ['interaction_id', 'resolution'],
248
+ additionalProperties: false,
249
+ },
250
+ },
251
+ {
252
+ actionId: 'open_page',
253
+ title: 'Open page',
254
+ description: 'Open an HTTP(S) URL with the persistent profile and save a lightweight session.',
255
+ readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 60_000, cancellable: true, idempotent: false,
256
+ scopes: ['browser.read'], resourceClaims: readRemote,
257
+ argumentsSchema: sessionTargetSchema({ extract_text: { type: 'boolean' }, max_chars: { type: 'number' } }, ['url']),
258
+ },
259
+ {
260
+ actionId: 'navigate',
261
+ title: 'Navigate',
262
+ description: 'Navigate an existing session or open a new page to an HTTP(S) URL.',
263
+ readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 60_000, cancellable: true, idempotent: false,
264
+ scopes: ['browser.read', 'browser.interact', 'browser.profile'], resourceClaims: writeRemote,
265
+ argumentsSchema: sessionTargetSchema({}, ['url']),
266
+ },
267
+ {
268
+ actionId: 'reload',
269
+ title: 'Reload page',
270
+ description: 'Reload the current page for a session.',
271
+ readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 60_000, cancellable: true, idempotent: false,
272
+ scopes: ['browser.read', 'browser.interact', 'browser.profile'], resourceClaims: writeRemote,
273
+ argumentsSchema: sessionTargetSchema({}, ['session_id']),
274
+ },
275
+ {
276
+ actionId: 'go_back',
277
+ title: 'Go back',
278
+ description: 'Navigate back in history for a session.',
279
+ readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 60_000, cancellable: true, idempotent: false,
280
+ scopes: ['browser.read', 'browser.interact', 'browser.profile'], resourceClaims: writeRemote,
281
+ argumentsSchema: sessionTargetSchema({}, ['session_id']),
282
+ },
283
+ {
284
+ actionId: 'wait_for_load_state',
285
+ title: 'Wait for load state',
286
+ description: 'Wait for load/domcontentloaded/networkidle on a session page.',
287
+ readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 60_000, cancellable: true, idempotent: true,
288
+ scopes: ['browser.read'], resourceClaims: readRemote,
289
+ argumentsSchema: sessionTargetSchema({ state: { type: 'string', enum: ['load', 'domcontentloaded', 'networkidle'] } }),
290
+ },
291
+ {
292
+ actionId: 'get_text',
293
+ title: 'Get text',
294
+ description: 'Extract text from a URL or saved browser session.',
295
+ readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 60_000, cancellable: true, idempotent: true,
296
+ scopes: ['browser.read'], resourceClaims: [{ resource: 'remote', mode: 'read' }],
297
+ argumentsSchema: sessionTargetSchema({ selector: { type: 'string' }, max_chars: { type: 'number' }, ...frameScopeProperties }),
298
+ },
299
+ {
300
+ actionId: 'get_html',
301
+ title: 'Get HTML',
302
+ description: 'Extract HTML for the page or a selector (bounded).',
303
+ readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 60_000, cancellable: true, idempotent: true,
304
+ scopes: ['browser.read'], resourceClaims: [{ resource: 'remote', mode: 'read' }],
305
+ argumentsSchema: sessionTargetSchema({ selector: { type: 'string' }, max_chars: { type: 'number' }, ...frameScopeProperties }),
306
+ },
307
+ {
308
+ actionId: 'query_selector',
309
+ title: 'Query selector',
310
+ description: 'Return the first matching element summary with a stable selector hint.',
311
+ readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 60_000, cancellable: true, idempotent: true,
312
+ scopes: ['browser.read'], resourceClaims: [{ resource: 'remote', mode: 'read' }],
313
+ argumentsSchema: sessionTargetSchema({ selector: { type: 'string' }, ...frameScopeProperties }, ['selector']),
314
+ },
315
+ {
316
+ actionId: 'query_all',
317
+ title: 'Query all selectors',
318
+ description: 'Return matching element summaries (bounded).',
319
+ readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 60_000, cancellable: true, idempotent: true,
320
+ scopes: ['browser.read'], resourceClaims: [{ resource: 'remote', mode: 'read' }],
321
+ argumentsSchema: sessionTargetSchema({ selector: { type: 'string' }, limit: { type: 'number' }, ...frameScopeProperties }, ['selector']),
322
+ },
323
+ {
324
+ actionId: 'get_attribute',
325
+ title: 'Get attribute',
326
+ description: 'Read one attribute from a selector.',
327
+ readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 60_000, cancellable: true, idempotent: true,
328
+ scopes: ['browser.read'], resourceClaims: [{ resource: 'remote', mode: 'read' }],
329
+ argumentsSchema: sessionTargetSchema({ selector: { type: 'string' }, attribute: { type: 'string' }, ...frameScopeProperties }, ['selector', 'attribute']),
330
+ },
331
+ {
332
+ actionId: 'list_frames',
333
+ title: 'List page frames',
334
+ description: 'List bounded Playwright/CDP frame identities for explicit frame-scoped DOM observation.',
335
+ readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 60_000, cancellable: true, idempotent: true,
336
+ scopes: ['browser.read'], resourceClaims: [{ resource: 'remote', mode: 'read' }],
337
+ argumentsSchema: sessionTargetSchema({ limit: { type: 'number', minimum: 1, maximum: 100 } }),
338
+ },
339
+ {
340
+ actionId: 'verify_state',
341
+ title: 'Verify browser state',
342
+ description: 'Observe URL, selector existence/visibility, and bounded text criteria in one read-only call for post-action verification.',
343
+ readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 60_000, cancellable: true, idempotent: true,
344
+ scopes: ['browser.read'], resourceClaims: [{ resource: 'remote', mode: 'read' }],
345
+ argumentsSchema: sessionTargetSchema({
346
+ expected_url: { type: 'string' },
347
+ url_contains: { type: 'string' },
348
+ selector: { type: 'string' },
349
+ require_visible: { type: 'boolean' },
350
+ text_contains: { type: 'string', maxLength: 10000 },
351
+ max_chars: { type: 'number', minimum: 1, maximum: 100000 },
352
+ ...frameScopeProperties,
353
+ }, ['session_id']),
354
+ },
355
+ {
356
+ actionId: 'screenshot',
357
+ title: 'Screenshot',
358
+ description: 'Capture a page, full-page, or element screenshot under browser artifact storage.',
359
+ readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 60_000, cancellable: true, idempotent: false,
360
+ scopes: ['browser.read'], resourceClaims: readRemote,
361
+ argumentsSchema: sessionTargetSchema({ full_page: { type: 'boolean' }, selector: { type: 'string' } }),
362
+ },
363
+ {
364
+ actionId: 'extract_links',
365
+ title: 'Extract links',
366
+ description: 'Extract anchors with href/text from the page.',
367
+ readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 60_000, cancellable: true, idempotent: true,
368
+ scopes: ['browser.read'], resourceClaims: [{ resource: 'remote', mode: 'read' }],
369
+ argumentsSchema: sessionTargetSchema({ limit: { type: 'number' } }),
370
+ },
371
+ {
372
+ actionId: 'extract_tables',
373
+ title: 'Extract tables',
374
+ description: 'Extract simple HTML tables as row arrays.',
375
+ readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 60_000, cancellable: true, idempotent: true,
376
+ scopes: ['browser.read'], resourceClaims: [{ resource: 'remote', mode: 'read' }],
377
+ argumentsSchema: sessionTargetSchema({ limit: { type: 'number' } }),
378
+ },
379
+ {
380
+ actionId: 'extract_forms',
381
+ title: 'Extract forms',
382
+ description: 'Extract form field summaries without values that look like secrets.',
383
+ readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 60_000, cancellable: true, idempotent: true,
384
+ scopes: ['browser.read'], resourceClaims: [{ resource: 'remote', mode: 'read' }],
385
+ argumentsSchema: sessionTargetSchema({ limit: { type: 'number' } }),
386
+ },
387
+ {
388
+ actionId: 'snapshot_interactive',
389
+ title: 'Snapshot interactive elements',
390
+ description: 'Snapshot interactive elements with stable selector hints plus CSS-pixel viewport geometry for screenshot-to-input grounding.',
391
+ readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 60_000, cancellable: true, idempotent: true,
392
+ scopes: ['browser.read'], resourceClaims: [{ resource: 'remote', mode: 'read' }],
393
+ argumentsSchema: sessionTargetSchema({ limit: { type: 'number' }, ...frameScopeProperties }),
394
+ },
395
+ {
396
+ actionId: 'get_console_errors',
397
+ title: 'Get console errors',
398
+ description: 'Return captured console error messages for a page open cycle.',
399
+ readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 60_000, cancellable: true, idempotent: true,
400
+ scopes: ['browser.read'], resourceClaims: [{ resource: 'remote', mode: 'read' }],
401
+ argumentsSchema: sessionTargetSchema({}),
402
+ },
403
+ {
404
+ actionId: 'get_failed_requests',
405
+ title: 'Get failed requests',
406
+ description: 'Return failed network requests captured during a page open cycle.',
407
+ readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 60_000, cancellable: true, idempotent: true,
408
+ scopes: ['browser.read'], resourceClaims: [{ resource: 'remote', mode: 'read' }],
409
+ argumentsSchema: sessionTargetSchema({}),
410
+ },
411
+ {
412
+ actionId: 'activate_page',
413
+ title: 'Activate browser page',
414
+ description: 'Bring the exact saved browser page/tab to the foreground without opening or replacing a tab.',
415
+ readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 60_000, cancellable: true, idempotent: true,
416
+ scopes: ['browser.interact', 'browser.profile'], resourceClaims: writeRemote,
417
+ argumentsSchema: interactSchema({}, []),
418
+ },
419
+ {
420
+ actionId: 'click',
421
+ title: 'Click element',
422
+ description: 'Click a selector on an allowed page after authorization.',
423
+ readOnly: false, risk: 'remote_write', confirmation: 'authorization', defaultTimeoutMs: 60_000, cancellable: true, idempotent: false,
424
+ scopes: ['browser.interact', 'browser.profile'], resourceClaims: writeRemote,
425
+ argumentsSchema: interactSchema({ selector: { type: 'string' } }, ['selector']),
426
+ },
427
+ {
428
+ actionId: 'click_text',
429
+ title: 'Click exact visible text',
430
+ description: 'Click the smallest visible standard DOM element whose normalized text exactly matches the requested text. Closed shadow roots are intentionally not traversed.',
431
+ readOnly: false, risk: 'remote_write', confirmation: 'authorization', defaultTimeoutMs: 60_000, cancellable: true, idempotent: false,
432
+ scopes: ['browser.interact', 'browser.profile'], resourceClaims: writeRemote,
433
+ argumentsSchema: interactSchema({ text: { type: 'string', minLength: 1, maxLength: 200 } }, ['text']),
434
+ },
435
+ {
436
+ actionId: 'double_click',
437
+ title: 'Double-click element',
438
+ description: 'Double-click a selector after authorization.',
439
+ readOnly: false, risk: 'remote_write', confirmation: 'authorization', defaultTimeoutMs: 60_000, cancellable: true, idempotent: false,
440
+ scopes: ['browser.interact', 'browser.profile'], resourceClaims: writeRemote,
441
+ argumentsSchema: interactSchema({ selector: { type: 'string' } }, ['selector']),
442
+ },
443
+ {
444
+ actionId: 'hover',
445
+ title: 'Hover element',
446
+ description: 'Hover a selector after authorization.',
447
+ readOnly: false, risk: 'remote_write', confirmation: 'authorization', defaultTimeoutMs: 60_000, cancellable: true, idempotent: false,
448
+ scopes: ['browser.interact', 'browser.profile'], resourceClaims: writeRemote,
449
+ argumentsSchema: interactSchema({ selector: { type: 'string' } }, ['selector']),
450
+ },
451
+ {
452
+ actionId: 'focus',
453
+ title: 'Focus element',
454
+ description: 'Focus a selector after authorization.',
455
+ readOnly: false, risk: 'remote_write', confirmation: 'authorization', defaultTimeoutMs: 60_000, cancellable: true, idempotent: false,
456
+ scopes: ['browser.interact', 'browser.profile'], resourceClaims: writeRemote,
457
+ argumentsSchema: interactSchema({ selector: { type: 'string' } }, ['selector']),
458
+ },
459
+ {
460
+ actionId: 'type',
461
+ title: 'Type into element',
462
+ description: 'Type text into a selector (append-style) after authorization.',
463
+ readOnly: false, risk: 'remote_write', confirmation: 'authorization', defaultTimeoutMs: 60_000, cancellable: true, idempotent: false,
464
+ scopes: ['browser.interact', 'browser.profile'], resourceClaims: writeRemote,
465
+ argumentsSchema: interactSchema({ selector: { type: 'string' }, text: { type: 'string' } }, ['selector', 'text']),
466
+ },
467
+ {
468
+ actionId: 'fill',
469
+ title: 'Fill element',
470
+ description: 'Replace the value of a selector after authorization.',
471
+ readOnly: false, risk: 'remote_write', confirmation: 'authorization', defaultTimeoutMs: 60_000, cancellable: true, idempotent: false,
472
+ scopes: ['browser.interact', 'browser.profile'], resourceClaims: writeRemote,
473
+ argumentsSchema: interactSchema({ selector: { type: 'string' }, text: { type: 'string' } }, ['selector', 'text']),
474
+ },
475
+ {
476
+ actionId: 'select_option',
477
+ title: 'Select option',
478
+ description: 'Select one or more option values on a <select> after authorization.',
479
+ readOnly: false, risk: 'remote_write', confirmation: 'authorization', defaultTimeoutMs: 60_000, cancellable: true, idempotent: false,
480
+ scopes: ['browser.interact', 'browser.profile'], resourceClaims: writeRemote,
481
+ argumentsSchema: interactSchema({ selector: { type: 'string' }, values: { type: 'array', items: { type: 'string' } } }, ['selector', 'values']),
482
+ },
483
+ {
484
+ actionId: 'check',
485
+ title: 'Check checkbox',
486
+ description: 'Check a checkbox/radio after authorization.',
487
+ readOnly: false, risk: 'remote_write', confirmation: 'authorization', defaultTimeoutMs: 60_000, cancellable: true, idempotent: false,
488
+ scopes: ['browser.interact', 'browser.profile'], resourceClaims: writeRemote,
489
+ argumentsSchema: interactSchema({ selector: { type: 'string' } }, ['selector']),
490
+ },
491
+ {
492
+ actionId: 'uncheck',
493
+ title: 'Uncheck checkbox',
494
+ description: 'Uncheck a checkbox after authorization.',
495
+ readOnly: false, risk: 'remote_write', confirmation: 'authorization', defaultTimeoutMs: 60_000, cancellable: true, idempotent: false,
496
+ scopes: ['browser.interact', 'browser.profile'], resourceClaims: writeRemote,
497
+ argumentsSchema: interactSchema({ selector: { type: 'string' } }, ['selector']),
498
+ },
499
+ {
500
+ actionId: 'press',
501
+ title: 'Press key',
502
+ description: 'Press a key on a selector on an allowed page after authorization.',
503
+ readOnly: false, risk: 'remote_write', confirmation: 'authorization', defaultTimeoutMs: 60_000, cancellable: true, idempotent: false,
504
+ scopes: ['browser.interact', 'browser.profile'], resourceClaims: writeRemote,
505
+ argumentsSchema: interactSchema({ selector: { type: 'string' }, key: { type: 'string' } }, ['selector', 'key']),
506
+ },
507
+ {
508
+ actionId: 'keyboard_shortcut',
509
+ title: 'Keyboard shortcut',
510
+ description: 'Press a keyboard shortcut (e.g. Meta+A) after authorization.',
511
+ readOnly: false, risk: 'remote_write', confirmation: 'authorization', defaultTimeoutMs: 60_000, cancellable: true, idempotent: false,
512
+ scopes: ['browser.interact', 'browser.profile'], resourceClaims: writeRemote,
513
+ argumentsSchema: interactSchema({ key: { type: 'string' } }, ['key']),
514
+ },
515
+ {
516
+ actionId: 'trusted_input',
517
+ title: 'Trusted browser input',
518
+ description: 'Send bounded mouse, wheel, drag, key, or text input after authorization. Playwright/CDP uses page input primitives; native macOS sessions require the exact saved tab to already be foreground/active and use the stable Desktop Operator input broker without activating another tab.',
519
+ readOnly: false, risk: 'remote_write', confirmation: 'authorization', defaultTimeoutMs: 60_000, cancellable: true, idempotent: false,
520
+ scopes: ['browser.interact', 'browser.profile'], resourceClaims: writeRemote,
521
+ argumentsSchema: interactSchema({
522
+ kind: { type: 'string', enum: ['click', 'move', 'wheel', 'drag', 'key', 'text'] },
523
+ x: { type: 'number', minimum: 0, maximum: 100000 },
524
+ y: { type: 'number', minimum: 0, maximum: 100000 },
525
+ from_x: { type: 'number', minimum: 0, maximum: 100000 },
526
+ from_y: { type: 'number', minimum: 0, maximum: 100000 },
527
+ to_x: { type: 'number', minimum: 0, maximum: 100000 },
528
+ to_y: { type: 'number', minimum: 0, maximum: 100000 },
529
+ delta_x: { type: 'number', minimum: -100000, maximum: 100000 },
530
+ delta_y: { type: 'number', minimum: -100000, maximum: 100000 },
531
+ button: { type: 'string', enum: ['left', 'middle', 'right'] },
532
+ click_count: { type: 'number', minimum: 1, maximum: 3 },
533
+ steps: { type: 'number', minimum: 1, maximum: 100 },
534
+ key: { type: 'string', minLength: 1, maxLength: 100 },
535
+ text: { type: 'string', maxLength: 10000 },
536
+ guard_selector: { type: 'string' },
537
+ ...frameScopeProperties,
538
+ }, ['kind']),
539
+ },
540
+ {
541
+ actionId: 'dispatch_event',
542
+ title: 'Dispatch DOM event',
543
+ description: 'Dispatch one named bubbling/composed DOM CustomEvent on an explicit selector after authorization. No arbitrary JavaScript payload is accepted.',
544
+ readOnly: false, risk: 'remote_write', confirmation: 'authorization', defaultTimeoutMs: 60_000, cancellable: true, idempotent: false,
545
+ scopes: ['browser.interact', 'browser.profile'], resourceClaims: writeRemote,
546
+ argumentsSchema: interactSchema({ selector: { type: 'string' }, event: { type: 'string', minLength: 1, maxLength: 64 } }, ['selector', 'event']),
547
+ },
548
+ {
549
+ actionId: 'wait_for_selector',
550
+ title: 'Wait for selector',
551
+ description: 'Wait for a selector state on an allowed page after authorization.',
552
+ readOnly: true, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 60_000, cancellable: true, idempotent: true,
553
+ scopes: ['browser.interact', 'browser.profile'], resourceClaims: readRemote,
554
+ argumentsSchema: sessionTargetSchema({
555
+ selector: { type: 'string' },
556
+ state: { type: 'string', enum: ['attached', 'detached', 'visible', 'hidden'] },
557
+ }, ['selector']),
558
+ },
559
+ {
560
+ actionId: 'attach_local_file',
561
+ title: 'Attach local file(s)',
562
+ description: 'Set one or more allowed local files on input[type=file] after authorization. file_path remains backward-compatible; file_paths supports multiple selection. Never auto-opens executables.',
563
+ readOnly: false, risk: 'remote_write', confirmation: 'authorization', defaultTimeoutMs: 60_000, cancellable: true, idempotent: false,
564
+ scopes: ['browser.interact', 'browser.profile'], resourceClaims: writeRemote,
565
+ argumentsSchema: interactSchema({
566
+ selector: { type: 'string' },
567
+ file_path: { type: 'string' },
568
+ file_paths: { type: 'array', minItems: 1, maxItems: 32, items: { type: 'string' } },
569
+ }, ['selector']),
570
+ },
571
+ {
572
+ actionId: 'await_file_transfer',
573
+ title: 'Await file transfer',
574
+ description: 'Capture a browser download into bounded artifact storage after authorization. Never auto-opens downloaded files.',
575
+ readOnly: false, risk: 'remote_write', confirmation: 'authorization', defaultTimeoutMs: 120_000, cancellable: true, idempotent: false,
576
+ scopes: ['browser.interact', 'browser.profile'], resourceClaims: writeRemote,
577
+ argumentsSchema: interactSchema({ selector: { type: 'string' }, suggested_name: { type: 'string' } }, ['selector']),
578
+ },
579
+ {
580
+ actionId: 'close_page',
581
+ title: 'Close session',
582
+ description: 'Remove saved session metadata while keeping the persistent profile.',
583
+ readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 15_000, cancellable: true, idempotent: true,
584
+ scopes: ['browser.read', 'browser.profile'], resourceClaims: [{ resource: 'repo-state', mode: 'write' }],
585
+ argumentsSchema: { type: 'object', properties: { session_id: { type: 'string' } }, required: ['session_id'], additionalProperties: false },
586
+ },
587
+ ];
588
+ const foregroundRequired = new Set(['activate_page', 'request_human_handoff']);
589
+ const foregroundPossible = new Set([
590
+ 'create_session', 'open_page', 'navigate', 'reload', 'go_back', 'resolve_handoff',
591
+ 'click', 'click_text', 'double_click', 'hover', 'focus', 'type', 'fill', 'select_option',
592
+ 'check', 'uncheck', 'press', 'keyboard_shortcut', 'trusted_input', 'dispatch_event',
593
+ 'wait_for_selector', 'attach_local_file', 'await_file_transfer',
594
+ ]);
595
+ return descriptors.map((descriptor) => ({
596
+ ...descriptor,
597
+ foregroundEffect: foregroundRequired.has(descriptor.actionId)
598
+ ? 'required'
599
+ : foregroundPossible.has(descriptor.actionId)
600
+ ? 'possible'
601
+ : 'none',
602
+ }));
603
+ }