feat(tasks): Add plan mode support for cloud runs with permission relay#54466
Conversation
|
Size Change: 0 B Total Size: 129 MB ℹ️ View Unchanged
|
196dc82 to
0fcddef
Compare
|
…ay (#54466) Co-authored-by: tests-posthog[bot] <250237707+tests-posthog[bot]@users.noreply.github.com>
There was a problem hiding this comment.
all good, adding review here to address as follow up
- bypassPermissions is accepted from any caller — no authorization gate
initial_permission_mode accepts "bypassPermissions" as a valid choice directly from the API. Any authenticated user with task:write scope can start a run in bypass-permissions mode. The PR description says this is the default when the field is unset (for web/Slack tasks), but there's no distinction between "default fallback" and "explicitly requested by an arbitrary API caller."
If bypass-permissions is meant to be a privileged mode (e.g., only for unattended web/Slack tasks, or only when no desktop app is connected), the backend should enforce that — not rely on callers to be well-behaved. Consider either:
Removing bypassPermissions from the API-accepted choices and having the agent server fall back to it internally when no mode is set
Adding an authorization check (e.g., only allow bypass when run_source is not from a connected desktop client)
- Zero test coverage
None of the new functionality has tests:
No tests for initial_permission_mode on TaskRunCreateRequestSerializer (valid choices, None default, state plumbing)
No tests for permission_response / set_config_option being accepted in TaskRunCommandRequestSerializer
No tests for the extra_state assembly logic in api.py (especially the interaction between pending_user_message and initial_permission_mode both being set)
This is a security-sensitive feature (permission relay / mode switching). Tests should cover at least the serializer validation and the state assembly.
- initial_permission_mode is stored in state but never consumed in this codebase
The value goes into TaskRun.state via extra_state, but:
RunState (Pydantic model with extra="allow") doesn't declare it as a typed field — it's silently absorbed into extras
TaskProcessingContext doesn't reference it
start_agent_server activity doesn't read or pass it to the sandbox
Neither sandbox implementation's start_agent_server() method accepts it
The entire consumption presumably happens in the companion posthog/code repo (PR #1645), where the agent-server reads the value from TaskRun.state. This means:
The field is invisible to type checking in the PostHog backend
If the companion code regresses or changes how it reads state, the PostHog backend has no way to catch it
RunState should declare initial_permission_mode: str | None = None for type safety and discoverability
- No param validation for new command methods
The validate method in TaskRunCommandRequestSerializer only validates params for user_message. The two new methods accept arbitrary DictField params with no schema enforcement:
def validate(self, attrs):
method = attrs["method"]
params = attrs.get("params", {})
if method == "user_message":
content = params.get("content")
if not content or not isinstance(content, str) or not content.strip():
raise serializers.ValidationError(...)
return attrs
# ← permission_response and set_config_option: no validation
The agent server presumably validates these downstream, but this makes the PostHog API an essentially open proxy for arbitrary JSON payloads to the sandbox, limited only by method name. For defense in depth:
permission_response should at minimum validate expected fields (e.g., id, result)
set_config_option should validate key and value presence
- Stale API description on command endpoint
The @validated_request decorator description still reads:
"Forward a JSON-RPC command to the agent server running in the sandbox. Supports user_message, cancel, and close commands."
This should be updated to include permission_response and set_config_option.
Problem
Plan mode doesn't work in cloud runs — the cloud agent auto-approves all permissions instantly, so plans are never reviewed. There's also no mode switcher UI for cloud sessions.
Changes
initial_permission_modethrough the task run API so cloud sessions start in the correct mode (defaults tobypassPermissionswhen not set — web app/Slack tasks unaffected)initial_permission_modefield andpermission_response/set_config_optioncommandsHow did you test this code?
Publish to changelog?
No