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
2 changes: 1 addition & 1 deletion apps/desktop/e2e/composer-inline-completion.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FAKE_HOLD_OPEN_PROMPT } from '@maka/runtime/fake-backend';
import { FAKE_HOLD_OPEN_PROMPT } from '@maka/runtime/test-only/fake-backend';
import { test, expect, COMPOSER_INPUT } from './fixtures';
import type { Page } from '@playwright/test';

Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/e2e/slash-command-menu.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FAKE_HOLD_OPEN_PROMPT } from '@maka/runtime/fake-backend';
import { FAKE_HOLD_OPEN_PROMPT } from '@maka/runtime/test-only/fake-backend';
import { expect, test, COMPOSER_INPUT } from './fixtures';

test('shows only slash commands executable in the current session state', async ({
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/e2e/streaming-remount.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
FAKE_HOLD_OPEN_PROMPT,
FAKE_HOLD_OPEN_REWRITE_PROMPT,
FAKE_WAIT_FOR_STEERING_LARGE_RESPONSE_PROMPT,
} from '@maka/runtime/fake-backend';
} from '@maka/runtime/test-only/fake-backend';
import type { Locator } from '@playwright/test';
import { expect, COMPOSER_INPUT, test } from './fixtures';

Expand Down
10 changes: 9 additions & 1 deletion apps/desktop/electron-builder.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@ export default {
directories: {
output: 'release',
},
files: ['dist/**/*', 'dist-renderer/**/*', 'package.json', '!**/__tests__/**'],
files: [
'dist/**/*',
'dist-renderer/**/*',
'package.json',
'!**/__tests__/**',
// FakeBackend and the Desktop E2E candidate bootstrap live under
// `test-only/`; they must not reach a packaged app.
'!**/test-only/**',
],
extraResources: [
{
from: '../../node_modules/dugite/git',
Expand Down
10 changes: 8 additions & 2 deletions apps/desktop/src/main/runtime-host-boot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -456,10 +456,16 @@ runtimeHostManager = await startRuntimeHostDesktopManager(
rootPath: workspaceRoot,
clientInstanceId: runtimeHostClientInstanceId,
generation: runtimeHostGeneration,
// The Desktop E2E composition lives behind its own entry module, which
// release packaging drops: picking it here is what keeps FakeBackend and
// the E2E bootstrap out of the shipped Runtime Host.
candidateEntrypoint: new URL(
import.meta.resolve("@maka/runtime-host/execution-candidate-main"),
import.meta.resolve(
isE2e
? "@maka/runtime-host/test-only/execution-candidate-e2e-main"
: "@maka/runtime-host/execution-candidate-main",
),
),
...(isE2e ? { desktopE2e: true } : {}),
ipcMain,
workspaceRoot,
attachmentApprovals,
Expand Down
2 changes: 0 additions & 2 deletions apps/desktop/src/main/runtime-host-desktop-candidate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ export interface DesktopRuntimeHostCandidateStartInput
readonly connectTimeoutMs?: number;
readonly handshakeTimeoutMs?: number;
readonly candidateEntrypoint: string | URL;
readonly desktopE2e?: boolean;
readonly generation?: string;
readonly takeoverHostEpoch?: string;
readonly signal?: AbortSignal;
Expand Down Expand Up @@ -695,7 +694,6 @@ function connectInput(
...(input.handshakeTimeoutMs === undefined
? {}
: { handshakeTimeoutMs: input.handshakeTimeoutMs }),
...(input.desktopE2e ? { desktopE2e: true } : {}),
...(input.signal === undefined ? {} : { signal: input.signal }),
};
}
Expand Down
3 changes: 2 additions & 1 deletion packages/runtime-host/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"./protocol": "./dist/protocol/index.js",
"./client": "./dist/client/index.js",
"./execution-candidate-main": "./dist/execution-candidate-main.js",
"./server": "./dist/server/index.js"
"./server": "./dist/server/index.js",
"./test-only/execution-candidate-e2e-main": "./dist/test-only/execution-candidate-e2e-main.js"
},
"scripts": {
"clean": "node ../../scripts/clean-paths.mjs dist tsconfig.tsbuildinfo",
Expand Down
25 changes: 6 additions & 19 deletions packages/runtime-host/src/__tests__/candidate-cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { parseInteractiveRuntimeHostCandidateArguments } from '../candidate-cli.
const ROOT_ID = 'a'.repeat(64);
const STARTUP_ATTEMPT_ID = '00000000-0000-4000-8000-000000000001';

test('parses the production candidate flags without a desktop E2E override', () => {
test('parses the production candidate flags', () => {
const parsed = parseInteractiveRuntimeHostCandidateArguments([
'--root',
'/tmp/workspace',
Expand All @@ -20,24 +20,11 @@ test('parses the production candidate flags without a desktop E2E override', ()
assert.equal(parsed.expectedRootId, ROOT_ID);
assert.equal(parsed.startupAttemptId, STARTUP_ATTEMPT_ID);
assert.equal(parsed.idleGraceMs, 10_000);
assert.equal(parsed.desktopE2e, undefined);
});

test('parses the desktop E2E composition flag', () => {
const parsed = parseInteractiveRuntimeHostCandidateArguments([
'--root',
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'1',
]);
assert.equal(parsed.desktopE2e, true);
});

test('rejects an unknown desktop E2E flag value', () => {
// The Desktop E2E composition is selected by its own entry module, not by a
// flag on the production CLI — so `--desktop-e2e` is simply unknown here.
test('rejects the retired desktop E2E flag as an unknown argument', () => {
assert.throws(
() =>
parseInteractiveRuntimeHostCandidateArguments([
Expand All @@ -48,8 +35,8 @@ test('rejects an unknown desktop E2E flag value', () => {
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'true',
'1',
]),
/Invalid --desktop-e2e/,
/Invalid Runtime Host candidate argument: --desktop-e2e/,
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { RuntimeEvent } from '@maka/core/runtime-event';
import type { SessionHeader } from '@maka/core/session';
import type { HistoryCompactCheckpoint } from '@maka/runtime/history-compact-checkpoint';
import type { BackendFactoryContext } from '@maka/runtime/session-manager';
import { DesktopE2eBackend } from '../desktop-e2e-execution.js';
import { DesktopE2eBackend } from '../test-only/desktop-e2e-execution.js';

function backendContext(overrides: Partial<BackendFactoryContext> = {}): BackendFactoryContext {
return {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { createHash, randomUUID } from 'node:crypto';
import { mkdir, mkdtemp, rm } from 'node:fs/promises';
import { mkdir, mkdtemp, readFile, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
Expand Down Expand Up @@ -30,14 +30,14 @@ test('classifies invalid candidate arguments as an internal startup failure', ()
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'true',
'1',
],
{ encoding: 'utf8', timeout: 10_000 },
);

assert.equal(result.status, 70, result.stderr);
assert.match(result.stderr, /\[runtime-host\] startup failed:/);
assert.match(result.stderr, /Invalid --desktop-e2e/);
assert.match(result.stderr, /Invalid Runtime Host candidate argument: --desktop-e2e/);
});

test('preserves a valid Candidate invocation failure across the detached stderr boundary', async () => {
Expand Down Expand Up @@ -75,3 +75,72 @@ test('preserves a valid Candidate invocation failure across the detached stderr
await rm(controlDirectory, { recursive: true, force: true });
}
});

/**
* Release packaging drops every `test-only/` module, so the production
* candidate entry must not be able to reach one — statically, not merely at
* runtime. Walk the built module graph across the bundled `@maka/*` packages
* and report every test-only module it can reach.
*/
test('the production candidate entry never reaches a test-only module', async () => {
const entry = new URL('../execution-candidate-main.js', import.meta.url).href;
const seen = new Set<string>([entry]);
const queue: string[] = [entry];
const reached: string[] = [];

while (queue.length > 0) {
const current = queue.pop();
if (current === undefined) break;
if (current.includes('/test-only/')) {
reached.push(current);
continue;
}
let source: string;
try {
source = await readFile(new URL(current), 'utf8');
} catch {
continue;
}
for (const specifier of staticImportSpecifiers(source)) {
const resolved = resolveModule(specifier, current);
if (resolved === undefined || seen.has(resolved)) continue;
seen.add(resolved);
queue.push(resolved);
}
}

assert.deepEqual(reached, []);
assert.ok(seen.size > 50, `module graph looks truncated: ${seen.size} modules`);
});

function resolveModule(specifier: string, parent: string): string | undefined {
try {
if (specifier.startsWith('.')) return new URL(specifier, parent).href;
if (specifier.startsWith('@maka/')) {
const resolved = import.meta.resolve(specifier);
return resolved.startsWith('file:') ? resolved : undefined;
}
} catch {
return undefined;
}
return undefined;
}

function staticImportSpecifiers(source: string): string[] {
const specifiers: string[] = [];
for (const match of source.matchAll(
/(?:^|[\s;}])(?:import|export)\b[^'"();]*?from\s*['"]([^'"]+)['"]/g,
Comment thread
qodo-code-review[bot] marked this conversation as resolved.
)) {
if (match[1] !== undefined) specifiers.push(match[1]);
}
for (const match of source.matchAll(/(?:^|[\s;}])import\s*['"]([^'"]+)['"]/g)) {
if (match[1] !== undefined) specifiers.push(match[1]);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
// Literal dynamic imports are real edges in the shipped graph — the built
// `dist` already contains several — so a walk that ignored them could pass
// while a production module reached test-only material through `import(…)`.
for (const match of source.matchAll(/\bimport\s*\(\s*['"]([^'"]+)['"]/g)) {
if (match[1] !== undefined) specifiers.push(match[1]);
}
return specifiers;
}
69 changes: 58 additions & 11 deletions packages/runtime-host/src/__tests__/execution-composition.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import assert from 'node:assert/strict';
import { parseNoRealConnectionError } from '@maka/core/connection-error-copy';
import { createRequire } from 'node:module';
import { mkdir, mkdtemp, rm, stat, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
Expand All @@ -9,7 +10,7 @@ import type {
AgentGraphIntentClaimRequest,
} from '@maka/core/agent-graph-control';
import type { ShellRunRecord } from '@maka/core/shell-run';
import { FAKE_ASK_USER_QUESTION_PROMPT, FakeBackend } from '@maka/runtime/fake-backend';
import { FAKE_ASK_USER_QUESTION_PROMPT, FakeBackend } from '@maka/runtime/test-only/fake-backend';
import { LOCAL_READ_AGENT_DEFINITION } from '@maka/runtime/agent-catalog';
import { SessionManager } from '@maka/runtime/session-manager';
import { fingerprintAgentGraphRunnableIntent } from '@maka/runtime/stream-graph-admission';
Expand Down Expand Up @@ -80,7 +81,7 @@ test('production composition closes long-term memory after a later startup failu
const stores = await openInteractiveExecutionStoresForWrite(owner.lease);
const session = await stores.sessionStore.create({
cwd: root,
backend: 'fake',
backend: 'ai-sdk',
llmConnectionSlug: 'fake',
model: 'fake-model',
permissionMode: 'ask',
Expand Down Expand Up @@ -120,14 +121,14 @@ test('production recovery preserves legacy Automation history and closes an orph
const stores = await openInteractiveExecutionStoresForWrite(owner.lease);
const historical = await stores.sessionStore.create({
cwd: root,
backend: 'fake',
backend: 'ai-sdk',
llmConnectionSlug: 'fake',
model: 'fake-model',
permissionMode: 'ask',
});
const pending = await stores.sessionStore.create({
cwd: root,
backend: 'fake',
backend: 'ai-sdk',
llmConnectionSlug: 'fake',
model: 'fake-model',
permissionMode: 'ask',
Expand Down Expand Up @@ -348,7 +349,7 @@ test('production composition commits automatic titles through Host-owned Session
try {
const session = await manager.createSession({
cwd: root,
backend: 'fake',
backend: 'ai-sdk',
llmConnectionSlug: 'fake',
model: 'fake-model',
permissionMode: 'ask',
Expand Down Expand Up @@ -378,12 +379,51 @@ test('production composition commits automatic titles through Host-owned Session
});
});

test('a legacy fake-backend session is refused with the product reason, not a registry error', async () => {
await withCompositionRoot(async ({ root, owner }) => {
const { composition, manager } = await createCapturedExecutionComposition(owner);
try {
// Written by an older build: this one never produces `fake`, but the
// durable header survives and activation dispatches straight off it.
const legacy = await manager.createSession({
cwd: root,
backend: 'fake',
llmConnectionSlug: 'fake',
model: 'fake-model',
permissionMode: 'ask',
});
const failure = await composition.handlers['turn.start'](
{
sessionId: legacy.id,
turnId: 'turn-legacy-fake',
content: { text: 'resume a retired local simulation' },
},
{
hostEpoch: 'execution-composition-test',
connectionId: 'legacy-fake-client',
surface: 'tui',
principal: 'local_os_user',
acquireResidency: () => ({ release() {} }),
},
).then(
(result) => result,
(error: unknown) => error,
);
const message = failure instanceof Error ? failure.message : JSON.stringify(failure);
assert.doesNotMatch(message, /No backend factory registered/);
assert.equal(parseNoRealConnectionError(message).reason, 'fake_backend');
} finally {
await composition.close();
}
});
});

test('production composition orphans ownerless ShellRuns before serving Resource queries', async () => {
await withCompositionRoot(async ({ root, owner }) => {
const stores = await openInteractiveExecutionStoresForWrite(owner.lease);
const session = await stores.sessionStore.create({
cwd: root,
backend: 'fake',
backend: 'ai-sdk',
llmConnectionSlug: 'fake',
model: 'fake-model',
permissionMode: 'ask',
Expand Down Expand Up @@ -431,7 +471,7 @@ test('production Skill catalog resolves a Graph child durable tool surface', asy
const stores = await openInteractiveExecutionStoresForWrite(owner.lease);
const parent = await stores.sessionStore.create({
cwd: root,
backend: 'fake',
backend: 'ai-sdk',
llmConnectionSlug: 'fake',
model: 'fake-model',
permissionMode: 'ask',
Expand Down Expand Up @@ -599,7 +639,7 @@ test('Skill capability previews omit unavailable Tavily search surfaces', async
const stores = await openInteractiveExecutionStoresForWrite(owner.lease);
const session = await stores.sessionStore.create({
cwd: root,
backend: 'fake',
backend: 'ai-sdk',
llmConnectionSlug: connection.slug,
model: 'fake-model',
permissionMode: 'bypass',
Expand Down Expand Up @@ -656,7 +696,7 @@ test('production composition validates graph stop before aborting a claimed chil
const claims = createAgentGraphControlStore(root);
const parent = await stores.sessionStore.create({
cwd: root,
backend: 'fake',
backend: 'ai-sdk',
llmConnectionSlug: 'fake',
model: 'fake-model',
permissionMode: 'ask',
Expand Down Expand Up @@ -868,7 +908,14 @@ async function createCapturedExecutionComposition(owner: InteractiveRootOwner):
return originalRecover.call(this, stores);
};
try {
const composition = await createExecutionRuntimeHostComposition(compositionContext(owner));
// The production composition no longer registers a test backend of its
// own; the deterministic one arrives through the same `primaryBackendFactory`
// seam the Desktop E2E run uses.
const composition = await createExecutionRuntimeHostComposition(
compositionContext(owner),
{},
{ primaryBackendFactory: (backendContext) => new FakeBackend(backendContext) },
);
await composition.recover();
if (!manager) throw new Error('Production execution composition did not construct Runtime');
return { composition, manager };
Expand All @@ -889,7 +936,7 @@ async function createClaimedGraphChild(input: {
const child = await input.stores.sessionStore.createSubagent({
cwd: input.root,
name: `Graph operator ${input.suffix}`,
backend: 'fake',
backend: 'ai-sdk',
llmConnectionSlug: 'fake',
model: 'fake-model',
permissionMode: 'explore',
Expand Down
Loading
Loading