Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Action safety: user-approvable ACL / permission gating for dispatched actions

Open
#2,628 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
35/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Quiet
Tech stack
typescript

Research direction

Start with executeAction() in ts/packages/dispatcher/dispatcher/src/execute/actionHandlers.ts, then read TaskPolicy and ApprovalFn in the workflow model and runner for the policy precedent. Review the listed session, agent SDK, schema, and collision-preference files before resolving the open risk-source and precedence questions. Done first means a design note recommending the model and defining its decision and persistence rules.

Written by the indexing model from the issue text.

Description

dispatcher enhancement security

Summary

Introduce a first-class action safety layer in the dispatcher so that potentially sensitive or destructive typed actions can be gated behind user approval and governed by an ACL-style policy. Today, once an action is translated and validated, it is dispatched straight to the owning agent's executeAction() with no permission check and no user confirmation. This issue proposes a mechanism for users to approve actions (with an "always allow" memory) plus an ACL / policy model so we can ship with action safety.

Scope note: This is separate from the reasoning-loop tool-call approval (the approveAll / onPermissionRequest posture in the Claude/Copilot reasoning adapters), which is tracked independently. This issue targets the general dispatcher action-execution path that every agent action flows through.

Motivation

TypeAgent routes natural language into typed actions, many of which have real-world side effects and are irreversible. Examples across current agents:

  • Destructive / side-effecting: sendEmail / replyEmail / forwardEmail (email), removeEvent / scheduleEvent / addParticipant (calendar), prMerge / deleteRepo / deleteIssue / deleteGist (github-cli), removeItems / clearList (list), LaunchProgram / CloseProgram (desktop).
  • Read-only / safe: findEmail, findEvents, getList, listIssues, browseRepo.

Currently all of these execute without confirmation. As agent capabilities and automation grow, users should be able to review and approve sensitive actions, and we need a safety story we can enable in shipped builds.

Goals

  • A single, uniform enforcement point for gating action execution.
  • An ACL / policy model that classifies actions and decides allow / prompt / deny.
  • Approval UX that works across all surfaces (Electron shell, CLI, VS Code shell) with an "always allow" option.
  • Persist approvals at two tiers: current session and per-profile ("always allow this action").
  • Allow-by-default, opt-in gating initially: ship the mechanism without regressing behavior, with a clear path toward stricter defaults.

Non-goals

  • Reasoning-loop tool-call permission (tracked separately).
  • Sandboxing / OS-level isolation of agent code.
  • Network / credential / secrets management.

Current state (research)

Enforcement chokepoint. executeAction() in ts/packages/dispatcher/dispatcher/src/execute/actionHandlers.ts is the single function every action passes through (regular, flow-based, and agent-queued additional actions). It already contains a pre-flight readiness gate (checkAgentReady) that runs "as late as possible, right before we invoke the agent" and can short-circuit with a replacement ActionResult. An approval/ACL gate belongs right next to it, immediately before appAgent.executeAction(action, actionContext). Action identity = schemaName + actionName (agent = getAppAgentName(schemaName)).

No security metadata today.

  • AppAgentManifest (ts/packages/agentSdk/src/agentInterface.ts) has no permission / capability / risk fields.
  • No per-action risk or side-effect categorization exists in any agent.

Reusable primitives.

  • SessionContext.popupQuestion(message, choices?, defaultId?): Promise<number> (ts/packages/agentSdk/src/agentInterface.ts; impl in ts/packages/dispatcher/dispatcher/src/execute/sessionContext.ts) delegates to ClientIO.question() and is already rendered as choice cards in the shell, a terminal menu in the CLI, and a QuickPick in the VS Code shell.
  • "Remember my choice" pattern via createPickRememberChoiceResult() (agentSdk helpers).

Precedents to mirror.

  • Policy model: the workflow engine's TaskPolicy = allow | prompt | deny with ApprovalFn / ApprovalResult and secure-by-default gating (ts/examples/workflow/model/src/taskDefinition.ts, ts/examples/workflow/engine/src/runner.ts).
  • Persistence: CollisionPreferenceStore (collisionPreferences.json in the profile/instance dir) with three-tier resolution (session one-shot -> persisted profile -> interactive) in ts/packages/dispatcher/dispatcher/src/context/collisionPreferences.ts. An action-approval allowlist can mirror this (e.g. actionApprovals.json).
  • Schema metadata: action types already carry JSDoc comments parsed into schema (ts/packages/actionSchema/src/type.ts, parser.ts) — a viable home for author-declared risk tags if we choose that route.

Proposed design

1. Enforcement point

Add an approval/ACL gate inside executeAction() immediately before dispatch, alongside the existing checkAgentReady pre-flight. Because additional/queued actions also flow through here, gating is uniform and per-action.

2. Policy / ACL model

Resolve a decision allow | prompt | deny for each action from a layered policy:

  1. Author-declared default (see open investigation below).
  2. Central / admin policy config (maps schema.action -> mode).
  3. User overrides / ACL allowlist (built from approvals).

Most-specific / most-restrictive layer wins (exact resolution rules TBD).

3. Default posture — allow-by-default, opt-in gating

Ship the mechanism off by default (allow-by-default) so nothing regresses; gating is opt-in via config, with a documented path toward secure-by-default for clearly destructive actions once classification is trustworthy. Add config under the existing execution settings (ts/packages/dispatcher/dispatcher/src/context/session.ts), e.g.:

execution.actionApproval: "off" | "prompt-destructive" | "prompt-all"   // default "off"
execution.actionApprovalAllowlist: string[]                             // "schema.action" auto-approved
4. Approval UX

Reuse popupQuestion / choice cards uniformly across surfaces. When prompting, offer at least Allow once / Deny / Always allow (this action) — the last writes to the persisted allowlist. Render the action (schema.action + key parameters) so the user can decide with context.

5. Approval persistence (two tiers)
  • Session tier: in-memory override consumed for the current session.
  • Profile tier: persist "always allow this action" to a per-profile store (mirror CollisionPreferenceStore -> actionApprovals.json in the instance/profile dir under ~/.typeagent/).
6. OPEN INVESTIGATION — where action sensitivity/risk is declared

Intentionally not decided; part of this work is to evaluate the options and pick one (or a combination):

  • (A) Author annotations — declare risk on actions via manifest capabilities and/or schema JSDoc tags (e.g. @sideEffect, @risk high, @requires email.send). Pros: co-located and precise. Cons: relies on every agent author; needs schema plumbing; unannotated = unknown.
  • (B) Central policy config — a maintained map of schema.action -> risk/mode shipped with the dispatcher. Pros: no agent changes, centrally auditable. Cons: must track every agent/action; drift.
  • (C) User-defined ACL — users classify/allow/deny at runtime, building their own allowlist. Pros: user control, no upfront taxonomy. Cons: cold-start; inconsistent defaults.
  • (D) Heuristic / inferred — infer side-effects from action verb/name or (future) effect-inference. Pros: zero config. Cons: unreliable.

Likely outcome: a combination (author-declared default + user ACL override, with an optional central policy). Deliverable: a short design note recommending the approach with tradeoffs.

Open questions

  • Resolution/precedence rules when layers disagree.
  • Risk taxonomy: binary (safe/destructive) vs. graded levels.
  • How to treat "unknown risk" actions under allow-by-default.
  • Whether deny is silent or surfaced; auditing/logging of decisions.
  • Behavior in headless / non-interactive / automated contexts (no user to prompt).
  • Scope of "always allow": per action, per action+params, or per agent?
  • Telemetry for gated / approved / denied actions.

Proposed implementation (phased)

  • Phase 0 — Investigation: Evaluate risk-source options (A–D); produce a short design note + recommendation. Define the ACL decision model and resolution rules.
  • Phase 1 — Enforcement scaffold: Add the gate in executeAction() (allow-all no-op by default) + execution.actionApproval config. Wire popupQuestion-based approval with Allow once / Deny.
  • Phase 2 — Persistence: Add session + profile approval store (mirror CollisionPreferenceStore); "Always allow" writes to the profile allowlist; add allowlist config.
  • Phase 3 — Classification: Implement the chosen risk-source for a first set of agents (email, calendar, github-cli, list, desktop). Enable prompt-destructive opt-in.
  • Phase 4 — Surfaces & polish: Consistent UX across shell / CLI / VS Code shell; render action + params; telemetry; docs.
  • Phase 5 — Path to default-on: Once classification is trustworthy, propose secure-by-default for destructive actions.

Acceptance criteria

  • A single dispatcher-level gate governs all dispatched actions; disabled by default (no behavior change).
  • With gating enabled, destructive actions prompt the user; "Always allow" persists across sessions per profile; "Allow once" is session-scoped.
  • Approval UX works on the Electron shell, CLI, and VS Code shell.
  • Config documented; decisions logged/traceable; unit tests for the decision model and persistence.

Related code

  • ts/packages/dispatcher/dispatcher/src/execute/actionHandlers.ts — executeAction() chokepoint (and checkAgentReady precedent).
  • ts/packages/agentSdk/src/agentInterface.ts — AppAgentManifest, SessionContext.popupQuestion.
  • ts/packages/dispatcher/dispatcher/src/execute/sessionContext.ts — popupQuestion implementation.
  • ts/packages/dispatcher/dispatcher/src/context/collisionPreferences.ts — persistence precedent.
  • ts/packages/dispatcher/dispatcher/src/context/session.ts — execution config.
  • ts/packages/actionSchema/src/type.ts, ts/packages/actionSchema/src/parser.ts — schema comment/metadata extraction.
  • ts/examples/workflow/model/src/taskDefinition.ts, ts/examples/workflow/engine/src/runner.ts — TaskPolicy / ApprovalFn precedent.
Dominant language
TypeScript
Stars
744
Forks
107
Avg merge
1d 59m
Merged PRs (30d)
113

Getting set up

Open in Codespaces

Starts the project's dev container in your browser, under your own GitHub account.

  • No Dockerfile or Docker Compose file
  • No pull request template
  • No contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from microsoft/TypeAgent

All issues in microsoft/TypeAgent

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.