@ashx-j/lunr-dev 0.2.13-dev.5.1 → 0.2.13-dev.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builtin-extensions/ashxj-tui.d.ts.map +1 -1
- package/dist/builtin-extensions/ashxj-tui.js +4 -4
- package/dist/builtin-extensions/ashxj-tui.js.map +1 -1
- package/dist/builtin-extensions/index.d.ts.map +1 -1
- package/dist/builtin-extensions/index.js +2 -0
- package/dist/builtin-extensions/index.js.map +1 -1
- package/dist/builtin-extensions/lunr-settings-tools.d.ts +4 -0
- package/dist/builtin-extensions/lunr-settings-tools.d.ts.map +1 -0
- package/dist/builtin-extensions/lunr-settings-tools.js +120 -0
- package/dist/builtin-extensions/lunr-settings-tools.js.map +1 -0
- package/dist/builtin-extensions/pi-prompt-template-model/subagent-runtime.d.ts +2 -2
- package/dist/builtin-extensions/pi-prompt-template-model/subagent-runtime.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/subagent-runtime.js.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/subagent-step.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/subagent-step.js +3 -3
- package/dist/builtin-extensions/pi-prompt-template-model/subagent-step.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/api/delegation.d.ts +2 -2
- package/dist/builtin-extensions/pi-subagents/src/api/delegation.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/api/delegation.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/api/delegation.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js +2 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.d.ts +5 -11
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js +12 -15
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts +3 -3
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js +14 -27
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.js +6 -4
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.js +2 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.js +6 -4
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-status.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-status.js +4 -2
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-status.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-clarify.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-clarify.js +3 -35
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-clarify.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js +5 -12
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js +14 -31
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts +2 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js +28 -22
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts +1 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.js +9 -4
- package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/formatters.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/formatters.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/formatters.js +2 -2
- package/dist/builtin-extensions/pi-subagents/src/shared/formatters.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts +4 -7
- package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/types.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/types.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/delegation-adapters.d.ts +3 -3
- package/dist/builtin-extensions/pi-subagents/src/slash/delegation-adapters.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/delegation-adapters.js +7 -6
- package/dist/builtin-extensions/pi-subagents/src/slash/delegation-adapters.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/delegation-request.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/delegation-request.js +3 -3
- package/dist/builtin-extensions/pi-subagents/src/slash/delegation-request.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js +6 -4
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js +15 -10
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/fleet.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/fleet.js +3 -2
- package/dist/builtin-extensions/pi-subagents/src/tui/fleet.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +2 -2
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js +75 -43
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
- package/dist/catalog/models.json +1 -1
- package/dist/catalog/providers/amazon-bedrock.json +1 -1
- package/dist/catalog/providers/anthropic.json +1 -1
- package/dist/catalog/providers/cloudflare-ai-gateway.json +1 -1
- package/dist/catalog/providers/fireworks.json +1 -1
- package/dist/catalog/providers/github-copilot.json +1 -1
- package/dist/catalog/providers/google-vertex.json +1 -1
- package/dist/catalog/providers/google.json +1 -1
- package/dist/catalog/providers/huggingface.json +1 -1
- package/dist/catalog/providers/nvidia.json +1 -1
- package/dist/catalog/providers/opencode-go.json +1 -1
- package/dist/catalog/providers/opencode.json +1 -1
- package/dist/catalog/providers/openrouter.json +1 -1
- package/dist/catalog/providers/vercel-ai-gateway.json +1 -1
- package/dist/catalog/publication.json +3 -3
- package/dist/core/agent-session-runtime.d.ts +5 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +7 -2
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session.d.ts +2 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +20 -5
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/context-breakdown.d.ts +2 -0
- package/dist/core/context-breakdown.d.ts.map +1 -1
- package/dist/core/context-breakdown.js +6 -2
- package/dist/core/context-breakdown.js.map +1 -1
- package/dist/core/injected-prompt.d.ts +3 -3
- package/dist/core/injected-prompt.d.ts.map +1 -1
- package/dist/core/injected-prompt.js +2 -7
- package/dist/core/injected-prompt.js.map +1 -1
- package/dist/core/large-subagent-launch.d.ts +13 -0
- package/dist/core/large-subagent-launch.d.ts.map +1 -0
- package/dist/core/large-subagent-launch.js +54 -0
- package/dist/core/large-subagent-launch.js.map +1 -0
- package/dist/core/model-instructions.d.ts +24 -0
- package/dist/core/model-instructions.d.ts.map +1 -0
- package/dist/core/model-instructions.js +123 -0
- package/dist/core/model-instructions.js.map +1 -0
- package/dist/core/permissions.d.ts +11 -10
- package/dist/core/permissions.d.ts.map +1 -1
- package/dist/core/permissions.js +61 -45
- package/dist/core/permissions.js.map +1 -1
- package/dist/core/plan-mode.d.ts.map +1 -1
- package/dist/core/plan-mode.js +10 -1
- package/dist/core/plan-mode.js.map +1 -1
- package/dist/core/resource-loader.d.ts +3 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +13 -4
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/runtime-bridges.d.ts +4 -0
- package/dist/core/runtime-bridges.d.ts.map +1 -0
- package/dist/core/runtime-bridges.js +16 -0
- package/dist/core/runtime-bridges.js.map +1 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +1 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +17 -16
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +13 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +32 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/settings-tools-bridge.d.ts +35 -0
- package/dist/core/settings-tools-bridge.d.ts.map +1 -0
- package/dist/core/settings-tools-bridge.js +56 -0
- package/dist/core/settings-tools-bridge.js.map +1 -0
- package/dist/core/swarm.d.ts +0 -31
- package/dist/core/swarm.d.ts.map +1 -1
- package/dist/core/swarm.js +2 -76
- package/dist/core/swarm.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +1 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/gateway/agent-bridge.d.ts.map +1 -1
- package/dist/gateway/agent-bridge.js +3 -2
- package/dist/gateway/agent-bridge.js.map +1 -1
- package/dist/gateway/approval.d.ts.map +1 -1
- package/dist/gateway/approval.js +4 -3
- package/dist/gateway/approval.js.map +1 -1
- package/dist/gateway/cron.d.ts.map +1 -1
- package/dist/gateway/cron.js +3 -2
- package/dist/gateway/cron.js.map +1 -1
- package/dist/gateway/router.d.ts +1 -1
- package/dist/gateway/router.d.ts.map +1 -1
- package/dist/gateway/router.js +1 -1
- package/dist/gateway/router.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +3 -9
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +4 -0
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/injected-prompt-message.d.ts +1 -1
- package/dist/modes/interactive/components/injected-prompt-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/injected-prompt-message.js +1 -1
- package/dist/modes/interactive/components/injected-prompt-message.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +8 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +61 -1
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +31 -23
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/startup/interactive-shell.d.ts.map +1 -1
- package/dist/startup/interactive-shell.js +2 -1
- package/dist/startup/interactive-shell.js.map +1 -1
- package/docs/features.md +7 -6
- package/docs/quickstart.md +3 -2
- package/docs/settings.md +3 -1
- package/docs/termux.md +1 -1
- package/docs/usage.md +2 -1
- package/examples/extensions/subagent/README.md +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../src/core/permissions.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR: permission modes (Manual / YOLO / Plan / Auto).\n *\n * Per-session only — never persisted per session, but the default mode new\n * sessions start with is configurable in /settings (`defaultPermissionMode`,\n * default \"manual\").\n *\n * - manual: every mutating tool call (bash, edit, write) requires approval.\n * - yolo: auto-approve tools; the agent may still ask questions.\n * - plan: read-only; mutating tools are hard-blocked (planModeBlockReason).\n * - auto: fully autonomous; a system-prompt addendum steers the model to\n * self-decide, and rollback is force-enabled for the session.\n *\n * The gate is wired into `agent-session.ts` `_installAgentToolHooks` before\n * the synchronous `_toolCallGates` loop. beforeToolCall is already async, so\n * awaiting the approval dialog is fine.\n *\n * State (mode + session-scoped approvals) is keyed by session id so\n * concurrent gateway chats do not share permission decisions.\n */\n\nimport { dirname, join, resolve } from \"node:path\";\nimport { getAgentDir } from \"../config.ts\";\nimport { isCodeRewriteMutating, planModeBlockReason } from \"./plan-mode.ts\";\nimport { effectiveSwarmCountForTurn, SWARM_APPROVAL_THRESHOLD } from \"./swarm.ts\";\n\n/** Shift+Tab cycle order. */\nexport const PERMISSION_MODES = [\"manual\", \"yolo\", \"plan\", \"auto\"] as const;\nexport type PermissionMode = (typeof PERMISSION_MODES)[number];\n\nexport function nextPermissionMode(current: PermissionMode): PermissionMode {\n\tconst i = PERMISSION_MODES.indexOf(current);\n\treturn PERMISSION_MODES[(i + 1) % PERMISSION_MODES.length];\n}\n\n/** True when the (default or session) permission mode is plan. */\nexport function isPlanModeActive(sessionId?: string): boolean {\n\treturn getPermissionMode(sessionId) === \"plan\";\n}\n\n/**\n * Destination mode when leaving plan via approve / `/plan off` / `/plan <text>`.\n * Explicit Shift+Tab or `/mode` picks do not use this — they already chose a mode.\n */\nexport function restorePermissionModeAfterPlan(\n\tprevious: PermissionMode | undefined,\n\tdefaultMode: PermissionMode,\n): PermissionMode {\n\tif (previous && previous !== \"plan\") return previous;\n\treturn defaultMode === \"plan\" ? \"yolo\" : defaultMode;\n}\n\nexport interface ApprovalRequest {\n\ttoolName: string;\n\t/** \"bash\" (detail = command), \"edit\"/\"write\" (detail = path),\n\t * \"edit-outside\"/\"write-outside\" when path escapes cwd,\n\t * \"subagent-full\" for full-access children, or \"swarm\" for swarms. */\n\taction: string;\n\tdetail: string;\n\t/** \"swarm\" when this is the agent-swarm approval prompt, \"plan\" for the\n\t * present_plan plan-approval prompt — the UI renders a dedicated dialog\n\t * for each instead of the generic tool dialog. */\n\tkind?: \"swarm\" | \"plan\";\n}\n\nexport type ApprovalDecision = \"once\" | \"session\" | \"reject\";\n/** Handlers may return a bare decision string, a reject carrying user feedback\n * that becomes the block reason the model sees, or an approve carrying optional\n * feedback (plan approval with comments). */\nexport type ApprovalResponse =\n\t| ApprovalDecision\n\t| { decision: \"reject\"; feedback: string }\n\t| { decision: \"approve\"; feedback?: string };\n\n/** Read-only tools that never need approval. */\nconst READ_ONLY_TOOLS = new Set([\n\t\"read\",\n\t\"grep\",\n\t\"find\",\n\t\"ls\",\n\t\"web_search\",\n\t\"web_fetch\",\n\t\"fetch_content\",\n\t\"get_search_content\",\n\t\"sourcegraph\",\n\t\"repo_map\",\n\t\"symbol_outline\",\n\t\"read_block\",\n\t\"code_overview\",\n\t\"ast_search\",\n\t\"lsp_diagnostics\",\n\t\"lsp_hover\",\n\t\"lsp_definition\",\n\t\"lsp_references\",\n\t\"lsp_symbols\",\n\t\"lsp_code_actions\",\n\t\"lsp_completions\",\n\t\"memory_load\",\n]);\n\n/** Tools that mutate state and must be gated in manual mode. */\nconst MUTATING_TOOLS = new Set([\"bash\", \"edit\", \"write\", \"memory_add\", \"memory_remove\", \"cron\"]);\n\nconst REJECT_REASON = \"Rejected by user (permission mode: manual).\";\nexport const NO_HANDLER_REASON = \"Mutating tool blocked in manual mode: no approval channel available.\";\nexport const SWARM_REJECT_REASON = \"Agent swarm rejected by user.\";\nexport const NO_SWARM_HANDLER_REASON = \"Agent swarm blocked: no approval channel available.\";\n/** Session-approval keys for child launches. */\nconst SWARM_ACTION = \"swarm\";\nconst FULL_CHILD_ACTION = \"subagent-full\";\n\ninterface PermissionContext {\n\tmode: PermissionMode;\n\tapprovals: Set<string>;\n}\n\nconst contexts = new Map<string, PermissionContext>();\nlet defaultContext: PermissionContext = { mode: \"manual\", approvals: new Set() };\nlet approvalHandler: ((req: ApprovalRequest) => Promise<ApprovalResponse>) | undefined;\n/** One swarm prompt covers every sibling SINGLE `subagent` on the same assistant message. */\nconst turnSwarmDecisions = new WeakMap<\n\tobject,\n\t{ decision: ApprovalDecision; feedback?: string; fullChildrenApproved: boolean }\n>();\n\nfunction getContext(sessionId?: string): PermissionContext {\n\tif (!sessionId) return defaultContext;\n\treturn contexts.get(sessionId) ?? defaultContext;\n}\n\nexport function createPermissionContext(sessionId: string, mode: PermissionMode = defaultContext.mode): void {\n\tcontexts.set(sessionId, { mode, approvals: new Set() });\n}\n\nexport function deletePermissionContext(sessionId: string): void {\n\tcontexts.delete(sessionId);\n}\n\nexport function getPermissionMode(sessionId?: string): PermissionMode {\n\treturn getContext(sessionId).mode;\n}\n\nexport function setPermissionMode(mode: PermissionMode, sessionId?: string): void {\n\tgetContext(sessionId).mode = mode;\n}\n\nexport function resetPermissions(defaultMode: PermissionMode = \"manual\", sessionId?: string): void {\n\tif (sessionId) {\n\t\tcontexts.set(sessionId, { mode: defaultMode, approvals: new Set() });\n\t} else {\n\t\tdefaultContext = { mode: defaultMode, approvals: new Set() };\n\t}\n}\n\nexport function registerApprovalHandler(fn: ((req: ApprovalRequest) => Promise<ApprovalResponse>) | undefined): void {\n\tapprovalHandler = fn;\n}\n\n/** Model-facing result texts for the present_plan tool. */\nexport const PLAN_APPROVED_TEXT = \"Plan approved — implement it now.\";\nexport const PLAN_DECLINED_TEXT = \"Plan declined — revise the plan and call present_plan again.\";\nexport const PLAN_PASS_THROUGH_TEXT = \"Plan presented. The user will review and reply.\";\n\n/** lunr: map an approval response to the model-facing present_plan result text. */\nexport function planApprovalResultText(resp: ApprovalResponse): string {\n\tconst decision = typeof resp === \"string\" ? resp : resp.decision;\n\tconst feedback = typeof resp === \"string\" ? undefined : resp.feedback?.trim();\n\tif (decision === \"reject\") {\n\t\treturn feedback\n\t\t\t? `Plan declined: ${feedback} — revise the plan and call present_plan again.`\n\t\t\t: PLAN_DECLINED_TEXT;\n\t}\n\treturn feedback ? `Plan approved with feedback: ${feedback} — implement it now.` : PLAN_APPROVED_TEXT;\n}\n\n/**\n * lunr: present_plan approval request. Unlike the mutating-tool and swarm gates\n * this fails OPEN when no approval handler is registered (or none is reachable,\n * e.g. a gateway turn without a chat context): headless sessions have no one to\n * show the dialog to, so the plan is presented and the user replies in chat\n * instead of deadlocking the turn. Everything else keeps fail-closed.\n */\nexport async function requestPlanApproval(summary: string): Promise<string> {\n\tif (!approvalHandler) return PLAN_PASS_THROUGH_TEXT;\n\tlet resp: ApprovalResponse;\n\ttry {\n\t\tresp = await approvalHandler({ toolName: \"present_plan\", action: \"plan\", detail: summary, kind: \"plan\" });\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : \"approval request failed\";\n\t\tif (message === NO_HANDLER_REASON) return PLAN_PASS_THROUGH_TEXT;\n\t\treturn `Plan declined: ${message} — revise the plan and call present_plan again.`;\n\t}\n\treturn planApprovalResultText(resp);\n}\n\nexport function clearSessionApprovals(sessionId?: string): void {\n\tgetContext(sessionId).approvals.clear();\n}\n\n/** Test seam: remove every per-session context and restore the default. */\nexport function resetAllPermissionContexts(): void {\n\tcontexts.clear();\n\tdefaultContext = { mode: \"manual\", approvals: new Set() };\n\tapprovalHandler = undefined;\n}\n\n/** Appended to the system prompt while auto mode is active. */\nexport const AUTO_MODE_ADDENDUM =\n\t\"You are running fully autonomously. Do not ask the user questions or wait for confirmation; make reasonable decisions and complete the task end-to-end.\";\n\nfunction isPathOutsideCwd(absPath: string, cwd: string): boolean {\n\tconst norm = (p: string) => p.replace(/\\\\/g, \"/\").replace(/\\/$/, \"\");\n\tconst np = norm(absPath);\n\tconst nc = norm(cwd);\n\tif (np === nc) return false;\n\treturn !np.startsWith(`${nc}/`);\n}\n\nfunction resolvePath(cwd: string, p: unknown): string {\n\tif (typeof p !== \"string\") return \"\";\n\tif (!p) return \"\";\n\treturn resolve(cwd, p);\n}\n\nexport const GLOBAL_AGENTS_FILE_WRITE_BLOCK_REASON =\n\t\"The global AGENTS.md file is user-managed. The agent cannot change ~/.lunr/agent/AGENTS.md.\";\nexport const MEMORY_FILE_DIRECT_WRITE_BLOCK_REASON =\n\t\"Memory is model-managed through the memory tools. Do not directly change ~/.lunr/simple-memory/memory.md.\";\n\nfunction protectedFileWriteReason(toolName: string, input: Record<string, unknown>, cwd: string): string | undefined {\n\tif (toolName !== \"edit\" && toolName !== \"write\" && toolName !== \"code_rewrite\") return undefined;\n\tconst target = resolvePath(cwd, input.path);\n\tif (!target) return undefined;\n\tconst normalize = (path: string) => {\n\t\tconst normalized = resolve(path).replace(/\\\\/g, \"/\").replace(/\\/$/, \"\");\n\t\treturn process.platform === \"win32\" ? normalized.toLowerCase() : normalized;\n\t};\n\tconst agentsPaths = [join(getAgentDir(), \"AGENTS.md\"), join(getAgentDir(), \"AGENTS.MD\")];\n\tif (agentsPaths.some((candidate) => normalize(target) === normalize(candidate))) {\n\t\treturn GLOBAL_AGENTS_FILE_WRITE_BLOCK_REASON;\n\t}\n\tif (normalize(target) === normalize(join(dirname(getAgentDir()), \"simple-memory\", \"memory.md\"))) {\n\t\treturn MEMORY_FILE_DIRECT_WRITE_BLOCK_REASON;\n\t}\n\treturn undefined;\n}\n\nfunction isMutatingTool(toolName: string): boolean {\n\treturn MUTATING_TOOLS.has(toolName);\n}\n\ninterface RequestedChildLaunch {\n\tdescription: string;\n\tpermissions: \"full\" | \"read-only\";\n}\n\nfunction collectRequestedChildLaunches(value: unknown, launches: RequestedChildLaunch[]): void {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) return;\n\tconst input = value as Record<string, unknown>;\n\tif (typeof input.task === \"string\" && input.task.trim()) {\n\t\tlaunches.push({\n\t\t\tdescription:\n\t\t\t\ttypeof input.description === \"string\" && input.description.trim()\n\t\t\t\t\t? input.description.trim()\n\t\t\t\t\t: input.task.trim(),\n\t\t\tpermissions: input.permissions === \"read-only\" ? \"read-only\" : \"full\",\n\t\t});\n\t\treturn;\n\t}\n\tfor (const key of [\"tasks\", \"chain\", \"parallel\"] as const) {\n\t\tconst children = input[key];\n\t\tif (Array.isArray(children)) {\n\t\t\tfor (const child of children) collectRequestedChildLaunches(child, launches);\n\t\t} else if (key === \"parallel\") {\n\t\t\tcollectRequestedChildLaunches(children, launches);\n\t\t}\n\t}\n}\n\nfunction getRequestedChildLaunches(input: Record<string, unknown>): RequestedChildLaunch[] {\n\tconst launches: RequestedChildLaunch[] = [];\n\tcollectRequestedChildLaunches(input, launches);\n\treturn launches;\n}\n\nfunction requiresManualApproval(toolName: string, input: Record<string, unknown>): boolean {\n\tif (isMutatingTool(toolName)) return true;\n\tif (toolName === \"subagent\") {\n\t\treturn getRequestedChildLaunches(input).some((launch) => launch.permissions === \"full\");\n\t}\n\treturn toolName === \"code_rewrite\" && isCodeRewriteMutating(input);\n}\n\nexport interface GateOptions {\n\t/** True when the current turn started from an explicit /swarm prompt — the\n\t * user already asked for the swarm, so the swarm approval gate is skipped. */\n\texplicitSwarmTurn?: boolean;\n\t/** Assistant message that issued this tool call. Same-turn sibling SINGLE\n\t * `subagent` calls count toward the swarm threshold. */\n\tassistantMessage?: {\n\t\tcontent?: ReadonlyArray<{ type?: string; name?: string; arguments?: unknown }>;\n\t};\n}\n\nfunction firstTaskText(value: unknown): string {\n\tif (typeof value === \"string\" && value.trim()) return sanitizeDetail(value);\n\treturn \"\";\n}\n\n/** First task text of a parallel fan-out or sibling SINGLE, used as the dialog summary. */\nfunction swarmTaskSummary(input: Record<string, unknown>, assistantMessage?: GateOptions[\"assistantMessage\"]): string {\n\tif (Array.isArray(input.tasks)) {\n\t\tfor (const task of input.tasks) {\n\t\t\tconst text = task && typeof task === \"object\" ? firstTaskText((task as { task?: unknown }).task) : \"\";\n\t\t\tif (text) return text;\n\t\t}\n\t}\n\tconst own = firstTaskText(input.task);\n\tif (own) return own;\n\tfor (const block of assistantMessage?.content ?? []) {\n\t\tif (block.type !== \"toolCall\" || block.name !== \"subagent\") continue;\n\t\tconst args =\n\t\t\tblock.arguments && typeof block.arguments === \"object\" && !Array.isArray(block.arguments)\n\t\t\t\t? (block.arguments as Record<string, unknown>)\n\t\t\t\t: undefined;\n\t\tconst text = firstTaskText(args?.task);\n\t\tif (text) return text;\n\t}\n\treturn \"\";\n}\n\n/**\n * Agent-swarm gate. An auto-activated swarm (more than SWARM_APPROVAL_THRESHOLD\n * parallel subagents in one `tasks`/`chain.parallel` call, or that many same-turn\n * SINGLE `subagent` calls) requires user approval in manual AND yolo modes; auto\n * mode runs it unconditionally and explicit /swarm turns are pre-approved.\n * Fail-closed without an approval handler, same as the mutating-tool gate.\n */\nasync function gateSwarmCall(\n\tinput: Record<string, unknown>,\n\tctx: PermissionContext,\n\toptions?: GateOptions,\n): Promise<{ block: true; reason: string } | { fullChildrenApproved: boolean } | undefined> {\n\tif (ctx.mode === \"auto\") return undefined;\n\tif (options?.explicitSwarmTurn) return undefined;\n\tconst count = effectiveSwarmCountForTurn(input, options?.assistantMessage);\n\tif (count <= SWARM_APPROVAL_THRESHOLD) return undefined;\n\tif (ctx.approvals.has(SWARM_ACTION)) return { fullChildrenApproved: false };\n\n\tconst turnKey = options?.assistantMessage;\n\tconst cached = turnKey ? turnSwarmDecisions.get(turnKey) : undefined;\n\tif (cached) {\n\t\tif (cached.decision === \"reject\") {\n\t\t\treturn {\n\t\t\t\tblock: true,\n\t\t\t\treason: cached.feedback ? `${SWARM_REJECT_REASON} ${cached.feedback}` : SWARM_REJECT_REASON,\n\t\t\t};\n\t\t}\n\t\treturn { fullChildrenApproved: cached.fullChildrenApproved };\n\t}\n\n\tif (!approvalHandler) {\n\t\treturn { block: true, reason: NO_SWARM_HANDLER_REASON };\n\t}\n\n\tconst summary = swarmTaskSummary(input, options?.assistantMessage);\n\tconst siblingLaunches = (options?.assistantMessage?.content ?? []).flatMap((block) => {\n\t\tif (block.type !== \"toolCall\" || block.name !== \"subagent\") return [];\n\t\tconst args =\n\t\t\tblock.arguments && typeof block.arguments === \"object\" && !Array.isArray(block.arguments)\n\t\t\t\t? (block.arguments as Record<string, unknown>)\n\t\t\t\t: undefined;\n\t\treturn args ? getRequestedChildLaunches(args) : [];\n\t});\n\tconst launches = siblingLaunches.length > 0 ? siblingLaunches : getRequestedChildLaunches(input);\n\tconst fullChildrenApproved = launches.some((launch) => launch.permissions === \"full\");\n\tconst launchSummary = launches\n\t\t.map((launch) => `${launch.description}\\npermissions: ${launch.permissions}`)\n\t\t.join(\"\\n\");\n\tlet resp: ApprovalResponse;\n\ttry {\n\t\tresp = await approvalHandler({\n\t\t\ttoolName: \"subagent\",\n\t\t\taction: SWARM_ACTION,\n\t\t\tdetail: `agent swarm (${count} subagents)${launchSummary || summary ? `\\n${launchSummary || summary}` : \"\"}`,\n\t\t\tkind: \"swarm\",\n\t\t});\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : NO_SWARM_HANDLER_REASON;\n\t\treturn { block: true, reason: message };\n\t}\n\n\tconst rawDecision = typeof resp === \"string\" ? resp : resp.decision;\n\tconst decision: ApprovalDecision = rawDecision === \"approve\" ? \"once\" : rawDecision;\n\tconst feedback = typeof resp === \"string\" ? undefined : resp.feedback?.trim();\n\tif (turnKey) {\n\t\tturnSwarmDecisions.set(turnKey, { decision, ...(feedback ? { feedback } : {}), fullChildrenApproved });\n\t}\n\tif (decision === \"session\") {\n\t\tctx.approvals.add(SWARM_ACTION);\n\t\tif (fullChildrenApproved) ctx.approvals.add(FULL_CHILD_ACTION);\n\t\treturn { fullChildrenApproved };\n\t}\n\tif (decision === \"reject\") {\n\t\treturn { block: true, reason: feedback ? `${SWARM_REJECT_REASON} ${feedback}` : SWARM_REJECT_REASON };\n\t}\n\treturn { fullChildrenApproved };\n}\n\nfunction sanitizeDetail(value: unknown): string {\n\tconst text = String(value ?? \"\").trim();\n\treturn text.length > 200 ? `${text.slice(0, 200)}…` : text;\n}\n\n/**\n * Returns a block result when the tool call should be blocked, else undefined.\n * Read-only tools always pass. YOLO and Auto always pass mutating tools. Plan\n * mode hard-blocks mutating tools (no prompt). Manual mode gates every mutating\n * tool; if an approval handler is registered it is asked, otherwise the call is\n * blocked (fail-closed — no silent allow).\n */\nexport async function gateToolCall(\n\ttoolName: string,\n\tinput: Record<string, unknown>,\n\tcwd: string,\n\tsessionId?: string,\n\toptions?: GateOptions,\n): Promise<{ block: true; reason: string } | undefined> {\n\tconst ctx = getContext(sessionId);\n\tconst protectedWriteReason = protectedFileWriteReason(toolName, input, cwd);\n\tif (protectedWriteReason) {\n\t\treturn { block: true, reason: protectedWriteReason };\n\t}\n\tif (READ_ONLY_TOOLS.has(toolName)) return undefined;\n\n\t// lunr: agent-swarm gate runs before the mode early-return — it applies in\n\t// yolo mode too (only auto mode bypasses it).\n\tlet swarmCoveredManualApproval = false;\n\tif (toolName === \"subagent\") {\n\t\tconst swarmResult = await gateSwarmCall(input, ctx, options);\n\t\tif (swarmResult && \"block\" in swarmResult) return swarmResult;\n\t\tswarmCoveredManualApproval = ctx.mode === \"manual\" && swarmResult?.fullChildrenApproved === true;\n\t}\n\n\tif (ctx.mode === \"plan\") {\n\t\tconst reason = planModeBlockReason(toolName, input);\n\t\treturn reason ? { block: true, reason } : undefined;\n\t}\n\n\tif (ctx.mode !== \"manual\") return undefined;\n\tif (!requiresManualApproval(toolName, input) || swarmCoveredManualApproval) return undefined;\n\n\tlet action: string;\n\tlet detail: string;\n\n\tif (toolName === \"bash\") {\n\t\taction = \"bash\";\n\t\tdetail = sanitizeDetail(input.command);\n\t} else if (toolName === \"edit\" || toolName === \"write\") {\n\t\tconst path = resolvePath(cwd, input.path);\n\t\tconst outside = path ? isPathOutsideCwd(path, cwd) : false;\n\t\taction = outside ? `${toolName}-outside` : toolName;\n\t\tdetail = path || String(input.path ?? \"\");\n\t} else if (toolName === \"memory_add\") {\n\t\taction = toolName;\n\t\tdetail = sanitizeDetail(input.content);\n\t} else if (toolName === \"memory_remove\") {\n\t\taction = toolName;\n\t\tdetail = sanitizeDetail(input.line);\n\t} else if (toolName === \"cron\") {\n\t\taction = \"cron\";\n\t\tdetail = sanitizeDetail(input.action);\n\t} else if (toolName === \"subagent\") {\n\t\taction = FULL_CHILD_ACTION;\n\t\tconst launches = getRequestedChildLaunches(input).filter((launch) => launch.permissions === \"full\");\n\t\tdetail = sanitizeDetail(\n\t\t\tlaunches.map((launch) => `${launch.description}\\npermissions: ${launch.permissions}`).join(\"\\n\"),\n\t\t);\n\t} else if (toolName === \"code_rewrite\") {\n\t\taction = \"code_rewrite\";\n\t\tdetail = sanitizeDetail(input.path ?? input.pattern);\n\t} else {\n\t\t// All other mutating tools fall back to the tool name with no extra detail.\n\t\taction = toolName;\n\t\tdetail = \"\";\n\t}\n\n\tif (ctx.approvals.has(action)) return undefined;\n\n\tif (!approvalHandler) {\n\t\treturn { block: true, reason: NO_HANDLER_REASON };\n\t}\n\n\tlet resp: ApprovalResponse;\n\ttry {\n\t\tresp = await approvalHandler({ toolName, action, detail });\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : NO_HANDLER_REASON;\n\t\treturn { block: true, reason: message };\n\t}\n\n\tconst decision = typeof resp === \"string\" ? resp : resp.decision;\n\tif (decision === \"session\") {\n\t\tctx.approvals.add(action);\n\t\treturn undefined;\n\t}\n\tif (decision === \"reject\") {\n\t\tconst feedback = typeof resp === \"string\" ? undefined : resp.feedback?.trim();\n\t\treturn { block: true, reason: feedback ? `${REJECT_REASON} ${feedback}` : REJECT_REASON };\n\t}\n\t// \"once\" — allow this call only\n\treturn undefined;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../src/core/permissions.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR: permission modes (Manual / YOLO / Plan / Auto).\n *\n * Per-session only — never persisted per session, but the default mode new\n * sessions start with is configurable in /settings (`defaultPermissionMode`,\n * default \"manual\").\n *\n * - manual: every mutating tool call (bash, edit, write) requires approval.\n * - yolo: auto-approve tools; the agent may still ask questions.\n * - plan: read-only; mutating tools are hard-blocked (planModeBlockReason).\n * - auto: fully autonomous; a system-prompt addendum steers the model to\n * self-decide, and rollback is force-enabled for the session.\n *\n * The gate is wired into `agent-session.ts` `_installAgentToolHooks` before\n * the synchronous `_toolCallGates` loop. beforeToolCall is already async, so\n * awaiting the approval dialog is fine.\n *\n * State (mode + session-scoped approvals) is keyed by session id so\n * concurrent gateway chats do not share permission decisions.\n */\n\nimport { dirname, join, resolve } from \"node:path\";\nimport { getAgentDir } from \"../config.ts\";\nimport { effectiveLargeSubagentLaunchCountForTurn, LARGE_SUBAGENT_LAUNCH_THRESHOLD } from \"./large-subagent-launch.ts\";\nimport { isUserInstructionsPath } from \"./model-instructions.ts\";\nimport { isCodeRewriteMutating, planModeBlockReason } from \"./plan-mode.ts\";\n\n/** Shift+Tab cycle order. */\nexport const PERMISSION_MODES = [\"manual\", \"yolo\", \"plan\", \"auto\"] as const;\nexport type PermissionMode = (typeof PERMISSION_MODES)[number];\n\nexport function nextPermissionMode(current: PermissionMode): PermissionMode {\n\tconst i = PERMISSION_MODES.indexOf(current);\n\treturn PERMISSION_MODES[(i + 1) % PERMISSION_MODES.length];\n}\n\n/** True when the (default or session) permission mode is plan. */\nexport function isPlanModeActive(sessionId?: string): boolean {\n\treturn getPermissionMode(sessionId) === \"plan\";\n}\n\n/**\n * Destination mode when leaving plan via approve / `/plan off` / `/plan <text>`.\n * Explicit Shift+Tab or `/mode` picks do not use this — they already chose a mode.\n */\nexport function restorePermissionModeAfterPlan(\n\tprevious: PermissionMode | undefined,\n\tdefaultMode: PermissionMode,\n): PermissionMode {\n\tif (previous && previous !== \"plan\") return previous;\n\treturn defaultMode === \"plan\" ? \"yolo\" : defaultMode;\n}\n\nexport interface ApprovalRequest {\n\ttoolName: string;\n\t/** \"bash\" (detail = command), \"edit\"/\"write\" (detail = path),\n\t * \"edit-outside\"/\"write-outside\" when path escapes cwd,\n\t * \"subagent-full\" for full-access children, or \"large-subagent-launch\" for aggregate confirmation. */\n\taction: string;\n\tdetail: string;\n\t/** \"large-subagent-launch\" for aggregate child confirmation, \"plan\" for the\n\t * present_plan plan-approval prompt — the UI renders a dedicated dialog\n\t * for each instead of the generic tool dialog. */\n\tkind?: \"large-subagent-launch\" | \"plan\";\n}\n\nexport type ApprovalDecision = \"once\" | \"session\" | \"reject\";\n/** Handlers may return a bare decision string, a reject carrying user feedback\n * that becomes the block reason the model sees, or an approve carrying optional\n * feedback (plan approval with comments). */\nexport type ApprovalResponse =\n\t| ApprovalDecision\n\t| { decision: \"reject\"; feedback: string }\n\t| { decision: \"approve\"; feedback?: string };\n\n/** Read-only tools that never need approval. */\nconst READ_ONLY_TOOLS = new Set([\n\t\"read\",\n\t\"grep\",\n\t\"find\",\n\t\"ls\",\n\t\"web_search\",\n\t\"web_fetch\",\n\t\"fetch_content\",\n\t\"get_search_content\",\n\t\"sourcegraph\",\n\t\"repo_map\",\n\t\"symbol_outline\",\n\t\"read_block\",\n\t\"code_overview\",\n\t\"ast_search\",\n\t\"lsp_diagnostics\",\n\t\"lsp_hover\",\n\t\"lsp_definition\",\n\t\"lsp_references\",\n\t\"lsp_symbols\",\n\t\"lsp_code_actions\",\n\t\"lsp_completions\",\n\t\"memory_load\",\n\t\"settings_load\",\n]);\n\n/** Tools that mutate state and must be gated in manual mode. */\nconst MUTATING_TOOLS = new Set([\"bash\", \"edit\", \"write\", \"memory_add\", \"memory_remove\", \"cron\"]);\n\nconst REJECT_REASON = \"Rejected by user (permission mode: manual).\";\nexport const NO_HANDLER_REASON = \"Mutating tool blocked in manual mode: no approval channel available.\";\nexport const LARGE_SUBAGENT_LAUNCH_REJECT_REASON = \"Large subagent launch rejected by user.\";\nexport const NO_LARGE_SUBAGENT_LAUNCH_HANDLER_REASON = \"Large subagent launch blocked: no approval channel available.\";\n/** Session-approval keys for child launches. */\nconst LARGE_SUBAGENT_LAUNCH_ACTION = \"large-subagent-launch\";\nconst FULL_CHILD_ACTION = \"subagent-full\";\n\ninterface PermissionContext {\n\tmode: PermissionMode;\n\tapprovals: Set<string>;\n}\n\nconst contexts = new Map<string, PermissionContext>();\nlet defaultContext: PermissionContext = { mode: \"manual\", approvals: new Set() };\nlet approvalHandler: ((req: ApprovalRequest) => Promise<ApprovalResponse>) | undefined;\n/** One aggregate prompt covers every sibling SINGLE `subagent` on the same assistant message. */\nconst turnLargeLaunchDecisions = new WeakMap<\n\tobject,\n\t{ decision: ApprovalDecision; feedback?: string; fullChildrenApproved: boolean }\n>();\n\nfunction getContext(sessionId?: string): PermissionContext {\n\tif (!sessionId) return defaultContext;\n\treturn contexts.get(sessionId) ?? defaultContext;\n}\n\nexport function createPermissionContext(sessionId: string, mode: PermissionMode = defaultContext.mode): void {\n\tcontexts.set(sessionId, { mode, approvals: new Set() });\n}\n\nexport function deletePermissionContext(sessionId: string): void {\n\tcontexts.delete(sessionId);\n}\n\nexport function getPermissionMode(sessionId?: string): PermissionMode {\n\treturn getContext(sessionId).mode;\n}\n\nexport function setPermissionMode(mode: PermissionMode, sessionId?: string): void {\n\tgetContext(sessionId).mode = mode;\n}\n\nexport function resetPermissions(defaultMode: PermissionMode = \"manual\", sessionId?: string): void {\n\tif (sessionId) {\n\t\tcontexts.set(sessionId, { mode: defaultMode, approvals: new Set() });\n\t} else {\n\t\tdefaultContext = { mode: defaultMode, approvals: new Set() };\n\t}\n}\n\nexport function registerApprovalHandler(fn: ((req: ApprovalRequest) => Promise<ApprovalResponse>) | undefined): void {\n\tapprovalHandler = fn;\n}\n\n/** Model-facing result texts for the present_plan tool. */\nexport const PLAN_APPROVED_TEXT = \"Plan approved — implement it now.\";\nexport const PLAN_DECLINED_TEXT = \"Plan declined — revise the plan and call present_plan again.\";\nexport const PLAN_PASS_THROUGH_TEXT = \"Plan presented. The user will review and reply.\";\n\n/** lunr: map an approval response to the model-facing present_plan result text. */\nexport function planApprovalResultText(resp: ApprovalResponse): string {\n\tconst decision = typeof resp === \"string\" ? resp : resp.decision;\n\tconst feedback = typeof resp === \"string\" ? undefined : resp.feedback?.trim();\n\tif (decision === \"reject\") {\n\t\treturn feedback\n\t\t\t? `Plan declined: ${feedback} — revise the plan and call present_plan again.`\n\t\t\t: PLAN_DECLINED_TEXT;\n\t}\n\treturn feedback ? `Plan approved with feedback: ${feedback} — implement it now.` : PLAN_APPROVED_TEXT;\n}\n\n/**\n * lunr: present_plan approval request. Unlike mutating-tool and aggregate launch gates\n * this fails OPEN when no approval handler is registered (or none is reachable,\n * e.g. a gateway turn without a chat context): headless sessions have no one to\n * show the dialog to, so the plan is presented and the user replies in chat\n * instead of deadlocking the turn. Everything else keeps fail-closed.\n */\nexport async function requestPlanApproval(summary: string): Promise<string> {\n\tif (!approvalHandler) return PLAN_PASS_THROUGH_TEXT;\n\tlet resp: ApprovalResponse;\n\ttry {\n\t\tresp = await approvalHandler({ toolName: \"present_plan\", action: \"plan\", detail: summary, kind: \"plan\" });\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : \"approval request failed\";\n\t\tif (message === NO_HANDLER_REASON) return PLAN_PASS_THROUGH_TEXT;\n\t\treturn `Plan declined: ${message} — revise the plan and call present_plan again.`;\n\t}\n\treturn planApprovalResultText(resp);\n}\n\nexport function clearSessionApprovals(sessionId?: string): void {\n\tgetContext(sessionId).approvals.clear();\n}\n\n/** Test seam: remove every per-session context and restore the default. */\nexport function resetAllPermissionContexts(): void {\n\tcontexts.clear();\n\tdefaultContext = { mode: \"manual\", approvals: new Set() };\n\tapprovalHandler = undefined;\n}\n\n/** Appended to the system prompt while auto mode is active. */\nexport const AUTO_MODE_ADDENDUM =\n\t\"You are running fully autonomously. Do not ask the user questions or wait for confirmation; make reasonable decisions and complete the task end-to-end.\";\n\nfunction isPathOutsideCwd(absPath: string, cwd: string): boolean {\n\tconst norm = (p: string) => p.replace(/\\\\/g, \"/\").replace(/\\/$/, \"\");\n\tconst np = norm(absPath);\n\tconst nc = norm(cwd);\n\tif (np === nc) return false;\n\treturn !np.startsWith(`${nc}/`);\n}\n\nfunction resolvePath(cwd: string, p: unknown): string {\n\tif (typeof p !== \"string\") return \"\";\n\tif (!p) return \"\";\n\treturn resolve(cwd, p);\n}\n\nexport const GLOBAL_AGENTS_FILE_WRITE_BLOCK_REASON =\n\t\"The agents instruction tree is user-managed. The agent cannot change ~/.lunr/agent/agents/.\";\nexport const MEMORY_FILE_DIRECT_WRITE_BLOCK_REASON =\n\t\"Memory is model-managed through the memory tools. Do not directly change ~/.lunr/simple-memory/memory.md.\";\n\nfunction protectedFileWriteReason(toolName: string, input: Record<string, unknown>, cwd: string): string | undefined {\n\tif (toolName !== \"edit\" && toolName !== \"write\" && toolName !== \"code_rewrite\") return undefined;\n\tconst target = resolvePath(cwd, input.path);\n\tif (!target) return undefined;\n\tconst normalize = (path: string) => {\n\t\tconst normalized = resolve(path).replace(/\\\\/g, \"/\").replace(/\\/$/, \"\");\n\t\treturn process.platform === \"win32\" ? normalized.toLowerCase() : normalized;\n\t};\n\tif (isUserInstructionsPath(target, getAgentDir())) {\n\t\treturn GLOBAL_AGENTS_FILE_WRITE_BLOCK_REASON;\n\t}\n\tif (normalize(target) === normalize(join(dirname(getAgentDir()), \"simple-memory\", \"memory.md\"))) {\n\t\treturn MEMORY_FILE_DIRECT_WRITE_BLOCK_REASON;\n\t}\n\treturn undefined;\n}\n\nfunction isMutatingTool(toolName: string): boolean {\n\treturn MUTATING_TOOLS.has(toolName);\n}\n\ninterface RequestedChildLaunch {\n\tdescription: string;\n\tpermissions: \"full\" | \"read-only\";\n}\n\nfunction collectRequestedChildLaunches(value: unknown, launches: RequestedChildLaunch[]): void {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) return;\n\tconst input = value as Record<string, unknown>;\n\tlet nested = false;\n\tfor (const key of [\"tasks\", \"chain\", \"parallel\"] as const) {\n\t\tconst children = input[key];\n\t\tif (Array.isArray(children)) {\n\t\t\tnested = true;\n\t\t\tfor (const child of children) collectRequestedChildLaunches(child, launches);\n\t\t} else if (key === \"parallel\" && children && typeof children === \"object\") {\n\t\t\tnested = true;\n\t\t\tcollectRequestedChildLaunches(children, launches);\n\t\t}\n\t}\n\tif (nested) return;\n\tconst task = typeof input.task === \"string\" ? input.task.trim() : \"\";\n\tconst description = typeof input.description === \"string\" ? input.description.trim() : \"\";\n\tconst hasPermissions = input.permissions === \"full\" || input.permissions === \"read-only\";\n\t// Chain steps may omit task (later steps default to {previous}) and still launch.\n\tif (!task && !description && !hasPermissions) return;\n\tlaunches.push({\n\t\tdescription: description || task || \"subagent\",\n\t\tpermissions: input.permissions === \"read-only\" ? \"read-only\" : \"full\",\n\t});\n}\n\nfunction getRequestedChildLaunches(input: Record<string, unknown>): RequestedChildLaunch[] {\n\tconst launches: RequestedChildLaunch[] = [];\n\tcollectRequestedChildLaunches(input, launches);\n\treturn launches;\n}\n\nfunction requiresManualApproval(toolName: string, input: Record<string, unknown>): boolean {\n\tif (isMutatingTool(toolName)) return true;\n\tif (toolName.startsWith(\"settings_\") && toolName !== \"settings_load\") return Object.keys(input).length > 0;\n\tif (toolName === \"subagent\") {\n\t\treturn getRequestedChildLaunches(input).some((launch) => launch.permissions === \"full\");\n\t}\n\treturn toolName === \"code_rewrite\" && isCodeRewriteMutating(input);\n}\n\nexport interface GateOptions {\n\t/** Global preference for aggregate confirmation. Defaults to enabled. */\n\tconfirmLargeSubagentLaunches?: boolean;\n\t/** True when the current turn started from an explicit /swarm prompt. */\n\texplicitSwarmTurn?: boolean;\n\t/** Assistant message that issued this tool call. Same-turn sibling SINGLE\n\t * `subagent` calls count toward the aggregate threshold. */\n\tassistantMessage?: {\n\t\tcontent?: ReadonlyArray<{ type?: string; name?: string; arguments?: unknown }>;\n\t};\n}\n\nfunction firstTaskText(value: unknown): string {\n\tif (typeof value === \"string\" && value.trim()) return sanitizeDetail(value);\n\treturn \"\";\n}\n\n/** First task text of a parallel fan-out or sibling SINGLE, used as the dialog summary. */\nfunction largeLaunchTaskSummary(\n\tinput: Record<string, unknown>,\n\tassistantMessage?: GateOptions[\"assistantMessage\"],\n): string {\n\tif (Array.isArray(input.tasks)) {\n\t\tfor (const task of input.tasks) {\n\t\t\tconst text = task && typeof task === \"object\" ? firstTaskText((task as { task?: unknown }).task) : \"\";\n\t\t\tif (text) return text;\n\t\t}\n\t}\n\tconst own = firstTaskText(input.task);\n\tif (own) return own;\n\tfor (const block of assistantMessage?.content ?? []) {\n\t\tif (block.type !== \"toolCall\" || block.name !== \"subagent\") continue;\n\t\tconst args =\n\t\t\tblock.arguments && typeof block.arguments === \"object\" && !Array.isArray(block.arguments)\n\t\t\t\t? (block.arguments as Record<string, unknown>)\n\t\t\t\t: undefined;\n\t\tconst text = firstTaskText(args?.task);\n\t\tif (text) return text;\n\t}\n\treturn \"\";\n}\n\n/**\n * Aggregate launch gate. A launch above LARGE_SUBAGENT_LAUNCH_THRESHOLD\n * parallel subagents in one `tasks`/`chain.parallel` call, or that many same-turn\n * SINGLE `subagent` calls) requires user approval in manual AND yolo modes; auto\n * mode runs it unconditionally.\n * Fail-closed without an approval handler, same as the mutating-tool gate.\n */\nasync function gateLargeSubagentLaunch(\n\tinput: Record<string, unknown>,\n\tctx: PermissionContext,\n\toptions?: GateOptions,\n): Promise<{ block: true; reason: string } | { fullChildrenApproved: boolean } | undefined> {\n\tif (ctx.mode === \"auto\") return undefined;\n\tif (options?.explicitSwarmTurn) return undefined;\n\tif (options?.confirmLargeSubagentLaunches === false) return undefined;\n\tconst count = effectiveLargeSubagentLaunchCountForTurn(input, options?.assistantMessage);\n\tif (count <= LARGE_SUBAGENT_LAUNCH_THRESHOLD) return undefined;\n\tif (ctx.approvals.has(LARGE_SUBAGENT_LAUNCH_ACTION)) return { fullChildrenApproved: false };\n\n\tconst turnKey = options?.assistantMessage;\n\tconst cached = turnKey ? turnLargeLaunchDecisions.get(turnKey) : undefined;\n\tif (cached) {\n\t\tif (cached.decision === \"reject\") {\n\t\t\treturn {\n\t\t\t\tblock: true,\n\t\t\t\treason: cached.feedback\n\t\t\t\t\t? `${LARGE_SUBAGENT_LAUNCH_REJECT_REASON} ${cached.feedback}`\n\t\t\t\t\t: LARGE_SUBAGENT_LAUNCH_REJECT_REASON,\n\t\t\t};\n\t\t}\n\t\treturn { fullChildrenApproved: cached.fullChildrenApproved };\n\t}\n\n\tif (!approvalHandler) {\n\t\treturn { block: true, reason: NO_LARGE_SUBAGENT_LAUNCH_HANDLER_REASON };\n\t}\n\n\tconst summary = largeLaunchTaskSummary(input, options?.assistantMessage);\n\tconst siblingLaunches = (options?.assistantMessage?.content ?? []).flatMap((block) => {\n\t\tif (block.type !== \"toolCall\" || block.name !== \"subagent\") return [];\n\t\tconst args =\n\t\t\tblock.arguments && typeof block.arguments === \"object\" && !Array.isArray(block.arguments)\n\t\t\t\t? (block.arguments as Record<string, unknown>)\n\t\t\t\t: undefined;\n\t\treturn args ? getRequestedChildLaunches(args) : [];\n\t});\n\tconst launches = siblingLaunches.length > 0 ? siblingLaunches : getRequestedChildLaunches(input);\n\tconst fullChildrenApproved = launches.some((launch) => launch.permissions === \"full\");\n\tconst launchSummary = launches\n\t\t.map((launch) => `${launch.description}\\npermissions: ${launch.permissions}`)\n\t\t.join(\"\\n\");\n\tlet resp: ApprovalResponse;\n\ttry {\n\t\tresp = await approvalHandler({\n\t\t\ttoolName: \"subagent\",\n\t\t\taction: LARGE_SUBAGENT_LAUNCH_ACTION,\n\t\t\tdetail: `large subagent launch (${count} children)${launchSummary || summary ? `\\n${launchSummary || summary}` : \"\"}`,\n\t\t\tkind: \"large-subagent-launch\",\n\t\t});\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : NO_LARGE_SUBAGENT_LAUNCH_HANDLER_REASON;\n\t\treturn { block: true, reason: message };\n\t}\n\n\tconst rawDecision = typeof resp === \"string\" ? resp : resp.decision;\n\tconst decision: ApprovalDecision = rawDecision === \"approve\" ? \"once\" : rawDecision;\n\tconst feedback = typeof resp === \"string\" ? undefined : resp.feedback?.trim();\n\tif (turnKey) {\n\t\tturnLargeLaunchDecisions.set(turnKey, { decision, ...(feedback ? { feedback } : {}), fullChildrenApproved });\n\t}\n\tif (decision === \"session\") {\n\t\tctx.approvals.add(LARGE_SUBAGENT_LAUNCH_ACTION);\n\t\tif (fullChildrenApproved) ctx.approvals.add(FULL_CHILD_ACTION);\n\t\treturn { fullChildrenApproved };\n\t}\n\tif (decision === \"reject\") {\n\t\treturn {\n\t\t\tblock: true,\n\t\t\treason: feedback ? `${LARGE_SUBAGENT_LAUNCH_REJECT_REASON} ${feedback}` : LARGE_SUBAGENT_LAUNCH_REJECT_REASON,\n\t\t};\n\t}\n\treturn { fullChildrenApproved };\n}\n\nfunction sanitizeDetail(value: unknown): string {\n\tconst text = String(value ?? \"\").trim();\n\treturn text.length > 200 ? `${text.slice(0, 200)}…` : text;\n}\n\n/**\n * Returns a block result when the tool call should be blocked, else undefined.\n * Read-only tools always pass. YOLO and Auto always pass mutating tools. Plan\n * mode hard-blocks mutating tools (no prompt). Manual mode gates every mutating\n * tool; if an approval handler is registered it is asked, otherwise the call is\n * blocked (fail-closed — no silent allow).\n */\nexport async function gateToolCall(\n\ttoolName: string,\n\tinput: Record<string, unknown>,\n\tcwd: string,\n\tsessionId?: string,\n\toptions?: GateOptions,\n): Promise<{ block: true; reason: string } | undefined> {\n\tconst ctx = getContext(sessionId);\n\tconst protectedWriteReason = protectedFileWriteReason(toolName, input, cwd);\n\tif (protectedWriteReason) {\n\t\treturn { block: true, reason: protectedWriteReason };\n\t}\n\tif (READ_ONLY_TOOLS.has(toolName)) return undefined;\n\n\t// Aggregate launch confirmation runs before the mode early-return, including YOLO.\n\t// yolo mode too (only auto mode bypasses it).\n\tlet largeLaunchCoveredManualApproval = false;\n\tif (toolName === \"subagent\") {\n\t\tconst launchResult = await gateLargeSubagentLaunch(input, ctx, options);\n\t\tif (launchResult && \"block\" in launchResult) return launchResult;\n\t\tlargeLaunchCoveredManualApproval = ctx.mode === \"manual\" && launchResult?.fullChildrenApproved === true;\n\t}\n\n\tif (ctx.mode === \"plan\") {\n\t\tconst reason = planModeBlockReason(toolName, input);\n\t\treturn reason ? { block: true, reason } : undefined;\n\t}\n\n\tif (ctx.mode !== \"manual\") return undefined;\n\tif (!requiresManualApproval(toolName, input) || largeLaunchCoveredManualApproval) return undefined;\n\n\tlet action: string;\n\tlet detail: string;\n\n\tif (toolName === \"bash\") {\n\t\taction = \"bash\";\n\t\tdetail = sanitizeDetail(input.command);\n\t} else if (toolName === \"edit\" || toolName === \"write\") {\n\t\tconst path = resolvePath(cwd, input.path);\n\t\tconst outside = path ? isPathOutsideCwd(path, cwd) : false;\n\t\taction = outside ? `${toolName}-outside` : toolName;\n\t\tdetail = path || String(input.path ?? \"\");\n\t} else if (toolName === \"memory_add\") {\n\t\taction = toolName;\n\t\tdetail = sanitizeDetail(input.content);\n\t} else if (toolName === \"memory_remove\") {\n\t\taction = toolName;\n\t\tdetail = sanitizeDetail(input.line);\n\t} else if (toolName === \"cron\") {\n\t\taction = \"cron\";\n\t\tdetail = sanitizeDetail(input.action);\n\t} else if (toolName === \"subagent\") {\n\t\taction = FULL_CHILD_ACTION;\n\t\tconst launches = getRequestedChildLaunches(input).filter((launch) => launch.permissions === \"full\");\n\t\tdetail = sanitizeDetail(\n\t\t\tlaunches.map((launch) => `${launch.description}\\npermissions: ${launch.permissions}`).join(\"\\n\"),\n\t\t);\n\t} else if (toolName === \"code_rewrite\") {\n\t\taction = \"code_rewrite\";\n\t\tdetail = sanitizeDetail(input.path ?? input.pattern);\n\t} else {\n\t\t// All other mutating tools fall back to the tool name with no extra detail.\n\t\taction = toolName;\n\t\tdetail = \"\";\n\t}\n\n\tif (ctx.approvals.has(action)) return undefined;\n\n\tif (!approvalHandler) {\n\t\treturn { block: true, reason: NO_HANDLER_REASON };\n\t}\n\n\tlet resp: ApprovalResponse;\n\ttry {\n\t\tresp = await approvalHandler({ toolName, action, detail });\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : NO_HANDLER_REASON;\n\t\treturn { block: true, reason: message };\n\t}\n\n\tconst decision = typeof resp === \"string\" ? resp : resp.decision;\n\tif (decision === \"session\") {\n\t\tctx.approvals.add(action);\n\t\treturn undefined;\n\t}\n\tif (decision === \"reject\") {\n\t\tconst feedback = typeof resp === \"string\" ? undefined : resp.feedback?.trim();\n\t\treturn { block: true, reason: feedback ? `${REJECT_REASON} ${feedback}` : REJECT_REASON };\n\t}\n\t// \"once\" — allow this call only\n\treturn undefined;\n}\n"]}
|
package/dist/core/permissions.js
CHANGED
|
@@ -20,8 +20,9 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import { dirname, join, resolve } from "node:path";
|
|
22
22
|
import { getAgentDir } from "../config.js";
|
|
23
|
+
import { effectiveLargeSubagentLaunchCountForTurn, LARGE_SUBAGENT_LAUNCH_THRESHOLD } from "./large-subagent-launch.js";
|
|
24
|
+
import { isUserInstructionsPath } from "./model-instructions.js";
|
|
23
25
|
import { isCodeRewriteMutating, planModeBlockReason } from "./plan-mode.js";
|
|
24
|
-
import { effectiveSwarmCountForTurn, SWARM_APPROVAL_THRESHOLD } from "./swarm.js";
|
|
25
26
|
/** Shift+Tab cycle order. */
|
|
26
27
|
export const PERMISSION_MODES = ["manual", "yolo", "plan", "auto"];
|
|
27
28
|
export function nextPermissionMode(current) {
|
|
@@ -65,21 +66,22 @@ const READ_ONLY_TOOLS = new Set([
|
|
|
65
66
|
"lsp_code_actions",
|
|
66
67
|
"lsp_completions",
|
|
67
68
|
"memory_load",
|
|
69
|
+
"settings_load",
|
|
68
70
|
]);
|
|
69
71
|
/** Tools that mutate state and must be gated in manual mode. */
|
|
70
72
|
const MUTATING_TOOLS = new Set(["bash", "edit", "write", "memory_add", "memory_remove", "cron"]);
|
|
71
73
|
const REJECT_REASON = "Rejected by user (permission mode: manual).";
|
|
72
74
|
export const NO_HANDLER_REASON = "Mutating tool blocked in manual mode: no approval channel available.";
|
|
73
|
-
export const
|
|
74
|
-
export const
|
|
75
|
+
export const LARGE_SUBAGENT_LAUNCH_REJECT_REASON = "Large subagent launch rejected by user.";
|
|
76
|
+
export const NO_LARGE_SUBAGENT_LAUNCH_HANDLER_REASON = "Large subagent launch blocked: no approval channel available.";
|
|
75
77
|
/** Session-approval keys for child launches. */
|
|
76
|
-
const
|
|
78
|
+
const LARGE_SUBAGENT_LAUNCH_ACTION = "large-subagent-launch";
|
|
77
79
|
const FULL_CHILD_ACTION = "subagent-full";
|
|
78
80
|
const contexts = new Map();
|
|
79
81
|
let defaultContext = { mode: "manual", approvals: new Set() };
|
|
80
82
|
let approvalHandler;
|
|
81
|
-
/** One
|
|
82
|
-
const
|
|
83
|
+
/** One aggregate prompt covers every sibling SINGLE `subagent` on the same assistant message. */
|
|
84
|
+
const turnLargeLaunchDecisions = new WeakMap();
|
|
83
85
|
function getContext(sessionId) {
|
|
84
86
|
if (!sessionId)
|
|
85
87
|
return defaultContext;
|
|
@@ -124,7 +126,7 @@ export function planApprovalResultText(resp) {
|
|
|
124
126
|
return feedback ? `Plan approved with feedback: ${feedback} — implement it now.` : PLAN_APPROVED_TEXT;
|
|
125
127
|
}
|
|
126
128
|
/**
|
|
127
|
-
* lunr: present_plan approval request. Unlike
|
|
129
|
+
* lunr: present_plan approval request. Unlike mutating-tool and aggregate launch gates
|
|
128
130
|
* this fails OPEN when no approval handler is registered (or none is reachable,
|
|
129
131
|
* e.g. a gateway turn without a chat context): headless sessions have no one to
|
|
130
132
|
* show the dialog to, so the plan is presented and the user replies in chat
|
|
@@ -171,7 +173,7 @@ function resolvePath(cwd, p) {
|
|
|
171
173
|
return "";
|
|
172
174
|
return resolve(cwd, p);
|
|
173
175
|
}
|
|
174
|
-
export const GLOBAL_AGENTS_FILE_WRITE_BLOCK_REASON = "The
|
|
176
|
+
export const GLOBAL_AGENTS_FILE_WRITE_BLOCK_REASON = "The agents instruction tree is user-managed. The agent cannot change ~/.lunr/agent/agents/.";
|
|
175
177
|
export const MEMORY_FILE_DIRECT_WRITE_BLOCK_REASON = "Memory is model-managed through the memory tools. Do not directly change ~/.lunr/simple-memory/memory.md.";
|
|
176
178
|
function protectedFileWriteReason(toolName, input, cwd) {
|
|
177
179
|
if (toolName !== "edit" && toolName !== "write" && toolName !== "code_rewrite")
|
|
@@ -183,8 +185,7 @@ function protectedFileWriteReason(toolName, input, cwd) {
|
|
|
183
185
|
const normalized = resolve(path).replace(/\\/g, "/").replace(/\/$/, "");
|
|
184
186
|
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
185
187
|
};
|
|
186
|
-
|
|
187
|
-
if (agentsPaths.some((candidate) => normalize(target) === normalize(candidate))) {
|
|
188
|
+
if (isUserInstructionsPath(target, getAgentDir())) {
|
|
188
189
|
return GLOBAL_AGENTS_FILE_WRITE_BLOCK_REASON;
|
|
189
190
|
}
|
|
190
191
|
if (normalize(target) === normalize(join(dirname(getAgentDir()), "simple-memory", "memory.md"))) {
|
|
@@ -199,25 +200,31 @@ function collectRequestedChildLaunches(value, launches) {
|
|
|
199
200
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
200
201
|
return;
|
|
201
202
|
const input = value;
|
|
202
|
-
|
|
203
|
-
launches.push({
|
|
204
|
-
description: typeof input.description === "string" && input.description.trim()
|
|
205
|
-
? input.description.trim()
|
|
206
|
-
: input.task.trim(),
|
|
207
|
-
permissions: input.permissions === "read-only" ? "read-only" : "full",
|
|
208
|
-
});
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
203
|
+
let nested = false;
|
|
211
204
|
for (const key of ["tasks", "chain", "parallel"]) {
|
|
212
205
|
const children = input[key];
|
|
213
206
|
if (Array.isArray(children)) {
|
|
207
|
+
nested = true;
|
|
214
208
|
for (const child of children)
|
|
215
209
|
collectRequestedChildLaunches(child, launches);
|
|
216
210
|
}
|
|
217
|
-
else if (key === "parallel") {
|
|
211
|
+
else if (key === "parallel" && children && typeof children === "object") {
|
|
212
|
+
nested = true;
|
|
218
213
|
collectRequestedChildLaunches(children, launches);
|
|
219
214
|
}
|
|
220
215
|
}
|
|
216
|
+
if (nested)
|
|
217
|
+
return;
|
|
218
|
+
const task = typeof input.task === "string" ? input.task.trim() : "";
|
|
219
|
+
const description = typeof input.description === "string" ? input.description.trim() : "";
|
|
220
|
+
const hasPermissions = input.permissions === "full" || input.permissions === "read-only";
|
|
221
|
+
// Chain steps may omit task (later steps default to {previous}) and still launch.
|
|
222
|
+
if (!task && !description && !hasPermissions)
|
|
223
|
+
return;
|
|
224
|
+
launches.push({
|
|
225
|
+
description: description || task || "subagent",
|
|
226
|
+
permissions: input.permissions === "read-only" ? "read-only" : "full",
|
|
227
|
+
});
|
|
221
228
|
}
|
|
222
229
|
function getRequestedChildLaunches(input) {
|
|
223
230
|
const launches = [];
|
|
@@ -227,6 +234,8 @@ function getRequestedChildLaunches(input) {
|
|
|
227
234
|
function requiresManualApproval(toolName, input) {
|
|
228
235
|
if (isMutatingTool(toolName))
|
|
229
236
|
return true;
|
|
237
|
+
if (toolName.startsWith("settings_") && toolName !== "settings_load")
|
|
238
|
+
return Object.keys(input).length > 0;
|
|
230
239
|
if (toolName === "subagent") {
|
|
231
240
|
return getRequestedChildLaunches(input).some((launch) => launch.permissions === "full");
|
|
232
241
|
}
|
|
@@ -238,7 +247,7 @@ function firstTaskText(value) {
|
|
|
238
247
|
return "";
|
|
239
248
|
}
|
|
240
249
|
/** First task text of a parallel fan-out or sibling SINGLE, used as the dialog summary. */
|
|
241
|
-
function
|
|
250
|
+
function largeLaunchTaskSummary(input, assistantMessage) {
|
|
242
251
|
if (Array.isArray(input.tasks)) {
|
|
243
252
|
for (const task of input.tasks) {
|
|
244
253
|
const text = task && typeof task === "object" ? firstTaskText(task.task) : "";
|
|
@@ -262,37 +271,41 @@ function swarmTaskSummary(input, assistantMessage) {
|
|
|
262
271
|
return "";
|
|
263
272
|
}
|
|
264
273
|
/**
|
|
265
|
-
*
|
|
274
|
+
* Aggregate launch gate. A launch above LARGE_SUBAGENT_LAUNCH_THRESHOLD
|
|
266
275
|
* parallel subagents in one `tasks`/`chain.parallel` call, or that many same-turn
|
|
267
276
|
* SINGLE `subagent` calls) requires user approval in manual AND yolo modes; auto
|
|
268
|
-
* mode runs it unconditionally
|
|
277
|
+
* mode runs it unconditionally.
|
|
269
278
|
* Fail-closed without an approval handler, same as the mutating-tool gate.
|
|
270
279
|
*/
|
|
271
|
-
async function
|
|
280
|
+
async function gateLargeSubagentLaunch(input, ctx, options) {
|
|
272
281
|
if (ctx.mode === "auto")
|
|
273
282
|
return undefined;
|
|
274
283
|
if (options?.explicitSwarmTurn)
|
|
275
284
|
return undefined;
|
|
276
|
-
|
|
277
|
-
|
|
285
|
+
if (options?.confirmLargeSubagentLaunches === false)
|
|
286
|
+
return undefined;
|
|
287
|
+
const count = effectiveLargeSubagentLaunchCountForTurn(input, options?.assistantMessage);
|
|
288
|
+
if (count <= LARGE_SUBAGENT_LAUNCH_THRESHOLD)
|
|
278
289
|
return undefined;
|
|
279
|
-
if (ctx.approvals.has(
|
|
290
|
+
if (ctx.approvals.has(LARGE_SUBAGENT_LAUNCH_ACTION))
|
|
280
291
|
return { fullChildrenApproved: false };
|
|
281
292
|
const turnKey = options?.assistantMessage;
|
|
282
|
-
const cached = turnKey ?
|
|
293
|
+
const cached = turnKey ? turnLargeLaunchDecisions.get(turnKey) : undefined;
|
|
283
294
|
if (cached) {
|
|
284
295
|
if (cached.decision === "reject") {
|
|
285
296
|
return {
|
|
286
297
|
block: true,
|
|
287
|
-
reason: cached.feedback
|
|
298
|
+
reason: cached.feedback
|
|
299
|
+
? `${LARGE_SUBAGENT_LAUNCH_REJECT_REASON} ${cached.feedback}`
|
|
300
|
+
: LARGE_SUBAGENT_LAUNCH_REJECT_REASON,
|
|
288
301
|
};
|
|
289
302
|
}
|
|
290
303
|
return { fullChildrenApproved: cached.fullChildrenApproved };
|
|
291
304
|
}
|
|
292
305
|
if (!approvalHandler) {
|
|
293
|
-
return { block: true, reason:
|
|
306
|
+
return { block: true, reason: NO_LARGE_SUBAGENT_LAUNCH_HANDLER_REASON };
|
|
294
307
|
}
|
|
295
|
-
const summary =
|
|
308
|
+
const summary = largeLaunchTaskSummary(input, options?.assistantMessage);
|
|
296
309
|
const siblingLaunches = (options?.assistantMessage?.content ?? []).flatMap((block) => {
|
|
297
310
|
if (block.type !== "toolCall" || block.name !== "subagent")
|
|
298
311
|
return [];
|
|
@@ -310,29 +323,32 @@ async function gateSwarmCall(input, ctx, options) {
|
|
|
310
323
|
try {
|
|
311
324
|
resp = await approvalHandler({
|
|
312
325
|
toolName: "subagent",
|
|
313
|
-
action:
|
|
314
|
-
detail: `
|
|
315
|
-
kind: "
|
|
326
|
+
action: LARGE_SUBAGENT_LAUNCH_ACTION,
|
|
327
|
+
detail: `large subagent launch (${count} children)${launchSummary || summary ? `\n${launchSummary || summary}` : ""}`,
|
|
328
|
+
kind: "large-subagent-launch",
|
|
316
329
|
});
|
|
317
330
|
}
|
|
318
331
|
catch (err) {
|
|
319
|
-
const message = err instanceof Error ? err.message :
|
|
332
|
+
const message = err instanceof Error ? err.message : NO_LARGE_SUBAGENT_LAUNCH_HANDLER_REASON;
|
|
320
333
|
return { block: true, reason: message };
|
|
321
334
|
}
|
|
322
335
|
const rawDecision = typeof resp === "string" ? resp : resp.decision;
|
|
323
336
|
const decision = rawDecision === "approve" ? "once" : rawDecision;
|
|
324
337
|
const feedback = typeof resp === "string" ? undefined : resp.feedback?.trim();
|
|
325
338
|
if (turnKey) {
|
|
326
|
-
|
|
339
|
+
turnLargeLaunchDecisions.set(turnKey, { decision, ...(feedback ? { feedback } : {}), fullChildrenApproved });
|
|
327
340
|
}
|
|
328
341
|
if (decision === "session") {
|
|
329
|
-
ctx.approvals.add(
|
|
342
|
+
ctx.approvals.add(LARGE_SUBAGENT_LAUNCH_ACTION);
|
|
330
343
|
if (fullChildrenApproved)
|
|
331
344
|
ctx.approvals.add(FULL_CHILD_ACTION);
|
|
332
345
|
return { fullChildrenApproved };
|
|
333
346
|
}
|
|
334
347
|
if (decision === "reject") {
|
|
335
|
-
return {
|
|
348
|
+
return {
|
|
349
|
+
block: true,
|
|
350
|
+
reason: feedback ? `${LARGE_SUBAGENT_LAUNCH_REJECT_REASON} ${feedback}` : LARGE_SUBAGENT_LAUNCH_REJECT_REASON,
|
|
351
|
+
};
|
|
336
352
|
}
|
|
337
353
|
return { fullChildrenApproved };
|
|
338
354
|
}
|
|
@@ -355,14 +371,14 @@ export async function gateToolCall(toolName, input, cwd, sessionId, options) {
|
|
|
355
371
|
}
|
|
356
372
|
if (READ_ONLY_TOOLS.has(toolName))
|
|
357
373
|
return undefined;
|
|
358
|
-
//
|
|
374
|
+
// Aggregate launch confirmation runs before the mode early-return, including YOLO.
|
|
359
375
|
// yolo mode too (only auto mode bypasses it).
|
|
360
|
-
let
|
|
376
|
+
let largeLaunchCoveredManualApproval = false;
|
|
361
377
|
if (toolName === "subagent") {
|
|
362
|
-
const
|
|
363
|
-
if (
|
|
364
|
-
return
|
|
365
|
-
|
|
378
|
+
const launchResult = await gateLargeSubagentLaunch(input, ctx, options);
|
|
379
|
+
if (launchResult && "block" in launchResult)
|
|
380
|
+
return launchResult;
|
|
381
|
+
largeLaunchCoveredManualApproval = ctx.mode === "manual" && launchResult?.fullChildrenApproved === true;
|
|
366
382
|
}
|
|
367
383
|
if (ctx.mode === "plan") {
|
|
368
384
|
const reason = planModeBlockReason(toolName, input);
|
|
@@ -370,7 +386,7 @@ export async function gateToolCall(toolName, input, cwd, sessionId, options) {
|
|
|
370
386
|
}
|
|
371
387
|
if (ctx.mode !== "manual")
|
|
372
388
|
return undefined;
|
|
373
|
-
if (!requiresManualApproval(toolName, input) ||
|
|
389
|
+
if (!requiresManualApproval(toolName, input) || largeLaunchCoveredManualApproval)
|
|
374
390
|
return undefined;
|
|
375
391
|
let action;
|
|
376
392
|
let detail;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../src/core/permissions.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR: permission modes (Manual / YOLO / Plan / Auto).\n *\n * Per-session only — never persisted per session, but the default mode new\n * sessions start with is configurable in /settings (`defaultPermissionMode`,\n * default \"manual\").\n *\n * - manual: every mutating tool call (bash, edit, write) requires approval.\n * - yolo: auto-approve tools; the agent may still ask questions.\n * - plan: read-only; mutating tools are hard-blocked (planModeBlockReason).\n * - auto: fully autonomous; a system-prompt addendum steers the model to\n * self-decide, and rollback is force-enabled for the session.\n *\n * The gate is wired into `agent-session.ts` `_installAgentToolHooks` before\n * the synchronous `_toolCallGates` loop. beforeToolCall is already async, so\n * awaiting the approval dialog is fine.\n *\n * State (mode + session-scoped approvals) is keyed by session id so\n * concurrent gateway chats do not share permission decisions.\n */\n\nimport { dirname, join, resolve } from \"node:path\";\nimport { getAgentDir } from \"../config.ts\";\nimport { isCodeRewriteMutating, planModeBlockReason } from \"./plan-mode.ts\";\nimport { effectiveSwarmCountForTurn, SWARM_APPROVAL_THRESHOLD } from \"./swarm.ts\";\n\n/** Shift+Tab cycle order. */\nexport const PERMISSION_MODES = [\"manual\", \"yolo\", \"plan\", \"auto\"] as const;\nexport type PermissionMode = (typeof PERMISSION_MODES)[number];\n\nexport function nextPermissionMode(current: PermissionMode): PermissionMode {\n\tconst i = PERMISSION_MODES.indexOf(current);\n\treturn PERMISSION_MODES[(i + 1) % PERMISSION_MODES.length];\n}\n\n/** True when the (default or session) permission mode is plan. */\nexport function isPlanModeActive(sessionId?: string): boolean {\n\treturn getPermissionMode(sessionId) === \"plan\";\n}\n\n/**\n * Destination mode when leaving plan via approve / `/plan off` / `/plan <text>`.\n * Explicit Shift+Tab or `/mode` picks do not use this — they already chose a mode.\n */\nexport function restorePermissionModeAfterPlan(\n\tprevious: PermissionMode | undefined,\n\tdefaultMode: PermissionMode,\n): PermissionMode {\n\tif (previous && previous !== \"plan\") return previous;\n\treturn defaultMode === \"plan\" ? \"yolo\" : defaultMode;\n}\n\nexport interface ApprovalRequest {\n\ttoolName: string;\n\t/** \"bash\" (detail = command), \"edit\"/\"write\" (detail = path),\n\t * \"edit-outside\"/\"write-outside\" when path escapes cwd,\n\t * \"subagent-full\" for full-access children, or \"swarm\" for swarms. */\n\taction: string;\n\tdetail: string;\n\t/** \"swarm\" when this is the agent-swarm approval prompt, \"plan\" for the\n\t * present_plan plan-approval prompt — the UI renders a dedicated dialog\n\t * for each instead of the generic tool dialog. */\n\tkind?: \"swarm\" | \"plan\";\n}\n\nexport type ApprovalDecision = \"once\" | \"session\" | \"reject\";\n/** Handlers may return a bare decision string, a reject carrying user feedback\n * that becomes the block reason the model sees, or an approve carrying optional\n * feedback (plan approval with comments). */\nexport type ApprovalResponse =\n\t| ApprovalDecision\n\t| { decision: \"reject\"; feedback: string }\n\t| { decision: \"approve\"; feedback?: string };\n\n/** Read-only tools that never need approval. */\nconst READ_ONLY_TOOLS = new Set([\n\t\"read\",\n\t\"grep\",\n\t\"find\",\n\t\"ls\",\n\t\"web_search\",\n\t\"web_fetch\",\n\t\"fetch_content\",\n\t\"get_search_content\",\n\t\"sourcegraph\",\n\t\"repo_map\",\n\t\"symbol_outline\",\n\t\"read_block\",\n\t\"code_overview\",\n\t\"ast_search\",\n\t\"lsp_diagnostics\",\n\t\"lsp_hover\",\n\t\"lsp_definition\",\n\t\"lsp_references\",\n\t\"lsp_symbols\",\n\t\"lsp_code_actions\",\n\t\"lsp_completions\",\n\t\"memory_load\",\n]);\n\n/** Tools that mutate state and must be gated in manual mode. */\nconst MUTATING_TOOLS = new Set([\"bash\", \"edit\", \"write\", \"memory_add\", \"memory_remove\", \"cron\"]);\n\nconst REJECT_REASON = \"Rejected by user (permission mode: manual).\";\nexport const NO_HANDLER_REASON = \"Mutating tool blocked in manual mode: no approval channel available.\";\nexport const SWARM_REJECT_REASON = \"Agent swarm rejected by user.\";\nexport const NO_SWARM_HANDLER_REASON = \"Agent swarm blocked: no approval channel available.\";\n/** Session-approval keys for child launches. */\nconst SWARM_ACTION = \"swarm\";\nconst FULL_CHILD_ACTION = \"subagent-full\";\n\ninterface PermissionContext {\n\tmode: PermissionMode;\n\tapprovals: Set<string>;\n}\n\nconst contexts = new Map<string, PermissionContext>();\nlet defaultContext: PermissionContext = { mode: \"manual\", approvals: new Set() };\nlet approvalHandler: ((req: ApprovalRequest) => Promise<ApprovalResponse>) | undefined;\n/** One swarm prompt covers every sibling SINGLE `subagent` on the same assistant message. */\nconst turnSwarmDecisions = new WeakMap<\n\tobject,\n\t{ decision: ApprovalDecision; feedback?: string; fullChildrenApproved: boolean }\n>();\n\nfunction getContext(sessionId?: string): PermissionContext {\n\tif (!sessionId) return defaultContext;\n\treturn contexts.get(sessionId) ?? defaultContext;\n}\n\nexport function createPermissionContext(sessionId: string, mode: PermissionMode = defaultContext.mode): void {\n\tcontexts.set(sessionId, { mode, approvals: new Set() });\n}\n\nexport function deletePermissionContext(sessionId: string): void {\n\tcontexts.delete(sessionId);\n}\n\nexport function getPermissionMode(sessionId?: string): PermissionMode {\n\treturn getContext(sessionId).mode;\n}\n\nexport function setPermissionMode(mode: PermissionMode, sessionId?: string): void {\n\tgetContext(sessionId).mode = mode;\n}\n\nexport function resetPermissions(defaultMode: PermissionMode = \"manual\", sessionId?: string): void {\n\tif (sessionId) {\n\t\tcontexts.set(sessionId, { mode: defaultMode, approvals: new Set() });\n\t} else {\n\t\tdefaultContext = { mode: defaultMode, approvals: new Set() };\n\t}\n}\n\nexport function registerApprovalHandler(fn: ((req: ApprovalRequest) => Promise<ApprovalResponse>) | undefined): void {\n\tapprovalHandler = fn;\n}\n\n/** Model-facing result texts for the present_plan tool. */\nexport const PLAN_APPROVED_TEXT = \"Plan approved — implement it now.\";\nexport const PLAN_DECLINED_TEXT = \"Plan declined — revise the plan and call present_plan again.\";\nexport const PLAN_PASS_THROUGH_TEXT = \"Plan presented. The user will review and reply.\";\n\n/** lunr: map an approval response to the model-facing present_plan result text. */\nexport function planApprovalResultText(resp: ApprovalResponse): string {\n\tconst decision = typeof resp === \"string\" ? resp : resp.decision;\n\tconst feedback = typeof resp === \"string\" ? undefined : resp.feedback?.trim();\n\tif (decision === \"reject\") {\n\t\treturn feedback\n\t\t\t? `Plan declined: ${feedback} — revise the plan and call present_plan again.`\n\t\t\t: PLAN_DECLINED_TEXT;\n\t}\n\treturn feedback ? `Plan approved with feedback: ${feedback} — implement it now.` : PLAN_APPROVED_TEXT;\n}\n\n/**\n * lunr: present_plan approval request. Unlike the mutating-tool and swarm gates\n * this fails OPEN when no approval handler is registered (or none is reachable,\n * e.g. a gateway turn without a chat context): headless sessions have no one to\n * show the dialog to, so the plan is presented and the user replies in chat\n * instead of deadlocking the turn. Everything else keeps fail-closed.\n */\nexport async function requestPlanApproval(summary: string): Promise<string> {\n\tif (!approvalHandler) return PLAN_PASS_THROUGH_TEXT;\n\tlet resp: ApprovalResponse;\n\ttry {\n\t\tresp = await approvalHandler({ toolName: \"present_plan\", action: \"plan\", detail: summary, kind: \"plan\" });\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : \"approval request failed\";\n\t\tif (message === NO_HANDLER_REASON) return PLAN_PASS_THROUGH_TEXT;\n\t\treturn `Plan declined: ${message} — revise the plan and call present_plan again.`;\n\t}\n\treturn planApprovalResultText(resp);\n}\n\nexport function clearSessionApprovals(sessionId?: string): void {\n\tgetContext(sessionId).approvals.clear();\n}\n\n/** Test seam: remove every per-session context and restore the default. */\nexport function resetAllPermissionContexts(): void {\n\tcontexts.clear();\n\tdefaultContext = { mode: \"manual\", approvals: new Set() };\n\tapprovalHandler = undefined;\n}\n\n/** Appended to the system prompt while auto mode is active. */\nexport const AUTO_MODE_ADDENDUM =\n\t\"You are running fully autonomously. Do not ask the user questions or wait for confirmation; make reasonable decisions and complete the task end-to-end.\";\n\nfunction isPathOutsideCwd(absPath: string, cwd: string): boolean {\n\tconst norm = (p: string) => p.replace(/\\\\/g, \"/\").replace(/\\/$/, \"\");\n\tconst np = norm(absPath);\n\tconst nc = norm(cwd);\n\tif (np === nc) return false;\n\treturn !np.startsWith(`${nc}/`);\n}\n\nfunction resolvePath(cwd: string, p: unknown): string {\n\tif (typeof p !== \"string\") return \"\";\n\tif (!p) return \"\";\n\treturn resolve(cwd, p);\n}\n\nexport const GLOBAL_AGENTS_FILE_WRITE_BLOCK_REASON =\n\t\"The global AGENTS.md file is user-managed. The agent cannot change ~/.lunr/agent/AGENTS.md.\";\nexport const MEMORY_FILE_DIRECT_WRITE_BLOCK_REASON =\n\t\"Memory is model-managed through the memory tools. Do not directly change ~/.lunr/simple-memory/memory.md.\";\n\nfunction protectedFileWriteReason(toolName: string, input: Record<string, unknown>, cwd: string): string | undefined {\n\tif (toolName !== \"edit\" && toolName !== \"write\" && toolName !== \"code_rewrite\") return undefined;\n\tconst target = resolvePath(cwd, input.path);\n\tif (!target) return undefined;\n\tconst normalize = (path: string) => {\n\t\tconst normalized = resolve(path).replace(/\\\\/g, \"/\").replace(/\\/$/, \"\");\n\t\treturn process.platform === \"win32\" ? normalized.toLowerCase() : normalized;\n\t};\n\tconst agentsPaths = [join(getAgentDir(), \"AGENTS.md\"), join(getAgentDir(), \"AGENTS.MD\")];\n\tif (agentsPaths.some((candidate) => normalize(target) === normalize(candidate))) {\n\t\treturn GLOBAL_AGENTS_FILE_WRITE_BLOCK_REASON;\n\t}\n\tif (normalize(target) === normalize(join(dirname(getAgentDir()), \"simple-memory\", \"memory.md\"))) {\n\t\treturn MEMORY_FILE_DIRECT_WRITE_BLOCK_REASON;\n\t}\n\treturn undefined;\n}\n\nfunction isMutatingTool(toolName: string): boolean {\n\treturn MUTATING_TOOLS.has(toolName);\n}\n\ninterface RequestedChildLaunch {\n\tdescription: string;\n\tpermissions: \"full\" | \"read-only\";\n}\n\nfunction collectRequestedChildLaunches(value: unknown, launches: RequestedChildLaunch[]): void {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) return;\n\tconst input = value as Record<string, unknown>;\n\tif (typeof input.task === \"string\" && input.task.trim()) {\n\t\tlaunches.push({\n\t\t\tdescription:\n\t\t\t\ttypeof input.description === \"string\" && input.description.trim()\n\t\t\t\t\t? input.description.trim()\n\t\t\t\t\t: input.task.trim(),\n\t\t\tpermissions: input.permissions === \"read-only\" ? \"read-only\" : \"full\",\n\t\t});\n\t\treturn;\n\t}\n\tfor (const key of [\"tasks\", \"chain\", \"parallel\"] as const) {\n\t\tconst children = input[key];\n\t\tif (Array.isArray(children)) {\n\t\t\tfor (const child of children) collectRequestedChildLaunches(child, launches);\n\t\t} else if (key === \"parallel\") {\n\t\t\tcollectRequestedChildLaunches(children, launches);\n\t\t}\n\t}\n}\n\nfunction getRequestedChildLaunches(input: Record<string, unknown>): RequestedChildLaunch[] {\n\tconst launches: RequestedChildLaunch[] = [];\n\tcollectRequestedChildLaunches(input, launches);\n\treturn launches;\n}\n\nfunction requiresManualApproval(toolName: string, input: Record<string, unknown>): boolean {\n\tif (isMutatingTool(toolName)) return true;\n\tif (toolName === \"subagent\") {\n\t\treturn getRequestedChildLaunches(input).some((launch) => launch.permissions === \"full\");\n\t}\n\treturn toolName === \"code_rewrite\" && isCodeRewriteMutating(input);\n}\n\nexport interface GateOptions {\n\t/** True when the current turn started from an explicit /swarm prompt — the\n\t * user already asked for the swarm, so the swarm approval gate is skipped. */\n\texplicitSwarmTurn?: boolean;\n\t/** Assistant message that issued this tool call. Same-turn sibling SINGLE\n\t * `subagent` calls count toward the swarm threshold. */\n\tassistantMessage?: {\n\t\tcontent?: ReadonlyArray<{ type?: string; name?: string; arguments?: unknown }>;\n\t};\n}\n\nfunction firstTaskText(value: unknown): string {\n\tif (typeof value === \"string\" && value.trim()) return sanitizeDetail(value);\n\treturn \"\";\n}\n\n/** First task text of a parallel fan-out or sibling SINGLE, used as the dialog summary. */\nfunction swarmTaskSummary(input: Record<string, unknown>, assistantMessage?: GateOptions[\"assistantMessage\"]): string {\n\tif (Array.isArray(input.tasks)) {\n\t\tfor (const task of input.tasks) {\n\t\t\tconst text = task && typeof task === \"object\" ? firstTaskText((task as { task?: unknown }).task) : \"\";\n\t\t\tif (text) return text;\n\t\t}\n\t}\n\tconst own = firstTaskText(input.task);\n\tif (own) return own;\n\tfor (const block of assistantMessage?.content ?? []) {\n\t\tif (block.type !== \"toolCall\" || block.name !== \"subagent\") continue;\n\t\tconst args =\n\t\t\tblock.arguments && typeof block.arguments === \"object\" && !Array.isArray(block.arguments)\n\t\t\t\t? (block.arguments as Record<string, unknown>)\n\t\t\t\t: undefined;\n\t\tconst text = firstTaskText(args?.task);\n\t\tif (text) return text;\n\t}\n\treturn \"\";\n}\n\n/**\n * Agent-swarm gate. An auto-activated swarm (more than SWARM_APPROVAL_THRESHOLD\n * parallel subagents in one `tasks`/`chain.parallel` call, or that many same-turn\n * SINGLE `subagent` calls) requires user approval in manual AND yolo modes; auto\n * mode runs it unconditionally and explicit /swarm turns are pre-approved.\n * Fail-closed without an approval handler, same as the mutating-tool gate.\n */\nasync function gateSwarmCall(\n\tinput: Record<string, unknown>,\n\tctx: PermissionContext,\n\toptions?: GateOptions,\n): Promise<{ block: true; reason: string } | { fullChildrenApproved: boolean } | undefined> {\n\tif (ctx.mode === \"auto\") return undefined;\n\tif (options?.explicitSwarmTurn) return undefined;\n\tconst count = effectiveSwarmCountForTurn(input, options?.assistantMessage);\n\tif (count <= SWARM_APPROVAL_THRESHOLD) return undefined;\n\tif (ctx.approvals.has(SWARM_ACTION)) return { fullChildrenApproved: false };\n\n\tconst turnKey = options?.assistantMessage;\n\tconst cached = turnKey ? turnSwarmDecisions.get(turnKey) : undefined;\n\tif (cached) {\n\t\tif (cached.decision === \"reject\") {\n\t\t\treturn {\n\t\t\t\tblock: true,\n\t\t\t\treason: cached.feedback ? `${SWARM_REJECT_REASON} ${cached.feedback}` : SWARM_REJECT_REASON,\n\t\t\t};\n\t\t}\n\t\treturn { fullChildrenApproved: cached.fullChildrenApproved };\n\t}\n\n\tif (!approvalHandler) {\n\t\treturn { block: true, reason: NO_SWARM_HANDLER_REASON };\n\t}\n\n\tconst summary = swarmTaskSummary(input, options?.assistantMessage);\n\tconst siblingLaunches = (options?.assistantMessage?.content ?? []).flatMap((block) => {\n\t\tif (block.type !== \"toolCall\" || block.name !== \"subagent\") return [];\n\t\tconst args =\n\t\t\tblock.arguments && typeof block.arguments === \"object\" && !Array.isArray(block.arguments)\n\t\t\t\t? (block.arguments as Record<string, unknown>)\n\t\t\t\t: undefined;\n\t\treturn args ? getRequestedChildLaunches(args) : [];\n\t});\n\tconst launches = siblingLaunches.length > 0 ? siblingLaunches : getRequestedChildLaunches(input);\n\tconst fullChildrenApproved = launches.some((launch) => launch.permissions === \"full\");\n\tconst launchSummary = launches\n\t\t.map((launch) => `${launch.description}\\npermissions: ${launch.permissions}`)\n\t\t.join(\"\\n\");\n\tlet resp: ApprovalResponse;\n\ttry {\n\t\tresp = await approvalHandler({\n\t\t\ttoolName: \"subagent\",\n\t\t\taction: SWARM_ACTION,\n\t\t\tdetail: `agent swarm (${count} subagents)${launchSummary || summary ? `\\n${launchSummary || summary}` : \"\"}`,\n\t\t\tkind: \"swarm\",\n\t\t});\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : NO_SWARM_HANDLER_REASON;\n\t\treturn { block: true, reason: message };\n\t}\n\n\tconst rawDecision = typeof resp === \"string\" ? resp : resp.decision;\n\tconst decision: ApprovalDecision = rawDecision === \"approve\" ? \"once\" : rawDecision;\n\tconst feedback = typeof resp === \"string\" ? undefined : resp.feedback?.trim();\n\tif (turnKey) {\n\t\tturnSwarmDecisions.set(turnKey, { decision, ...(feedback ? { feedback } : {}), fullChildrenApproved });\n\t}\n\tif (decision === \"session\") {\n\t\tctx.approvals.add(SWARM_ACTION);\n\t\tif (fullChildrenApproved) ctx.approvals.add(FULL_CHILD_ACTION);\n\t\treturn { fullChildrenApproved };\n\t}\n\tif (decision === \"reject\") {\n\t\treturn { block: true, reason: feedback ? `${SWARM_REJECT_REASON} ${feedback}` : SWARM_REJECT_REASON };\n\t}\n\treturn { fullChildrenApproved };\n}\n\nfunction sanitizeDetail(value: unknown): string {\n\tconst text = String(value ?? \"\").trim();\n\treturn text.length > 200 ? `${text.slice(0, 200)}…` : text;\n}\n\n/**\n * Returns a block result when the tool call should be blocked, else undefined.\n * Read-only tools always pass. YOLO and Auto always pass mutating tools. Plan\n * mode hard-blocks mutating tools (no prompt). Manual mode gates every mutating\n * tool; if an approval handler is registered it is asked, otherwise the call is\n * blocked (fail-closed — no silent allow).\n */\nexport async function gateToolCall(\n\ttoolName: string,\n\tinput: Record<string, unknown>,\n\tcwd: string,\n\tsessionId?: string,\n\toptions?: GateOptions,\n): Promise<{ block: true; reason: string } | undefined> {\n\tconst ctx = getContext(sessionId);\n\tconst protectedWriteReason = protectedFileWriteReason(toolName, input, cwd);\n\tif (protectedWriteReason) {\n\t\treturn { block: true, reason: protectedWriteReason };\n\t}\n\tif (READ_ONLY_TOOLS.has(toolName)) return undefined;\n\n\t// lunr: agent-swarm gate runs before the mode early-return — it applies in\n\t// yolo mode too (only auto mode bypasses it).\n\tlet swarmCoveredManualApproval = false;\n\tif (toolName === \"subagent\") {\n\t\tconst swarmResult = await gateSwarmCall(input, ctx, options);\n\t\tif (swarmResult && \"block\" in swarmResult) return swarmResult;\n\t\tswarmCoveredManualApproval = ctx.mode === \"manual\" && swarmResult?.fullChildrenApproved === true;\n\t}\n\n\tif (ctx.mode === \"plan\") {\n\t\tconst reason = planModeBlockReason(toolName, input);\n\t\treturn reason ? { block: true, reason } : undefined;\n\t}\n\n\tif (ctx.mode !== \"manual\") return undefined;\n\tif (!requiresManualApproval(toolName, input) || swarmCoveredManualApproval) return undefined;\n\n\tlet action: string;\n\tlet detail: string;\n\n\tif (toolName === \"bash\") {\n\t\taction = \"bash\";\n\t\tdetail = sanitizeDetail(input.command);\n\t} else if (toolName === \"edit\" || toolName === \"write\") {\n\t\tconst path = resolvePath(cwd, input.path);\n\t\tconst outside = path ? isPathOutsideCwd(path, cwd) : false;\n\t\taction = outside ? `${toolName}-outside` : toolName;\n\t\tdetail = path || String(input.path ?? \"\");\n\t} else if (toolName === \"memory_add\") {\n\t\taction = toolName;\n\t\tdetail = sanitizeDetail(input.content);\n\t} else if (toolName === \"memory_remove\") {\n\t\taction = toolName;\n\t\tdetail = sanitizeDetail(input.line);\n\t} else if (toolName === \"cron\") {\n\t\taction = \"cron\";\n\t\tdetail = sanitizeDetail(input.action);\n\t} else if (toolName === \"subagent\") {\n\t\taction = FULL_CHILD_ACTION;\n\t\tconst launches = getRequestedChildLaunches(input).filter((launch) => launch.permissions === \"full\");\n\t\tdetail = sanitizeDetail(\n\t\t\tlaunches.map((launch) => `${launch.description}\\npermissions: ${launch.permissions}`).join(\"\\n\"),\n\t\t);\n\t} else if (toolName === \"code_rewrite\") {\n\t\taction = \"code_rewrite\";\n\t\tdetail = sanitizeDetail(input.path ?? input.pattern);\n\t} else {\n\t\t// All other mutating tools fall back to the tool name with no extra detail.\n\t\taction = toolName;\n\t\tdetail = \"\";\n\t}\n\n\tif (ctx.approvals.has(action)) return undefined;\n\n\tif (!approvalHandler) {\n\t\treturn { block: true, reason: NO_HANDLER_REASON };\n\t}\n\n\tlet resp: ApprovalResponse;\n\ttry {\n\t\tresp = await approvalHandler({ toolName, action, detail });\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : NO_HANDLER_REASON;\n\t\treturn { block: true, reason: message };\n\t}\n\n\tconst decision = typeof resp === \"string\" ? resp : resp.decision;\n\tif (decision === \"session\") {\n\t\tctx.approvals.add(action);\n\t\treturn undefined;\n\t}\n\tif (decision === \"reject\") {\n\t\tconst feedback = typeof resp === \"string\" ? undefined : resp.feedback?.trim();\n\t\treturn { block: true, reason: feedback ? `${REJECT_REASON} ${feedback}` : REJECT_REASON };\n\t}\n\t// \"once\" — allow this call only\n\treturn undefined;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../src/core/permissions.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR: permission modes (Manual / YOLO / Plan / Auto).\n *\n * Per-session only — never persisted per session, but the default mode new\n * sessions start with is configurable in /settings (`defaultPermissionMode`,\n * default \"manual\").\n *\n * - manual: every mutating tool call (bash, edit, write) requires approval.\n * - yolo: auto-approve tools; the agent may still ask questions.\n * - plan: read-only; mutating tools are hard-blocked (planModeBlockReason).\n * - auto: fully autonomous; a system-prompt addendum steers the model to\n * self-decide, and rollback is force-enabled for the session.\n *\n * The gate is wired into `agent-session.ts` `_installAgentToolHooks` before\n * the synchronous `_toolCallGates` loop. beforeToolCall is already async, so\n * awaiting the approval dialog is fine.\n *\n * State (mode + session-scoped approvals) is keyed by session id so\n * concurrent gateway chats do not share permission decisions.\n */\n\nimport { dirname, join, resolve } from \"node:path\";\nimport { getAgentDir } from \"../config.ts\";\nimport { effectiveLargeSubagentLaunchCountForTurn, LARGE_SUBAGENT_LAUNCH_THRESHOLD } from \"./large-subagent-launch.ts\";\nimport { isUserInstructionsPath } from \"./model-instructions.ts\";\nimport { isCodeRewriteMutating, planModeBlockReason } from \"./plan-mode.ts\";\n\n/** Shift+Tab cycle order. */\nexport const PERMISSION_MODES = [\"manual\", \"yolo\", \"plan\", \"auto\"] as const;\nexport type PermissionMode = (typeof PERMISSION_MODES)[number];\n\nexport function nextPermissionMode(current: PermissionMode): PermissionMode {\n\tconst i = PERMISSION_MODES.indexOf(current);\n\treturn PERMISSION_MODES[(i + 1) % PERMISSION_MODES.length];\n}\n\n/** True when the (default or session) permission mode is plan. */\nexport function isPlanModeActive(sessionId?: string): boolean {\n\treturn getPermissionMode(sessionId) === \"plan\";\n}\n\n/**\n * Destination mode when leaving plan via approve / `/plan off` / `/plan <text>`.\n * Explicit Shift+Tab or `/mode` picks do not use this — they already chose a mode.\n */\nexport function restorePermissionModeAfterPlan(\n\tprevious: PermissionMode | undefined,\n\tdefaultMode: PermissionMode,\n): PermissionMode {\n\tif (previous && previous !== \"plan\") return previous;\n\treturn defaultMode === \"plan\" ? \"yolo\" : defaultMode;\n}\n\nexport interface ApprovalRequest {\n\ttoolName: string;\n\t/** \"bash\" (detail = command), \"edit\"/\"write\" (detail = path),\n\t * \"edit-outside\"/\"write-outside\" when path escapes cwd,\n\t * \"subagent-full\" for full-access children, or \"large-subagent-launch\" for aggregate confirmation. */\n\taction: string;\n\tdetail: string;\n\t/** \"large-subagent-launch\" for aggregate child confirmation, \"plan\" for the\n\t * present_plan plan-approval prompt — the UI renders a dedicated dialog\n\t * for each instead of the generic tool dialog. */\n\tkind?: \"large-subagent-launch\" | \"plan\";\n}\n\nexport type ApprovalDecision = \"once\" | \"session\" | \"reject\";\n/** Handlers may return a bare decision string, a reject carrying user feedback\n * that becomes the block reason the model sees, or an approve carrying optional\n * feedback (plan approval with comments). */\nexport type ApprovalResponse =\n\t| ApprovalDecision\n\t| { decision: \"reject\"; feedback: string }\n\t| { decision: \"approve\"; feedback?: string };\n\n/** Read-only tools that never need approval. */\nconst READ_ONLY_TOOLS = new Set([\n\t\"read\",\n\t\"grep\",\n\t\"find\",\n\t\"ls\",\n\t\"web_search\",\n\t\"web_fetch\",\n\t\"fetch_content\",\n\t\"get_search_content\",\n\t\"sourcegraph\",\n\t\"repo_map\",\n\t\"symbol_outline\",\n\t\"read_block\",\n\t\"code_overview\",\n\t\"ast_search\",\n\t\"lsp_diagnostics\",\n\t\"lsp_hover\",\n\t\"lsp_definition\",\n\t\"lsp_references\",\n\t\"lsp_symbols\",\n\t\"lsp_code_actions\",\n\t\"lsp_completions\",\n\t\"memory_load\",\n\t\"settings_load\",\n]);\n\n/** Tools that mutate state and must be gated in manual mode. */\nconst MUTATING_TOOLS = new Set([\"bash\", \"edit\", \"write\", \"memory_add\", \"memory_remove\", \"cron\"]);\n\nconst REJECT_REASON = \"Rejected by user (permission mode: manual).\";\nexport const NO_HANDLER_REASON = \"Mutating tool blocked in manual mode: no approval channel available.\";\nexport const LARGE_SUBAGENT_LAUNCH_REJECT_REASON = \"Large subagent launch rejected by user.\";\nexport const NO_LARGE_SUBAGENT_LAUNCH_HANDLER_REASON = \"Large subagent launch blocked: no approval channel available.\";\n/** Session-approval keys for child launches. */\nconst LARGE_SUBAGENT_LAUNCH_ACTION = \"large-subagent-launch\";\nconst FULL_CHILD_ACTION = \"subagent-full\";\n\ninterface PermissionContext {\n\tmode: PermissionMode;\n\tapprovals: Set<string>;\n}\n\nconst contexts = new Map<string, PermissionContext>();\nlet defaultContext: PermissionContext = { mode: \"manual\", approvals: new Set() };\nlet approvalHandler: ((req: ApprovalRequest) => Promise<ApprovalResponse>) | undefined;\n/** One aggregate prompt covers every sibling SINGLE `subagent` on the same assistant message. */\nconst turnLargeLaunchDecisions = new WeakMap<\n\tobject,\n\t{ decision: ApprovalDecision; feedback?: string; fullChildrenApproved: boolean }\n>();\n\nfunction getContext(sessionId?: string): PermissionContext {\n\tif (!sessionId) return defaultContext;\n\treturn contexts.get(sessionId) ?? defaultContext;\n}\n\nexport function createPermissionContext(sessionId: string, mode: PermissionMode = defaultContext.mode): void {\n\tcontexts.set(sessionId, { mode, approvals: new Set() });\n}\n\nexport function deletePermissionContext(sessionId: string): void {\n\tcontexts.delete(sessionId);\n}\n\nexport function getPermissionMode(sessionId?: string): PermissionMode {\n\treturn getContext(sessionId).mode;\n}\n\nexport function setPermissionMode(mode: PermissionMode, sessionId?: string): void {\n\tgetContext(sessionId).mode = mode;\n}\n\nexport function resetPermissions(defaultMode: PermissionMode = \"manual\", sessionId?: string): void {\n\tif (sessionId) {\n\t\tcontexts.set(sessionId, { mode: defaultMode, approvals: new Set() });\n\t} else {\n\t\tdefaultContext = { mode: defaultMode, approvals: new Set() };\n\t}\n}\n\nexport function registerApprovalHandler(fn: ((req: ApprovalRequest) => Promise<ApprovalResponse>) | undefined): void {\n\tapprovalHandler = fn;\n}\n\n/** Model-facing result texts for the present_plan tool. */\nexport const PLAN_APPROVED_TEXT = \"Plan approved — implement it now.\";\nexport const PLAN_DECLINED_TEXT = \"Plan declined — revise the plan and call present_plan again.\";\nexport const PLAN_PASS_THROUGH_TEXT = \"Plan presented. The user will review and reply.\";\n\n/** lunr: map an approval response to the model-facing present_plan result text. */\nexport function planApprovalResultText(resp: ApprovalResponse): string {\n\tconst decision = typeof resp === \"string\" ? resp : resp.decision;\n\tconst feedback = typeof resp === \"string\" ? undefined : resp.feedback?.trim();\n\tif (decision === \"reject\") {\n\t\treturn feedback\n\t\t\t? `Plan declined: ${feedback} — revise the plan and call present_plan again.`\n\t\t\t: PLAN_DECLINED_TEXT;\n\t}\n\treturn feedback ? `Plan approved with feedback: ${feedback} — implement it now.` : PLAN_APPROVED_TEXT;\n}\n\n/**\n * lunr: present_plan approval request. Unlike mutating-tool and aggregate launch gates\n * this fails OPEN when no approval handler is registered (or none is reachable,\n * e.g. a gateway turn without a chat context): headless sessions have no one to\n * show the dialog to, so the plan is presented and the user replies in chat\n * instead of deadlocking the turn. Everything else keeps fail-closed.\n */\nexport async function requestPlanApproval(summary: string): Promise<string> {\n\tif (!approvalHandler) return PLAN_PASS_THROUGH_TEXT;\n\tlet resp: ApprovalResponse;\n\ttry {\n\t\tresp = await approvalHandler({ toolName: \"present_plan\", action: \"plan\", detail: summary, kind: \"plan\" });\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : \"approval request failed\";\n\t\tif (message === NO_HANDLER_REASON) return PLAN_PASS_THROUGH_TEXT;\n\t\treturn `Plan declined: ${message} — revise the plan and call present_plan again.`;\n\t}\n\treturn planApprovalResultText(resp);\n}\n\nexport function clearSessionApprovals(sessionId?: string): void {\n\tgetContext(sessionId).approvals.clear();\n}\n\n/** Test seam: remove every per-session context and restore the default. */\nexport function resetAllPermissionContexts(): void {\n\tcontexts.clear();\n\tdefaultContext = { mode: \"manual\", approvals: new Set() };\n\tapprovalHandler = undefined;\n}\n\n/** Appended to the system prompt while auto mode is active. */\nexport const AUTO_MODE_ADDENDUM =\n\t\"You are running fully autonomously. Do not ask the user questions or wait for confirmation; make reasonable decisions and complete the task end-to-end.\";\n\nfunction isPathOutsideCwd(absPath: string, cwd: string): boolean {\n\tconst norm = (p: string) => p.replace(/\\\\/g, \"/\").replace(/\\/$/, \"\");\n\tconst np = norm(absPath);\n\tconst nc = norm(cwd);\n\tif (np === nc) return false;\n\treturn !np.startsWith(`${nc}/`);\n}\n\nfunction resolvePath(cwd: string, p: unknown): string {\n\tif (typeof p !== \"string\") return \"\";\n\tif (!p) return \"\";\n\treturn resolve(cwd, p);\n}\n\nexport const GLOBAL_AGENTS_FILE_WRITE_BLOCK_REASON =\n\t\"The agents instruction tree is user-managed. The agent cannot change ~/.lunr/agent/agents/.\";\nexport const MEMORY_FILE_DIRECT_WRITE_BLOCK_REASON =\n\t\"Memory is model-managed through the memory tools. Do not directly change ~/.lunr/simple-memory/memory.md.\";\n\nfunction protectedFileWriteReason(toolName: string, input: Record<string, unknown>, cwd: string): string | undefined {\n\tif (toolName !== \"edit\" && toolName !== \"write\" && toolName !== \"code_rewrite\") return undefined;\n\tconst target = resolvePath(cwd, input.path);\n\tif (!target) return undefined;\n\tconst normalize = (path: string) => {\n\t\tconst normalized = resolve(path).replace(/\\\\/g, \"/\").replace(/\\/$/, \"\");\n\t\treturn process.platform === \"win32\" ? normalized.toLowerCase() : normalized;\n\t};\n\tif (isUserInstructionsPath(target, getAgentDir())) {\n\t\treturn GLOBAL_AGENTS_FILE_WRITE_BLOCK_REASON;\n\t}\n\tif (normalize(target) === normalize(join(dirname(getAgentDir()), \"simple-memory\", \"memory.md\"))) {\n\t\treturn MEMORY_FILE_DIRECT_WRITE_BLOCK_REASON;\n\t}\n\treturn undefined;\n}\n\nfunction isMutatingTool(toolName: string): boolean {\n\treturn MUTATING_TOOLS.has(toolName);\n}\n\ninterface RequestedChildLaunch {\n\tdescription: string;\n\tpermissions: \"full\" | \"read-only\";\n}\n\nfunction collectRequestedChildLaunches(value: unknown, launches: RequestedChildLaunch[]): void {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) return;\n\tconst input = value as Record<string, unknown>;\n\tlet nested = false;\n\tfor (const key of [\"tasks\", \"chain\", \"parallel\"] as const) {\n\t\tconst children = input[key];\n\t\tif (Array.isArray(children)) {\n\t\t\tnested = true;\n\t\t\tfor (const child of children) collectRequestedChildLaunches(child, launches);\n\t\t} else if (key === \"parallel\" && children && typeof children === \"object\") {\n\t\t\tnested = true;\n\t\t\tcollectRequestedChildLaunches(children, launches);\n\t\t}\n\t}\n\tif (nested) return;\n\tconst task = typeof input.task === \"string\" ? input.task.trim() : \"\";\n\tconst description = typeof input.description === \"string\" ? input.description.trim() : \"\";\n\tconst hasPermissions = input.permissions === \"full\" || input.permissions === \"read-only\";\n\t// Chain steps may omit task (later steps default to {previous}) and still launch.\n\tif (!task && !description && !hasPermissions) return;\n\tlaunches.push({\n\t\tdescription: description || task || \"subagent\",\n\t\tpermissions: input.permissions === \"read-only\" ? \"read-only\" : \"full\",\n\t});\n}\n\nfunction getRequestedChildLaunches(input: Record<string, unknown>): RequestedChildLaunch[] {\n\tconst launches: RequestedChildLaunch[] = [];\n\tcollectRequestedChildLaunches(input, launches);\n\treturn launches;\n}\n\nfunction requiresManualApproval(toolName: string, input: Record<string, unknown>): boolean {\n\tif (isMutatingTool(toolName)) return true;\n\tif (toolName.startsWith(\"settings_\") && toolName !== \"settings_load\") return Object.keys(input).length > 0;\n\tif (toolName === \"subagent\") {\n\t\treturn getRequestedChildLaunches(input).some((launch) => launch.permissions === \"full\");\n\t}\n\treturn toolName === \"code_rewrite\" && isCodeRewriteMutating(input);\n}\n\nexport interface GateOptions {\n\t/** Global preference for aggregate confirmation. Defaults to enabled. */\n\tconfirmLargeSubagentLaunches?: boolean;\n\t/** True when the current turn started from an explicit /swarm prompt. */\n\texplicitSwarmTurn?: boolean;\n\t/** Assistant message that issued this tool call. Same-turn sibling SINGLE\n\t * `subagent` calls count toward the aggregate threshold. */\n\tassistantMessage?: {\n\t\tcontent?: ReadonlyArray<{ type?: string; name?: string; arguments?: unknown }>;\n\t};\n}\n\nfunction firstTaskText(value: unknown): string {\n\tif (typeof value === \"string\" && value.trim()) return sanitizeDetail(value);\n\treturn \"\";\n}\n\n/** First task text of a parallel fan-out or sibling SINGLE, used as the dialog summary. */\nfunction largeLaunchTaskSummary(\n\tinput: Record<string, unknown>,\n\tassistantMessage?: GateOptions[\"assistantMessage\"],\n): string {\n\tif (Array.isArray(input.tasks)) {\n\t\tfor (const task of input.tasks) {\n\t\t\tconst text = task && typeof task === \"object\" ? firstTaskText((task as { task?: unknown }).task) : \"\";\n\t\t\tif (text) return text;\n\t\t}\n\t}\n\tconst own = firstTaskText(input.task);\n\tif (own) return own;\n\tfor (const block of assistantMessage?.content ?? []) {\n\t\tif (block.type !== \"toolCall\" || block.name !== \"subagent\") continue;\n\t\tconst args =\n\t\t\tblock.arguments && typeof block.arguments === \"object\" && !Array.isArray(block.arguments)\n\t\t\t\t? (block.arguments as Record<string, unknown>)\n\t\t\t\t: undefined;\n\t\tconst text = firstTaskText(args?.task);\n\t\tif (text) return text;\n\t}\n\treturn \"\";\n}\n\n/**\n * Aggregate launch gate. A launch above LARGE_SUBAGENT_LAUNCH_THRESHOLD\n * parallel subagents in one `tasks`/`chain.parallel` call, or that many same-turn\n * SINGLE `subagent` calls) requires user approval in manual AND yolo modes; auto\n * mode runs it unconditionally.\n * Fail-closed without an approval handler, same as the mutating-tool gate.\n */\nasync function gateLargeSubagentLaunch(\n\tinput: Record<string, unknown>,\n\tctx: PermissionContext,\n\toptions?: GateOptions,\n): Promise<{ block: true; reason: string } | { fullChildrenApproved: boolean } | undefined> {\n\tif (ctx.mode === \"auto\") return undefined;\n\tif (options?.explicitSwarmTurn) return undefined;\n\tif (options?.confirmLargeSubagentLaunches === false) return undefined;\n\tconst count = effectiveLargeSubagentLaunchCountForTurn(input, options?.assistantMessage);\n\tif (count <= LARGE_SUBAGENT_LAUNCH_THRESHOLD) return undefined;\n\tif (ctx.approvals.has(LARGE_SUBAGENT_LAUNCH_ACTION)) return { fullChildrenApproved: false };\n\n\tconst turnKey = options?.assistantMessage;\n\tconst cached = turnKey ? turnLargeLaunchDecisions.get(turnKey) : undefined;\n\tif (cached) {\n\t\tif (cached.decision === \"reject\") {\n\t\t\treturn {\n\t\t\t\tblock: true,\n\t\t\t\treason: cached.feedback\n\t\t\t\t\t? `${LARGE_SUBAGENT_LAUNCH_REJECT_REASON} ${cached.feedback}`\n\t\t\t\t\t: LARGE_SUBAGENT_LAUNCH_REJECT_REASON,\n\t\t\t};\n\t\t}\n\t\treturn { fullChildrenApproved: cached.fullChildrenApproved };\n\t}\n\n\tif (!approvalHandler) {\n\t\treturn { block: true, reason: NO_LARGE_SUBAGENT_LAUNCH_HANDLER_REASON };\n\t}\n\n\tconst summary = largeLaunchTaskSummary(input, options?.assistantMessage);\n\tconst siblingLaunches = (options?.assistantMessage?.content ?? []).flatMap((block) => {\n\t\tif (block.type !== \"toolCall\" || block.name !== \"subagent\") return [];\n\t\tconst args =\n\t\t\tblock.arguments && typeof block.arguments === \"object\" && !Array.isArray(block.arguments)\n\t\t\t\t? (block.arguments as Record<string, unknown>)\n\t\t\t\t: undefined;\n\t\treturn args ? getRequestedChildLaunches(args) : [];\n\t});\n\tconst launches = siblingLaunches.length > 0 ? siblingLaunches : getRequestedChildLaunches(input);\n\tconst fullChildrenApproved = launches.some((launch) => launch.permissions === \"full\");\n\tconst launchSummary = launches\n\t\t.map((launch) => `${launch.description}\\npermissions: ${launch.permissions}`)\n\t\t.join(\"\\n\");\n\tlet resp: ApprovalResponse;\n\ttry {\n\t\tresp = await approvalHandler({\n\t\t\ttoolName: \"subagent\",\n\t\t\taction: LARGE_SUBAGENT_LAUNCH_ACTION,\n\t\t\tdetail: `large subagent launch (${count} children)${launchSummary || summary ? `\\n${launchSummary || summary}` : \"\"}`,\n\t\t\tkind: \"large-subagent-launch\",\n\t\t});\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : NO_LARGE_SUBAGENT_LAUNCH_HANDLER_REASON;\n\t\treturn { block: true, reason: message };\n\t}\n\n\tconst rawDecision = typeof resp === \"string\" ? resp : resp.decision;\n\tconst decision: ApprovalDecision = rawDecision === \"approve\" ? \"once\" : rawDecision;\n\tconst feedback = typeof resp === \"string\" ? undefined : resp.feedback?.trim();\n\tif (turnKey) {\n\t\tturnLargeLaunchDecisions.set(turnKey, { decision, ...(feedback ? { feedback } : {}), fullChildrenApproved });\n\t}\n\tif (decision === \"session\") {\n\t\tctx.approvals.add(LARGE_SUBAGENT_LAUNCH_ACTION);\n\t\tif (fullChildrenApproved) ctx.approvals.add(FULL_CHILD_ACTION);\n\t\treturn { fullChildrenApproved };\n\t}\n\tif (decision === \"reject\") {\n\t\treturn {\n\t\t\tblock: true,\n\t\t\treason: feedback ? `${LARGE_SUBAGENT_LAUNCH_REJECT_REASON} ${feedback}` : LARGE_SUBAGENT_LAUNCH_REJECT_REASON,\n\t\t};\n\t}\n\treturn { fullChildrenApproved };\n}\n\nfunction sanitizeDetail(value: unknown): string {\n\tconst text = String(value ?? \"\").trim();\n\treturn text.length > 200 ? `${text.slice(0, 200)}…` : text;\n}\n\n/**\n * Returns a block result when the tool call should be blocked, else undefined.\n * Read-only tools always pass. YOLO and Auto always pass mutating tools. Plan\n * mode hard-blocks mutating tools (no prompt). Manual mode gates every mutating\n * tool; if an approval handler is registered it is asked, otherwise the call is\n * blocked (fail-closed — no silent allow).\n */\nexport async function gateToolCall(\n\ttoolName: string,\n\tinput: Record<string, unknown>,\n\tcwd: string,\n\tsessionId?: string,\n\toptions?: GateOptions,\n): Promise<{ block: true; reason: string } | undefined> {\n\tconst ctx = getContext(sessionId);\n\tconst protectedWriteReason = protectedFileWriteReason(toolName, input, cwd);\n\tif (protectedWriteReason) {\n\t\treturn { block: true, reason: protectedWriteReason };\n\t}\n\tif (READ_ONLY_TOOLS.has(toolName)) return undefined;\n\n\t// Aggregate launch confirmation runs before the mode early-return, including YOLO.\n\t// yolo mode too (only auto mode bypasses it).\n\tlet largeLaunchCoveredManualApproval = false;\n\tif (toolName === \"subagent\") {\n\t\tconst launchResult = await gateLargeSubagentLaunch(input, ctx, options);\n\t\tif (launchResult && \"block\" in launchResult) return launchResult;\n\t\tlargeLaunchCoveredManualApproval = ctx.mode === \"manual\" && launchResult?.fullChildrenApproved === true;\n\t}\n\n\tif (ctx.mode === \"plan\") {\n\t\tconst reason = planModeBlockReason(toolName, input);\n\t\treturn reason ? { block: true, reason } : undefined;\n\t}\n\n\tif (ctx.mode !== \"manual\") return undefined;\n\tif (!requiresManualApproval(toolName, input) || largeLaunchCoveredManualApproval) return undefined;\n\n\tlet action: string;\n\tlet detail: string;\n\n\tif (toolName === \"bash\") {\n\t\taction = \"bash\";\n\t\tdetail = sanitizeDetail(input.command);\n\t} else if (toolName === \"edit\" || toolName === \"write\") {\n\t\tconst path = resolvePath(cwd, input.path);\n\t\tconst outside = path ? isPathOutsideCwd(path, cwd) : false;\n\t\taction = outside ? `${toolName}-outside` : toolName;\n\t\tdetail = path || String(input.path ?? \"\");\n\t} else if (toolName === \"memory_add\") {\n\t\taction = toolName;\n\t\tdetail = sanitizeDetail(input.content);\n\t} else if (toolName === \"memory_remove\") {\n\t\taction = toolName;\n\t\tdetail = sanitizeDetail(input.line);\n\t} else if (toolName === \"cron\") {\n\t\taction = \"cron\";\n\t\tdetail = sanitizeDetail(input.action);\n\t} else if (toolName === \"subagent\") {\n\t\taction = FULL_CHILD_ACTION;\n\t\tconst launches = getRequestedChildLaunches(input).filter((launch) => launch.permissions === \"full\");\n\t\tdetail = sanitizeDetail(\n\t\t\tlaunches.map((launch) => `${launch.description}\\npermissions: ${launch.permissions}`).join(\"\\n\"),\n\t\t);\n\t} else if (toolName === \"code_rewrite\") {\n\t\taction = \"code_rewrite\";\n\t\tdetail = sanitizeDetail(input.path ?? input.pattern);\n\t} else {\n\t\t// All other mutating tools fall back to the tool name with no extra detail.\n\t\taction = toolName;\n\t\tdetail = \"\";\n\t}\n\n\tif (ctx.approvals.has(action)) return undefined;\n\n\tif (!approvalHandler) {\n\t\treturn { block: true, reason: NO_HANDLER_REASON };\n\t}\n\n\tlet resp: ApprovalResponse;\n\ttry {\n\t\tresp = await approvalHandler({ toolName, action, detail });\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : NO_HANDLER_REASON;\n\t\treturn { block: true, reason: message };\n\t}\n\n\tconst decision = typeof resp === \"string\" ? resp : resp.decision;\n\tif (decision === \"session\") {\n\t\tctx.approvals.add(action);\n\t\treturn undefined;\n\t}\n\tif (decision === \"reject\") {\n\t\tconst feedback = typeof resp === \"string\" ? undefined : resp.feedback?.trim();\n\t\treturn { block: true, reason: feedback ? `${REJECT_REASON} ${feedback}` : REJECT_REASON };\n\t}\n\t// \"once\" — allow this call only\n\treturn undefined;\n}\n"]}
|