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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ __pycache__/

# Build outputs
/apps/ade-cli/dist/
/apps/ade-cli/dist-static/
/apps/ade-code/dist/
/apps/desktop/release/
/apps/desktop/dist/
Expand Down
8 changes: 4 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@

- Desktop checks:
- `npm --prefix apps/desktop run typecheck`
- `npm --prefix apps/desktop run test`
- `npm run test:desktop:sharded`
- `npm --prefix apps/desktop run build`
- `npm --prefix apps/desktop run lint`
- ADE CLI checks:
- `npm --prefix apps/ade-cli run typecheck`
- `npm --prefix apps/ade-cli run test`
- `npm --prefix apps/ade-cli run build`
- Run the smallest relevant subset first when iterating, then finish with the broader checks that cover the touched surfaces.
- If even running the full desktop test suit, u ahve to shard like ci
- Run full desktop tests with the root `npm run test:desktop:sharded` command; use single-file or single-shard Vitest commands for iteration.

## Terminology

Expand Down Expand Up @@ -81,7 +81,7 @@ Desktop release:
- **Node.js 22.x** is required (`node:sqlite` is used as the primary database engine).
- Each app under `apps/` has its own independent `node_modules` and `package-lock.json` (no npm workspaces).
- Validation commands are documented in the "Validation" section above.
- The desktop test suite (265 test files) is large; CI shards it. For local iteration, run targeted tests (e.g. `npm --prefix apps/desktop run test:unit`) or a single file rather than the full suite.
- The desktop test suite is large; CI shards it. For local iteration, run a single file or one CI-style shard rather than the full suite.

### Inspecting the local Electron desktop app with Codex Computer Use on macOS

Expand Down Expand Up @@ -111,7 +111,7 @@ Desktop release:
- The `ADE_PROJECT_ROOT=/workspace` env var tells the main process to auto-open a project at startup. However, there is a timing race: the renderer's initial `getProject()` call may return null before the async project switch completes, causing the welcome screen to appear even though the backend loaded the project. A workaround is to open the project manually via the "Open a project" button in the top bar.
- Computer-use features (screenshot, video capture, GUI automation) are macOS-only (`screencapture`, `osascript`). On Linux these gracefully degrade — the app returns `blocked_by_capability`.
- `electron-builder` config only defines a `mac` target. Distributable Linux builds (deb/AppImage) are not configured, but dev mode works fine.
- The `test:unit` script (`npm --prefix apps/desktop run test:unit`) uses `--project unit` which the pinned vitest 0.34.6 doesn't support. Use `npx vitest run <specific-test-file>` in `apps/desktop` for targeted tests, or `npm --prefix apps/desktop run test` for the full suite.
- The pinned Vitest 0.34.6 does not support `--project`. Use `npx vitest run <specific-test-file>` in `apps/desktop` for targeted tests, `npx vitest run --shard=<n>/8` for a CI-style shard, or `npm run test:desktop:sharded` from the repo root for the full desktop unit workspace.
- In the Cursor Cloud VM the active X display is `:1`, not `:99`. When launching Electron set `DISPLAY=:1`.
- To launch the desktop dev app quickly when the CLI is already built: `npm run dev:desktop -- --skip-runtime-build`.
- To launch the TUI against an already-running dev runtime: `npm run dev:code -- --skip-runtime-build --attach --project-root <path> --workspace-root <path>`.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ Daily desktop dev:
npm run dev
```

That aliases to `npm run dev:desktop`: it rebuilds `apps/ade-cli`, launches the Electron desktop app, and points it at the dev runtime socket `/tmp/ade-runtime-dev.sock`. If no dev runtime is listening, desktop is allowed to create it. This is the normal desktop-dev flow.
That aliases to `npm run dev:desktop`: it rebuilds `apps/ade-cli`, refreshes the shared dev runtime at `/tmp/ade-runtime-dev.sock` when needed, launches the Electron desktop app, and points desktop at that runtime. This is the normal desktop-dev flow.

When these commands are run from an ADE lane worktree under `.ade/worktrees/`,
they still run code from that lane checkout, but they open the primary checkout's
Expand All @@ -165,7 +165,7 @@ and uses the lane path as the workspace root for `dev:code`.
Dev command matrix:

```bash
npm run dev:desktop # desktop only; dev socket; desktop may auto-create runtime
npm run dev:desktop # refresh shared dev runtime, then launch desktop
npm run dev:desktop:attach # desktop only; fail if dev runtime is not already running
npm run dev:desktop:clean # desktop only; clear Vite cache before launch
npm run dev:code:web # `ade code` in the browser (PTY + inspector WebSocket)
Expand All @@ -192,11 +192,11 @@ ADE_DEV_RUNTIME_SOCKET_PATH=/tmp/my-ade-dev.sock npm run dev:runtime
ADE_DESKTOP_BRIDGE_SOCKET_PATH=/tmp/my-bridge.sock npm run dev:desktop
```

To test auto-runtime creation, use the `:auto`/default commands after stopping the dev runtime:
To test auto-runtime creation, use the default dev commands after stopping the dev runtime:

```bash
npm run dev:stop
npm run dev:desktop # tests desktop creating the dev runtime
npm run dev:desktop # tests the desktop wrapper creating the dev runtime
npm run dev:stop
npm run dev:code # tests TUI wrapper creating the dev runtime
```
Expand All @@ -211,7 +211,7 @@ npm run package:beta # origin/main -> ADE Beta.app, ade-beta, ~/.ade-bet
These are unsigned local macOS app builds under `apps/desktop/release-alpha` and `apps/desktop/release-beta`. Beta fetches `origin/main`, fast-forwards the local `main` checkout when possible, and builds that checkout as `ADE Beta`. It does not create a packaging worktree. These builds do not replace the production `ADE.app`, production `ade`, or `~/.ade` runtime/state. Alpha and Beta also use separate Electron profile directories (`ade-desktop-alpha` / `ade-desktop-beta`) so their browser storage and window state do not collide with dev or stable.
Local channel packages include the host runtime binary for this Mac. Release builds still require the full cross-platform runtime artifact set used by remote runtime bootstrap.

Validate with `npm --prefix apps/desktop run typecheck` and `run test`. The desktop test suite is large run the smallest relevant subset first.
Validate with `npm --prefix apps/desktop run typecheck` and `npm run test:desktop:sharded` for the full desktop suite. The desktop test suite is large, so run the smallest relevant subset first.

## Links

Expand Down
63 changes: 59 additions & 4 deletions apps/ade-cli/src/adeRpcServer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1441,6 +1441,31 @@ describe("adeRpcServer", () => {
}
});

it("allows trusted runtimes to serve lower-privilege requested roles", async () => {
await withEnv({ ADE_DEFAULT_ROLE: "cto" }, async () => {
const { runtime } = createRuntime();
const handler = createAdeRpcRequestHandler({ runtime, serverVersion: "test" });

await handler({
jsonrpc: "2.0",
id: 1,
method: "ade/initialize",
params: {
identity: {
callerId: "agent-client",
role: "agent",
},
},
});
const result = (await handler({ jsonrpc: "2.0", id: 3, method: "ade/actions/list" })) as any;

const names = (result.actions ?? []).map((tool: any) => tool.name);
expect(names).toContain("delegate_to_subagent");
expect(names).not.toContain("get_cto_state");
expect(names).not.toContain("getLinearSyncDashboard");
});
});

it("lists the full tool surface including coordinator orchestration tools for orchestrator callers", async () => {
const { runtime } = createRuntime();
const handler = createAdeRpcRequestHandler({ runtime, serverVersion: "test" });
Expand Down Expand Up @@ -1592,6 +1617,32 @@ describe("adeRpcServer", () => {
);
});

it("reflects backend availability changes in the action list", async () => {
const fixture = createRuntime();
const handler = createAdeRpcRequestHandler({ runtime: fixture.runtime, serverVersion: "test" });

await initialize(handler, {
callerId: "worker-1",
role: "agent",
missionId: "mission-1",
runId: "run-1",
stepId: "step-1",
attemptId: "attempt-1",
});
const before = (await handler({ jsonrpc: "2.0", id: 3, method: "ade/actions/list" })) as any;
const beforeNames = (before.actions ?? []).map((tool: any) => tool.name);
expect(beforeNames).toContain("screenshot_environment");

fixture.runtime.computerUseArtifactBrokerService.getBackendStatus.mockReturnValue({
backends: [{ id: "external-proof", available: true }],
});

const after = (await handler({ jsonrpc: "2.0", id: 4, method: "ade/actions/list" })) as any;
const afterNames = (after.actions ?? []).map((tool: any) => tool.name);
expect(afterNames).not.toContain("screenshot_environment");
expect(fixture.runtime.computerUseArtifactBrokerService.getBackendStatus).toHaveBeenCalledTimes(2);
});

it("routes macOS VM computer-use tools and ingests screenshots as proof artifacts", async () => {
const fixture = createRuntime();
const handler = createAdeRpcRequestHandler({ runtime: fixture.runtime, serverVersion: "test" });
Expand All @@ -1608,7 +1659,7 @@ describe("adeRpcServer", () => {
});
expect(fixture.runtime.computerUseArtifactBrokerService.ingest).toHaveBeenCalledWith(
expect.objectContaining({
backend: { name: "macos-vm", toolName: "macos_vm_screenshot" },
backend: { name: "macos-vm", style: "local_fallback", toolName: "macos_vm_screenshot" },
owners: expect.arrayContaining([
expect.objectContaining({ kind: "lane", id: "lane-1" }),
expect.objectContaining({ kind: "chat_session", id: "chat-session-1" }),
Expand All @@ -1631,7 +1682,7 @@ describe("adeRpcServer", () => {
});
expect(fixture.runtime.computerUseArtifactBrokerService.ingest).toHaveBeenCalledWith(
expect.objectContaining({
backend: { name: "macos-vm", toolName: "macos_vm_select" },
backend: { name: "macos-vm", style: "local_fallback", toolName: "macos_vm_select" },
}),
);

Expand Down Expand Up @@ -1671,7 +1722,7 @@ describe("adeRpcServer", () => {
});
expect(fixture.runtime.computerUseArtifactBrokerService.ingest).toHaveBeenCalledWith(
expect.objectContaining({
backend: { name: "macos-vm", toolName: "screenshot_environment" },
backend: { name: "macos-vm", style: "local_fallback", toolName: "screenshot_environment" },
}),
);

Expand Down Expand Up @@ -1967,6 +2018,10 @@ describe("adeRpcServer", () => {

expect(runtime.computerUseArtifactBrokerService.ingest).toHaveBeenCalledWith(
expect.objectContaining({
backend: expect.objectContaining({
name: "agent-browser",
style: "external_cli",
}),
owners: expect.arrayContaining([
expect.objectContaining({
kind: "chat_session",
Expand Down Expand Up @@ -2360,7 +2415,7 @@ describe("adeRpcServer", () => {
params: { identity: { callerId: "coord-1", role: "orchestrator" } }
}) as any;

expect(response.capabilities?.actions).toBeTruthy();
expect(response.capabilities?.actions).toEqual({ listChanged: true });
expect(response.capabilities?.resources).toBeUndefined();
} finally {
if (previousRole == null) delete process.env.ADE_DEFAULT_ROLE;
Expand Down
Loading
Loading