{"record":{"id":"1c9c7823a5f35dc9","repo":"Hmbown/CodeWhale","slug":"foreground-consent-required","errorCode":"foreground_consent_required","errorMessage":"open_application activate:true would take this computer's shared pointer and focus — ask the user, then record their answer with consent {action:\"allow\"|\"deny\", scope:\"foreground\"}. Background control (activate:false) needs no such consent.","messagePattern":"open_application activate:true would take this computer's shared pointer and focus — ask the user, then record their answer with consent (.+?)\\. Background control \\(activate:false\\) needs no such consent\\.","errorType":"error_code","errorClass":"ServerError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/mcp/server.mjs","lineNumber":601,"sourceCode":"    if (verdict.state === \"undecided\") {\n      throw new ServerError(\"consent_required\",\n        `Codewhale needs the user's permission to use ${desc} on this computer — ask them, then record their answer with consent {action:\"allow\"|\"deny\", app:\"${arg}\"}.`,\n        { app: known });\n    }\n    grant = { ref: known, persisted: verdict.persisted === true };\n  }\n  // Taking the shared pointer/focus is a second, separate consent: the first\n  // activate:true is the moment the agent stops being background — on every\n  // platform, not just macOS.\n  if (name === \"open_application\" && args.activate === true) {\n    const fg = consent.foregroundDecision(computer.id);\n    if (fg.state === \"denied\") {\n      throw new ServerError(\"foreground_denied\",\n        `the user denied shared-desktop (foreground) control on this computer — continue with open_application activate:false (background control) or ask them to reconsider.`,\n        { scope: \"foreground\" });\n    }\n    if (fg.state === \"undecided\") {\n      throw new ServerError(\"foreground_consent_required\",\n        `open_application activate:true would take this computer's shared pointer and focus — ask the user, then record their answer with consent {action:\"allow\"|\"deny\", scope:\"foreground\"}. Background control (activate:false) needs no such consent.`,\n        { scope: \"foreground\" });\n    }\n  }\n  return grant ? { grant } : null;\n}\n\n// ---------- tool dispatch ----------\nasync function callTool(params) {\n  const requested = params.name;\n  if (!TOOL_NAMES.has(requested)) {\n    return { content: [{ type: \"text\", text: JSON.stringify({ ok: false, error: { code: \"unknown_tool\", message: `unknown tool \"${requested}\"` } }) }], isError: true };\n  }\n  // Merged tools (click, pointer, clipboard, recording, computer, key+duration)\n  // resolve to the wire tool they dispatch to before any gate below, so they\n  // cannot bypass required args, the kill switch or routing. Wire names stay\n  // callable as aliases.\n  let name = requested;","sourceCodeStart":583,"sourceCodeEnd":619,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/mcp/server.mjs#L583-L619","documentation":"Thrown when open_application is called with activate:true and the foreground-consent decision is \"undecided\" — the user has never allowed or denied shared-desktop control. Because activate:true would take the computer's shared pointer and focus, the server requires an explicit recorded decision first and explains the consent call syntax; background control (activate:false) is explicitly exempt.","triggerScenarios":"open_application with args.activate === true while consent.foregroundDecision(computer.id).state === \"undecided\" — i.e. no allow/deny recorded for scope:\"foreground\" on this computer.","commonSituations":"First foreground launch of an app in a new session; a script always requests activate:true without ever recording a foreground decision; persisted foreground consent absent on a new machine.","solutions":["Ask the user whether the agent may take the shared pointer/focus.","Record the answer: consent {action:\"allow\", scope:\"foreground\"} or {action:\"deny\", scope:\"foreground\"}.","Alternatively use activate:false, which needs no foreground consent.","Retry open_application activate:true after the decision is recorded."],"exampleFix":"// before: activate:true with no foreground decision\nawait callTool({ name: \"open_application\", arguments: { name: \"Firefox\", activate: true } }); // throws foreground_consent_required\n\n// after: ask, record, then activate\nawait callTool({ name: \"consent\", arguments: { action: \"allow\", scope: \"foreground\" } });\nawait callTool({ name: \"open_application\", arguments: { name: \"Firefox\", activate: true } });","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  return await callTool({ name: \"open_application\", arguments: { ...args, activate: true } });\n} catch (e) {\n  if (e.code === \"foreground_consent_required\") {\n    const ok = await askUser(\"May I take the mouse/keyboard focus?\");\n    await callTool({ name: \"consent\", arguments: { action: ok ? \"allow\" : \"deny\", scope: \"foreground\" } });\n    if (!ok) return callTool({ name: \"open_application\", arguments: { ...args, activate: false } });\n    return await callTool({ name: \"open_application\", arguments: { ...args, activate: true } });\n  }\n  throw e;\n}","preventionTips":["Record a foreground decision once at session start if the workflow needs activate:true.","Prefer activate:false by default; ask only when foreground focus is required.","Handle the error by asking the user — never by silently retrying activate:true."],"tags":["consent","foreground","user-permission","computer-use"],"backgroundTag":"authentication-required","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T16:17:23.217Z"}