Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions apps/desktop/bundled-tools.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,5 +31,19 @@
"thirdPartyNotices": "missing",
"notarization": "missing",
"distributionReady": false
},
"makaCu": {
"repo": "maka-agent/maka-cu",
"branch": "maka/base",
"commit": "ca7ef80c721fdaf6e7f4af882f16b9c555f14733",
"expectedProtocolVersion": "maka.cu/2",
"binaryName": "maka-cu",
"binarySizeBytes": 2861584,
"binarySha256": "26d45d5243fefd993dc754d5b6bdd64c8c5ff38f3d0ccf6dbb132db89a49f2a7",
"buildProvenance": "local-source-build",
"signature": "adhoc",
"hardenedRuntime": false,
"notarization": "missing",
"distributionReady": false
}
}
41 changes: 41 additions & 0 deletions apps/desktop/src/main/__tests__/computer-use-host.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -65,6 +65,47 @@ describe('Computer Use host health', () => {
assert.equal(computerUseServiceHealth('none', undefined).state, 'not_available');
});

it('reads the executor that is selected, not the role pair one of them happens to have', () => {
// maka-cu supervises one child (§11) and reports its own shape, so it has
// no `action`/`capture` pair to read. This function took only that pair,
// while the availability half of the same capability card had already been
// widened to "any selected executor" — executed against the built desktop
// module with a genuinely ready maka-cu backend, the card read:
//
// executorState() = {"state":"ready","generation":1}
// serviceState (boot) = undefined
// health = not_available, reason naming cua-driver
// artifactAvailable = true
//
// available, state not_available, and a reason naming an executor that is
// not the one running.
assert.deepEqual(
computerUseServiceHealth('maka-cu', { state: 'ready', generation: 1, restartAttempts: 0 }),
{ state: 'healthy', reason: 'maka-cu 操作与截图服务已就绪。' },
);
assert.equal(
computerUseServiceHealth('maka-cu', {
state: 'backing_off',
generation: 1,
restartAttempts: 1,
}).state,
'degraded',
);
assert.deepEqual(
computerUseServiceHealth('maka-cu', {
state: 'unavailable',
generation: 1,
restartAttempts: 3,
}),
{ state: 'not_available', reason: 'maka-cu service 启动失败或已退出。' },
);
assert.equal(
computerUseServiceHealth('maka-cu', { state: 'idle', generation: 0, restartAttempts: 0 })
.state,
'not_run',
);
});

it('constructs a backend only when the local artifact matches the manifest hash', async () => {
const directory = await mkdtemp(join(tmpdir(), 'maka-cu-host-'));
try {
Expand Down
10 changes: 8 additions & 2 deletions apps/desktop/src/main/boot.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -1430,9 +1430,15 @@ wireAppLifecycle({
});

function computerUseCapabilityInput() {
const serviceState = computerUse.backend?.serviceState?.();
// Whichever executor was selected reports its own shape: cua-driver an
// action/capture role pair, maka-cu (§11) a single supervised child. Reading
// only `serviceState` meant a ready maka-cu backend produced `undefined`
// here, and the card said "not available" while its own availability half
// said the opposite.
const executorState =
computerUse.backend?.serviceState?.() ?? computerUse.backend?.executorState?.();
return {
backendId: computerUse.backendId,
health: computerUseServiceHealth(computerUse.backendId, serviceState),
health: computerUseServiceHealth(computerUse.backendId, executorState),
};
}
24 changes: 14 additions & 10 deletions apps/desktop/src/main/capability-snapshot.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@ import {
type OsPermissionSnapshot,
type PermissionSnapshot,
} from '@maka/core';
import type { CuBackendId } from '@maka/computer-use';
import type { BotStatus } from '@maka/runtime';
import type { computerUseServiceHealth } from './computer-use-host.js';
import {
Expand DownExpand Up@@ -46,7 +47,7 @@ export function buildCapabilitySnapshotCollection(input: {
permissions: PermissionSnapshot;
botStatuses: Record<BotProvider, BotStatus>;
computerUse?: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
};
now?: number;
Expand DownExpand Up@@ -123,13 +124,16 @@ export function buildCapabilitySnapshotCollection(input: {

function computerUseCapability(
input: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
} | undefined,
permissions: PermissionSnapshot['permissions'],
now: number,
): CapabilitySnapshot {
const artifactAvailable = input?.backendId === 'cua-driver';
// Any selected executor is an executor. Naming one here made the capability
// read `not_available` for a machine that had a working backend, merely a
// different one.
const artifactAvailable = input !== undefined && input.backendId !== 'none';
return staticCapability({
id: 'computer_use',
label: 'Computer Use',
Expand All@@ -152,23 +156,23 @@ function computerUseCapability(
state: input?.health.state ?? 'not_available',
source: 'runtime_probe',
lastCheckedAt: now,
reason: input?.health.reason ?? 'cua-driver 后端当前不可用。',
reason: input?.health.reason ?? 'Computer Use 后端当前不可用。',
},
});
}

function computerUseCapabilityReason(
input: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
} | undefined,
permissions: PermissionSnapshot['permissions'],
): string {
if (input?.backendId !== 'cua-driver') {
return '未找到通过完整性检查的 cua-driver artifact。';
if (input === undefined || input.backendId === 'none') {
return '未找到通过完整性检查的 Computer Use 执行器 artifact。';
}

const reasons = ['cua-driver artifact 已通过本地完整性检查。'];
const reasons = [`${input.backendId} artifact 已通过本地完整性检查。`];
const missingPermissions = [
['辅助功能', permissions.accessibility.status],
['屏幕录制', permissions.screen_recording.status],
Expand All@@ -178,10 +182,10 @@ function computerUseCapabilityReason(
}
switch (input.health.state) {
case 'not_available':
reasons.push('cua-driver service 启动失败、已退出或已停止。');
reasons.push(`${input.backendId} service 启动失败、已退出或已停止。`);
break;
case 'degraded':
reasons.push('cua-driver service 正在启动或恢复。');
reasons.push(`${input.backendId} service 正在启动或恢复。`);
break;
case 'healthy':
reasons.push('操作与截图 service 已就绪;按目标与动作类别授权后可操作本机应用。');
Expand Down
62 changes: 43 additions & 19 deletions apps/desktop/src/main/computer-use-host.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,6 +11,7 @@ import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import type { CuaDriverRoleSnapshot } from '@maka/computer-use';
import type { CuaDriverBackendOptions } from '@maka/computer-use';
import type { MakaCuServiceSnapshot } from '@maka/computer-use';
import {
selectComputerUseBackend,
type SelectedComputerUseBackend,
Expand DownExpand Up@@ -118,53 +119,76 @@ export function createDesktopPhysicalInputGuard(
return () => getSystemIdleTime() < 1;
}

/**
* The health half of the Computer Use capability card, for whichever executor
* was selected.
*
* This used to take the cua-driver role pair and nothing else, while the card's
* `available` half had already been widened to "any selected executor". With a
* genuinely ready maka-cu backend the two halves disagreed, and executing the
* built desktop module against one showed exactly how:
*
* executorState() = {"state":"ready","generation":1}
* serviceState (boot) = undefined
* health = {"state":"not_available","reason":"未找到通过完整性检查且可分发的 cua-driver artifact。"}
* artifactAvailable = true
*
* — available, state not_available, and a reason naming an executor that is not
* the one running. cua-driver supervises an action/capture role pair; maka-cu
* supervises one child (§11) and reports its own shape. Both are read here as a
* list of role states, so the card is right for either, and neither is selected
* by being described.
*/
export type ComputerUseExecutorState =
| { action: CuaDriverRoleSnapshot; capture: CuaDriverRoleSnapshot }
| MakaCuServiceSnapshot;

function roleStates(state: ComputerUseExecutorState): Array<CuaDriverRoleSnapshot['state']> {
return 'action' in state ? [state.action.state, state.capture.state] : [state.state];
}

export function computerUseServiceHealth(
backendId: SelectedComputerUseBackend['backendId'],
state: {
action: CuaDriverRoleSnapshot;
capture: CuaDriverRoleSnapshot;
} | undefined,
state: ComputerUseExecutorState | undefined,
): {
state: 'not_available' | 'not_run' | 'healthy' | 'degraded';
reason: string;
} {
if (backendId === 'none' || !state) {
return {
state: 'not_available',
reason: '未找到通过完整性检查且可分发的 cua-driver artifact。',
reason: '未找到通过完整性检查且可分发的 Computer Use 执行器 artifact。',
};
}
const roles = [state.action, state.capture];
if (roles.some((role) =>
role.state === 'unavailable' || role.state === 'disposed')) {
const roles = roleStates(state);
if (roles.some((role) => role === 'unavailable' || role === 'disposed')) {
return {
state: 'not_available',
reason: roles.some((role) => role.state === 'disposed')
? 'cua-driver service 已停止。'
: 'cua-driver service 启动失败或已退出。',
reason: roles.some((role) => role === 'disposed')
? `${backendId} service 已停止。`
: `${backendId} service 启动失败或已退出。`,
};
}
if (roles.some((role) =>
role.state === 'starting' || role.state === 'backing_off')) {
if (roles.some((role) => role === 'starting' || role === 'backing_off')) {
return {
state: 'degraded',
reason: 'cua-driver service 正在启动或恢复。',
reason: `${backendId} service 正在启动或恢复。`,
};
}
if (roles.every((role) => role.state === 'ready')) {
if (roles.every((role) => role === 'ready')) {
return {
state: 'healthy',
reason: 'cua-driver 操作与截图服务已就绪。',
reason: `${backendId} 操作与截图服务已就绪。`,
};
}
if (roles.some((role) => role.state === 'ready')) {
if (roles.some((role) => role === 'ready')) {
return {
state: 'not_run',
reason: 'cua-driver 部分服务已启动,其余服务将在需要时启动。',
reason: `${backendId} 部分服务已启动,其余服务将在需要时启动。`,
};
}
return {
state: 'not_run',
reason: 'cua-driver 已可用,将在首次调用时启动。',
reason: `${backendId} 已可用,将在首次调用时启动。`,
};
}
84 changes: 84 additions & 0 deletions docs/computer-use-provenance.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
# Computer Use provenance

Maka's Computer Use surface was built against other people's work, in three
materially different ways. They are separated here because the obligations
differ: one is redistribution under a license, one is reading licensed source,
and one is observing a proprietary binary and holds no license grant at all.

Paths are Maka's unless they carry the upstream repository name, as in
`open-codex-computer-use/...`.

Every entry names what was taken, where it landed, and what the evidence was.
When you add or change a borrowed design, add the row here and put the same
statement in the file that carries it — an in-file comment answers "why is this
constant 200?" at the moment someone asks it, and this file answers "what did we
build on?" for the project as a whole.

## 1. Redistributed under license

Ships inside the Maka artifact. Requires the license text and copyright notice
to travel with it.

| Component | License | Where the notice lives |
|---|---|---|
| npm dependencies | various | `apps/desktop/resources/licenses/npm/`, generated by `scripts/generate-third-party-notices.mjs` and byte-checked at build by `scripts/check-third-party-notices.mjs` |
| cua-driver | MIT | `apps/desktop/resources/licenses/cua-driver/`, pinned by digest in `apps/desktop/bundled-tools.json` |

cua-driver is the Computer Use executor Maka currently defaults to. It is a
third-party binary, fetched by `scripts/prepare-cua-driver.mjs` and verified
against the digests recorded in the manifest.

Maka's own executor, `maka-cu`, is built from Maka's own source by
`scripts/prepare-maka-cu.mjs` and pinned by digest in the same manifest. It is
not signed, so it is not distributed at all yet: its `distributionReady` is
false and `scripts/verify-macos-arm64-dmg.mjs` forbids its path in a packaged
build. It is selectable in a development build and nothing selects it by
default.

`maka-cu` is itself a fork of MIT-licensed `iFurySt/open-codex-computer-use`
(§2), so when it does ship, that notice travels with it.

## 2. Licensed source read as reference

MIT-licensed source we read while designing. No code was copied into this
repository; what was taken is design — a format, a decision, or an archived
measurement. Attribution is given because it was load-bearing, not because MIT
compels it for ideas.

### iFurySt/open-codex-computer-use, and its fork QwenLM/open-computer-use

Both MIT, © 2026 Leo. An independent reimplementation of Codex's Computer Use
as an MCP server.

| Taken | Landed in | Notes |
|---|---|---|
| An archived capture of Codex's real `get_app_state` result | `packages/runtime/src/computer-use-tools.ts` | Their `open-codex-computer-use/artifacts/tool-comparisons/20260417-focus-behavior/`. This is what turned Maka's model of Codex's observation format from inference into an observed sample. |
| The one-line-per-element observation shape: indentation for containment, states written only when not the default | same file | Maka's version keeps `observation_id` in the header (frame binding is protocol here, prose there) and keeps element geometry (Codex has no coordinate action surface to need it; Maka's is disabled by default rather than absent). |
| Writing only the informative half of a state — `disabled`, never `enabled` | same file | Their `summarizeTraits`. |
| Filtering `AXPress` out of an element's advertised action list, because pressing is what `click` does | not yet landed — waits on `trycua/cua#2622` exposing per-element AX actions | Their `meaningfulActions`. |
| The permission-onboarding pattern: anchor a guidance panel to the System Settings window, track it, and distinguish grants that need an app relaunch | not yet landed — for the `feat/permission-onboarding` work | Their `open-codex-computer-use/apps/OpenComputerUse/Sources/OpenComputerUse/PermissionOnboardingApp.swift`. |
| Archiving side-by-side tool captures in-repo as evidence | practice, not code | Worth adopting for Maka's own Codex comparisons. |

Not taken, recorded so the decision is not re-litigated: their `SkyLightSPI` /
`SkyClickSimulation` synthetic-focus click. Their own comment states the recipe
is derived from cua-driver and yabai. Maka read the same recipe rather than
that code.

## 3. Observed, not licensed

Codex Computer Use (`SkyComputerUseService` and its helper bundles) is
proprietary and ships no source. Nothing was copied from it, because there is
nothing to copy: what exists here was reimplemented from observed behaviour and
from constants recovered by inspecting the shipped binary.

This confers no rights and is not a license. Statements about it are
descriptions of what a build did on a given date, and are dated for that reason.

| Recovered | Landed in |
|---|---|
| The agent cursor: shape, hotspot, motion thresholds, spring constants, and the candidate-path scoring function | `apps/desktop/src/renderer/computer-use-overlay/engine/cursor-engine.ts` |
| Overlay level policy — an occluded target raises the cursor rather than hiding it | same file, and `apps/desktop/src/main/computer-use/cursor-overlay-window.ts` |
| The observation text shape | `packages/runtime/src/computer-use-tools.ts`, corroborated by the archived capture in §2 |

Where Maka deliberately diverges, the divergence is stated at the point of
divergence rather than here, so it is read by whoever is changing that code.
3 changes: 2 additions & 1 deletion package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,7 +31,7 @@
"test:dist": "npm run test:scripts:full && node scripts/run-workspace-tests-parallel.mjs --concurrency=3",
"test:dist:serial": "npm run test:scripts:full && node scripts/run-workspace-tests-parallel.mjs --serial",
"test:fast": "npm run build:test && npm run test:scripts && node scripts/run-workspace-tests-parallel.mjs --concurrency=3",
"test:scripts": "node --test scripts/fixture-env.test.mjs scripts/electron-lifecycle.test.mjs scripts/check-story-annotations.test.mjs scripts/ci-test-plan.test.mjs scripts/run-headless-tests.test.mjs scripts/run-workspace-tests-parallel.test.mjs scripts/cu-e2e-scenarios.test.mjs scripts/cu-report-sanitize.test.mjs",
"test:scripts": "node --test scripts/fixture-env.test.mjs scripts/electron-lifecycle.test.mjs scripts/check-story-annotations.test.mjs scripts/ci-test-plan.test.mjs scripts/run-headless-tests.test.mjs scripts/run-workspace-tests-parallel.test.mjs scripts/cu-e2e-scenarios.test.mjs scripts/cu-report-sanitize.test.mjs scripts/computer-use-provenance.test.mjs",
"test:scripts:extended": "node --test scripts/cua-driver-provenance.test.mjs scripts/cu-provider-matrix.test.mjs scripts/cu-real-model-launcher.test.mjs scripts/macos-arm64-release.test.mjs scripts/measure-session-bundle.test.mjs",
"test:scripts:full": "npm run test:scripts && npm run test:scripts:extended",
"dev": "npm --workspace @maka/desktop run dev:hmr --",
Expand All@@ -52,6 +52,7 @@
"cost:deepseek-baseline": "node scripts/deepseek-live-cost-baseline.mjs",
"benchmark:kimi-protocol-ab": "node packages/headless/harbor/run-kimi-protocol-ab.mjs",
"prepare:cua-driver": "node scripts/prepare-cua-driver.mjs",
"prepare:maka-cu": "node scripts/prepare-maka-cu.mjs",
"check:cua-driver-artifact": "node scripts/check-cua-driver-bundle.mjs",
"e2e:computer-use-real": "node scripts/cu-real-ax-model-e2e-launcher.mjs",
"e2e:computer-use-process-restart": "MAKA_CU_AX_MODEL_SCENARIO=restart-recovery node scripts/cu-real-ax-model-e2e-launcher.mjs",
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
feat(computer-use): add maka-cu as a selectable executor backend by hqhq1025 · Pull Request #1953 · apache/maka · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions apps/desktop/bundled-tools.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,5 +31,19 @@
"thirdPartyNotices": "missing",
"notarization": "missing",
"distributionReady": false
},
"makaCu": {
"repo": "maka-agent/maka-cu",
"branch": "maka/base",
"commit": "ca7ef80c721fdaf6e7f4af882f16b9c555f14733",
"expectedProtocolVersion": "maka.cu/2",
"binaryName": "maka-cu",
"binarySizeBytes": 2861584,
"binarySha256": "26d45d5243fefd993dc754d5b6bdd64c8c5ff38f3d0ccf6dbb132db89a49f2a7",
"buildProvenance": "local-source-build",
"signature": "adhoc",
"hardenedRuntime": false,
"notarization": "missing",
"distributionReady": false
}
}
41 changes: 41 additions & 0 deletions apps/desktop/src/main/__tests__/computer-use-host.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -65,6 +65,47 @@ describe('Computer Use host health', () => {
assert.equal(computerUseServiceHealth('none', undefined).state, 'not_available');
});

it('reads the executor that is selected, not the role pair one of them happens to have', () => {
// maka-cu supervises one child (§11) and reports its own shape, so it has
// no `action`/`capture` pair to read. This function took only that pair,
// while the availability half of the same capability card had already been
// widened to "any selected executor" — executed against the built desktop
// module with a genuinely ready maka-cu backend, the card read:
//
// executorState() = {"state":"ready","generation":1}
// serviceState (boot) = undefined
// health = not_available, reason naming cua-driver
// artifactAvailable = true
//
// available, state not_available, and a reason naming an executor that is
// not the one running.
assert.deepEqual(
computerUseServiceHealth('maka-cu', { state: 'ready', generation: 1, restartAttempts: 0 }),
{ state: 'healthy', reason: 'maka-cu 操作与截图服务已就绪。' },
);
assert.equal(
computerUseServiceHealth('maka-cu', {
state: 'backing_off',
generation: 1,
restartAttempts: 1,
}).state,
'degraded',
);
assert.deepEqual(
computerUseServiceHealth('maka-cu', {
state: 'unavailable',
generation: 1,
restartAttempts: 3,
}),
{ state: 'not_available', reason: 'maka-cu service 启动失败或已退出。' },
);
assert.equal(
computerUseServiceHealth('maka-cu', { state: 'idle', generation: 0, restartAttempts: 0 })
.state,
'not_run',
);
});

it('constructs a backend only when the local artifact matches the manifest hash', async () => {
const directory = await mkdtemp(join(tmpdir(), 'maka-cu-host-'));
try {
Expand Down
10 changes: 8 additions & 2 deletions apps/desktop/src/main/boot.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -1430,9 +1430,15 @@ wireAppLifecycle({
});

function computerUseCapabilityInput() {
const serviceState = computerUse.backend?.serviceState?.();
// Whichever executor was selected reports its own shape: cua-driver an
// action/capture role pair, maka-cu (§11) a single supervised child. Reading
// only `serviceState` meant a ready maka-cu backend produced `undefined`
// here, and the card said "not available" while its own availability half
// said the opposite.
const executorState =
computerUse.backend?.serviceState?.() ?? computerUse.backend?.executorState?.();
return {
backendId: computerUse.backendId,
health: computerUseServiceHealth(computerUse.backendId, serviceState),
health: computerUseServiceHealth(computerUse.backendId, executorState),
};
}
24 changes: 14 additions & 10 deletions apps/desktop/src/main/capability-snapshot.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@ import {
type OsPermissionSnapshot,
type PermissionSnapshot,
} from '@maka/core';
import type { CuBackendId } from '@maka/computer-use';
import type { BotStatus } from '@maka/runtime';
import type { computerUseServiceHealth } from './computer-use-host.js';
import {
Expand DownExpand Up@@ -46,7 +47,7 @@ export function buildCapabilitySnapshotCollection(input: {
permissions: PermissionSnapshot;
botStatuses: Record<BotProvider, BotStatus>;
computerUse?: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
};
now?: number;
Expand DownExpand Up@@ -123,13 +124,16 @@ export function buildCapabilitySnapshotCollection(input: {

function computerUseCapability(
input: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
} | undefined,
permissions: PermissionSnapshot['permissions'],
now: number,
): CapabilitySnapshot {
const artifactAvailable = input?.backendId === 'cua-driver';
// Any selected executor is an executor. Naming one here made the capability
// read `not_available` for a machine that had a working backend, merely a
// different one.
const artifactAvailable = input !== undefined && input.backendId !== 'none';
return staticCapability({
id: 'computer_use',
label: 'Computer Use',
Expand All@@ -152,23 +156,23 @@ function computerUseCapability(
state: input?.health.state ?? 'not_available',
source: 'runtime_probe',
lastCheckedAt: now,
reason: input?.health.reason ?? 'cua-driver 后端当前不可用。',
reason: input?.health.reason ?? 'Computer Use 后端当前不可用。',
},
});
}

function computerUseCapabilityReason(
input: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
} | undefined,
permissions: PermissionSnapshot['permissions'],
): string {
if (input?.backendId !== 'cua-driver') {
return '未找到通过完整性检查的 cua-driver artifact。';
if (input === undefined || input.backendId === 'none') {
return '未找到通过完整性检查的 Computer Use 执行器 artifact。';
}

const reasons = ['cua-driver artifact 已通过本地完整性检查。'];
const reasons = [`${input.backendId} artifact 已通过本地完整性检查。`];
const missingPermissions = [
['辅助功能', permissions.accessibility.status],
['屏幕录制', permissions.screen_recording.status],
Expand All@@ -178,10 +182,10 @@ function computerUseCapabilityReason(
}
switch (input.health.state) {
case 'not_available':
reasons.push('cua-driver service 启动失败、已退出或已停止。');
reasons.push(`${input.backendId} service 启动失败、已退出或已停止。`);
break;
case 'degraded':
reasons.push('cua-driver service 正在启动或恢复。');
reasons.push(`${input.backendId} service 正在启动或恢复。`);
break;
case 'healthy':
reasons.push('操作与截图 service 已就绪;按目标与动作类别授权后可操作本机应用。');
Expand Down
62 changes: 43 additions & 19 deletions apps/desktop/src/main/computer-use-host.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,6 +11,7 @@ import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import type { CuaDriverRoleSnapshot } from '@maka/computer-use';
import type { CuaDriverBackendOptions } from '@maka/computer-use';
import type { MakaCuServiceSnapshot } from '@maka/computer-use';
import {
selectComputerUseBackend,
type SelectedComputerUseBackend,
Expand DownExpand Up@@ -118,53 +119,76 @@ export function createDesktopPhysicalInputGuard(
return () => getSystemIdleTime() < 1;
}

/**
* The health half of the Computer Use capability card, for whichever executor
* was selected.
*
* This used to take the cua-driver role pair and nothing else, while the card's
* `available` half had already been widened to "any selected executor". With a
* genuinely ready maka-cu backend the two halves disagreed, and executing the
* built desktop module against one showed exactly how:
*
* executorState() = {"state":"ready","generation":1}
* serviceState (boot) = undefined
* health = {"state":"not_available","reason":"未找到通过完整性检查且可分发的 cua-driver artifact。"}
* artifactAvailable = true
*
* — available, state not_available, and a reason naming an executor that is not
* the one running. cua-driver supervises an action/capture role pair; maka-cu
* supervises one child (§11) and reports its own shape. Both are read here as a
* list of role states, so the card is right for either, and neither is selected
* by being described.
*/
export type ComputerUseExecutorState =
| { action: CuaDriverRoleSnapshot; capture: CuaDriverRoleSnapshot }
| MakaCuServiceSnapshot;

function roleStates(state: ComputerUseExecutorState): Array<CuaDriverRoleSnapshot['state']> {
return 'action' in state ? [state.action.state, state.capture.state] : [state.state];
}

export function computerUseServiceHealth(
backendId: SelectedComputerUseBackend['backendId'],
state: {
action: CuaDriverRoleSnapshot;
capture: CuaDriverRoleSnapshot;
} | undefined,
state: ComputerUseExecutorState | undefined,
): {
state: 'not_available' | 'not_run' | 'healthy' | 'degraded';
reason: string;
} {
if (backendId === 'none' || !state) {
return {
state: 'not_available',
reason: '未找到通过完整性检查且可分发的 cua-driver artifact。',
reason: '未找到通过完整性检查且可分发的 Computer Use 执行器 artifact。',
};
}
const roles = [state.action, state.capture];
if (roles.some((role) =>
role.state === 'unavailable' || role.state === 'disposed')) {
const roles = roleStates(state);
if (roles.some((role) => role === 'unavailable' || role === 'disposed')) {
return {
state: 'not_available',
reason: roles.some((role) => role.state === 'disposed')
? 'cua-driver service 已停止。'
: 'cua-driver service 启动失败或已退出。',
reason: roles.some((role) => role === 'disposed')
? `${backendId} service 已停止。`
: `${backendId} service 启动失败或已退出。`,
};
}
if (roles.some((role) =>
role.state === 'starting' || role.state === 'backing_off')) {
if (roles.some((role) => role === 'starting' || role === 'backing_off')) {
return {
state: 'degraded',
reason: 'cua-driver service 正在启动或恢复。',
reason: `${backendId} service 正在启动或恢复。`,
};
}
if (roles.every((role) => role.state === 'ready')) {
if (roles.every((role) => role === 'ready')) {
return {
state: 'healthy',
reason: 'cua-driver 操作与截图服务已就绪。',
reason: `${backendId} 操作与截图服务已就绪。`,
};
}
if (roles.some((role) => role.state === 'ready')) {
if (roles.some((role) => role === 'ready')) {
return {
state: 'not_run',
reason: 'cua-driver 部分服务已启动,其余服务将在需要时启动。',
reason: `${backendId} 部分服务已启动,其余服务将在需要时启动。`,
};
}
return {
state: 'not_run',
reason: 'cua-driver 已可用,将在首次调用时启动。',
reason: `${backendId} 已可用,将在首次调用时启动。`,
};
}
84 changes: 84 additions & 0 deletions docs/computer-use-provenance.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
# Computer Use provenance

Maka's Computer Use surface was built against other people's work, in three
materially different ways. They are separated here because the obligations
differ: one is redistribution under a license, one is reading licensed source,
and one is observing a proprietary binary and holds no license grant at all.

Paths are Maka's unless they carry the upstream repository name, as in
`open-codex-computer-use/...`.

Every entry names what was taken, where it landed, and what the evidence was.
When you add or change a borrowed design, add the row here and put the same
statement in the file that carries it — an in-file comment answers "why is this
constant 200?" at the moment someone asks it, and this file answers "what did we
build on?" for the project as a whole.

## 1. Redistributed under license

Ships inside the Maka artifact. Requires the license text and copyright notice
to travel with it.

| Component | License | Where the notice lives |
|---|---|---|
| npm dependencies | various | `apps/desktop/resources/licenses/npm/`, generated by `scripts/generate-third-party-notices.mjs` and byte-checked at build by `scripts/check-third-party-notices.mjs` |
| cua-driver | MIT | `apps/desktop/resources/licenses/cua-driver/`, pinned by digest in `apps/desktop/bundled-tools.json` |

cua-driver is the Computer Use executor Maka currently defaults to. It is a
third-party binary, fetched by `scripts/prepare-cua-driver.mjs` and verified
against the digests recorded in the manifest.

Maka's own executor, `maka-cu`, is built from Maka's own source by
`scripts/prepare-maka-cu.mjs` and pinned by digest in the same manifest. It is
not signed, so it is not distributed at all yet: its `distributionReady` is
false and `scripts/verify-macos-arm64-dmg.mjs` forbids its path in a packaged
build. It is selectable in a development build and nothing selects it by
default.

`maka-cu` is itself a fork of MIT-licensed `iFurySt/open-codex-computer-use`
(§2), so when it does ship, that notice travels with it.

## 2. Licensed source read as reference

MIT-licensed source we read while designing. No code was copied into this
repository; what was taken is design — a format, a decision, or an archived
measurement. Attribution is given because it was load-bearing, not because MIT
compels it for ideas.

### iFurySt/open-codex-computer-use, and its fork QwenLM/open-computer-use

Both MIT, © 2026 Leo. An independent reimplementation of Codex's Computer Use
as an MCP server.

| Taken | Landed in | Notes |
|---|---|---|
| An archived capture of Codex's real `get_app_state` result | `packages/runtime/src/computer-use-tools.ts` | Their `open-codex-computer-use/artifacts/tool-comparisons/20260417-focus-behavior/`. This is what turned Maka's model of Codex's observation format from inference into an observed sample. |
| The one-line-per-element observation shape: indentation for containment, states written only when not the default | same file | Maka's version keeps `observation_id` in the header (frame binding is protocol here, prose there) and keeps element geometry (Codex has no coordinate action surface to need it; Maka's is disabled by default rather than absent). |
| Writing only the informative half of a state — `disabled`, never `enabled` | same file | Their `summarizeTraits`. |
| Filtering `AXPress` out of an element's advertised action list, because pressing is what `click` does | not yet landed — waits on `trycua/cua#2622` exposing per-element AX actions | Their `meaningfulActions`. |
| The permission-onboarding pattern: anchor a guidance panel to the System Settings window, track it, and distinguish grants that need an app relaunch | not yet landed — for the `feat/permission-onboarding` work | Their `open-codex-computer-use/apps/OpenComputerUse/Sources/OpenComputerUse/PermissionOnboardingApp.swift`. |
| Archiving side-by-side tool captures in-repo as evidence | practice, not code | Worth adopting for Maka's own Codex comparisons. |

Not taken, recorded so the decision is not re-litigated: their `SkyLightSPI` /
`SkyClickSimulation` synthetic-focus click. Their own comment states the recipe
is derived from cua-driver and yabai. Maka read the same recipe rather than
that code.

## 3. Observed, not licensed

Codex Computer Use (`SkyComputerUseService` and its helper bundles) is
proprietary and ships no source. Nothing was copied from it, because there is
nothing to copy: what exists here was reimplemented from observed behaviour and
from constants recovered by inspecting the shipped binary.

This confers no rights and is not a license. Statements about it are
descriptions of what a build did on a given date, and are dated for that reason.

| Recovered | Landed in |
|---|---|
| The agent cursor: shape, hotspot, motion thresholds, spring constants, and the candidate-path scoring function | `apps/desktop/src/renderer/computer-use-overlay/engine/cursor-engine.ts` |
| Overlay level policy — an occluded target raises the cursor rather than hiding it | same file, and `apps/desktop/src/main/computer-use/cursor-overlay-window.ts` |
| The observation text shape | `packages/runtime/src/computer-use-tools.ts`, corroborated by the archived capture in §2 |

Where Maka deliberately diverges, the divergence is stated at the point of
divergence rather than here, so it is read by whoever is changing that code.
3 changes: 2 additions & 1 deletion package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,7 +31,7 @@
"test:dist": "npm run test:scripts:full && node scripts/run-workspace-tests-parallel.mjs --concurrency=3",
"test:dist:serial": "npm run test:scripts:full && node scripts/run-workspace-tests-parallel.mjs --serial",
"test:fast": "npm run build:test && npm run test:scripts && node scripts/run-workspace-tests-parallel.mjs --concurrency=3",
"test:scripts": "node --test scripts/fixture-env.test.mjs scripts/electron-lifecycle.test.mjs scripts/check-story-annotations.test.mjs scripts/ci-test-plan.test.mjs scripts/run-headless-tests.test.mjs scripts/run-workspace-tests-parallel.test.mjs scripts/cu-e2e-scenarios.test.mjs scripts/cu-report-sanitize.test.mjs",
"test:scripts": "node --test scripts/fixture-env.test.mjs scripts/electron-lifecycle.test.mjs scripts/check-story-annotations.test.mjs scripts/ci-test-plan.test.mjs scripts/run-headless-tests.test.mjs scripts/run-workspace-tests-parallel.test.mjs scripts/cu-e2e-scenarios.test.mjs scripts/cu-report-sanitize.test.mjs scripts/computer-use-provenance.test.mjs",
"test:scripts:extended": "node --test scripts/cua-driver-provenance.test.mjs scripts/cu-provider-matrix.test.mjs scripts/cu-real-model-launcher.test.mjs scripts/macos-arm64-release.test.mjs scripts/measure-session-bundle.test.mjs",
"test:scripts:full": "npm run test:scripts && npm run test:scripts:extended",
"dev": "npm --workspace @maka/desktop run dev:hmr --",
Expand All@@ -52,6 +52,7 @@
"cost:deepseek-baseline": "node scripts/deepseek-live-cost-baseline.mjs",
"benchmark:kimi-protocol-ab": "node packages/headless/harbor/run-kimi-protocol-ab.mjs",
"prepare:cua-driver": "node scripts/prepare-cua-driver.mjs",
"prepare:maka-cu": "node scripts/prepare-maka-cu.mjs",
"check:cua-driver-artifact": "node scripts/check-cua-driver-bundle.mjs",
"e2e:computer-use-real": "node scripts/cu-real-ax-model-e2e-launcher.mjs",
"e2e:computer-use-process-restart": "MAKA_CU_AX_MODEL_SCENARIO=restart-recovery node scripts/cu-real-ax-model-e2e-launcher.mjs",
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(computer-use): add maka-cu as a selectable executor backend by hqhq1025 · Pull Request #1953 · apache/maka · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions apps/desktop/bundled-tools.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,5 +31,19 @@
"thirdPartyNotices": "missing",
"notarization": "missing",
"distributionReady": false
},
"makaCu": {
"repo": "maka-agent/maka-cu",
"branch": "maka/base",
"commit": "ca7ef80c721fdaf6e7f4af882f16b9c555f14733",
"expectedProtocolVersion": "maka.cu/2",
"binaryName": "maka-cu",
"binarySizeBytes": 2861584,
"binarySha256": "26d45d5243fefd993dc754d5b6bdd64c8c5ff38f3d0ccf6dbb132db89a49f2a7",
"buildProvenance": "local-source-build",
"signature": "adhoc",
"hardenedRuntime": false,
"notarization": "missing",
"distributionReady": false
}
}
41 changes: 41 additions & 0 deletions apps/desktop/src/main/__tests__/computer-use-host.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -65,6 +65,47 @@ describe('Computer Use host health', () => {
assert.equal(computerUseServiceHealth('none', undefined).state, 'not_available');
});

it('reads the executor that is selected, not the role pair one of them happens to have', () => {
// maka-cu supervises one child (§11) and reports its own shape, so it has
// no `action`/`capture` pair to read. This function took only that pair,
// while the availability half of the same capability card had already been
// widened to "any selected executor" — executed against the built desktop
// module with a genuinely ready maka-cu backend, the card read:
//
// executorState() = {"state":"ready","generation":1}
// serviceState (boot) = undefined
// health = not_available, reason naming cua-driver
// artifactAvailable = true
//
// available, state not_available, and a reason naming an executor that is
// not the one running.
assert.deepEqual(
computerUseServiceHealth('maka-cu', { state: 'ready', generation: 1, restartAttempts: 0 }),
{ state: 'healthy', reason: 'maka-cu 操作与截图服务已就绪。' },
);
assert.equal(
computerUseServiceHealth('maka-cu', {
state: 'backing_off',
generation: 1,
restartAttempts: 1,
}).state,
'degraded',
);
assert.deepEqual(
computerUseServiceHealth('maka-cu', {
state: 'unavailable',
generation: 1,
restartAttempts: 3,
}),
{ state: 'not_available', reason: 'maka-cu service 启动失败或已退出。' },
);
assert.equal(
computerUseServiceHealth('maka-cu', { state: 'idle', generation: 0, restartAttempts: 0 })
.state,
'not_run',
);
});

it('constructs a backend only when the local artifact matches the manifest hash', async () => {
const directory = await mkdtemp(join(tmpdir(), 'maka-cu-host-'));
try {
Expand Down
10 changes: 8 additions & 2 deletions apps/desktop/src/main/boot.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -1430,9 +1430,15 @@ wireAppLifecycle({
});

function computerUseCapabilityInput() {
const serviceState = computerUse.backend?.serviceState?.();
// Whichever executor was selected reports its own shape: cua-driver an
// action/capture role pair, maka-cu (§11) a single supervised child. Reading
// only `serviceState` meant a ready maka-cu backend produced `undefined`
// here, and the card said "not available" while its own availability half
// said the opposite.
const executorState =
computerUse.backend?.serviceState?.() ?? computerUse.backend?.executorState?.();
return {
backendId: computerUse.backendId,
health: computerUseServiceHealth(computerUse.backendId, serviceState),
health: computerUseServiceHealth(computerUse.backendId, executorState),
};
}
24 changes: 14 additions & 10 deletions apps/desktop/src/main/capability-snapshot.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@ import {
type OsPermissionSnapshot,
type PermissionSnapshot,
} from '@maka/core';
import type { CuBackendId } from '@maka/computer-use';
import type { BotStatus } from '@maka/runtime';
import type { computerUseServiceHealth } from './computer-use-host.js';
import {
Expand DownExpand Up@@ -46,7 +47,7 @@ export function buildCapabilitySnapshotCollection(input: {
permissions: PermissionSnapshot;
botStatuses: Record<BotProvider, BotStatus>;
computerUse?: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
};
now?: number;
Expand DownExpand Up@@ -123,13 +124,16 @@ export function buildCapabilitySnapshotCollection(input: {

function computerUseCapability(
input: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
} | undefined,
permissions: PermissionSnapshot['permissions'],
now: number,
): CapabilitySnapshot {
const artifactAvailable = input?.backendId === 'cua-driver';
// Any selected executor is an executor. Naming one here made the capability
// read `not_available` for a machine that had a working backend, merely a
// different one.
const artifactAvailable = input !== undefined && input.backendId !== 'none';
return staticCapability({
id: 'computer_use',
label: 'Computer Use',
Expand All@@ -152,23 +156,23 @@ function computerUseCapability(
state: input?.health.state ?? 'not_available',
source: 'runtime_probe',
lastCheckedAt: now,
reason: input?.health.reason ?? 'cua-driver 后端当前不可用。',
reason: input?.health.reason ?? 'Computer Use 后端当前不可用。',
},
});
}

function computerUseCapabilityReason(
input: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
} | undefined,
permissions: PermissionSnapshot['permissions'],
): string {
if (input?.backendId !== 'cua-driver') {
return '未找到通过完整性检查的 cua-driver artifact。';
if (input === undefined || input.backendId === 'none') {
return '未找到通过完整性检查的 Computer Use 执行器 artifact。';
}

const reasons = ['cua-driver artifact 已通过本地完整性检查。'];
const reasons = [`${input.backendId} artifact 已通过本地完整性检查。`];
const missingPermissions = [
['辅助功能', permissions.accessibility.status],
['屏幕录制', permissions.screen_recording.status],
Expand All@@ -178,10 +182,10 @@ function computerUseCapabilityReason(
}
switch (input.health.state) {
case 'not_available':
reasons.push('cua-driver service 启动失败、已退出或已停止。');
reasons.push(`${input.backendId} service 启动失败、已退出或已停止。`);
break;
case 'degraded':
reasons.push('cua-driver service 正在启动或恢复。');
reasons.push(`${input.backendId} service 正在启动或恢复。`);
break;
case 'healthy':
reasons.push('操作与截图 service 已就绪;按目标与动作类别授权后可操作本机应用。');
Expand Down
62 changes: 43 additions & 19 deletions apps/desktop/src/main/computer-use-host.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,6 +11,7 @@ import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import type { CuaDriverRoleSnapshot } from '@maka/computer-use';
import type { CuaDriverBackendOptions } from '@maka/computer-use';
import type { MakaCuServiceSnapshot } from '@maka/computer-use';
import {
selectComputerUseBackend,
type SelectedComputerUseBackend,
Expand DownExpand Up@@ -118,53 +119,76 @@ export function createDesktopPhysicalInputGuard(
return () => getSystemIdleTime() < 1;
}

/**
* The health half of the Computer Use capability card, for whichever executor
* was selected.
*
* This used to take the cua-driver role pair and nothing else, while the card's
* `available` half had already been widened to "any selected executor". With a
* genuinely ready maka-cu backend the two halves disagreed, and executing the
* built desktop module against one showed exactly how:
*
* executorState() = {"state":"ready","generation":1}
* serviceState (boot) = undefined
* health = {"state":"not_available","reason":"未找到通过完整性检查且可分发的 cua-driver artifact。"}
* artifactAvailable = true
*
* — available, state not_available, and a reason naming an executor that is not
* the one running. cua-driver supervises an action/capture role pair; maka-cu
* supervises one child (§11) and reports its own shape. Both are read here as a
* list of role states, so the card is right for either, and neither is selected
* by being described.
*/
export type ComputerUseExecutorState =
| { action: CuaDriverRoleSnapshot; capture: CuaDriverRoleSnapshot }
| MakaCuServiceSnapshot;

function roleStates(state: ComputerUseExecutorState): Array<CuaDriverRoleSnapshot['state']> {
return 'action' in state ? [state.action.state, state.capture.state] : [state.state];
}

export function computerUseServiceHealth(
backendId: SelectedComputerUseBackend['backendId'],
state: {
action: CuaDriverRoleSnapshot;
capture: CuaDriverRoleSnapshot;
} | undefined,
state: ComputerUseExecutorState | undefined,
): {
state: 'not_available' | 'not_run' | 'healthy' | 'degraded';
reason: string;
} {
if (backendId === 'none' || !state) {
return {
state: 'not_available',
reason: '未找到通过完整性检查且可分发的 cua-driver artifact。',
reason: '未找到通过完整性检查且可分发的 Computer Use 执行器 artifact。',
};
}
const roles = [state.action, state.capture];
if (roles.some((role) =>
role.state === 'unavailable' || role.state === 'disposed')) {
const roles = roleStates(state);
if (roles.some((role) => role === 'unavailable' || role === 'disposed')) {
return {
state: 'not_available',
reason: roles.some((role) => role.state === 'disposed')
? 'cua-driver service 已停止。'
: 'cua-driver service 启动失败或已退出。',
reason: roles.some((role) => role === 'disposed')
? `${backendId} service 已停止。`
: `${backendId} service 启动失败或已退出。`,
};
}
if (roles.some((role) =>
role.state === 'starting' || role.state === 'backing_off')) {
if (roles.some((role) => role === 'starting' || role === 'backing_off')) {
return {
state: 'degraded',
reason: 'cua-driver service 正在启动或恢复。',
reason: `${backendId} service 正在启动或恢复。`,
};
}
if (roles.every((role) => role.state === 'ready')) {
if (roles.every((role) => role === 'ready')) {
return {
state: 'healthy',
reason: 'cua-driver 操作与截图服务已就绪。',
reason: `${backendId} 操作与截图服务已就绪。`,
};
}
if (roles.some((role) => role.state === 'ready')) {
if (roles.some((role) => role === 'ready')) {
return {
state: 'not_run',
reason: 'cua-driver 部分服务已启动,其余服务将在需要时启动。',
reason: `${backendId} 部分服务已启动,其余服务将在需要时启动。`,
};
}
return {
state: 'not_run',
reason: 'cua-driver 已可用,将在首次调用时启动。',
reason: `${backendId} 已可用,将在首次调用时启动。`,
};
}
84 changes: 84 additions & 0 deletions docs/computer-use-provenance.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
# Computer Use provenance

Maka's Computer Use surface was built against other people's work, in three
materially different ways. They are separated here because the obligations
differ: one is redistribution under a license, one is reading licensed source,
and one is observing a proprietary binary and holds no license grant at all.

Paths are Maka's unless they carry the upstream repository name, as in
`open-codex-computer-use/...`.

Every entry names what was taken, where it landed, and what the evidence was.
When you add or change a borrowed design, add the row here and put the same
statement in the file that carries it — an in-file comment answers "why is this
constant 200?" at the moment someone asks it, and this file answers "what did we
build on?" for the project as a whole.

## 1. Redistributed under license

Ships inside the Maka artifact. Requires the license text and copyright notice
to travel with it.

| Component | License | Where the notice lives |
|---|---|---|
| npm dependencies | various | `apps/desktop/resources/licenses/npm/`, generated by `scripts/generate-third-party-notices.mjs` and byte-checked at build by `scripts/check-third-party-notices.mjs` |
| cua-driver | MIT | `apps/desktop/resources/licenses/cua-driver/`, pinned by digest in `apps/desktop/bundled-tools.json` |

cua-driver is the Computer Use executor Maka currently defaults to. It is a
third-party binary, fetched by `scripts/prepare-cua-driver.mjs` and verified
against the digests recorded in the manifest.

Maka's own executor, `maka-cu`, is built from Maka's own source by
`scripts/prepare-maka-cu.mjs` and pinned by digest in the same manifest. It is
not signed, so it is not distributed at all yet: its `distributionReady` is
false and `scripts/verify-macos-arm64-dmg.mjs` forbids its path in a packaged
build. It is selectable in a development build and nothing selects it by
default.

`maka-cu` is itself a fork of MIT-licensed `iFurySt/open-codex-computer-use`
(§2), so when it does ship, that notice travels with it.

## 2. Licensed source read as reference

MIT-licensed source we read while designing. No code was copied into this
repository; what was taken is design — a format, a decision, or an archived
measurement. Attribution is given because it was load-bearing, not because MIT
compels it for ideas.

### iFurySt/open-codex-computer-use, and its fork QwenLM/open-computer-use

Both MIT, © 2026 Leo. An independent reimplementation of Codex's Computer Use
as an MCP server.

| Taken | Landed in | Notes |
|---|---|---|
| An archived capture of Codex's real `get_app_state` result | `packages/runtime/src/computer-use-tools.ts` | Their `open-codex-computer-use/artifacts/tool-comparisons/20260417-focus-behavior/`. This is what turned Maka's model of Codex's observation format from inference into an observed sample. |
| The one-line-per-element observation shape: indentation for containment, states written only when not the default | same file | Maka's version keeps `observation_id` in the header (frame binding is protocol here, prose there) and keeps element geometry (Codex has no coordinate action surface to need it; Maka's is disabled by default rather than absent). |
| Writing only the informative half of a state — `disabled`, never `enabled` | same file | Their `summarizeTraits`. |
| Filtering `AXPress` out of an element's advertised action list, because pressing is what `click` does | not yet landed — waits on `trycua/cua#2622` exposing per-element AX actions | Their `meaningfulActions`. |
| The permission-onboarding pattern: anchor a guidance panel to the System Settings window, track it, and distinguish grants that need an app relaunch | not yet landed — for the `feat/permission-onboarding` work | Their `open-codex-computer-use/apps/OpenComputerUse/Sources/OpenComputerUse/PermissionOnboardingApp.swift`. |
| Archiving side-by-side tool captures in-repo as evidence | practice, not code | Worth adopting for Maka's own Codex comparisons. |

Not taken, recorded so the decision is not re-litigated: their `SkyLightSPI` /
`SkyClickSimulation` synthetic-focus click. Their own comment states the recipe
is derived from cua-driver and yabai. Maka read the same recipe rather than
that code.

## 3. Observed, not licensed

Codex Computer Use (`SkyComputerUseService` and its helper bundles) is
proprietary and ships no source. Nothing was copied from it, because there is
nothing to copy: what exists here was reimplemented from observed behaviour and
from constants recovered by inspecting the shipped binary.

This confers no rights and is not a license. Statements about it are
descriptions of what a build did on a given date, and are dated for that reason.

| Recovered | Landed in |
|---|---|
| The agent cursor: shape, hotspot, motion thresholds, spring constants, and the candidate-path scoring function | `apps/desktop/src/renderer/computer-use-overlay/engine/cursor-engine.ts` |
| Overlay level policy — an occluded target raises the cursor rather than hiding it | same file, and `apps/desktop/src/main/computer-use/cursor-overlay-window.ts` |
| The observation text shape | `packages/runtime/src/computer-use-tools.ts`, corroborated by the archived capture in §2 |

Where Maka deliberately diverges, the divergence is stated at the point of
divergence rather than here, so it is read by whoever is changing that code.
3 changes: 2 additions & 1 deletion package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,7 +31,7 @@
"test:dist": "npm run test:scripts:full && node scripts/run-workspace-tests-parallel.mjs --concurrency=3",
"test:dist:serial": "npm run test:scripts:full && node scripts/run-workspace-tests-parallel.mjs --serial",
"test:fast": "npm run build:test && npm run test:scripts && node scripts/run-workspace-tests-parallel.mjs --concurrency=3",
"test:scripts": "node --test scripts/fixture-env.test.mjs scripts/electron-lifecycle.test.mjs scripts/check-story-annotations.test.mjs scripts/ci-test-plan.test.mjs scripts/run-headless-tests.test.mjs scripts/run-workspace-tests-parallel.test.mjs scripts/cu-e2e-scenarios.test.mjs scripts/cu-report-sanitize.test.mjs",
"test:scripts": "node --test scripts/fixture-env.test.mjs scripts/electron-lifecycle.test.mjs scripts/check-story-annotations.test.mjs scripts/ci-test-plan.test.mjs scripts/run-headless-tests.test.mjs scripts/run-workspace-tests-parallel.test.mjs scripts/cu-e2e-scenarios.test.mjs scripts/cu-report-sanitize.test.mjs scripts/computer-use-provenance.test.mjs",
"test:scripts:extended": "node --test scripts/cua-driver-provenance.test.mjs scripts/cu-provider-matrix.test.mjs scripts/cu-real-model-launcher.test.mjs scripts/macos-arm64-release.test.mjs scripts/measure-session-bundle.test.mjs",
"test:scripts:full": "npm run test:scripts && npm run test:scripts:extended",
"dev": "npm --workspace @maka/desktop run dev:hmr --",
Expand All@@ -52,6 +52,7 @@
"cost:deepseek-baseline": "node scripts/deepseek-live-cost-baseline.mjs",
"benchmark:kimi-protocol-ab": "node packages/headless/harbor/run-kimi-protocol-ab.mjs",
"prepare:cua-driver": "node scripts/prepare-cua-driver.mjs",
"prepare:maka-cu": "node scripts/prepare-maka-cu.mjs",
"check:cua-driver-artifact": "node scripts/check-cua-driver-bundle.mjs",
"e2e:computer-use-real": "node scripts/cu-real-ax-model-e2e-launcher.mjs",
"e2e:computer-use-process-restart": "MAKA_CU_AX_MODEL_SCENARIO=restart-recovery node scripts/cu-real-ax-model-e2e-launcher.mjs",
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(computer-use): add maka-cu as a selectable executor backend by hqhq1025 · Pull Request #1953 · apache/maka · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions apps/desktop/bundled-tools.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,5 +31,19 @@
"thirdPartyNotices": "missing",
"notarization": "missing",
"distributionReady": false
},
"makaCu": {
"repo": "maka-agent/maka-cu",
"branch": "maka/base",
"commit": "ca7ef80c721fdaf6e7f4af882f16b9c555f14733",
"expectedProtocolVersion": "maka.cu/2",
"binaryName": "maka-cu",
"binarySizeBytes": 2861584,
"binarySha256": "26d45d5243fefd993dc754d5b6bdd64c8c5ff38f3d0ccf6dbb132db89a49f2a7",
"buildProvenance": "local-source-build",
"signature": "adhoc",
"hardenedRuntime": false,
"notarization": "missing",
"distributionReady": false
}
}
41 changes: 41 additions & 0 deletions apps/desktop/src/main/__tests__/computer-use-host.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -65,6 +65,47 @@ describe('Computer Use host health', () => {
assert.equal(computerUseServiceHealth('none', undefined).state, 'not_available');
});

it('reads the executor that is selected, not the role pair one of them happens to have', () => {
// maka-cu supervises one child (§11) and reports its own shape, so it has
// no `action`/`capture` pair to read. This function took only that pair,
// while the availability half of the same capability card had already been
// widened to "any selected executor" — executed against the built desktop
// module with a genuinely ready maka-cu backend, the card read:
//
// executorState() = {"state":"ready","generation":1}
// serviceState (boot) = undefined
// health = not_available, reason naming cua-driver
// artifactAvailable = true
//
// available, state not_available, and a reason naming an executor that is
// not the one running.
assert.deepEqual(
computerUseServiceHealth('maka-cu', { state: 'ready', generation: 1, restartAttempts: 0 }),
{ state: 'healthy', reason: 'maka-cu 操作与截图服务已就绪。' },
);
assert.equal(
computerUseServiceHealth('maka-cu', {
state: 'backing_off',
generation: 1,
restartAttempts: 1,
}).state,
'degraded',
);
assert.deepEqual(
computerUseServiceHealth('maka-cu', {
state: 'unavailable',
generation: 1,
restartAttempts: 3,
}),
{ state: 'not_available', reason: 'maka-cu service 启动失败或已退出。' },
);
assert.equal(
computerUseServiceHealth('maka-cu', { state: 'idle', generation: 0, restartAttempts: 0 })
.state,
'not_run',
);
});

it('constructs a backend only when the local artifact matches the manifest hash', async () => {
const directory = await mkdtemp(join(tmpdir(), 'maka-cu-host-'));
try {
Expand Down
10 changes: 8 additions & 2 deletions apps/desktop/src/main/boot.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -1430,9 +1430,15 @@ wireAppLifecycle({
});

function computerUseCapabilityInput() {
const serviceState = computerUse.backend?.serviceState?.();
// Whichever executor was selected reports its own shape: cua-driver an
// action/capture role pair, maka-cu (§11) a single supervised child. Reading
// only `serviceState` meant a ready maka-cu backend produced `undefined`
// here, and the card said "not available" while its own availability half
// said the opposite.
const executorState =
computerUse.backend?.serviceState?.() ?? computerUse.backend?.executorState?.();
return {
backendId: computerUse.backendId,
health: computerUseServiceHealth(computerUse.backendId, serviceState),
health: computerUseServiceHealth(computerUse.backendId, executorState),
};
}
24 changes: 14 additions & 10 deletions apps/desktop/src/main/capability-snapshot.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@ import {
type OsPermissionSnapshot,
type PermissionSnapshot,
} from '@maka/core';
import type { CuBackendId } from '@maka/computer-use';
import type { BotStatus } from '@maka/runtime';
import type { computerUseServiceHealth } from './computer-use-host.js';
import {
Expand DownExpand Up@@ -46,7 +47,7 @@ export function buildCapabilitySnapshotCollection(input: {
permissions: PermissionSnapshot;
botStatuses: Record<BotProvider, BotStatus>;
computerUse?: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
};
now?: number;
Expand DownExpand Up@@ -123,13 +124,16 @@ export function buildCapabilitySnapshotCollection(input: {

function computerUseCapability(
input: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
} | undefined,
permissions: PermissionSnapshot['permissions'],
now: number,
): CapabilitySnapshot {
const artifactAvailable = input?.backendId === 'cua-driver';
// Any selected executor is an executor. Naming one here made the capability
// read `not_available` for a machine that had a working backend, merely a
// different one.
const artifactAvailable = input !== undefined && input.backendId !== 'none';
return staticCapability({
id: 'computer_use',
label: 'Computer Use',
Expand All@@ -152,23 +156,23 @@ function computerUseCapability(
state: input?.health.state ?? 'not_available',
source: 'runtime_probe',
lastCheckedAt: now,
reason: input?.health.reason ?? 'cua-driver 后端当前不可用。',
reason: input?.health.reason ?? 'Computer Use 后端当前不可用。',
},
});
}

function computerUseCapabilityReason(
input: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
} | undefined,
permissions: PermissionSnapshot['permissions'],
): string {
if (input?.backendId !== 'cua-driver') {
return '未找到通过完整性检查的 cua-driver artifact。';
if (input === undefined || input.backendId === 'none') {
return '未找到通过完整性检查的 Computer Use 执行器 artifact。';
}

const reasons = ['cua-driver artifact 已通过本地完整性检查。'];
const reasons = [`${input.backendId} artifact 已通过本地完整性检查。`];
const missingPermissions = [
['辅助功能', permissions.accessibility.status],
['屏幕录制', permissions.screen_recording.status],
Expand All@@ -178,10 +182,10 @@ function computerUseCapabilityReason(
}
switch (input.health.state) {
case 'not_available':
reasons.push('cua-driver service 启动失败、已退出或已停止。');
reasons.push(`${input.backendId} service 启动失败、已退出或已停止。`);
break;
case 'degraded':
reasons.push('cua-driver service 正在启动或恢复。');
reasons.push(`${input.backendId} service 正在启动或恢复。`);
break;
case 'healthy':
reasons.push('操作与截图 service 已就绪;按目标与动作类别授权后可操作本机应用。');
Expand Down
62 changes: 43 additions & 19 deletions apps/desktop/src/main/computer-use-host.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,6 +11,7 @@ import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import type { CuaDriverRoleSnapshot } from '@maka/computer-use';
import type { CuaDriverBackendOptions } from '@maka/computer-use';
import type { MakaCuServiceSnapshot } from '@maka/computer-use';
import {
selectComputerUseBackend,
type SelectedComputerUseBackend,
Expand DownExpand Up@@ -118,53 +119,76 @@ export function createDesktopPhysicalInputGuard(
return () => getSystemIdleTime() < 1;
}

/**
* The health half of the Computer Use capability card, for whichever executor
* was selected.
*
* This used to take the cua-driver role pair and nothing else, while the card's
* `available` half had already been widened to "any selected executor". With a
* genuinely ready maka-cu backend the two halves disagreed, and executing the
* built desktop module against one showed exactly how:
*
* executorState() = {"state":"ready","generation":1}
* serviceState (boot) = undefined
* health = {"state":"not_available","reason":"未找到通过完整性检查且可分发的 cua-driver artifact。"}
* artifactAvailable = true
*
* — available, state not_available, and a reason naming an executor that is not
* the one running. cua-driver supervises an action/capture role pair; maka-cu
* supervises one child (§11) and reports its own shape. Both are read here as a
* list of role states, so the card is right for either, and neither is selected
* by being described.
*/
export type ComputerUseExecutorState =
| { action: CuaDriverRoleSnapshot; capture: CuaDriverRoleSnapshot }
| MakaCuServiceSnapshot;

function roleStates(state: ComputerUseExecutorState): Array<CuaDriverRoleSnapshot['state']> {
return 'action' in state ? [state.action.state, state.capture.state] : [state.state];
}

export function computerUseServiceHealth(
backendId: SelectedComputerUseBackend['backendId'],
state: {
action: CuaDriverRoleSnapshot;
capture: CuaDriverRoleSnapshot;
} | undefined,
state: ComputerUseExecutorState | undefined,
): {
state: 'not_available' | 'not_run' | 'healthy' | 'degraded';
reason: string;
} {
if (backendId === 'none' || !state) {
return {
state: 'not_available',
reason: '未找到通过完整性检查且可分发的 cua-driver artifact。',
reason: '未找到通过完整性检查且可分发的 Computer Use 执行器 artifact。',
};
}
const roles = [state.action, state.capture];
if (roles.some((role) =>
role.state === 'unavailable' || role.state === 'disposed')) {
const roles = roleStates(state);
if (roles.some((role) => role === 'unavailable' || role === 'disposed')) {
return {
state: 'not_available',
reason: roles.some((role) => role.state === 'disposed')
? 'cua-driver service 已停止。'
: 'cua-driver service 启动失败或已退出。',
reason: roles.some((role) => role === 'disposed')
? `${backendId} service 已停止。`
: `${backendId} service 启动失败或已退出。`,
};
}
if (roles.some((role) =>
role.state === 'starting' || role.state === 'backing_off')) {
if (roles.some((role) => role === 'starting' || role === 'backing_off')) {
return {
state: 'degraded',
reason: 'cua-driver service 正在启动或恢复。',
reason: `${backendId} service 正在启动或恢复。`,
};
}
if (roles.every((role) => role.state === 'ready')) {
if (roles.every((role) => role === 'ready')) {
return {
state: 'healthy',
reason: 'cua-driver 操作与截图服务已就绪。',
reason: `${backendId} 操作与截图服务已就绪。`,
};
}
if (roles.some((role) => role.state === 'ready')) {
if (roles.some((role) => role === 'ready')) {
return {
state: 'not_run',
reason: 'cua-driver 部分服务已启动,其余服务将在需要时启动。',
reason: `${backendId} 部分服务已启动,其余服务将在需要时启动。`,
};
}
return {
state: 'not_run',
reason: 'cua-driver 已可用,将在首次调用时启动。',
reason: `${backendId} 已可用,将在首次调用时启动。`,
};
}
84 changes: 84 additions & 0 deletions docs/computer-use-provenance.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
# Computer Use provenance

Maka's Computer Use surface was built against other people's work, in three
materially different ways. They are separated here because the obligations
differ: one is redistribution under a license, one is reading licensed source,
and one is observing a proprietary binary and holds no license grant at all.

Paths are Maka's unless they carry the upstream repository name, as in
`open-codex-computer-use/...`.

Every entry names what was taken, where it landed, and what the evidence was.
When you add or change a borrowed design, add the row here and put the same
statement in the file that carries it — an in-file comment answers "why is this
constant 200?" at the moment someone asks it, and this file answers "what did we
build on?" for the project as a whole.

## 1. Redistributed under license

Ships inside the Maka artifact. Requires the license text and copyright notice
to travel with it.

| Component | License | Where the notice lives |
|---|---|---|
| npm dependencies | various | `apps/desktop/resources/licenses/npm/`, generated by `scripts/generate-third-party-notices.mjs` and byte-checked at build by `scripts/check-third-party-notices.mjs` |
| cua-driver | MIT | `apps/desktop/resources/licenses/cua-driver/`, pinned by digest in `apps/desktop/bundled-tools.json` |

cua-driver is the Computer Use executor Maka currently defaults to. It is a
third-party binary, fetched by `scripts/prepare-cua-driver.mjs` and verified
against the digests recorded in the manifest.

Maka's own executor, `maka-cu`, is built from Maka's own source by
`scripts/prepare-maka-cu.mjs` and pinned by digest in the same manifest. It is
not signed, so it is not distributed at all yet: its `distributionReady` is
false and `scripts/verify-macos-arm64-dmg.mjs` forbids its path in a packaged
build. It is selectable in a development build and nothing selects it by
default.

`maka-cu` is itself a fork of MIT-licensed `iFurySt/open-codex-computer-use`
(§2), so when it does ship, that notice travels with it.

## 2. Licensed source read as reference

MIT-licensed source we read while designing. No code was copied into this
repository; what was taken is design — a format, a decision, or an archived
measurement. Attribution is given because it was load-bearing, not because MIT
compels it for ideas.

### iFurySt/open-codex-computer-use, and its fork QwenLM/open-computer-use

Both MIT, © 2026 Leo. An independent reimplementation of Codex's Computer Use
as an MCP server.

| Taken | Landed in | Notes |
|---|---|---|
| An archived capture of Codex's real `get_app_state` result | `packages/runtime/src/computer-use-tools.ts` | Their `open-codex-computer-use/artifacts/tool-comparisons/20260417-focus-behavior/`. This is what turned Maka's model of Codex's observation format from inference into an observed sample. |
| The one-line-per-element observation shape: indentation for containment, states written only when not the default | same file | Maka's version keeps `observation_id` in the header (frame binding is protocol here, prose there) and keeps element geometry (Codex has no coordinate action surface to need it; Maka's is disabled by default rather than absent). |
| Writing only the informative half of a state — `disabled`, never `enabled` | same file | Their `summarizeTraits`. |
| Filtering `AXPress` out of an element's advertised action list, because pressing is what `click` does | not yet landed — waits on `trycua/cua#2622` exposing per-element AX actions | Their `meaningfulActions`. |
| The permission-onboarding pattern: anchor a guidance panel to the System Settings window, track it, and distinguish grants that need an app relaunch | not yet landed — for the `feat/permission-onboarding` work | Their `open-codex-computer-use/apps/OpenComputerUse/Sources/OpenComputerUse/PermissionOnboardingApp.swift`. |
| Archiving side-by-side tool captures in-repo as evidence | practice, not code | Worth adopting for Maka's own Codex comparisons. |

Not taken, recorded so the decision is not re-litigated: their `SkyLightSPI` /
`SkyClickSimulation` synthetic-focus click. Their own comment states the recipe
is derived from cua-driver and yabai. Maka read the same recipe rather than
that code.

## 3. Observed, not licensed

Codex Computer Use (`SkyComputerUseService` and its helper bundles) is
proprietary and ships no source. Nothing was copied from it, because there is
nothing to copy: what exists here was reimplemented from observed behaviour and
from constants recovered by inspecting the shipped binary.

This confers no rights and is not a license. Statements about it are
descriptions of what a build did on a given date, and are dated for that reason.

| Recovered | Landed in |
|---|---|
| The agent cursor: shape, hotspot, motion thresholds, spring constants, and the candidate-path scoring function | `apps/desktop/src/renderer/computer-use-overlay/engine/cursor-engine.ts` |
| Overlay level policy — an occluded target raises the cursor rather than hiding it | same file, and `apps/desktop/src/main/computer-use/cursor-overlay-window.ts` |
| The observation text shape | `packages/runtime/src/computer-use-tools.ts`, corroborated by the archived capture in §2 |

Where Maka deliberately diverges, the divergence is stated at the point of
divergence rather than here, so it is read by whoever is changing that code.
3 changes: 2 additions & 1 deletion package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,7 +31,7 @@
"test:dist": "npm run test:scripts:full && node scripts/run-workspace-tests-parallel.mjs --concurrency=3",
"test:dist:serial": "npm run test:scripts:full && node scripts/run-workspace-tests-parallel.mjs --serial",
"test:fast": "npm run build:test && npm run test:scripts && node scripts/run-workspace-tests-parallel.mjs --concurrency=3",
"test:scripts": "node --test scripts/fixture-env.test.mjs scripts/electron-lifecycle.test.mjs scripts/check-story-annotations.test.mjs scripts/ci-test-plan.test.mjs scripts/run-headless-tests.test.mjs scripts/run-workspace-tests-parallel.test.mjs scripts/cu-e2e-scenarios.test.mjs scripts/cu-report-sanitize.test.mjs",
"test:scripts": "node --test scripts/fixture-env.test.mjs scripts/electron-lifecycle.test.mjs scripts/check-story-annotations.test.mjs scripts/ci-test-plan.test.mjs scripts/run-headless-tests.test.mjs scripts/run-workspace-tests-parallel.test.mjs scripts/cu-e2e-scenarios.test.mjs scripts/cu-report-sanitize.test.mjs scripts/computer-use-provenance.test.mjs",
"test:scripts:extended": "node --test scripts/cua-driver-provenance.test.mjs scripts/cu-provider-matrix.test.mjs scripts/cu-real-model-launcher.test.mjs scripts/macos-arm64-release.test.mjs scripts/measure-session-bundle.test.mjs",
"test:scripts:full": "npm run test:scripts && npm run test:scripts:extended",
"dev": "npm --workspace @maka/desktop run dev:hmr --",
Expand All@@ -52,6 +52,7 @@
"cost:deepseek-baseline": "node scripts/deepseek-live-cost-baseline.mjs",
"benchmark:kimi-protocol-ab": "node packages/headless/harbor/run-kimi-protocol-ab.mjs",
"prepare:cua-driver": "node scripts/prepare-cua-driver.mjs",
"prepare:maka-cu": "node scripts/prepare-maka-cu.mjs",
"check:cua-driver-artifact": "node scripts/check-cua-driver-bundle.mjs",
"e2e:computer-use-real": "node scripts/cu-real-ax-model-e2e-launcher.mjs",
"e2e:computer-use-process-restart": "MAKA_CU_AX_MODEL_SCENARIO=restart-recovery node scripts/cu-real-ax-model-e2e-launcher.mjs",
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' feat(computer-use): add maka-cu as a selectable executor backend by hqhq1025 · Pull Request #1953 · apache/maka · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions apps/desktop/bundled-tools.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,5 +31,19 @@
"thirdPartyNotices": "missing",
"notarization": "missing",
"distributionReady": false
},
"makaCu": {
"repo": "maka-agent/maka-cu",
"branch": "maka/base",
"commit": "ca7ef80c721fdaf6e7f4af882f16b9c555f14733",
"expectedProtocolVersion": "maka.cu/2",
"binaryName": "maka-cu",
"binarySizeBytes": 2861584,
"binarySha256": "26d45d5243fefd993dc754d5b6bdd64c8c5ff38f3d0ccf6dbb132db89a49f2a7",
"buildProvenance": "local-source-build",
"signature": "adhoc",
"hardenedRuntime": false,
"notarization": "missing",
"distributionReady": false
}
}
41 changes: 41 additions & 0 deletions apps/desktop/src/main/__tests__/computer-use-host.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -65,6 +65,47 @@ describe('Computer Use host health', () => {
assert.equal(computerUseServiceHealth('none', undefined).state, 'not_available');
});

it('reads the executor that is selected, not the role pair one of them happens to have', () => {
// maka-cu supervises one child (§11) and reports its own shape, so it has
// no `action`/`capture` pair to read. This function took only that pair,
// while the availability half of the same capability card had already been
// widened to "any selected executor" — executed against the built desktop
// module with a genuinely ready maka-cu backend, the card read:
//
// executorState() = {"state":"ready","generation":1}
// serviceState (boot) = undefined
// health = not_available, reason naming cua-driver
// artifactAvailable = true
//
// available, state not_available, and a reason naming an executor that is
// not the one running.
assert.deepEqual(
computerUseServiceHealth('maka-cu', { state: 'ready', generation: 1, restartAttempts: 0 }),
{ state: 'healthy', reason: 'maka-cu 操作与截图服务已就绪。' },
);
assert.equal(
computerUseServiceHealth('maka-cu', {
state: 'backing_off',
generation: 1,
restartAttempts: 1,
}).state,
'degraded',
);
assert.deepEqual(
computerUseServiceHealth('maka-cu', {
state: 'unavailable',
generation: 1,
restartAttempts: 3,
}),
{ state: 'not_available', reason: 'maka-cu service 启动失败或已退出。' },
);
assert.equal(
computerUseServiceHealth('maka-cu', { state: 'idle', generation: 0, restartAttempts: 0 })
.state,
'not_run',
);
});

it('constructs a backend only when the local artifact matches the manifest hash', async () => {
const directory = await mkdtemp(join(tmpdir(), 'maka-cu-host-'));
try {
Expand Down
10 changes: 8 additions & 2 deletions apps/desktop/src/main/boot.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -1430,9 +1430,15 @@ wireAppLifecycle({
});

function computerUseCapabilityInput() {
const serviceState = computerUse.backend?.serviceState?.();
// Whichever executor was selected reports its own shape: cua-driver an
// action/capture role pair, maka-cu (§11) a single supervised child. Reading
// only `serviceState` meant a ready maka-cu backend produced `undefined`
// here, and the card said "not available" while its own availability half
// said the opposite.
const executorState =
computerUse.backend?.serviceState?.() ?? computerUse.backend?.executorState?.();
return {
backendId: computerUse.backendId,
health: computerUseServiceHealth(computerUse.backendId, serviceState),
health: computerUseServiceHealth(computerUse.backendId, executorState),
};
}
24 changes: 14 additions & 10 deletions apps/desktop/src/main/capability-snapshot.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@ import {
type OsPermissionSnapshot,
type PermissionSnapshot,
} from '@maka/core';
import type { CuBackendId } from '@maka/computer-use';
import type { BotStatus } from '@maka/runtime';
import type { computerUseServiceHealth } from './computer-use-host.js';
import {
Expand DownExpand Up@@ -46,7 +47,7 @@ export function buildCapabilitySnapshotCollection(input: {
permissions: PermissionSnapshot;
botStatuses: Record<BotProvider, BotStatus>;
computerUse?: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
};
now?: number;
Expand DownExpand Up@@ -123,13 +124,16 @@ export function buildCapabilitySnapshotCollection(input: {

function computerUseCapability(
input: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
} | undefined,
permissions: PermissionSnapshot['permissions'],
now: number,
): CapabilitySnapshot {
const artifactAvailable = input?.backendId === 'cua-driver';
// Any selected executor is an executor. Naming one here made the capability
// read `not_available` for a machine that had a working backend, merely a
// different one.
const artifactAvailable = input !== undefined && input.backendId !== 'none';
return staticCapability({
id: 'computer_use',
label: 'Computer Use',
Expand All@@ -152,23 +156,23 @@ function computerUseCapability(
state: input?.health.state ?? 'not_available',
source: 'runtime_probe',
lastCheckedAt: now,
reason: input?.health.reason ?? 'cua-driver 后端当前不可用。',
reason: input?.health.reason ?? 'Computer Use 后端当前不可用。',
},
});
}

function computerUseCapabilityReason(
input: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
} | undefined,
permissions: PermissionSnapshot['permissions'],
): string {
if (input?.backendId !== 'cua-driver') {
return '未找到通过完整性检查的 cua-driver artifact。';
if (input === undefined || input.backendId === 'none') {
return '未找到通过完整性检查的 Computer Use 执行器 artifact。';
}

const reasons = ['cua-driver artifact 已通过本地完整性检查。'];
const reasons = [`${input.backendId} artifact 已通过本地完整性检查。`];
const missingPermissions = [
['辅助功能', permissions.accessibility.status],
['屏幕录制', permissions.screen_recording.status],
Expand All@@ -178,10 +182,10 @@ function computerUseCapabilityReason(
}
switch (input.health.state) {
case 'not_available':
reasons.push('cua-driver service 启动失败、已退出或已停止。');
reasons.push(`${input.backendId} service 启动失败、已退出或已停止。`);
break;
case 'degraded':
reasons.push('cua-driver service 正在启动或恢复。');
reasons.push(`${input.backendId} service 正在启动或恢复。`);
break;
case 'healthy':
reasons.push('操作与截图 service 已就绪;按目标与动作类别授权后可操作本机应用。');
Expand Down
62 changes: 43 additions & 19 deletions apps/desktop/src/main/computer-use-host.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,6 +11,7 @@ import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import type { CuaDriverRoleSnapshot } from '@maka/computer-use';
import type { CuaDriverBackendOptions } from '@maka/computer-use';
import type { MakaCuServiceSnapshot } from '@maka/computer-use';
import {
selectComputerUseBackend,
type SelectedComputerUseBackend,
Expand DownExpand Up@@ -118,53 +119,76 @@ export function createDesktopPhysicalInputGuard(
return () => getSystemIdleTime() < 1;
}

/**
* The health half of the Computer Use capability card, for whichever executor
* was selected.
*
* This used to take the cua-driver role pair and nothing else, while the card's
* `available` half had already been widened to "any selected executor". With a
* genuinely ready maka-cu backend the two halves disagreed, and executing the
* built desktop module against one showed exactly how:
*
* executorState() = {"state":"ready","generation":1}
* serviceState (boot) = undefined
* health = {"state":"not_available","reason":"未找到通过完整性检查且可分发的 cua-driver artifact。"}
* artifactAvailable = true
*
* — available, state not_available, and a reason naming an executor that is not
* the one running. cua-driver supervises an action/capture role pair; maka-cu
* supervises one child (§11) and reports its own shape. Both are read here as a
* list of role states, so the card is right for either, and neither is selected
* by being described.
*/
export type ComputerUseExecutorState =
| { action: CuaDriverRoleSnapshot; capture: CuaDriverRoleSnapshot }
| MakaCuServiceSnapshot;

function roleStates(state: ComputerUseExecutorState): Array<CuaDriverRoleSnapshot['state']> {
return 'action' in state ? [state.action.state, state.capture.state] : [state.state];
}

export function computerUseServiceHealth(
backendId: SelectedComputerUseBackend['backendId'],
state: {
action: CuaDriverRoleSnapshot;
capture: CuaDriverRoleSnapshot;
} | undefined,
state: ComputerUseExecutorState | undefined,
): {
state: 'not_available' | 'not_run' | 'healthy' | 'degraded';
reason: string;
} {
if (backendId === 'none' || !state) {
return {
state: 'not_available',
reason: '未找到通过完整性检查且可分发的 cua-driver artifact。',
reason: '未找到通过完整性检查且可分发的 Computer Use 执行器 artifact。',
};
}
const roles = [state.action, state.capture];
if (roles.some((role) =>
role.state === 'unavailable' || role.state === 'disposed')) {
const roles = roleStates(state);
if (roles.some((role) => role === 'unavailable' || role === 'disposed')) {
return {
state: 'not_available',
reason: roles.some((role) => role.state === 'disposed')
? 'cua-driver service 已停止。'
: 'cua-driver service 启动失败或已退出。',
reason: roles.some((role) => role === 'disposed')
? `${backendId} service 已停止。`
: `${backendId} service 启动失败或已退出。`,
};
}
if (roles.some((role) =>
role.state === 'starting' || role.state === 'backing_off')) {
if (roles.some((role) => role === 'starting' || role === 'backing_off')) {
return {
state: 'degraded',
reason: 'cua-driver service 正在启动或恢复。',
reason: `${backendId} service 正在启动或恢复。`,
};
}
if (roles.every((role) => role.state === 'ready')) {
if (roles.every((role) => role === 'ready')) {
return {
state: 'healthy',
reason: 'cua-driver 操作与截图服务已就绪。',
reason: `${backendId} 操作与截图服务已就绪。`,
};
}
if (roles.some((role) => role.state === 'ready')) {
if (roles.some((role) => role === 'ready')) {
return {
state: 'not_run',
reason: 'cua-driver 部分服务已启动,其余服务将在需要时启动。',
reason: `${backendId} 部分服务已启动,其余服务将在需要时启动。`,
};
}
return {
state: 'not_run',
reason: 'cua-driver 已可用,将在首次调用时启动。',
reason: `${backendId} 已可用,将在首次调用时启动。`,
};
}
84 changes: 84 additions & 0 deletions docs/computer-use-provenance.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
# Computer Use provenance

Maka's Computer Use surface was built against other people's work, in three
materially different ways. They are separated here because the obligations
differ: one is redistribution under a license, one is reading licensed source,
and one is observing a proprietary binary and holds no license grant at all.

Paths are Maka's unless they carry the upstream repository name, as in
`open-codex-computer-use/...`.

Every entry names what was taken, where it landed, and what the evidence was.
When you add or change a borrowed design, add the row here and put the same
statement in the file that carries it — an in-file comment answers "why is this
constant 200?" at the moment someone asks it, and this file answers "what did we
build on?" for the project as a whole.

## 1. Redistributed under license

Ships inside the Maka artifact. Requires the license text and copyright notice
to travel with it.

| Component | License | Where the notice lives |
|---|---|---|
| npm dependencies | various | `apps/desktop/resources/licenses/npm/`, generated by `scripts/generate-third-party-notices.mjs` and byte-checked at build by `scripts/check-third-party-notices.mjs` |
| cua-driver | MIT | `apps/desktop/resources/licenses/cua-driver/`, pinned by digest in `apps/desktop/bundled-tools.json` |

cua-driver is the Computer Use executor Maka currently defaults to. It is a
third-party binary, fetched by `scripts/prepare-cua-driver.mjs` and verified
against the digests recorded in the manifest.

Maka's own executor, `maka-cu`, is built from Maka's own source by
`scripts/prepare-maka-cu.mjs` and pinned by digest in the same manifest. It is
not signed, so it is not distributed at all yet: its `distributionReady` is
false and `scripts/verify-macos-arm64-dmg.mjs` forbids its path in a packaged
build. It is selectable in a development build and nothing selects it by
default.

`maka-cu` is itself a fork of MIT-licensed `iFurySt/open-codex-computer-use`
(§2), so when it does ship, that notice travels with it.

## 2. Licensed source read as reference

MIT-licensed source we read while designing. No code was copied into this
repository; what was taken is design — a format, a decision, or an archived
measurement. Attribution is given because it was load-bearing, not because MIT
compels it for ideas.

### iFurySt/open-codex-computer-use, and its fork QwenLM/open-computer-use

Both MIT, © 2026 Leo. An independent reimplementation of Codex's Computer Use
as an MCP server.

| Taken | Landed in | Notes |
|---|---|---|
| An archived capture of Codex's real `get_app_state` result | `packages/runtime/src/computer-use-tools.ts` | Their `open-codex-computer-use/artifacts/tool-comparisons/20260417-focus-behavior/`. This is what turned Maka's model of Codex's observation format from inference into an observed sample. |
| The one-line-per-element observation shape: indentation for containment, states written only when not the default | same file | Maka's version keeps `observation_id` in the header (frame binding is protocol here, prose there) and keeps element geometry (Codex has no coordinate action surface to need it; Maka's is disabled by default rather than absent). |
| Writing only the informative half of a state — `disabled`, never `enabled` | same file | Their `summarizeTraits`. |
| Filtering `AXPress` out of an element's advertised action list, because pressing is what `click` does | not yet landed — waits on `trycua/cua#2622` exposing per-element AX actions | Their `meaningfulActions`. |
| The permission-onboarding pattern: anchor a guidance panel to the System Settings window, track it, and distinguish grants that need an app relaunch | not yet landed — for the `feat/permission-onboarding` work | Their `open-codex-computer-use/apps/OpenComputerUse/Sources/OpenComputerUse/PermissionOnboardingApp.swift`. |
| Archiving side-by-side tool captures in-repo as evidence | practice, not code | Worth adopting for Maka's own Codex comparisons. |

Not taken, recorded so the decision is not re-litigated: their `SkyLightSPI` /
`SkyClickSimulation` synthetic-focus click. Their own comment states the recipe
is derived from cua-driver and yabai. Maka read the same recipe rather than
that code.

## 3. Observed, not licensed

Codex Computer Use (`SkyComputerUseService` and its helper bundles) is
proprietary and ships no source. Nothing was copied from it, because there is
nothing to copy: what exists here was reimplemented from observed behaviour and
from constants recovered by inspecting the shipped binary.

This confers no rights and is not a license. Statements about it are
descriptions of what a build did on a given date, and are dated for that reason.

| Recovered | Landed in |
|---|---|
| The agent cursor: shape, hotspot, motion thresholds, spring constants, and the candidate-path scoring function | `apps/desktop/src/renderer/computer-use-overlay/engine/cursor-engine.ts` |
| Overlay level policy — an occluded target raises the cursor rather than hiding it | same file, and `apps/desktop/src/main/computer-use/cursor-overlay-window.ts` |
| The observation text shape | `packages/runtime/src/computer-use-tools.ts`, corroborated by the archived capture in §2 |

Where Maka deliberately diverges, the divergence is stated at the point of
divergence rather than here, so it is read by whoever is changing that code.
3 changes: 2 additions & 1 deletion package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,7 +31,7 @@
"test:dist": "npm run test:scripts:full && node scripts/run-workspace-tests-parallel.mjs --concurrency=3",
"test:dist:serial": "npm run test:scripts:full && node scripts/run-workspace-tests-parallel.mjs --serial",
"test:fast": "npm run build:test && npm run test:scripts && node scripts/run-workspace-tests-parallel.mjs --concurrency=3",
"test:scripts": "node --test scripts/fixture-env.test.mjs scripts/electron-lifecycle.test.mjs scripts/check-story-annotations.test.mjs scripts/ci-test-plan.test.mjs scripts/run-headless-tests.test.mjs scripts/run-workspace-tests-parallel.test.mjs scripts/cu-e2e-scenarios.test.mjs scripts/cu-report-sanitize.test.mjs",
"test:scripts": "node --test scripts/fixture-env.test.mjs scripts/electron-lifecycle.test.mjs scripts/check-story-annotations.test.mjs scripts/ci-test-plan.test.mjs scripts/run-headless-tests.test.mjs scripts/run-workspace-tests-parallel.test.mjs scripts/cu-e2e-scenarios.test.mjs scripts/cu-report-sanitize.test.mjs scripts/computer-use-provenance.test.mjs",
"test:scripts:extended": "node --test scripts/cua-driver-provenance.test.mjs scripts/cu-provider-matrix.test.mjs scripts/cu-real-model-launcher.test.mjs scripts/macos-arm64-release.test.mjs scripts/measure-session-bundle.test.mjs",
"test:scripts:full": "npm run test:scripts && npm run test:scripts:extended",
"dev": "npm --workspace @maka/desktop run dev:hmr --",
Expand All@@ -52,6 +52,7 @@
"cost:deepseek-baseline": "node scripts/deepseek-live-cost-baseline.mjs",
"benchmark:kimi-protocol-ab": "node packages/headless/harbor/run-kimi-protocol-ab.mjs",
"prepare:cua-driver": "node scripts/prepare-cua-driver.mjs",
"prepare:maka-cu": "node scripts/prepare-maka-cu.mjs",
"check:cua-driver-artifact": "node scripts/check-cua-driver-bundle.mjs",
"e2e:computer-use-real": "node scripts/cu-real-ax-model-e2e-launcher.mjs",
"e2e:computer-use-process-restart": "MAKA_CU_AX_MODEL_SCENARIO=restart-recovery node scripts/cu-real-ax-model-e2e-launcher.mjs",
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(computer-use): add maka-cu as a selectable executor backend by hqhq1025 · Pull Request #1953 · apache/maka · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions apps/desktop/bundled-tools.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,5 +31,19 @@
"thirdPartyNotices": "missing",
"notarization": "missing",
"distributionReady": false
},
"makaCu": {
"repo": "maka-agent/maka-cu",
"branch": "maka/base",
"commit": "ca7ef80c721fdaf6e7f4af882f16b9c555f14733",
"expectedProtocolVersion": "maka.cu/2",
"binaryName": "maka-cu",
"binarySizeBytes": 2861584,
"binarySha256": "26d45d5243fefd993dc754d5b6bdd64c8c5ff38f3d0ccf6dbb132db89a49f2a7",
"buildProvenance": "local-source-build",
"signature": "adhoc",
"hardenedRuntime": false,
"notarization": "missing",
"distributionReady": false
}
}
41 changes: 41 additions & 0 deletions apps/desktop/src/main/__tests__/computer-use-host.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -65,6 +65,47 @@ describe('Computer Use host health', () => {
assert.equal(computerUseServiceHealth('none', undefined).state, 'not_available');
});

it('reads the executor that is selected, not the role pair one of them happens to have', () => {
// maka-cu supervises one child (§11) and reports its own shape, so it has
// no `action`/`capture` pair to read. This function took only that pair,
// while the availability half of the same capability card had already been
// widened to "any selected executor" — executed against the built desktop
// module with a genuinely ready maka-cu backend, the card read:
//
// executorState() = {"state":"ready","generation":1}
// serviceState (boot) = undefined
// health = not_available, reason naming cua-driver
// artifactAvailable = true
//
// available, state not_available, and a reason naming an executor that is
// not the one running.
assert.deepEqual(
computerUseServiceHealth('maka-cu', { state: 'ready', generation: 1, restartAttempts: 0 }),
{ state: 'healthy', reason: 'maka-cu 操作与截图服务已就绪。' },
);
assert.equal(
computerUseServiceHealth('maka-cu', {
state: 'backing_off',
generation: 1,
restartAttempts: 1,
}).state,
'degraded',
);
assert.deepEqual(
computerUseServiceHealth('maka-cu', {
state: 'unavailable',
generation: 1,
restartAttempts: 3,
}),
{ state: 'not_available', reason: 'maka-cu service 启动失败或已退出。' },
);
assert.equal(
computerUseServiceHealth('maka-cu', { state: 'idle', generation: 0, restartAttempts: 0 })
.state,
'not_run',
);
});

it('constructs a backend only when the local artifact matches the manifest hash', async () => {
const directory = await mkdtemp(join(tmpdir(), 'maka-cu-host-'));
try {
Expand Down
10 changes: 8 additions & 2 deletions apps/desktop/src/main/boot.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -1430,9 +1430,15 @@ wireAppLifecycle({
});

function computerUseCapabilityInput() {
const serviceState = computerUse.backend?.serviceState?.();
// Whichever executor was selected reports its own shape: cua-driver an
// action/capture role pair, maka-cu (§11) a single supervised child. Reading
// only `serviceState` meant a ready maka-cu backend produced `undefined`
// here, and the card said "not available" while its own availability half
// said the opposite.
const executorState =
computerUse.backend?.serviceState?.() ?? computerUse.backend?.executorState?.();
return {
backendId: computerUse.backendId,
health: computerUseServiceHealth(computerUse.backendId, serviceState),
health: computerUseServiceHealth(computerUse.backendId, executorState),
};
}
24 changes: 14 additions & 10 deletions apps/desktop/src/main/capability-snapshot.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@ import {
type OsPermissionSnapshot,
type PermissionSnapshot,
} from '@maka/core';
import type { CuBackendId } from '@maka/computer-use';
import type { BotStatus } from '@maka/runtime';
import type { computerUseServiceHealth } from './computer-use-host.js';
import {
Expand DownExpand Up@@ -46,7 +47,7 @@ export function buildCapabilitySnapshotCollection(input: {
permissions: PermissionSnapshot;
botStatuses: Record<BotProvider, BotStatus>;
computerUse?: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
};
now?: number;
Expand DownExpand Up@@ -123,13 +124,16 @@ export function buildCapabilitySnapshotCollection(input: {

function computerUseCapability(
input: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
} | undefined,
permissions: PermissionSnapshot['permissions'],
now: number,
): CapabilitySnapshot {
const artifactAvailable = input?.backendId === 'cua-driver';
// Any selected executor is an executor. Naming one here made the capability
// read `not_available` for a machine that had a working backend, merely a
// different one.
const artifactAvailable = input !== undefined && input.backendId !== 'none';
return staticCapability({
id: 'computer_use',
label: 'Computer Use',
Expand All@@ -152,23 +156,23 @@ function computerUseCapability(
state: input?.health.state ?? 'not_available',
source: 'runtime_probe',
lastCheckedAt: now,
reason: input?.health.reason ?? 'cua-driver 后端当前不可用。',
reason: input?.health.reason ?? 'Computer Use 后端当前不可用。',
},
});
}

function computerUseCapabilityReason(
input: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
} | undefined,
permissions: PermissionSnapshot['permissions'],
): string {
if (input?.backendId !== 'cua-driver') {
return '未找到通过完整性检查的 cua-driver artifact。';
if (input === undefined || input.backendId === 'none') {
return '未找到通过完整性检查的 Computer Use 执行器 artifact。';
}

const reasons = ['cua-driver artifact 已通过本地完整性检查。'];
const reasons = [`${input.backendId} artifact 已通过本地完整性检查。`];
const missingPermissions = [
['辅助功能', permissions.accessibility.status],
['屏幕录制', permissions.screen_recording.status],
Expand All@@ -178,10 +182,10 @@ function computerUseCapabilityReason(
}
switch (input.health.state) {
case 'not_available':
reasons.push('cua-driver service 启动失败、已退出或已停止。');
reasons.push(`${input.backendId} service 启动失败、已退出或已停止。`);
break;
case 'degraded':
reasons.push('cua-driver service 正在启动或恢复。');
reasons.push(`${input.backendId} service 正在启动或恢复。`);
break;
case 'healthy':
reasons.push('操作与截图 service 已就绪;按目标与动作类别授权后可操作本机应用。');
Expand Down
62 changes: 43 additions & 19 deletions apps/desktop/src/main/computer-use-host.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,6 +11,7 @@ import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import type { CuaDriverRoleSnapshot } from '@maka/computer-use';
import type { CuaDriverBackendOptions } from '@maka/computer-use';
import type { MakaCuServiceSnapshot } from '@maka/computer-use';
import {
selectComputerUseBackend,
type SelectedComputerUseBackend,
Expand DownExpand Up@@ -118,53 +119,76 @@ export function createDesktopPhysicalInputGuard(
return () => getSystemIdleTime() < 1;
}

/**
* The health half of the Computer Use capability card, for whichever executor
* was selected.
*
* This used to take the cua-driver role pair and nothing else, while the card's
* `available` half had already been widened to "any selected executor". With a
* genuinely ready maka-cu backend the two halves disagreed, and executing the
* built desktop module against one showed exactly how:
*
* executorState() = {"state":"ready","generation":1}
* serviceState (boot) = undefined
* health = {"state":"not_available","reason":"未找到通过完整性检查且可分发的 cua-driver artifact。"}
* artifactAvailable = true
*
* — available, state not_available, and a reason naming an executor that is not
* the one running. cua-driver supervises an action/capture role pair; maka-cu
* supervises one child (§11) and reports its own shape. Both are read here as a
* list of role states, so the card is right for either, and neither is selected
* by being described.
*/
export type ComputerUseExecutorState =
| { action: CuaDriverRoleSnapshot; capture: CuaDriverRoleSnapshot }
| MakaCuServiceSnapshot;

function roleStates(state: ComputerUseExecutorState): Array<CuaDriverRoleSnapshot['state']> {
return 'action' in state ? [state.action.state, state.capture.state] : [state.state];
}

export function computerUseServiceHealth(
backendId: SelectedComputerUseBackend['backendId'],
state: {
action: CuaDriverRoleSnapshot;
capture: CuaDriverRoleSnapshot;
} | undefined,
state: ComputerUseExecutorState | undefined,
): {
state: 'not_available' | 'not_run' | 'healthy' | 'degraded';
reason: string;
} {
if (backendId === 'none' || !state) {
return {
state: 'not_available',
reason: '未找到通过完整性检查且可分发的 cua-driver artifact。',
reason: '未找到通过完整性检查且可分发的 Computer Use 执行器 artifact。',
};
}
const roles = [state.action, state.capture];
if (roles.some((role) =>
role.state === 'unavailable' || role.state === 'disposed')) {
const roles = roleStates(state);
if (roles.some((role) => role === 'unavailable' || role === 'disposed')) {
return {
state: 'not_available',
reason: roles.some((role) => role.state === 'disposed')
? 'cua-driver service 已停止。'
: 'cua-driver service 启动失败或已退出。',
reason: roles.some((role) => role === 'disposed')
? `${backendId} service 已停止。`
: `${backendId} service 启动失败或已退出。`,
};
}
if (roles.some((role) =>
role.state === 'starting' || role.state === 'backing_off')) {
if (roles.some((role) => role === 'starting' || role === 'backing_off')) {
return {
state: 'degraded',
reason: 'cua-driver service 正在启动或恢复。',
reason: `${backendId} service 正在启动或恢复。`,
};
}
if (roles.every((role) => role.state === 'ready')) {
if (roles.every((role) => role === 'ready')) {
return {
state: 'healthy',
reason: 'cua-driver 操作与截图服务已就绪。',
reason: `${backendId} 操作与截图服务已就绪。`,
};
}
if (roles.some((role) => role.state === 'ready')) {
if (roles.some((role) => role === 'ready')) {
return {
state: 'not_run',
reason: 'cua-driver 部分服务已启动,其余服务将在需要时启动。',
reason: `${backendId} 部分服务已启动,其余服务将在需要时启动。`,
};
}
return {
state: 'not_run',
reason: 'cua-driver 已可用,将在首次调用时启动。',
reason: `${backendId} 已可用,将在首次调用时启动。`,
};
}
84 changes: 84 additions & 0 deletions docs/computer-use-provenance.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
# Computer Use provenance

Maka's Computer Use surface was built against other people's work, in three
materially different ways. They are separated here because the obligations
differ: one is redistribution under a license, one is reading licensed source,
and one is observing a proprietary binary and holds no license grant at all.

Paths are Maka's unless they carry the upstream repository name, as in
`open-codex-computer-use/...`.

Every entry names what was taken, where it landed, and what the evidence was.
When you add or change a borrowed design, add the row here and put the same
statement in the file that carries it — an in-file comment answers "why is this
constant 200?" at the moment someone asks it, and this file answers "what did we
build on?" for the project as a whole.

## 1. Redistributed under license

Ships inside the Maka artifact. Requires the license text and copyright notice
to travel with it.

| Component | License | Where the notice lives |
|---|---|---|
| npm dependencies | various | `apps/desktop/resources/licenses/npm/`, generated by `scripts/generate-third-party-notices.mjs` and byte-checked at build by `scripts/check-third-party-notices.mjs` |
| cua-driver | MIT | `apps/desktop/resources/licenses/cua-driver/`, pinned by digest in `apps/desktop/bundled-tools.json` |

cua-driver is the Computer Use executor Maka currently defaults to. It is a
third-party binary, fetched by `scripts/prepare-cua-driver.mjs` and verified
against the digests recorded in the manifest.

Maka's own executor, `maka-cu`, is built from Maka's own source by
`scripts/prepare-maka-cu.mjs` and pinned by digest in the same manifest. It is
not signed, so it is not distributed at all yet: its `distributionReady` is
false and `scripts/verify-macos-arm64-dmg.mjs` forbids its path in a packaged
build. It is selectable in a development build and nothing selects it by
default.

`maka-cu` is itself a fork of MIT-licensed `iFurySt/open-codex-computer-use`
(§2), so when it does ship, that notice travels with it.

## 2. Licensed source read as reference

MIT-licensed source we read while designing. No code was copied into this
repository; what was taken is design — a format, a decision, or an archived
measurement. Attribution is given because it was load-bearing, not because MIT
compels it for ideas.

### iFurySt/open-codex-computer-use, and its fork QwenLM/open-computer-use

Both MIT, © 2026 Leo. An independent reimplementation of Codex's Computer Use
as an MCP server.

| Taken | Landed in | Notes |
|---|---|---|
| An archived capture of Codex's real `get_app_state` result | `packages/runtime/src/computer-use-tools.ts` | Their `open-codex-computer-use/artifacts/tool-comparisons/20260417-focus-behavior/`. This is what turned Maka's model of Codex's observation format from inference into an observed sample. |
| The one-line-per-element observation shape: indentation for containment, states written only when not the default | same file | Maka's version keeps `observation_id` in the header (frame binding is protocol here, prose there) and keeps element geometry (Codex has no coordinate action surface to need it; Maka's is disabled by default rather than absent). |
| Writing only the informative half of a state — `disabled`, never `enabled` | same file | Their `summarizeTraits`. |
| Filtering `AXPress` out of an element's advertised action list, because pressing is what `click` does | not yet landed — waits on `trycua/cua#2622` exposing per-element AX actions | Their `meaningfulActions`. |
| The permission-onboarding pattern: anchor a guidance panel to the System Settings window, track it, and distinguish grants that need an app relaunch | not yet landed — for the `feat/permission-onboarding` work | Their `open-codex-computer-use/apps/OpenComputerUse/Sources/OpenComputerUse/PermissionOnboardingApp.swift`. |
| Archiving side-by-side tool captures in-repo as evidence | practice, not code | Worth adopting for Maka's own Codex comparisons. |

Not taken, recorded so the decision is not re-litigated: their `SkyLightSPI` /
`SkyClickSimulation` synthetic-focus click. Their own comment states the recipe
is derived from cua-driver and yabai. Maka read the same recipe rather than
that code.

## 3. Observed, not licensed

Codex Computer Use (`SkyComputerUseService` and its helper bundles) is
proprietary and ships no source. Nothing was copied from it, because there is
nothing to copy: what exists here was reimplemented from observed behaviour and
from constants recovered by inspecting the shipped binary.

This confers no rights and is not a license. Statements about it are
descriptions of what a build did on a given date, and are dated for that reason.

| Recovered | Landed in |
|---|---|
| The agent cursor: shape, hotspot, motion thresholds, spring constants, and the candidate-path scoring function | `apps/desktop/src/renderer/computer-use-overlay/engine/cursor-engine.ts` |
| Overlay level policy — an occluded target raises the cursor rather than hiding it | same file, and `apps/desktop/src/main/computer-use/cursor-overlay-window.ts` |
| The observation text shape | `packages/runtime/src/computer-use-tools.ts`, corroborated by the archived capture in §2 |

Where Maka deliberately diverges, the divergence is stated at the point of
divergence rather than here, so it is read by whoever is changing that code.
3 changes: 2 additions & 1 deletion package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,7 +31,7 @@
"test:dist": "npm run test:scripts:full && node scripts/run-workspace-tests-parallel.mjs --concurrency=3",
"test:dist:serial": "npm run test:scripts:full && node scripts/run-workspace-tests-parallel.mjs --serial",
"test:fast": "npm run build:test && npm run test:scripts && node scripts/run-workspace-tests-parallel.mjs --concurrency=3",
"test:scripts": "node --test scripts/fixture-env.test.mjs scripts/electron-lifecycle.test.mjs scripts/check-story-annotations.test.mjs scripts/ci-test-plan.test.mjs scripts/run-headless-tests.test.mjs scripts/run-workspace-tests-parallel.test.mjs scripts/cu-e2e-scenarios.test.mjs scripts/cu-report-sanitize.test.mjs",
"test:scripts": "node --test scripts/fixture-env.test.mjs scripts/electron-lifecycle.test.mjs scripts/check-story-annotations.test.mjs scripts/ci-test-plan.test.mjs scripts/run-headless-tests.test.mjs scripts/run-workspace-tests-parallel.test.mjs scripts/cu-e2e-scenarios.test.mjs scripts/cu-report-sanitize.test.mjs scripts/computer-use-provenance.test.mjs",
"test:scripts:extended": "node --test scripts/cua-driver-provenance.test.mjs scripts/cu-provider-matrix.test.mjs scripts/cu-real-model-launcher.test.mjs scripts/macos-arm64-release.test.mjs scripts/measure-session-bundle.test.mjs",
"test:scripts:full": "npm run test:scripts && npm run test:scripts:extended",
"dev": "npm --workspace @maka/desktop run dev:hmr --",
Expand All@@ -52,6 +52,7 @@
"cost:deepseek-baseline": "node scripts/deepseek-live-cost-baseline.mjs",
"benchmark:kimi-protocol-ab": "node packages/headless/harbor/run-kimi-protocol-ab.mjs",
"prepare:cua-driver": "node scripts/prepare-cua-driver.mjs",
"prepare:maka-cu": "node scripts/prepare-maka-cu.mjs",
"check:cua-driver-artifact": "node scripts/check-cua-driver-bundle.mjs",
"e2e:computer-use-real": "node scripts/cu-real-ax-model-e2e-launcher.mjs",
"e2e:computer-use-process-restart": "MAKA_CU_AX_MODEL_SCENARIO=restart-recovery node scripts/cu-real-ax-model-e2e-launcher.mjs",
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(computer-use): add maka-cu as a selectable executor backend by hqhq1025 · Pull Request #1953 · apache/maka · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions apps/desktop/bundled-tools.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,5 +31,19 @@
"thirdPartyNotices": "missing",
"notarization": "missing",
"distributionReady": false
},
"makaCu": {
"repo": "maka-agent/maka-cu",
"branch": "maka/base",
"commit": "ca7ef80c721fdaf6e7f4af882f16b9c555f14733",
"expectedProtocolVersion": "maka.cu/2",
"binaryName": "maka-cu",
"binarySizeBytes": 2861584,
"binarySha256": "26d45d5243fefd993dc754d5b6bdd64c8c5ff38f3d0ccf6dbb132db89a49f2a7",
"buildProvenance": "local-source-build",
"signature": "adhoc",
"hardenedRuntime": false,
"notarization": "missing",
"distributionReady": false
}
}
41 changes: 41 additions & 0 deletions apps/desktop/src/main/__tests__/computer-use-host.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -65,6 +65,47 @@ describe('Computer Use host health', () => {
assert.equal(computerUseServiceHealth('none', undefined).state, 'not_available');
});

it('reads the executor that is selected, not the role pair one of them happens to have', () => {
// maka-cu supervises one child (§11) and reports its own shape, so it has
// no `action`/`capture` pair to read. This function took only that pair,
// while the availability half of the same capability card had already been
// widened to "any selected executor" — executed against the built desktop
// module with a genuinely ready maka-cu backend, the card read:
//
// executorState() = {"state":"ready","generation":1}
// serviceState (boot) = undefined
// health = not_available, reason naming cua-driver
// artifactAvailable = true
//
// available, state not_available, and a reason naming an executor that is
// not the one running.
assert.deepEqual(
computerUseServiceHealth('maka-cu', { state: 'ready', generation: 1, restartAttempts: 0 }),
{ state: 'healthy', reason: 'maka-cu 操作与截图服务已就绪。' },
);
assert.equal(
computerUseServiceHealth('maka-cu', {
state: 'backing_off',
generation: 1,
restartAttempts: 1,
}).state,
'degraded',
);
assert.deepEqual(
computerUseServiceHealth('maka-cu', {
state: 'unavailable',
generation: 1,
restartAttempts: 3,
}),
{ state: 'not_available', reason: 'maka-cu service 启动失败或已退出。' },
);
assert.equal(
computerUseServiceHealth('maka-cu', { state: 'idle', generation: 0, restartAttempts: 0 })
.state,
'not_run',
);
});

it('constructs a backend only when the local artifact matches the manifest hash', async () => {
const directory = await mkdtemp(join(tmpdir(), 'maka-cu-host-'));
try {
Expand Down
10 changes: 8 additions & 2 deletions apps/desktop/src/main/boot.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -1430,9 +1430,15 @@ wireAppLifecycle({
});

function computerUseCapabilityInput() {
const serviceState = computerUse.backend?.serviceState?.();
// Whichever executor was selected reports its own shape: cua-driver an
// action/capture role pair, maka-cu (§11) a single supervised child. Reading
// only `serviceState` meant a ready maka-cu backend produced `undefined`
// here, and the card said "not available" while its own availability half
// said the opposite.
const executorState =
computerUse.backend?.serviceState?.() ?? computerUse.backend?.executorState?.();
return {
backendId: computerUse.backendId,
health: computerUseServiceHealth(computerUse.backendId, serviceState),
health: computerUseServiceHealth(computerUse.backendId, executorState),
};
}
24 changes: 14 additions & 10 deletions apps/desktop/src/main/capability-snapshot.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@ import {
type OsPermissionSnapshot,
type PermissionSnapshot,
} from '@maka/core';
import type { CuBackendId } from '@maka/computer-use';
import type { BotStatus } from '@maka/runtime';
import type { computerUseServiceHealth } from './computer-use-host.js';
import {
Expand DownExpand Up@@ -46,7 +47,7 @@ export function buildCapabilitySnapshotCollection(input: {
permissions: PermissionSnapshot;
botStatuses: Record<BotProvider, BotStatus>;
computerUse?: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
};
now?: number;
Expand DownExpand Up@@ -123,13 +124,16 @@ export function buildCapabilitySnapshotCollection(input: {

function computerUseCapability(
input: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
} | undefined,
permissions: PermissionSnapshot['permissions'],
now: number,
): CapabilitySnapshot {
const artifactAvailable = input?.backendId === 'cua-driver';
// Any selected executor is an executor. Naming one here made the capability
// read `not_available` for a machine that had a working backend, merely a
// different one.
const artifactAvailable = input !== undefined && input.backendId !== 'none';
return staticCapability({
id: 'computer_use',
label: 'Computer Use',
Expand All@@ -152,23 +156,23 @@ function computerUseCapability(
state: input?.health.state ?? 'not_available',
source: 'runtime_probe',
lastCheckedAt: now,
reason: input?.health.reason ?? 'cua-driver 后端当前不可用。',
reason: input?.health.reason ?? 'Computer Use 后端当前不可用。',
},
});
}

function computerUseCapabilityReason(
input: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
} | undefined,
permissions: PermissionSnapshot['permissions'],
): string {
if (input?.backendId !== 'cua-driver') {
return '未找到通过完整性检查的 cua-driver artifact。';
if (input === undefined || input.backendId === 'none') {
return '未找到通过完整性检查的 Computer Use 执行器 artifact。';
}

const reasons = ['cua-driver artifact 已通过本地完整性检查。'];
const reasons = [`${input.backendId} artifact 已通过本地完整性检查。`];
const missingPermissions = [
['辅助功能', permissions.accessibility.status],
['屏幕录制', permissions.screen_recording.status],
Expand All@@ -178,10 +182,10 @@ function computerUseCapabilityReason(
}
switch (input.health.state) {
case 'not_available':
reasons.push('cua-driver service 启动失败、已退出或已停止。');
reasons.push(`${input.backendId} service 启动失败、已退出或已停止。`);
break;
case 'degraded':
reasons.push('cua-driver service 正在启动或恢复。');
reasons.push(`${input.backendId} service 正在启动或恢复。`);
break;
case 'healthy':
reasons.push('操作与截图 service 已就绪;按目标与动作类别授权后可操作本机应用。');
Expand Down
62 changes: 43 additions & 19 deletions apps/desktop/src/main/computer-use-host.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,6 +11,7 @@ import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import type { CuaDriverRoleSnapshot } from '@maka/computer-use';
import type { CuaDriverBackendOptions } from '@maka/computer-use';
import type { MakaCuServiceSnapshot } from '@maka/computer-use';
import {
selectComputerUseBackend,
type SelectedComputerUseBackend,
Expand DownExpand Up@@ -118,53 +119,76 @@ export function createDesktopPhysicalInputGuard(
return () => getSystemIdleTime() < 1;
}

/**
* The health half of the Computer Use capability card, for whichever executor
* was selected.
*
* This used to take the cua-driver role pair and nothing else, while the card's
* `available` half had already been widened to "any selected executor". With a
* genuinely ready maka-cu backend the two halves disagreed, and executing the
* built desktop module against one showed exactly how:
*
* executorState() = {"state":"ready","generation":1}
* serviceState (boot) = undefined
* health = {"state":"not_available","reason":"未找到通过完整性检查且可分发的 cua-driver artifact。"}
* artifactAvailable = true
*
* — available, state not_available, and a reason naming an executor that is not
* the one running. cua-driver supervises an action/capture role pair; maka-cu
* supervises one child (§11) and reports its own shape. Both are read here as a
* list of role states, so the card is right for either, and neither is selected
* by being described.
*/
export type ComputerUseExecutorState =
| { action: CuaDriverRoleSnapshot; capture: CuaDriverRoleSnapshot }
| MakaCuServiceSnapshot;

function roleStates(state: ComputerUseExecutorState): Array<CuaDriverRoleSnapshot['state']> {
return 'action' in state ? [state.action.state, state.capture.state] : [state.state];
}

export function computerUseServiceHealth(
backendId: SelectedComputerUseBackend['backendId'],
state: {
action: CuaDriverRoleSnapshot;
capture: CuaDriverRoleSnapshot;
} | undefined,
state: ComputerUseExecutorState | undefined,
): {
state: 'not_available' | 'not_run' | 'healthy' | 'degraded';
reason: string;
} {
if (backendId === 'none' || !state) {
return {
state: 'not_available',
reason: '未找到通过完整性检查且可分发的 cua-driver artifact。',
reason: '未找到通过完整性检查且可分发的 Computer Use 执行器 artifact。',
};
}
const roles = [state.action, state.capture];
if (roles.some((role) =>
role.state === 'unavailable' || role.state === 'disposed')) {
const roles = roleStates(state);
if (roles.some((role) => role === 'unavailable' || role === 'disposed')) {
return {
state: 'not_available',
reason: roles.some((role) => role.state === 'disposed')
? 'cua-driver service 已停止。'
: 'cua-driver service 启动失败或已退出。',
reason: roles.some((role) => role === 'disposed')
? `${backendId} service 已停止。`
: `${backendId} service 启动失败或已退出。`,
};
}
if (roles.some((role) =>
role.state === 'starting' || role.state === 'backing_off')) {
if (roles.some((role) => role === 'starting' || role === 'backing_off')) {
return {
state: 'degraded',
reason: 'cua-driver service 正在启动或恢复。',
reason: `${backendId} service 正在启动或恢复。`,
};
}
if (roles.every((role) => role.state === 'ready')) {
if (roles.every((role) => role === 'ready')) {
return {
state: 'healthy',
reason: 'cua-driver 操作与截图服务已就绪。',
reason: `${backendId} 操作与截图服务已就绪。`,
};
}
if (roles.some((role) => role.state === 'ready')) {
if (roles.some((role) => role === 'ready')) {
return {
state: 'not_run',
reason: 'cua-driver 部分服务已启动,其余服务将在需要时启动。',
reason: `${backendId} 部分服务已启动,其余服务将在需要时启动。`,
};
}
return {
state: 'not_run',
reason: 'cua-driver 已可用,将在首次调用时启动。',
reason: `${backendId} 已可用,将在首次调用时启动。`,
};
}
84 changes: 84 additions & 0 deletions docs/computer-use-provenance.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
# Computer Use provenance

Maka's Computer Use surface was built against other people's work, in three
materially different ways. They are separated here because the obligations
differ: one is redistribution under a license, one is reading licensed source,
and one is observing a proprietary binary and holds no license grant at all.

Paths are Maka's unless they carry the upstream repository name, as in
`open-codex-computer-use/...`.

Every entry names what was taken, where it landed, and what the evidence was.
When you add or change a borrowed design, add the row here and put the same
statement in the file that carries it — an in-file comment answers "why is this
constant 200?" at the moment someone asks it, and this file answers "what did we
build on?" for the project as a whole.

## 1. Redistributed under license

Ships inside the Maka artifact. Requires the license text and copyright notice
to travel with it.

| Component | License | Where the notice lives |
|---|---|---|
| npm dependencies | various | `apps/desktop/resources/licenses/npm/`, generated by `scripts/generate-third-party-notices.mjs` and byte-checked at build by `scripts/check-third-party-notices.mjs` |
| cua-driver | MIT | `apps/desktop/resources/licenses/cua-driver/`, pinned by digest in `apps/desktop/bundled-tools.json` |

cua-driver is the Computer Use executor Maka currently defaults to. It is a
third-party binary, fetched by `scripts/prepare-cua-driver.mjs` and verified
against the digests recorded in the manifest.

Maka's own executor, `maka-cu`, is built from Maka's own source by
`scripts/prepare-maka-cu.mjs` and pinned by digest in the same manifest. It is
not signed, so it is not distributed at all yet: its `distributionReady` is
false and `scripts/verify-macos-arm64-dmg.mjs` forbids its path in a packaged
build. It is selectable in a development build and nothing selects it by
default.

`maka-cu` is itself a fork of MIT-licensed `iFurySt/open-codex-computer-use`
(§2), so when it does ship, that notice travels with it.

## 2. Licensed source read as reference

MIT-licensed source we read while designing. No code was copied into this
repository; what was taken is design — a format, a decision, or an archived
measurement. Attribution is given because it was load-bearing, not because MIT
compels it for ideas.

### iFurySt/open-codex-computer-use, and its fork QwenLM/open-computer-use

Both MIT, © 2026 Leo. An independent reimplementation of Codex's Computer Use
as an MCP server.

| Taken | Landed in | Notes |
|---|---|---|
| An archived capture of Codex's real `get_app_state` result | `packages/runtime/src/computer-use-tools.ts` | Their `open-codex-computer-use/artifacts/tool-comparisons/20260417-focus-behavior/`. This is what turned Maka's model of Codex's observation format from inference into an observed sample. |
| The one-line-per-element observation shape: indentation for containment, states written only when not the default | same file | Maka's version keeps `observation_id` in the header (frame binding is protocol here, prose there) and keeps element geometry (Codex has no coordinate action surface to need it; Maka's is disabled by default rather than absent). |
| Writing only the informative half of a state — `disabled`, never `enabled` | same file | Their `summarizeTraits`. |
| Filtering `AXPress` out of an element's advertised action list, because pressing is what `click` does | not yet landed — waits on `trycua/cua#2622` exposing per-element AX actions | Their `meaningfulActions`. |
| The permission-onboarding pattern: anchor a guidance panel to the System Settings window, track it, and distinguish grants that need an app relaunch | not yet landed — for the `feat/permission-onboarding` work | Their `open-codex-computer-use/apps/OpenComputerUse/Sources/OpenComputerUse/PermissionOnboardingApp.swift`. |
| Archiving side-by-side tool captures in-repo as evidence | practice, not code | Worth adopting for Maka's own Codex comparisons. |

Not taken, recorded so the decision is not re-litigated: their `SkyLightSPI` /
`SkyClickSimulation` synthetic-focus click. Their own comment states the recipe
is derived from cua-driver and yabai. Maka read the same recipe rather than
that code.

## 3. Observed, not licensed

Codex Computer Use (`SkyComputerUseService` and its helper bundles) is
proprietary and ships no source. Nothing was copied from it, because there is
nothing to copy: what exists here was reimplemented from observed behaviour and
from constants recovered by inspecting the shipped binary.

This confers no rights and is not a license. Statements about it are
descriptions of what a build did on a given date, and are dated for that reason.

| Recovered | Landed in |
|---|---|
| The agent cursor: shape, hotspot, motion thresholds, spring constants, and the candidate-path scoring function | `apps/desktop/src/renderer/computer-use-overlay/engine/cursor-engine.ts` |
| Overlay level policy — an occluded target raises the cursor rather than hiding it | same file, and `apps/desktop/src/main/computer-use/cursor-overlay-window.ts` |
| The observation text shape | `packages/runtime/src/computer-use-tools.ts`, corroborated by the archived capture in §2 |

Where Maka deliberately diverges, the divergence is stated at the point of
divergence rather than here, so it is read by whoever is changing that code.
3 changes: 2 additions & 1 deletion package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,7 +31,7 @@
"test:dist": "npm run test:scripts:full && node scripts/run-workspace-tests-parallel.mjs --concurrency=3",
"test:dist:serial": "npm run test:scripts:full && node scripts/run-workspace-tests-parallel.mjs --serial",
"test:fast": "npm run build:test && npm run test:scripts && node scripts/run-workspace-tests-parallel.mjs --concurrency=3",
"test:scripts": "node --test scripts/fixture-env.test.mjs scripts/electron-lifecycle.test.mjs scripts/check-story-annotations.test.mjs scripts/ci-test-plan.test.mjs scripts/run-headless-tests.test.mjs scripts/run-workspace-tests-parallel.test.mjs scripts/cu-e2e-scenarios.test.mjs scripts/cu-report-sanitize.test.mjs",
"test:scripts": "node --test scripts/fixture-env.test.mjs scripts/electron-lifecycle.test.mjs scripts/check-story-annotations.test.mjs scripts/ci-test-plan.test.mjs scripts/run-headless-tests.test.mjs scripts/run-workspace-tests-parallel.test.mjs scripts/cu-e2e-scenarios.test.mjs scripts/cu-report-sanitize.test.mjs scripts/computer-use-provenance.test.mjs",
"test:scripts:extended": "node --test scripts/cua-driver-provenance.test.mjs scripts/cu-provider-matrix.test.mjs scripts/cu-real-model-launcher.test.mjs scripts/macos-arm64-release.test.mjs scripts/measure-session-bundle.test.mjs",
"test:scripts:full": "npm run test:scripts && npm run test:scripts:extended",
"dev": "npm --workspace @maka/desktop run dev:hmr --",
Expand All@@ -52,6 +52,7 @@
"cost:deepseek-baseline": "node scripts/deepseek-live-cost-baseline.mjs",
"benchmark:kimi-protocol-ab": "node packages/headless/harbor/run-kimi-protocol-ab.mjs",
"prepare:cua-driver": "node scripts/prepare-cua-driver.mjs",
"prepare:maka-cu": "node scripts/prepare-maka-cu.mjs",
"check:cua-driver-artifact": "node scripts/check-cua-driver-bundle.mjs",
"e2e:computer-use-real": "node scripts/cu-real-ax-model-e2e-launcher.mjs",
"e2e:computer-use-process-restart": "MAKA_CU_AX_MODEL_SCENARIO=restart-recovery node scripts/cu-real-ax-model-e2e-launcher.mjs",
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); feat(computer-use): add maka-cu as a selectable executor backend by hqhq1025 · Pull Request #1953 · apache/maka · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions apps/desktop/bundled-tools.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,5 +31,19 @@
"thirdPartyNotices": "missing",
"notarization": "missing",
"distributionReady": false
},
"makaCu": {
"repo": "maka-agent/maka-cu",
"branch": "maka/base",
"commit": "ca7ef80c721fdaf6e7f4af882f16b9c555f14733",
"expectedProtocolVersion": "maka.cu/2",
"binaryName": "maka-cu",
"binarySizeBytes": 2861584,
"binarySha256": "26d45d5243fefd993dc754d5b6bdd64c8c5ff38f3d0ccf6dbb132db89a49f2a7",
"buildProvenance": "local-source-build",
"signature": "adhoc",
"hardenedRuntime": false,
"notarization": "missing",
"distributionReady": false
}
}
41 changes: 41 additions & 0 deletions apps/desktop/src/main/__tests__/computer-use-host.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -65,6 +65,47 @@ describe('Computer Use host health', () => {
assert.equal(computerUseServiceHealth('none', undefined).state, 'not_available');
});

it('reads the executor that is selected, not the role pair one of them happens to have', () => {
// maka-cu supervises one child (§11) and reports its own shape, so it has
// no `action`/`capture` pair to read. This function took only that pair,
// while the availability half of the same capability card had already been
// widened to "any selected executor" — executed against the built desktop
// module with a genuinely ready maka-cu backend, the card read:
//
// executorState() = {"state":"ready","generation":1}
// serviceState (boot) = undefined
// health = not_available, reason naming cua-driver
// artifactAvailable = true
//
// available, state not_available, and a reason naming an executor that is
// not the one running.
assert.deepEqual(
computerUseServiceHealth('maka-cu', { state: 'ready', generation: 1, restartAttempts: 0 }),
{ state: 'healthy', reason: 'maka-cu 操作与截图服务已就绪。' },
);
assert.equal(
computerUseServiceHealth('maka-cu', {
state: 'backing_off',
generation: 1,
restartAttempts: 1,
}).state,
'degraded',
);
assert.deepEqual(
computerUseServiceHealth('maka-cu', {
state: 'unavailable',
generation: 1,
restartAttempts: 3,
}),
{ state: 'not_available', reason: 'maka-cu service 启动失败或已退出。' },
);
assert.equal(
computerUseServiceHealth('maka-cu', { state: 'idle', generation: 0, restartAttempts: 0 })
.state,
'not_run',
);
});

it('constructs a backend only when the local artifact matches the manifest hash', async () => {
const directory = await mkdtemp(join(tmpdir(), 'maka-cu-host-'));
try {
Expand Down
10 changes: 8 additions & 2 deletions apps/desktop/src/main/boot.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -1430,9 +1430,15 @@ wireAppLifecycle({
});

function computerUseCapabilityInput() {
const serviceState = computerUse.backend?.serviceState?.();
// Whichever executor was selected reports its own shape: cua-driver an
// action/capture role pair, maka-cu (§11) a single supervised child. Reading
// only `serviceState` meant a ready maka-cu backend produced `undefined`
// here, and the card said "not available" while its own availability half
// said the opposite.
const executorState =
computerUse.backend?.serviceState?.() ?? computerUse.backend?.executorState?.();
return {
backendId: computerUse.backendId,
health: computerUseServiceHealth(computerUse.backendId, serviceState),
health: computerUseServiceHealth(computerUse.backendId, executorState),
};
}
24 changes: 14 additions & 10 deletions apps/desktop/src/main/capability-snapshot.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@ import {
type OsPermissionSnapshot,
type PermissionSnapshot,
} from '@maka/core';
import type { CuBackendId } from '@maka/computer-use';
import type { BotStatus } from '@maka/runtime';
import type { computerUseServiceHealth } from './computer-use-host.js';
import {
Expand DownExpand Up@@ -46,7 +47,7 @@ export function buildCapabilitySnapshotCollection(input: {
permissions: PermissionSnapshot;
botStatuses: Record<BotProvider, BotStatus>;
computerUse?: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
};
now?: number;
Expand DownExpand Up@@ -123,13 +124,16 @@ export function buildCapabilitySnapshotCollection(input: {

function computerUseCapability(
input: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
} | undefined,
permissions: PermissionSnapshot['permissions'],
now: number,
): CapabilitySnapshot {
const artifactAvailable = input?.backendId === 'cua-driver';
// Any selected executor is an executor. Naming one here made the capability
// read `not_available` for a machine that had a working backend, merely a
// different one.
const artifactAvailable = input !== undefined && input.backendId !== 'none';
return staticCapability({
id: 'computer_use',
label: 'Computer Use',
Expand All@@ -152,23 +156,23 @@ function computerUseCapability(
state: input?.health.state ?? 'not_available',
source: 'runtime_probe',
lastCheckedAt: now,
reason: input?.health.reason ?? 'cua-driver 后端当前不可用。',
reason: input?.health.reason ?? 'Computer Use 后端当前不可用。',
},
});
}

function computerUseCapabilityReason(
input: {
backendId: 'cua-driver' | 'none';
backendId: CuBackendId | 'none';
health: ReturnType<typeof computerUseServiceHealth>;
} | undefined,
permissions: PermissionSnapshot['permissions'],
): string {
if (input?.backendId !== 'cua-driver') {
return '未找到通过完整性检查的 cua-driver artifact。';
if (input === undefined || input.backendId === 'none') {
return '未找到通过完整性检查的 Computer Use 执行器 artifact。';
}

const reasons = ['cua-driver artifact 已通过本地完整性检查。'];
const reasons = [`${input.backendId} artifact 已通过本地完整性检查。`];
const missingPermissions = [
['辅助功能', permissions.accessibility.status],
['屏幕录制', permissions.screen_recording.status],
Expand All@@ -178,10 +182,10 @@ function computerUseCapabilityReason(
}
switch (input.health.state) {
case 'not_available':
reasons.push('cua-driver service 启动失败、已退出或已停止。');
reasons.push(`${input.backendId} service 启动失败、已退出或已停止。`);
break;
case 'degraded':
reasons.push('cua-driver service 正在启动或恢复。');
reasons.push(`${input.backendId} service 正在启动或恢复。`);
break;
case 'healthy':
reasons.push('操作与截图 service 已就绪;按目标与动作类别授权后可操作本机应用。');
Expand Down
62 changes: 43 additions & 19 deletions apps/desktop/src/main/computer-use-host.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,6 +11,7 @@ import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import type { CuaDriverRoleSnapshot } from '@maka/computer-use';
import type { CuaDriverBackendOptions } from '@maka/computer-use';
import type { MakaCuServiceSnapshot } from '@maka/computer-use';
import {
selectComputerUseBackend,
type SelectedComputerUseBackend,
Expand DownExpand Up@@ -118,53 +119,76 @@ export function createDesktopPhysicalInputGuard(
return () => getSystemIdleTime() < 1;
}

/**
* The health half of the Computer Use capability card, for whichever executor
* was selected.
*
* This used to take the cua-driver role pair and nothing else, while the card's
* `available` half had already been widened to "any selected executor". With a
* genuinely ready maka-cu backend the two halves disagreed, and executing the
* built desktop module against one showed exactly how:
*
* executorState() = {"state":"ready","generation":1}
* serviceState (boot) = undefined
* health = {"state":"not_available","reason":"未找到通过完整性检查且可分发的 cua-driver artifact。"}
* artifactAvailable = true
*
* — available, state not_available, and a reason naming an executor that is not
* the one running. cua-driver supervises an action/capture role pair; maka-cu
* supervises one child (§11) and reports its own shape. Both are read here as a
* list of role states, so the card is right for either, and neither is selected
* by being described.
*/
export type ComputerUseExecutorState =
| { action: CuaDriverRoleSnapshot; capture: CuaDriverRoleSnapshot }
| MakaCuServiceSnapshot;

function roleStates(state: ComputerUseExecutorState): Array<CuaDriverRoleSnapshot['state']> {
return 'action' in state ? [state.action.state, state.capture.state] : [state.state];
}

export function computerUseServiceHealth(
backendId: SelectedComputerUseBackend['backendId'],
state: {
action: CuaDriverRoleSnapshot;
capture: CuaDriverRoleSnapshot;
} | undefined,
state: ComputerUseExecutorState | undefined,
): {
state: 'not_available' | 'not_run' | 'healthy' | 'degraded';
reason: string;
} {
if (backendId === 'none' || !state) {
return {
state: 'not_available',
reason: '未找到通过完整性检查且可分发的 cua-driver artifact。',
reason: '未找到通过完整性检查且可分发的 Computer Use 执行器 artifact。',
};
}
const roles = [state.action, state.capture];
if (roles.some((role) =>
role.state === 'unavailable' || role.state === 'disposed')) {
const roles = roleStates(state);
if (roles.some((role) => role === 'unavailable' || role === 'disposed')) {
return {
state: 'not_available',
reason: roles.some((role) => role.state === 'disposed')
? 'cua-driver service 已停止。'
: 'cua-driver service 启动失败或已退出。',
reason: roles.some((role) => role === 'disposed')
? `${backendId} service 已停止。`
: `${backendId} service 启动失败或已退出。`,
};
}
if (roles.some((role) =>
role.state === 'starting' || role.state === 'backing_off')) {
if (roles.some((role) => role === 'starting' || role === 'backing_off')) {
return {
state: 'degraded',
reason: 'cua-driver service 正在启动或恢复。',
reason: `${backendId} service 正在启动或恢复。`,
};
}
if (roles.every((role) => role.state === 'ready')) {
if (roles.every((role) => role === 'ready')) {
return {
state: 'healthy',
reason: 'cua-driver 操作与截图服务已就绪。',
reason: `${backendId} 操作与截图服务已就绪。`,
};
}
if (roles.some((role) => role.state === 'ready')) {
if (roles.some((role) => role === 'ready')) {
return {
state: 'not_run',
reason: 'cua-driver 部分服务已启动,其余服务将在需要时启动。',
reason: `${backendId} 部分服务已启动,其余服务将在需要时启动。`,
};
}
return {
state: 'not_run',
reason: 'cua-driver 已可用,将在首次调用时启动。',
reason: `${backendId} 已可用,将在首次调用时启动。`,
};
}
84 changes: 84 additions & 0 deletions docs/computer-use-provenance.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
# Computer Use provenance

Maka's Computer Use surface was built against other people's work, in three
materially different ways. They are separated here because the obligations
differ: one is redistribution under a license, one is reading licensed source,
and one is observing a proprietary binary and holds no license grant at all.

Paths are Maka's unless they carry the upstream repository name, as in
`open-codex-computer-use/...`.

Every entry names what was taken, where it landed, and what the evidence was.
When you add or change a borrowed design, add the row here and put the same
statement in the file that carries it — an in-file comment answers "why is this
constant 200?" at the moment someone asks it, and this file answers "what did we
build on?" for the project as a whole.

## 1. Redistributed under license

Ships inside the Maka artifact. Requires the license text and copyright notice
to travel with it.

| Component | License | Where the notice lives |
|---|---|---|
| npm dependencies | various | `apps/desktop/resources/licenses/npm/`, generated by `scripts/generate-third-party-notices.mjs` and byte-checked at build by `scripts/check-third-party-notices.mjs` |
| cua-driver | MIT | `apps/desktop/resources/licenses/cua-driver/`, pinned by digest in `apps/desktop/bundled-tools.json` |

cua-driver is the Computer Use executor Maka currently defaults to. It is a
third-party binary, fetched by `scripts/prepare-cua-driver.mjs` and verified
against the digests recorded in the manifest.

Maka's own executor, `maka-cu`, is built from Maka's own source by
`scripts/prepare-maka-cu.mjs` and pinned by digest in the same manifest. It is
not signed, so it is not distributed at all yet: its `distributionReady` is
false and `scripts/verify-macos-arm64-dmg.mjs` forbids its path in a packaged
build. It is selectable in a development build and nothing selects it by
default.

`maka-cu` is itself a fork of MIT-licensed `iFurySt/open-codex-computer-use`
(§2), so when it does ship, that notice travels with it.

## 2. Licensed source read as reference

MIT-licensed source we read while designing. No code was copied into this
repository; what was taken is design — a format, a decision, or an archived
measurement. Attribution is given because it was load-bearing, not because MIT
compels it for ideas.

### iFurySt/open-codex-computer-use, and its fork QwenLM/open-computer-use

Both MIT, © 2026 Leo. An independent reimplementation of Codex's Computer Use
as an MCP server.

| Taken | Landed in | Notes |
|---|---|---|
| An archived capture of Codex's real `get_app_state` result | `packages/runtime/src/computer-use-tools.ts` | Their `open-codex-computer-use/artifacts/tool-comparisons/20260417-focus-behavior/`. This is what turned Maka's model of Codex's observation format from inference into an observed sample. |
| The one-line-per-element observation shape: indentation for containment, states written only when not the default | same file | Maka's version keeps `observation_id` in the header (frame binding is protocol here, prose there) and keeps element geometry (Codex has no coordinate action surface to need it; Maka's is disabled by default rather than absent). |
| Writing only the informative half of a state — `disabled`, never `enabled` | same file | Their `summarizeTraits`. |
| Filtering `AXPress` out of an element's advertised action list, because pressing is what `click` does | not yet landed — waits on `trycua/cua#2622` exposing per-element AX actions | Their `meaningfulActions`. |
| The permission-onboarding pattern: anchor a guidance panel to the System Settings window, track it, and distinguish grants that need an app relaunch | not yet landed — for the `feat/permission-onboarding` work | Their `open-codex-computer-use/apps/OpenComputerUse/Sources/OpenComputerUse/PermissionOnboardingApp.swift`. |
| Archiving side-by-side tool captures in-repo as evidence | practice, not code | Worth adopting for Maka's own Codex comparisons. |

Not taken, recorded so the decision is not re-litigated: their `SkyLightSPI` /
`SkyClickSimulation` synthetic-focus click. Their own comment states the recipe
is derived from cua-driver and yabai. Maka read the same recipe rather than
that code.

## 3. Observed, not licensed

Codex Computer Use (`SkyComputerUseService` and its helper bundles) is
proprietary and ships no source. Nothing was copied from it, because there is
nothing to copy: what exists here was reimplemented from observed behaviour and
from constants recovered by inspecting the shipped binary.

This confers no rights and is not a license. Statements about it are
descriptions of what a build did on a given date, and are dated for that reason.

| Recovered | Landed in |
|---|---|
| The agent cursor: shape, hotspot, motion thresholds, spring constants, and the candidate-path scoring function | `apps/desktop/src/renderer/computer-use-overlay/engine/cursor-engine.ts` |
| Overlay level policy — an occluded target raises the cursor rather than hiding it | same file, and `apps/desktop/src/main/computer-use/cursor-overlay-window.ts` |
| The observation text shape | `packages/runtime/src/computer-use-tools.ts`, corroborated by the archived capture in §2 |

Where Maka deliberately diverges, the divergence is stated at the point of
divergence rather than here, so it is read by whoever is changing that code.
3 changes: 2 additions & 1 deletion package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,7 +31,7 @@
"test:dist": "npm run test:scripts:full && node scripts/run-workspace-tests-parallel.mjs --concurrency=3",
"test:dist:serial": "npm run test:scripts:full && node scripts/run-workspace-tests-parallel.mjs --serial",
"test:fast": "npm run build:test && npm run test:scripts && node scripts/run-workspace-tests-parallel.mjs --concurrency=3",
"test:scripts": "node --test scripts/fixture-env.test.mjs scripts/electron-lifecycle.test.mjs scripts/check-story-annotations.test.mjs scripts/ci-test-plan.test.mjs scripts/run-headless-tests.test.mjs scripts/run-workspace-tests-parallel.test.mjs scripts/cu-e2e-scenarios.test.mjs scripts/cu-report-sanitize.test.mjs",
"test:scripts": "node --test scripts/fixture-env.test.mjs scripts/electron-lifecycle.test.mjs scripts/check-story-annotations.test.mjs scripts/ci-test-plan.test.mjs scripts/run-headless-tests.test.mjs scripts/run-workspace-tests-parallel.test.mjs scripts/cu-e2e-scenarios.test.mjs scripts/cu-report-sanitize.test.mjs scripts/computer-use-provenance.test.mjs",
"test:scripts:extended": "node --test scripts/cua-driver-provenance.test.mjs scripts/cu-provider-matrix.test.mjs scripts/cu-real-model-launcher.test.mjs scripts/macos-arm64-release.test.mjs scripts/measure-session-bundle.test.mjs",
"test:scripts:full": "npm run test:scripts && npm run test:scripts:extended",
"dev": "npm --workspace @maka/desktop run dev:hmr --",
Expand All@@ -52,6 +52,7 @@
"cost:deepseek-baseline": "node scripts/deepseek-live-cost-baseline.mjs",
"benchmark:kimi-protocol-ab": "node packages/headless/harbor/run-kimi-protocol-ab.mjs",
"prepare:cua-driver": "node scripts/prepare-cua-driver.mjs",
"prepare:maka-cu": "node scripts/prepare-maka-cu.mjs",
"check:cua-driver-artifact": "node scripts/check-cua-driver-bundle.mjs",
"e2e:computer-use-real": "node scripts/cu-real-ax-model-e2e-launcher.mjs",
"e2e:computer-use-process-restart": "MAKA_CU_AX_MODEL_SCENARIO=restart-recovery node scripts/cu-real-ax-model-e2e-launcher.mjs",
Expand Down
Loading
Loading