Skip to content

Pre-Approve System

Reduces operator approval bottleneck in autonomous modes by auto-approving predictable operations via glob-based pattern matching.

PhaseScopeStatus
Phase 1Core pattern matching + isDangerous integration + --pre-approve CLI flagDone
Phase 2Haiku planning pass + tabbed approval UIDone
Phase 3Audit trail (SQLite) + DAG integrationDone

interface PreApprovalPattern {
tool: string; // 'bash', 'write_file', 'read_file', etc.
pattern: string; // Glob: "npm run *", "dist/**"
label: string; // Human-readable description
risk: 'low' | 'medium' | 'high';
}
interface PreApprovalSet {
id: string;
approvedAt: string;
approvedBy: 'operator';
taskSummary: string;
patterns: PreApprovalPattern[];
maxUses: number; // 0 = unlimited
ttlMs: number; // 0 = session-scoped
usageCounts: number[]; // Per-pattern counter
}
  • src/core/pre-approve.tsglobToRegex(), extractMatchString(), matchesPreApproval(), buildApprovalSet()
  • src/tools/permission-guard.tsisDangerous() 4th param preApproval?, inline matching (avoids circular dep)
  • src/core/agent.tspreApproval field, passed to isDangerous()
  • src/index.ts--pre-approve <glob> (repeatable)
  • Critical patterns (CRITICAL_BASH) silently filtered by buildApprovalSet()
  • Session-scoped by default (ttlMs=0)
  • maxUses=10 default
  • Glob-only, no backtracking
  • autoApprovePatterns NOT in PROJECT_SAFE_KEYS
Terminal window
nodyn --pre-approve "npm run *" \
--pre-approve "rm dist/**"

Phase 2 — Haiku Planning Pass + Approval UI (Done)

Section titled “Phase 2 — Haiku Planning Pass + Approval UI (Done)”

Before executing a task in autonomous modes, a fast Haiku planning pass analyzes the goal and proposes pre-approval patterns. The operator reviews them in a tabbed dialog before execution begins.

User: --approve "bash:npm run *" --approve "write_file:dist/**"
┌─── CLI Patterns ────────────┐
│ Build PreApprovalSet from │
│ --approve patterns │
└───────────┬─────────────────┘
Agent runs with approved set
interface ApprovalDialogResult {
approved: boolean;
patterns: PreApprovalPattern[]; // operator-selected subset
maxUses: number;
ttlMs: number;
}
async function showApprovalDialog(
proposed: PlanningResult,
goal: string,
promptTabs: (questions: TabQuestion[]) => Promise<string[]>,
): Promise<ApprovalDialogResult>
  • Tab 1: Goal + Haiku reasoning (read-only)
  • Tab 2: Pattern checkboxes with risk badges (low/medium default checked, high unchecked)
  • Tab 3: Limits — maxUses (5/10/25/unlimited), TTL (session/30min/1h/4h)
  • src/index.ts--auto-approve-all auto-approves low + medium risk patterns
  • src/core/session.ts — Pass apiKey/apiBaseURL to mode controller for planner
  • src/index.ts--no-pre-approve (skip planning), --auto-approve-all (approve low+medium without dialog)
  • Haiku receives only goal text + tool names (no secrets)
  • All proposed patterns filtered through isCriticalTool() (in permission-guard.ts)
  • Operator has final approval via dialog
  • --auto-approve-all only auto-approves low + medium risk
  • Planning failure is never fatal

Phase 3 — Audit Trail + DAG Integration (Done)

Section titled “Phase 3 — Audit Trail + DAG Integration (Done)”

All pre-approval decisions and usage are persisted to SQLite for compliance tracking. The DAG engine supports per-step pre-approval patterns.

  • src/core/pre-approve-audit.ts(deleted) Audit trail functionality consolidated into permission-guard.ts
  • SQLite tables (migration v4):
    • pre_approval_sets — set metadata (id, run_id, patterns JSON, approved_by, task_summary, etc.)
    • pre_approval_events — individual check decisions (set_id, tool, pattern, decision, timestamp)
  • src/tools/permission-guard.ts — 5th audit param on isDangerous(): records approval/exhausted/expired decisions via PreApproveAuditLike
  • src/core/agent.tsaudit field in AgentConfig, passed to isDangerous()
  • DAG per-step pre-approval — manifest steps can declare pre_approve patterns, built into per-step PreApprovalSet
  • /approvals slash command — list sets, show details, audit history, export
  • Observability channels: nodyn:preapproval:match, nodyn:preapproval:exhausted, nodyn:preapproval:expired
  • RunHistoryinsertPreApprovalSet(), insertPreApprovalEvent(), getPreApprovalSets(), getPreApprovalEvents(), getPreApprovalSummary()
  • pre-approve-audit.test.ts(deleted) Tests consolidated into permission-guard.test.ts