Skip to content
Open
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
18 changes: 14 additions & 4 deletions app/src/main/hl/engines/claude-code/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,16 @@ const claudeCodeAdapter: EngineAdapter = {
wrapPrompt(ctx: SpawnContext): string {
const lines: string[] = [
'You are driving a specific Chromium browser view on this machine.',
`Your target is CDP target_id=${ctx.targetId} on port ${ctx.cdpPort} (env BU_TARGET_ID / BU_CDP_PORT).`,
];
if (ctx.cdpUrl) {
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Connection-mode env vars are added without clearing conflicting inherited vars, so stale BU_DAEMON_SOCKET / BU_CDP_WS / BU_TARGET_ID / BU_CDP_PORT values can force the helper into the wrong browser-connection mode.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/src/main/hl/engines/claude-code/adapter.ts, line 139:

<comment>Connection-mode env vars are added without clearing conflicting inherited vars, so stale BU_DAEMON_SOCKET / BU_CDP_WS / BU_TARGET_ID / BU_CDP_PORT values can force the helper into the wrong browser-connection mode.</comment>

<file context>
@@ -135,10 +135,16 @@ const claudeCodeAdapter: EngineAdapter = {
       'You are driving a specific Chromium browser view on this machine.',
-      `Your target is CDP target_id=${ctx.targetId} on port ${ctx.cdpPort} (env BU_TARGET_ID / BU_CDP_PORT).`,
+    ];
+    if (ctx.cdpUrl) {
+      lines.push(`You are connected to an external browser via CDP (env BU_DAEMON_SOCKET).`);
+    } else {
</file context>
Fix with Cubic

lines.push(`You are connected to an external browser via CDP (env BU_DAEMON_SOCKET).`);
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Prompt mentions the wrong env var for external CDP WebSocket mode; it should refer to BU_CDP_WS, not BU_DAEMON_SOCKET.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/src/main/hl/engines/claude-code/adapter.ts, line 140:

<comment>Prompt mentions the wrong env var for external CDP WebSocket mode; it should refer to `BU_CDP_WS`, not `BU_DAEMON_SOCKET`.</comment>

<file context>
@@ -135,10 +135,16 @@ const claudeCodeAdapter: EngineAdapter = {
-      `Your target is CDP target_id=${ctx.targetId} on port ${ctx.cdpPort} (env BU_TARGET_ID / BU_CDP_PORT).`,
+    ];
+    if (ctx.cdpUrl) {
+      lines.push(`You are connected to an external browser via CDP (env BU_DAEMON_SOCKET).`);
+    } else {
+      lines.push(`Your target is CDP target_id=${ctx.targetId} on port ${ctx.cdpPort} (env BU_TARGET_ID / BU_CDP_PORT).`);
</file context>
Suggested change
lines.push(`You are connected to an external browser via CDP (env BU_DAEMON_SOCKET).`);
+ lines.push(`You are connected to an external browser via CDP WebSocket (env BU_CDP_WS).`);
Fix with Cubic

} else {
lines.push(`Your target is CDP target_id=${ctx.targetId} on port ${ctx.cdpPort} (env BU_TARGET_ID / BU_CDP_PORT).`);
}
lines.push(
'Read `./AGENTS.md` for how to drive the browser in this harness.',
'Always read `./helpers.js` before writing scripts — that is where the functions live. Edit it if a helper is missing.',
];
);
if (ctx.attachmentRefs.length > 0) {
lines.push('', 'The user attached these files for this task. Read each with your Read tool before acting:');
for (const a of ctx.attachmentRefs) lines.push(` - ${a.relPath} (${a.mime}, ${a.size} bytes)`);
Expand Down Expand Up @@ -176,8 +182,12 @@ const claudeCodeAdapter: EngineAdapter = {
delete env.CLAUDE_CODE_USE_VERTEX;
delete env.CLAUDE_CODE_USE_FOUNDRY;
if (ctx.savedApiKey) env.ANTHROPIC_API_KEY = ctx.savedApiKey;
env.BU_TARGET_ID = ctx.targetId;
env.BU_CDP_PORT = String(ctx.cdpPort);
if (ctx.cdpUrl) {
env.BU_CDP_WS = ctx.cdpUrl;
} else {
env.BU_TARGET_ID = ctx.targetId;
env.BU_CDP_PORT = String(ctx.cdpPort);
}
return env;
},

Expand Down
18 changes: 14 additions & 4 deletions app/src/main/hl/engines/codex/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,16 @@
wrapPrompt(ctx: SpawnContext): string {
const lines: string[] = [
'You are driving a specific Chromium browser view on this machine.',
`Your target is CDP target_id=${ctx.targetId} on port ${ctx.cdpPort} (env BU_TARGET_ID / BU_CDP_PORT).`,
];
if (ctx.cdpUrl) {
lines.push(`You are connected to an external browser via CDP (env BU_DAEMON_SOCKET).`);
} else {
lines.push(`Your target is CDP target_id=${ctx.targetId} on port ${ctx.cdpPort} (env BU_TARGET_ID / BU_CDP_PORT).`);
}
lines.push(
'Read `./AGENTS.md` for how to drive the browser in this harness.',
'Always read `./helpers.js` before writing scripts — that is where the functions live. Edit it if a helper is missing.',
];
);
if (ctx.attachmentRefs.length > 0) {
lines.push('', 'The user attached these files for this task. Read each one before acting:');
for (const a of ctx.attachmentRefs) lines.push(` - ${a.relPath} (${a.mime}, ${a.size} bytes)`);
Expand Down Expand Up @@ -176,8 +182,12 @@
// Prefer CODEX_API_KEY — the docs recommend it for `codex exec` mode.
env.CODEX_API_KEY = ctx.savedApiKey;
}
env.BU_TARGET_ID = ctx.targetId;
env.BU_CDP_PORT = String(ctx.cdpPort);
if (ctx.cdpUrl) {
env.BU_CDP_WS = ctx.cdpUrl;
} else {
env.BU_TARGET_ID = ctx.targetId;
env.BU_CDP_PORT = String(ctx.cdpPort);
}
return env;
},

Expand All @@ -189,7 +199,7 @@
const type = e.type as string | undefined;
const events: HlEvent[] = [];
let capturedSessionId: string | undefined;
let terminalDone = false;

Check warning on line 202 in app/src/main/hl/engines/codex/adapter.ts

View workflow job for this annotation

GitHub Actions / Lint (TS)

'terminalDone' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 202 in app/src/main/hl/engines/codex/adapter.ts

View workflow job for this annotation

GitHub Actions / Lint (TS)

'terminalDone' is never reassigned. Use 'const' instead
let terminalError: string | undefined;

if (type === 'thread.started') {
Expand Down
30 changes: 22 additions & 8 deletions app/src/main/hl/engines/runEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,21 @@ export async function runEngine(opts: RunEngineOptions): Promise<void> {
return;
}

// 1. Resolve CDP target for the session's browser view.
// 1. Resolve CDP target for the session's browser view (or external CDP).
let targetId: string;
try {
targetId = await resolveTargetIdForWebContents(opts.webContents);
} catch (err) {
const msg = `Failed to resolve CDP target id: ${(err as Error).message}`;
engineLogger.error('engines.run.resolveTarget.failed', { engineId: opts.engineId, error: msg });
opts.onEvent({ type: 'error', message: msg });
if (opts.cdpUrl) {
targetId = 'external';
} else if (opts.webContents) {
try {
targetId = await resolveTargetIdForWebContents(opts.webContents);
} catch (err) {
const msg = `Failed to resolve CDP target id: ${(err as Error).message}`;
engineLogger.error('engines.run.resolveTarget.failed', { engineId: opts.engineId, error: msg });
opts.onEvent({ type: 'error', message: msg });
return;
}
} else {
opts.onEvent({ type: 'error', message: 'runEngine: must provide webContents or cdpUrl' });
return;
}

Expand Down Expand Up @@ -178,13 +185,18 @@ export async function runEngine(opts: RunEngineOptions): Promise<void> {
sessionId: opts.sessionId,
targetId,
cdpPort: opts.cdpPort,
cdpUrl: opts.cdpUrl,
resumeSessionId: opts.resumeSessionId,
savedApiKey,
attachmentRefs,
};
const wrappedPrompt = adapter.wrapPrompt(spawnCtx);
const args = adapter.buildSpawnArgs(spawnCtx, wrappedPrompt);
const env = adapter.buildEnv(spawnCtx, { ...process.env });
if (opts.daemonSocket) {
env.BU_DAEMON_SOCKET = opts.daemonSocket;
try { fs.writeFileSync(path.join(opts.harnessDir, 'DAEMON_SOCKET'), opts.daemonSocket, 'utf-8'); } catch { /* noop */ }
}

engineLogger.info('engines.run.spawn', {
engineId: adapter.id,
Expand All @@ -196,11 +208,13 @@ export async function runEngine(opts: RunEngineOptions): Promise<void> {
attachmentCount: attachmentRefs.length,
authSource: savedApiKey ? 'savedApiKey' : 'cliManaged',
args: args.map((a) => (a.length > 120 ? `${a.slice(0, 100)}…<${a.length}ch>` : a)),
envAuthFlags: {
envFlags: {
ANTHROPIC_API_KEY: env.ANTHROPIC_API_KEY ? `set(${env.ANTHROPIC_API_KEY.length}ch)` : 'unset',
ANTHROPIC_AUTH_TOKEN: env.ANTHROPIC_AUTH_TOKEN ? 'set' : 'unset',
CLAUDE_CODE_USE_BEDROCK: env.CLAUDE_CODE_USE_BEDROCK ?? 'unset',
CLAUDE_CODE_USE_VERTEX: env.CLAUDE_CODE_USE_VERTEX ?? 'unset',
BU_DAEMON_SOCKET: env.BU_DAEMON_SOCKET ? `set(${env.BU_DAEMON_SOCKET.length}ch)` : 'unset',
BU_CDP_WS: env.BU_CDP_WS ? `set(${env.BU_CDP_WS.length}ch)` : 'unset',
},
});

Expand Down
7 changes: 6 additions & 1 deletion app/src/main/hl/engines/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export interface SpawnContext {
targetId: string;
/** Port Electron exposes CDP on. */
cdpPort: number;
/** If set, connect to an existing browser via CDP WebSocket instead of the embedded view. */
cdpUrl?: string;
/** If set, ask the CLI to continue a prior conversation with this id. */
resumeSessionId?: string;
/** Optional user-supplied API key; adapter decides how to inject. */
Expand Down Expand Up @@ -119,8 +121,11 @@ export interface RunEngineOptions {
engineId: string;
prompt: string;
sessionId: string;
webContents: WebContents;
webContents?: WebContents;
cdpPort: number;
cdpUrl?: string;
/** If set, reuse an existing daemon socket instead of starting a new one. */
daemonSocket?: string;
harnessDir: string;
attachments?: Array<{ name: string; mime: string; bytes: Buffer | Uint8Array }>;
resumeSessionId?: string;
Expand Down
19 changes: 18 additions & 1 deletion app/src/main/hl/harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { mainLogger } from '../logger';
import STOCK_HELPERS_JS from './stock/helpers.js?raw';
import STOCK_TOOLS_JSON from './stock/TOOLS.json?raw';
import STOCK_SKILL_MD from './stock/AGENTS.md?raw';
import STOCK_DAEMON_JS from './stock/daemon.js?raw';

// Bundled domain-skills tree. Vite eagerly inlines every file under
// stock/domain-skills/ as a raw string at build time. Keys are the full
Expand All @@ -38,6 +39,7 @@ export function harnessDir(): string {
export function helpersPath(): string { return path.join(harnessDir(), 'helpers.js'); }
export function toolsPath(): string { return path.join(harnessDir(), 'TOOLS.json'); }
export function skillPath(): string { return path.join(harnessDir(), 'AGENTS.md'); }
export function daemonPath(): string { return path.join(harnessDir(), 'daemon.js'); }
export function domainSkillsDir(): string { return path.join(harnessDir(), 'domain-skills'); }

/**
Expand All @@ -61,7 +63,10 @@ export function bootstrapHarness(): void {

const hp = helpersPath();
const needsHelpers = !fs.existsSync(hp) || (() => {
try { return !fs.readFileSync(hp, 'utf-8').includes('createContext'); }
try {
const content = fs.readFileSync(hp, 'utf-8');
return !content.includes('createContext') || !content.includes('BU_CDP_WS');
}
catch { return true; }
})();
if (needsHelpers) {
Expand Down Expand Up @@ -90,6 +95,18 @@ export function bootstrapHarness(): void {
mainLogger.info('harness.bootstrap.wroteTools', { path: tp, bytes: (STOCK_TOOLS_JSON as string).length });
}

const dp = daemonPath();
const needsDaemon = !fs.existsSync(dp) || (() => {
try {
const content = fs.readFileSync(dp, 'utf-8');
return !content.includes('CdpWs');
} catch { return true; }
})();
if (needsDaemon) {
fs.writeFileSync(dp, STOCK_DAEMON_JS as string, 'utf-8');
mainLogger.info('harness.bootstrap.wroteDaemon', { path: dp, bytes: (STOCK_DAEMON_JS as string).length });
}

materializeDomainSkills();
}

Expand Down
Loading
Loading