-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Problem
Starting with Copilot SDK v0.1.28 (copilot-sdk PR #509), all tool operations (file reads/writes, shell commands, URL fetches, MCP calls) are denied by default unless the application provides an on_permission_request handler.
Previously, permissions were only requested when a handler was explicitly provided. Now requestPermission: true is always sent in session.create and session.resume RPC calls. Without a handler, the CLI returns:
Permission denied and could not request permission from user
This affects all Conductor workflows — every agent's tool calls (view, powershell, grep, glob, edit, etc.) silently fail, causing agents to either:
- Skip tool usage entirely and produce results with incomplete context
- Attempt multiple tool calls, get denied on all, then report "access restrictions"
Reproduction
# Any workflow where agents need filesystem tools
conductor run workflow.yaml --input source_dir="/path/to/code"
# Agent output will report: "Permission denied and could not request permission from user"A minimal debug workflow is available at examples/docs-eval/debug-tools.yaml that isolates the issue by testing view, powershell, and grep tool access against a target directory.
Root Cause
In src/conductor/providers/copilot.py, the _execute_sdk_call method builds a session_config dict passed to client.create_session(). The config has no on_permission_request handler, triggering the SDK's deny-by-default behavior:
# copilot SDK internal (session.py)
async def _handle_permission_request(self, request):
if not handler:
return {"kind": "denied-no-approval-rule-and-could-not-request-from-user"}Proposed Fix
Add an on_permission_request handler to the session config. The SDK defines 5 permission request kinds and 4 result kinds:
Permission request kinds: shell, write, read, url, mcp
Permission result kinds: approved, denied-by-rules, denied-interactively-by-user, denied-no-approval-rule-and-could-not-request-from-user
Minimal fix (auto-approve all)
def _auto_approve_tool(request, context):
return {"kind": "approved"}
session_config["on_permission_request"] = _auto_approve_toolThis matches the --allow-all behavior the SDK FAQ states should be the default.
Recommended: Expose as workflow configuration
Add an optional tool_permissions field to the workflow runtime config, allowing per-workflow control of tool permissions:
workflow:
runtime:
provider: copilot
tool_permissions:
default: allow # "allow" (default) | "deny"
rules:
- kind: shell
decision: deny # deny shell for sandboxed workflows
- kind: write
decision: deny # read-only evaluation workflows
# read, url, mcp inherit from default: allowImplementation sketch:
# schema.py
class ToolPermissionRule(BaseModel):
kind: Literal["shell", "write", "read", "url", "mcp"]
decision: Literal["allow", "deny"] = "allow"
class ToolPermissionsConfig(BaseModel):
default: Literal["allow", "deny"] = "allow"
rules: list[ToolPermissionRule] = Field(default_factory=list)
class RuntimeConfig(BaseModel):
# ... existing fields ...
tool_permissions: ToolPermissionsConfig = Field(default_factory=ToolPermissionsConfig)# copilot.py - in _execute_sdk_call()
def _make_permission_handler(config: ToolPermissionsConfig):
rules = {r.kind: r.decision for r in config.rules}
def handler(request, context):
kind = request.get("kind", "")
decision = rules.get(kind, config.default)
if decision == "allow":
return {"kind": "approved"}
return {"kind": "denied-by-rules"}
return handlerWhen no tool_permissions is specified, default to allow — maintaining backward compatibility and matching the SDK FAQ's stated behavior.
Use cases for granular control
- Read-only evaluation workflows: allow
readbut denywriteandshell - Sandboxed/untrusted workflows: deny all except
read - Audit logging: log all permission decisions without blocking
References
- copilot-sdk PR #509: Breaking change: deny all permissions by default
- SDK FAQ: What tools are enabled by default?
- SDK Python README: Permission Requests
- SDK v0.1.28+ ships
PermissionHandler.approve_allconvenience helper