{"record":{"id":"d47f0a116a3a5516","repo":"Hmbown/CodeWhale","slug":"consent-action-args-action-needs-an-app-name-bundle-id-pid","errorCode":null,"errorMessage":"consent action \"${args.action}\" needs an app (name, bundle_id, pid or app string) — or scope:\"foreground\" for the shared-pointer decision","messagePattern":"consent action \"(.+?)\" needs an app \\(name, bundle_id, pid or app string\\) — or scope:\"foreground\" for the shared-pointer decision","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/tools.mjs","lineNumber":818,"sourceCode":"      const rest = { ...args };\n      delete rest.action;\n      const wire = { list: \"computer_list\", switch: \"computer_switch\", register: \"computer_register\", spawn: \"computer_spawn\", remove: \"computer_remove\" }[args.action];\n      if (!wire) throw bad(`computer action must be list, switch, register, spawn or remove (got ${JSON.stringify(args.action)})`);\n      if (args.action === \"list\") return { name: wire, args: {} };\n      if (rest.id == null) throw bad(`computer action \"${args.action}\" requires id`);\n      const id = rest.id;\n      delete rest.id;\n      return { name: wire, args: { ...rest, computer: id } };\n    }\n    case \"consent\": {\n      const rest = { ...args };\n      delete rest.action;\n      const wire = { status: \"consent_status\", allow: \"consent_allow\", deny: \"consent_deny\", revoke: \"consent_revoke\" }[args.action];\n      if (!wire) throw bad(`consent action must be status, allow, deny or revoke (got ${JSON.stringify(args.action)})`);\n      if (args.action === \"status\") return { name: wire, args: { computer: rest.computer } };\n      const foreground = rest.scope === \"foreground\";\n      if (!foreground && rest.app == null && rest.name == null && rest.bundle_id == null && rest.pid == null) {\n        throw bad(`consent action \"${args.action}\" needs an app (name, bundle_id, pid or app string) — or scope:\"foreground\" for the shared-pointer decision`);\n      }\n      return { name: wire, args: rest };\n    }\n    case \"key\": {\n      if (args.duration == null) return { name, args };\n      const { duration, repeat, target, ...rest } = args;\n      if (repeat != null || target != null) throw bad(\"key with duration holds the key — repeat and target cannot be combined with it\");\n      if (!Number.isFinite(duration) || duration < 0.05 || duration > 30) throw bad(\"duration must be 0.05..30 seconds\");\n      return { name: \"hold_key\", args: { ...rest, duration } };\n    }\n    case \"browser\": {\n      const rest = { ...args };\n      delete rest.action;\n      switch (args.action) {\n        case \"start\":\n          if (rest.url != null && typeof rest.url !== \"string\") throw bad(\"browser start url must be a string\");\n          return { name: \"browser_start\", args: rest };\n        case \"status\": return { name: \"browser_status\", args: rest };","sourceCodeStart":800,"sourceCodeEnd":836,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/tools.mjs#L800-L836","documentation":"For consent actions allow/deny/revoke, the target app must be identified: either scope:\"foreground\" (the shared-pointer decision) or one of app, name, bundle_id, or pid. When none is supplied the resolver throws this descriptive error rather than sending an untargeted consent change.","triggerScenarios":"resolveTool(\"consent\", { action: \"allow\" }) with no app/name/bundle_id/pid and scope not \"foreground\"; scope misspelled as e.g. \"active\" so the foreground shortcut is not taken.","commonSituations":"A model wants to grant consent globally without naming an app (unsupported); scope:\"Foreground\" wrong case so it is not recognized; the app fields are put under a nested object instead of top-level.","solutions":["Add the target app identification: app (string), name, bundle_id, or pid","Set scope:\"foreground\" if the decision applies to the foreground/shared-pointer app","Fix scope casing to lowercase \"foreground\" if that was the intent"],"exampleFix":"// before\nresolveTool(\"consent\", { action: \"deny\", scope: \"Foreground\" })\n// after\nresolveTool(\"consent\", { action: \"deny\", scope: \"foreground\" }) // or add app: \"Notes\"","handlingStrategy":"validation","validationCode":"function consentTargeted(a) {\n  if (a.action === 'status') return true;\n  return a.scope === 'foreground' || a.app != null || a.name != null || a.bundle_id != null || a.pid != null;\n}\nif (!consentTargeted(args)) throw new Error('consent allow/deny/revoke needs app or scope:\"foreground\"');","typeGuard":"function hasConsentTarget(a) { return ['app','name','bundle_id','pid'].some(k => a[k] != null) || a.scope === 'foreground'; }","tryCatchPattern":"try { resolveTool('consent', args); } catch (e) { if (String(e.message).includes('needs an app')) { /* prompt for target app or set scope:'foreground' */ } else throw e; }","preventionTips":["Always identify the app (name/bundle_id/pid) or explicitly set scope:\"foreground\" (lowercase)","Remember consent changes are per-app; there is no global grant","Keep app identification fields at the top level of args"],"tags":["validation","required-field","consent"],"backgroundTag":"missing-required-argument","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"}