Skip to content

feat: rework permission decision policies#26

Open
kermanx wants to merge 14 commits into
mainfrom
codex/permission-decision-policies
Open

feat: rework permission decision policies#26
kermanx wants to merge 14 commits into
mainfrom
codex/permission-decision-policies

Conversation

@kermanx
Copy link
Copy Markdown
Collaborator

@kermanx kermanx commented May 25, 2026

Summary

  • Rework permission decisions into an ordered policy queue aligned with resolved tool execution metadata.
  • Move approval UI and rule matching data to tool execution outputs, including exact-argument session approval matching.
  • Centralize permission telemetry and remove legacy coarse action/session-rule helpers.
  • Add a changeset for the agent-core and CLI packages.

Testing

  • pnpm --filter @moonshot-ai/agent-core typecheck

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 44be73568f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/agent-core/src/tools/support/rule-match.ts
Comment thread packages/agent-core/src/agent/permission/policies/index.ts Outdated
Comment thread packages/agent-core/src/agent/permission/policies/index.ts Outdated
Comment on lines +7 to +11
import type { PermissionRule, PermissionRuleDecision, PermissionRuleScope } from '../types';

const USER_CONFIGURED_SCOPES = new Set<PermissionRuleScope>([
'turn-override',
'project',
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor session-runtime rules during rule matching

Rule matching now filters to only turn-override, project, and user scopes, so existing session-runtime rules are ignored. That drops previously persisted session approvals on resume/inheritance paths, causing calls that were already approved for-session to prompt again after reload even though the rule data is still present.

Useful? React with 👍 / 👎.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 25, 2026

🦋 Changeset detected

Latest commit: 9ad3a9b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@moonshot-ai/agent-core Minor
@moonshot-ai/kimi-code Minor
@moonshot-ai/migration-legacy Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1f0679b9de

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const pattern = actionToRulePattern(record.action, record.toolName);
this.sessionApprovedActions.add(record.action);
if (pattern === undefined) return;
if (record.sessionApprovalKey === undefined) return;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve session approvals when replaying legacy records

Do not drop approved-for-session records that were written before sessionApprovalKey existed. recordApprovalResult now ignores session approvals when that field is absent, so resuming an older session (same wire protocol version, no migration path) loses previously granted session approvals and re-prompts users for calls that were already approved in-session.

Useful? React with 👍 / 👎.

const cwd = this.agent.config.cwd;
if (cwd.length === 0) return undefined;
const pathClass = this.agent.runtime.kaos.pathClass();
const marker = await this.findGitMarker(cwd);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid probing git markers before confirming file access

Move git-marker lookup behind a file-access check (and/or cache negative lookups), because this call currently runs for every evaluated tool call in manual/yolo mode. In non-git directories, repeated findGitWorkTreeMarker traversals add avoidable filesystem overhead and increase permission-check latency even for tools that do not touch files.

Useful? React with 👍 / 👎.

@kermanx
Copy link
Copy Markdown
Collaborator Author

kermanx commented May 25, 2026

@codex

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4cb74ffced

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

let execution: ToolExecution;
try {
execution = call.tool.resolveExecution(effectiveArgs);
execution = await call.tool.resolveExecution(effectiveArgs);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Run authorization before resolving tool execution

resolveExecution is awaited before runAuthorizeToolExecutionHook, but permission policies now host the PreToolUse block and deny logic. That means side effects in resolveExecution can happen before authorization (for example, ExitPlanMode.resolveExecution reads plan data), and calls that fail during resolve never hit pre-tool hooks at all. In environments relying on PreToolUse for strict pre-execution enforcement/auditing, blocked calls are no longer consistently intercepted before tool setup work runs.

Useful? React with 👍 / 👎.

Comment on lines +302 to +305
execution.display ?? {
kind: 'generic',
summary: execution.description ?? `Approve ${toolName}`,
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve argument detail in generic approval display

The generic approval fallback now only sets a summary and drops tool input details, so approval prompts for tools without a custom display no longer show the actual arguments being approved. In manual-mode ask flows (including third-party/custom tools and built-ins lacking a rich display), users can’t review critical parameters before approving, which weakens approval safety compared with the previous behavior that included full input detail.

Useful? React with 👍 / 👎.

Comment on lines +32 to +33
new UserConfiguredAskPermissionPolicy(agent),
new ExitPlanModeReviewAskPermissionPolicy(agent),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Evaluate plan-review policy before user ask rules

Placing UserConfiguredAskPermissionPolicy ahead of ExitPlanModeReviewAskPermissionPolicy lets generic ask rules short-circuit the specialized ExitPlanMode review flow. When that happens, approval metadata like the selected plan option is never attached, so ExitPlanMode cannot enforce or echo the chosen approach and loses the revise/reject-specific handling provided by the plan-review policy. This regression appears whenever users configure an ask rule that matches ExitPlanMode (for example, broad ask patterns).

Useful? React with 👍 / 👎.

Comment on lines +326 to +327
const executionMetadata = authorization?.executionMetadata ?? decision.metadata;
await dispatchToolCall(step, call, effectiveArgs, displayFields);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply authorize-hook argument rewrites before execution

The new authorizeToolExecution hook shares PrepareToolExecutionResult, but updatedArgs from that hook are ignored and execution always continues with effectiveArgs from the earlier prepare phase. If an integration uses authorize-time rewriting (for example sanitizing or constraining arguments after inspecting resolved execution metadata), the rewrite is silently dropped and the original arguments still run, which can invalidate host safety assumptions.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 67c3d326f7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +45 to +47
if (this.gitMarkerCache.has(cwd)) return this.gitMarkerCache.get(cwd) ?? null;
const marker = await findGitWorkTreeMarker(this.agent.runtime.kaos, cwd);
this.gitMarkerCache.set(cwd, marker);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not cache missing git markers permanently

Avoid storing null in the git marker cache here: when a session starts in a non-git directory and the user later runs git init in the same cwd, this policy will keep returning the cached miss and never re-check the filesystem, so Write/Edit calls continue to bypass the intended git-cwd auto-approval path and keep prompting. Caching only positive markers (or expiring negative entries) preserves expected behavior after repository initialization.

Useful? React with 👍 / 👎.

this.rules = [...(options.initialRules ?? [])];
this.parent = options.parent;
this.policies = options.policies ?? createBuiltinPermissionPolicies();
this.policies = createPermissionDecisionPolicies(this.agent);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve custom permission policy injection

Restore support for caller-supplied permission policies in PermissionManagerOptions; this constructor now always installs createPermissionDecisionPolicies(...), so hosts that previously passed custom options.policies lose their enforcement/approval logic at runtime and silently fall back to the built-in queue. That is a behavioral regression for embedded deployments that relied on bespoke policy gates.

Useful? React with 👍 / 👎.

Comment on lines +70 to +72
if (this.gitMarkerCache.has(cwd)) return this.gitMarkerCache.get(cwd) ?? null;
const marker = await findGitWorkTreeMarker(this.agent.runtime.kaos, cwd);
this.gitMarkerCache.set(cwd, marker);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Re-probe git marker after negative cache results

This policy also caches null git-marker lookups indefinitely, so a session that starts outside a repo and later enters a repo state (for example after git init or after switching to a linked worktree layout) can keep skipping marker-based control-dir detection for the rest of the session. That weakens the git_control_path ask guard for paths that do not literally contain a .git segment and depend on controlDirPath resolution.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9ad3a9bace

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +151 to +154
const sessionApprovalRule =
response.decision === 'approved' && response.scope === 'session'
? context.execution.approvalRule
: undefined;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Fallback session approvals when approvalRule is missing

Session-scoped approvals are now cached only from context.execution.approvalRule, and if that field is absent the cache is never written even when the user explicitly chooses “approve for session”. Because approvalRule is optional on tool executions, this causes repeated prompts for tools that do not provide it (notably custom/third-party tools), which is a regression from the previous behavior where session approvals still persisted via coarse action/tool matching.

Useful? React with 👍 / 👎.

Comment on lines +24 to +25
'edit file': 'Write',
'edit file outside of working directory': 'Write',
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve Edit session approvals in v1.2 migration

The migration maps the legacy "edit file" action to Write for all records, but v1.1 approval records also include toolName and can legitimately come from Edit. After migration, those Edit approvals become Write-only rules and stop matching future Edit calls, so resumed sessions lose previously granted session approval and re-prompt users unexpectedly.

Useful? React with 👍 / 👎.


const LEGACY_SESSION_APPROVAL_ACTION_TO_PATTERN: Readonly<Record<string, string>> = {
'run command': 'Bash',
'run background command': 'BackgroundRun',
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Map legacy background approvals to an existing tool pattern

The v1.2 migration rewrites "run background command" approvals to BackgroundRun, but there is no BackgroundRun tool in this codebase, so migrated session rules can never match any future call. Legacy sessions that previously approved background shell execution for the session will therefore lose that approval after replay and prompt again on every run.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant