mixdog 1.1.0 → 1.1.2

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 (174) hide show
  1. package/README.md +13 -6
  2. package/package.json +1 -2
  3. package/scripts/latest-guard.mjs +63 -0
  4. package/scripts/lib/run-node-tests.mjs +103 -27
  5. package/scripts/lib/test-failure-summary.mjs +7 -2
  6. package/scripts/merge-mac-update-feed.mjs +107 -0
  7. package/scripts/mobile-release.mjs +210 -0
  8. package/scripts/post-release-check.mjs +353 -0
  9. package/scripts/publish-asset-release.mjs +43 -0
  10. package/scripts/release-guard.mjs +63 -0
  11. package/scripts/release-paths.mjs +39 -2
  12. package/scripts/test.mjs +29 -7
  13. package/src/defaults/shipped-history.json +2 -0
  14. package/src/defaults/skills/computer-use/SKILL.md +1 -1
  15. package/src/defaults/skills/setup/references/actions.md +1 -0
  16. package/src/headless-role.mjs +2 -2
  17. package/src/lib/keychain-cjs.cjs +582 -17
  18. package/src/lib/rules-builder.cjs +0 -23
  19. package/src/rules/lead/LEAD.md +2 -2
  20. package/src/runtime/agent/orchestrator/config-storage.mjs +32 -4
  21. package/src/runtime/agent/orchestrator/config.mjs +18 -0
  22. package/src/runtime/agent/orchestrator/context/instruction-files.mjs +130 -0
  23. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +4 -0
  24. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +11 -0
  25. package/src/runtime/agent/orchestrator/providers/lib/open-login-url.mjs +9 -0
  26. package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +254 -0
  27. package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +218 -0
  28. package/src/runtime/agent/orchestrator/providers/mimo-oauth-tokens.mjs +239 -0
  29. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +233 -0
  30. package/src/runtime/agent/orchestrator/providers/muse-code-oauth-login.mjs +247 -0
  31. package/src/runtime/agent/orchestrator/providers/muse-code-oauth-tokens.mjs +225 -0
  32. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +542 -0
  33. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +25 -0
  34. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +4 -1
  35. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +13 -0
  36. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +14 -6
  37. package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog-store.mjs +99 -0
  38. package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog.mjs +4 -8
  39. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +2 -7
  40. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +8 -0
  41. package/src/runtime/agent/orchestrator/providers/registry.mjs +4 -0
  42. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +2 -0
  43. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-defs.mjs +15 -1
  44. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +1 -5
  45. package/src/runtime/agent/orchestrator/session/context-utils.mjs +12 -14
  46. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +11 -1
  47. package/src/runtime/agent/orchestrator/session/loop/tool-call-ids.mjs +90 -0
  48. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +19 -11
  49. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +1 -0
  50. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +28 -0
  51. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +8 -0
  52. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -0
  53. package/src/runtime/agent/orchestrator/session/manager.mjs +1 -5
  54. package/src/runtime/agent/orchestrator/session/read-dedup.mjs +0 -1
  55. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +3 -0
  56. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +2 -0
  57. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +2 -0
  58. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +23 -0
  59. package/src/runtime/agent/orchestrator/session/turn-effort.mjs +18 -0
  60. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +11 -8
  61. package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +9 -4
  62. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/bashism-rewrite.mjs +245 -0
  63. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/command-preflight.mjs +13 -1
  64. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +3 -1
  65. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
  66. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +49 -0
  67. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +3 -3
  68. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +9 -7
  69. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +61 -8
  70. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +28 -3
  71. package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +73 -1
  72. package/src/runtime/agent/orchestrator/tools/builtin/read-single-guards.mjs +2 -2
  73. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +3 -1
  74. package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +17 -6
  75. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +23 -5
  76. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +93 -43
  77. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +32 -6
  78. package/src/runtime/agent/orchestrator/tools/builtin.mjs +9 -2
  79. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +3 -1
  80. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +12 -15
  81. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background.mjs +2 -2
  82. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +4 -2
  83. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +124 -91
  84. package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +19 -4
  85. package/src/runtime/channels/index.mjs +1 -5
  86. package/src/runtime/channels/lib/runtime-paths.mjs +8 -23
  87. package/src/runtime/channels/lib/tool-dispatch.mjs +1 -13
  88. package/src/runtime/channels/lib/worker-ipc.mjs +2 -2
  89. package/src/runtime/channels/lib/worker-main.mjs +4 -34
  90. package/src/runtime/computer-bridge/client.mjs +25 -1
  91. package/src/runtime/computer-bridge/linux-action-capabilities.mjs +84 -0
  92. package/src/runtime/computer-bridge/linux-readiness.d.mts +52 -0
  93. package/src/runtime/computer-bridge/linux-readiness.mjs +411 -0
  94. package/src/runtime/computer-bridge/test-linux-display.mjs +5 -0
  95. package/src/runtime/effort-judge/compact-tokenizer.mjs +13 -8
  96. package/src/runtime/effort-judge/judge-input.mjs +5 -2
  97. package/src/runtime/effort-judge/model-install.mjs +9 -3
  98. package/src/runtime/effort-judge/model-manifest.json +7 -7
  99. package/src/runtime/memory/lib/embedding-worker.mjs +9 -3
  100. package/src/runtime/memory/lib/pg/supervisor.mjs +3 -0
  101. package/src/runtime/shared/config.mjs +50 -2
  102. package/src/runtime/shared/developer-options.mjs +7 -0
  103. package/src/runtime/shared/instruction-files-mode.mjs +9 -0
  104. package/src/runtime/shared/llm/model-catalog.mjs +2 -1
  105. package/src/runtime/shared/llm/openai-daybreak.mjs +79 -0
  106. package/src/runtime/shared/pristine-execution-contract.json +9 -0
  107. package/src/runtime/shared/pristine-execution.mjs +1 -1
  108. package/src/runtime/shared/provider-accounts.mjs +2 -0
  109. package/src/runtime/shared/resource-admission.mjs +10 -3
  110. package/src/runtime/shared/schedule-model-ref.mjs +11 -0
  111. package/src/runtime/shared/semver.d.mts +1 -0
  112. package/src/runtime/shared/semver.mjs +57 -0
  113. package/src/runtime/shared/session-approval-wait.mjs +35 -0
  114. package/src/runtime/shared/shell-display.mjs +4 -2
  115. package/src/runtime/shared/tool-surface.mjs +2 -2
  116. package/src/runtime/shared/turn-worktree-snapshot.mjs +11 -10
  117. package/src/runtime/shared/update-checker.mjs +2 -31
  118. package/src/session-runtime/boot/apis.mjs +5 -0
  119. package/src/session-runtime/boot/begin.mjs +0 -2
  120. package/src/session-runtime/boot/tools.mjs +5 -4
  121. package/src/session-runtime/model-recency.mjs +3 -0
  122. package/src/session-runtime/provider-auth/login.mjs +10 -10
  123. package/src/session-runtime/provider-auth/setup.mjs +22 -2
  124. package/src/session-runtime/provider-models/row-hydration.mjs +12 -4
  125. package/src/session-runtime/provider-readiness.mjs +4 -0
  126. package/src/session-runtime/resource-mcp-api.mjs +7 -3
  127. package/src/session-runtime/runtime-bootstrap.mjs +0 -4
  128. package/src/session-runtime/runtime-facade.mjs +5 -0
  129. package/src/session-runtime/runtime-paths.mjs +0 -1
  130. package/src/session-runtime/schedule-session-run.mjs +3 -1
  131. package/src/session-runtime/services/agent-tool/index-write-queue.mjs +55 -14
  132. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +15 -3
  133. package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +2 -0
  134. package/src/session-runtime/services/agent-tool/worker-index/approval-wait.mjs +97 -0
  135. package/src/session-runtime/services/agent-tool/worker-index/mutation-batch.mjs +4 -2
  136. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +8 -2
  137. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +3 -0
  138. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +58 -2
  139. package/src/session-runtime/services/agent-tool/worker-index.mjs +69 -15
  140. package/src/session-runtime/services/agent-tool/worker-rows.mjs +2 -0
  141. package/src/session-runtime/services/channel-admin.mjs +4 -2
  142. package/src/session-runtime/services/channel-worker.mjs +1 -1
  143. package/src/session-runtime/services/provider-admin.mjs +80 -17
  144. package/src/session-runtime/services/seeds.mjs +7 -10
  145. package/src/session-runtime/services/usage-dashboard-model.mjs +6 -0
  146. package/src/session-runtime/settings-builtin-tools-api.mjs +15 -0
  147. package/src/session-runtime/settings-profile-api.mjs +27 -2
  148. package/src/session-runtime/setup-tool/executor.mjs +5 -0
  149. package/src/session-runtime/setup-tool/tool-defs.mjs +8 -0
  150. package/src/session-runtime/starter-guide-state.mjs +62 -0
  151. package/src/session-runtime/tool-policy-refresh.mjs +3 -0
  152. package/src/session-runtime/webhook-session-run.mjs +3 -1
  153. package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -2
  154. package/src/standalone/channel-binding.mjs +1 -2
  155. package/src/standalone/channel-transport/call-dispatch.mjs +4 -10
  156. package/src/standalone/daemon-channels-loader.mjs +2 -3
  157. package/src/standalone/local-session-runtime.mjs +2 -78
  158. package/src/standalone/session-protocol.mjs +2 -0
  159. package/src/tui/app/model-options.mjs +13 -1
  160. package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +10 -4
  161. package/src/tui/app/provider-setup/oauth-actions.mjs +5 -0
  162. package/src/tui/dist/index.mjs +84 -34
  163. package/src/tui/session/context-state.mjs +2 -0
  164. package/src/tui/session/live-share/owner.mjs +17 -0
  165. package/src/tui/session/oauth-flows.mjs +2 -0
  166. package/src/tui/session/session-api/extensions.mjs +12 -8
  167. package/src/tui/session/session-api/integrations.mjs +9 -8
  168. package/src/tui/session/session-api/routes.mjs +2 -2
  169. package/src/tui/session/session-api/settings.mjs +1 -0
  170. package/src/tui/session/session-api/shared.mjs +10 -6
  171. package/src/tui/session/tool-card-results/grouped-result.mjs +11 -2
  172. package/src/tui/session/transcript-spill/spill-dir.mjs +22 -6
  173. package/src/tui/spinner-meta.mjs +26 -10
  174. package/src/runtime/channels/tool-defs.mjs +0 -17
@@ -4,7 +4,6 @@ import { createRequire } from 'node:module';
4
4
  import { loadConfig, createProvider, DATA_DIR } from './config.mjs';
5
5
  import { resolveVoiceRuntime } from './voice-runtime-fetcher.mjs';
6
6
  import { whisperThreadCount } from './voice-transcription.mjs';
7
- import { TOOL_DEFS } from '../tool-defs.mjs';
8
7
  import { ensureReady, stopVoiceWhisperServer } from './whisper-server.mjs';
9
8
  import { ensurePrivateRuntimeRoot, resolveRuntimeRoot } from '../../shared/runtime-root.mjs';
10
9
  import { Scheduler } from './scheduler.mjs';
@@ -102,7 +101,6 @@ runWorkerBootstrap({
102
101
  pruneStalePluginDataLogSiblings,
103
102
  DEFAULT_STALE_LOG_SIBLING_MAX,
104
103
  });
105
- const INSTRUCTIONS = '';
106
104
 
107
105
  // ── Parent notification helper ───────────────────────────────────────
108
106
  // This worker has no MCP transport of its own. All notifications flow
@@ -129,10 +127,7 @@ let _reloadDebounce = null;
129
127
  const STATUS_FILE = getStatusPath(INSTANCE_ID);
130
128
  const statusState = new JsonStateFile(STATUS_FILE, {});
131
129
  statusState.ensure();
132
- const scheduler = new Scheduler(
133
- config.nonInteractive ?? [],
134
- config.interactive ?? []
135
- );
130
+ const scheduler = new Scheduler(config.nonInteractive ?? [], config.interactive ?? []);
136
131
  // Schedules fire as visible sessions; the session runner belongs to the
137
132
  // session-runtime layer, so this worker entry injects it (lazy, like webhooks).
138
133
  scheduler.setScheduleRunner(async (schedule, opts) => {
@@ -250,19 +245,12 @@ function wireEventQueueHandlers(eventQueue) {
250
245
  // re-read at probe time so a stale-connected flag cannot mask a lost seat.
251
246
  eventQueue.setOwnerGetter(() => bridgeRuntimeConnected && currentOwnerState().owned);
252
247
  }
253
- // Inbound messaging, provider interaction handlers, and channel voice-message
254
- // transcription are DELETED with Discord/Telegram (user decision: PWA replaces
255
- // channels). The headless provider emits no events, so nothing wires here.
256
- // Tool dispatch in worker mode goes through the IPC `call` handler at the
257
- // bottom of this file (parent's `callWorker` → `handleToolCall`). There is no
258
- // orphan worker-level MCP Server: the parent (server.mjs) owns the single
259
- // connected transport and routes CallTool through the IPC `call` path.
260
248
  // ── Worker/HTTP tool-call dispatch ──────────────────────────────────────────
261
249
  // Runtime lifecycle dispatch only (activate_channel_bridge / reload_config):
262
250
  // threaded as a lifecycle bag of lazy getters so the module reads live
263
251
  // file-level references at call time. Used by the HTTP MCP CallTool path and
264
252
  // the worker IPC `call` handler at the bottom of this file.
265
- const { handleToolCall, handleToolCallWithBridgeRetry } = createToolDispatch({
253
+ const { handleToolCall } = createToolDispatch({
266
254
  isChannelsDegraded,
267
255
  lifecycle: {
268
256
  getChannelBridgeActive: () => channelBridgeActive,
@@ -277,14 +265,6 @@ const { handleToolCall, handleToolCallWithBridgeRetry } = createToolDispatch({
277
265
  reloadRuntimeConfig,
278
266
  },
279
267
  });
280
- async function init(_sharedMcp) {
281
- // _sharedMcp is no longer used. Notifications now flow via IPC to the parent
282
- // (sendNotifyToParent above). The parameter is retained for backward
283
- // compatibility with any caller that still passes a Server reference.
284
- scheduler.setInjectHandler((channelId, name, content, options) => {
285
- injectAndRecord(channelId, name, content, options);
286
- });
287
- }
288
268
  function ensureConfigWatcher() {
289
269
  if (_configWatcher) return;
290
270
  try {
@@ -374,19 +354,9 @@ if (_isWorkerMode && process.send && process.env.MIXDOG_DAEMON_HOST !== '1') {
374
354
  getProvider: () => provider,
375
355
  getConfig: () => config,
376
356
  handleMemoryCallResponse,
377
- handleToolCallWithBridgeRetry,
357
+ handleToolCall,
378
358
  bootProfile,
379
359
  });
380
360
  }
381
361
 
382
- export {
383
- TOOL_DEFS,
384
- handleToolCall,
385
- handleToolCallWithBridgeRetry,
386
- init,
387
- INSTRUCTIONS as instructions,
388
- isChannelBridgeActive,
389
- isChannelsDegraded,
390
- start,
391
- stop,
392
- };
362
+ export { handleToolCall, isChannelBridgeActive, isChannelsDegraded, start, stop };
@@ -23,6 +23,7 @@ import { computerErrorCode } from './error-code.mjs';
23
23
  import { bridgeDiscoveryChanged, readBridgeDiscovery } from '../bridge-discovery.mjs';
24
24
  import { MAX_COMPUTER_REQUEST_BYTES, readComputerBridgeJson, validateComputerReply } from './limits.mjs';
25
25
  import { computerActionHas } from './actions.mjs';
26
+ import { linuxCaptureFallback, linuxComputerPreflight } from './linux-readiness.mjs';
26
27
  import { continuePendingComputerWork, isPendingComputerWork } from './pending-continuation.mjs';
27
28
 
28
29
  export { canonicalComputerResultText };
@@ -245,6 +246,13 @@ function computerToolResult(value, args) {
245
246
  /** Execute one `computer` tool call. Returns MCP-shaped content so the
246
247
  * internal-tools normalizer forwards text and screenshot images as-is. */
247
248
  export async function executeComputerTool(rawArgs, context = {}) {
249
+ const note = { text: '' };
250
+ const result = await executeComputerToolInner(rawArgs, context, note);
251
+ // The fallback note rides as its own block so the JSON result text stays intact.
252
+ return note.text && !result.isError ? { ...result, content: [...result.content, { type: 'text', text: note.text }] } : result;
253
+ }
254
+
255
+ async function executeComputerToolInner(rawArgs, context, note) {
248
256
  const discovery = readBridgeDiscovery(DISCOVERY_FILE);
249
257
  if (!discovery) return computerErrorResult(`Error: ${BRIDGE_UNAVAILABLE_MESSAGE}`);
250
258
  // Resolve the argument shape once so validation, host translation, and the
@@ -252,12 +260,28 @@ export async function executeComputerTool(rawArgs, context = {}) {
252
260
  const args = normalizeComputerToolArgs(rawArgs);
253
261
  const validationError = validateComputerToolArgs(args);
254
262
  if (validationError) return computerErrorResult(`Error: ${validationError}`);
255
- const command = toComputerHostCommand(args);
263
+ let command = toComputerHostCommand(args);
256
264
  const sessionId = context?.sessionId ? String(context.sessionId) : '';
265
+ const fallback = await linuxCaptureFallback({ command });
266
+ if (fallback) {
267
+ command = fallback.command;
268
+ note.text = fallback.note;
269
+ }
257
270
  const encoded = JSON.stringify({ ...command, ...(sessionId ? { session_id: sessionId } : {}) });
258
271
  if (Buffer.byteLength(encoded) > MAX_COMPUTER_REQUEST_BYTES) {
259
272
  return computerErrorResult('Error: computer request exceeds byte limit; no input was dispatched');
260
273
  }
274
+ // Linux: refuse an action whose own capability (input device, window
275
+ // control, AT-SPI, ...) is missing, before anything is dispatched, instead of
276
+ // failing mid-task. Actions that need none of it still run.
277
+ const notReady = await linuxComputerPreflight({ command });
278
+ if (notReady) {
279
+ return {
280
+ ...computerErrorResult(`Error: ${notReady.message}`),
281
+ // Structured twin of the text for the transcript card (no text parsing).
282
+ toolMeta: { code: notReady.code, missing: [...new Set(notReady.missing.map((item) => item.capability))] },
283
+ };
284
+ }
261
285
  const releaseError = await awaitPendingSessionRelease(sessionId, args, context);
262
286
  if (releaseError) return releaseError;
263
287
  trackComputerSession(sessionId, command);
@@ -0,0 +1,84 @@
1
+ // Which Linux capability each host command really needs, read from
2
+ // native/mixdog-computer (host/*.rs) and the desktop app's observation layer.
3
+ // Only a hard requirement is listed: an action that merely works better with a
4
+ // capability (OCR for capture, AT-SPI for a state capture, which degrades to
5
+ // pixels) does not name it, so a missing optional piece never blocks it.
6
+ //
7
+ // windows a window list/handle (X11 EWMH, sway, Hyprland, GNOME Window
8
+ // Calls). Every native window target (window_id, app, the
9
+ // foreground window) resolves through it.
10
+ // accessibility the AT-SPI bus.
11
+ // semanticActions AT-SPI actions (invoke, toggle, set_value, menu): they act
12
+ // on an accessible found through a window handle, so they need
13
+ // both and use no input device.
14
+ // pointer/keyboard the input device (uinput on Wayland, XTest on X11).
15
+
16
+ const WINDOW_ACTIONS = new Set([
17
+ 'list_windows', 'window_snapshot', 'related_windows', 'window_bounds', 'window_capture',
18
+ 'window_integrity', 'validate_background_input', 'list_apps', 'verify', 'focus_window',
19
+ 'move_window', 'window_state', 'close_window', 'terminate_process',
20
+ ]);
21
+ const ACCESSIBILITY_ACTIONS = new Set(['snapshot', 'find', 'window_predicates', 'accessibility_probe']);
22
+ const SEMANTIC_ACTIONS = new Set(['invoke', 'set_value', 'toggle', 'invoke_menu']);
23
+ const POINTER_ACTIONS = new Set([
24
+ 'click', 'double_click', 'right_click', 'middle_click', 'triple_click',
25
+ 'mouse_down', 'mouse_up', 'mouse_move', 'drag', 'scroll',
26
+ ]);
27
+ const KEYBOARD_ACTIONS = new Set(['type', 'key', 'key_down', 'key_up']);
28
+
29
+ const namesWindow = (command) => Boolean(command?.window_id || command?.app || command?.window);
30
+
31
+ function addStepNeeds(need, step, delivery) {
32
+ const action = String(step?.action || '');
33
+ const foreground = delivery === 'foreground';
34
+ if (step?.ref || step?.element) need.add('accessibility');
35
+ if (SEMANTIC_ACTIONS.has(action)) {
36
+ // A foreground invoke is a real click; every other semantic action is AT-SPI.
37
+ if (action === 'invoke' && foreground) need.add('pointer');
38
+ else need.add('semanticActions');
39
+ if (action === 'invoke_menu') need.add('windows');
40
+ } else if (POINTER_ACTIONS.has(action)) {
41
+ need.add('pointer');
42
+ } else if (KEYBOARD_ACTIONS.has(action)) {
43
+ // Background typing into a settable element goes through its value.
44
+ if (action === 'type' && step.ref && !foreground) need.add('semanticActions');
45
+ else need.add('keyboard');
46
+ }
47
+ }
48
+
49
+ /** A capture that names no window or screen: it resolves the foreground window. */
50
+ export function isForegroundWindowCapture(command) {
51
+ return String(command?.action) === 'capture' && !namesWindow(command) && command.screen === undefined;
52
+ }
53
+
54
+ /** The capabilities a host command (as toComputerHostCommand emits it) requires. */
55
+ export function requiredCapabilities(command) {
56
+ const need = new Set();
57
+ const action = String(command?.action || '');
58
+ if (WINDOW_ACTIONS.has(action)) need.add('windows');
59
+ else if (ACCESSIBILITY_ACTIONS.has(action)) {
60
+ need.add('windows');
61
+ need.add('accessibility');
62
+ } else if (action === 'capture') {
63
+ need.add('capture');
64
+ const mode = String(command.mode || 'state');
65
+ // Only a screen-index vision capture needs no window: every other mode
66
+ // resolves a named window or the foreground one.
67
+ const screenOnly = mode === 'vision' && command.screen !== undefined && !namesWindow(command);
68
+ if (!screenOnly) need.add('windows');
69
+ if (mode === 'ax') need.add('accessibility');
70
+ } else if (action === 'screenshot' || action === 'zoom') {
71
+ need.add('capture');
72
+ } else if (action === 'clipboard_read' || action === 'clipboard_write') {
73
+ need.add('clipboard');
74
+ } else if (action === 'sequence') {
75
+ // An act always names its window (the schema requires it).
76
+ need.add('windows');
77
+ const delivery = String(command.delivery || 'background');
78
+ for (const step of Array.isArray(command.steps) ? command.steps : []) addStepNeeds(need, step, delivery);
79
+ } else {
80
+ addStepNeeds(need, command, String(command?.delivery || 'background'));
81
+ if (need.size > 0 && namesWindow(command)) need.add('windows');
82
+ }
83
+ return need;
84
+ }
@@ -0,0 +1,52 @@
1
+ export type LinuxComputerCapabilityState = 'ready' | 'needs_setup' | 'unsupported' | 'unknown';
2
+ export interface LinuxComputerCapability {
3
+ state: LinuxComputerCapabilityState;
4
+ fix?: 'allow_input' | 'relogin' | 'enable_window_helper' | 'install_package';
5
+ package?: string;
6
+ optional?: boolean;
7
+ }
8
+ export type LinuxComputerCapabilityName =
9
+ | 'capture'
10
+ | 'windows'
11
+ | 'accessibility'
12
+ | 'semanticActions'
13
+ | 'pointer'
14
+ | 'keyboard'
15
+ | 'clipboard'
16
+ | 'ocr';
17
+ export interface LinuxComputerReadiness {
18
+ session: 'wayland' | 'x11';
19
+ desktop: 'gnome' | 'kde' | 'sway' | 'hyprland' | 'other';
20
+ capabilities: Record<LinuxComputerCapabilityName, LinuxComputerCapability>;
21
+ }
22
+ export interface LinuxComputerMissingCapability {
23
+ capability: LinuxComputerCapabilityName;
24
+ state: LinuxComputerCapabilityState;
25
+ fix?: LinuxComputerCapability['fix'];
26
+ package?: string;
27
+ code: string;
28
+ message: string;
29
+ }
30
+ export interface LinuxComputerRefusal {
31
+ code: 'computer_capability_unavailable';
32
+ missing: LinuxComputerMissingCapability[];
33
+ message: string;
34
+ }
35
+ export const MIN_WINDOW_HELPER_VERSION: number;
36
+ export const CAPABILITY_UNAVAILABLE_CODE: 'computer_capability_unavailable';
37
+ export function detectSession(env: Record<string, string | undefined>): 'wayland' | 'x11';
38
+ export function detectDesktop(env: Record<string, string | undefined>, session: 'wayland' | 'x11'): LinuxComputerReadiness['desktop'];
39
+ export function checkLinuxComputerReadiness(options?: Record<string, unknown>): Promise<LinuxComputerReadiness>;
40
+ export function getLinuxComputerReadiness(options?: Record<string, unknown>): Promise<LinuxComputerReadiness>;
41
+ export function missingCapabilities(
42
+ readiness: LinuxComputerReadiness,
43
+ command: Record<string, unknown>
44
+ ): LinuxComputerMissingCapability[];
45
+ export function linuxComputerPreflight(options?: Record<string, unknown>): Promise<LinuxComputerRefusal | null>;
46
+ export function resetLinuxComputerPreflightCache(): void;
47
+ export const GNOME_EXTENSION_UUID: 'mixdog-windows@mixdog.io';
48
+ export function gnomeExtensionDirs(env?: Record<string, string | undefined>): string[];
49
+ export const FIX_MESSAGES: Readonly<Record<'allow_input' | 'relogin' | 'enable_window_helper' | 'install_package', string>>;
50
+ export const REFUSAL_MESSAGE: string;
51
+ export const CAPTURE_FALLBACK_NOTE: string;
52
+ export function linuxCaptureFallback(options?: Record<string, unknown>): Promise<{ command: Record<string, unknown>; note: string } | null>;