import { getConfigSnapshot } from '../config/index.js'; import type { PermissionMode, BackgroundAgentsMode } from '../config/schema.js'; import type { PermissionAttribution, PermissionRequestHandler } from './prompt.js'; import type { UserPermissionRuleStore } from './user-rule-store.js'; import type { PolicyRuntimeState } from '../runtime/permissions/policy-runtime.js'; import type { DecisionOtlpConfig } from '../runtime/permissions/decision-otlp.js'; import type { FeatureFlagManager } from '../runtime/feature-flags/index.js'; import type { HookDispatcher } from '../hooks/index.js'; import type { ConfigManager } from '../config/manager.js'; import type { PermissionCategory, PermissionCheckResult } from './types.js'; export type { PermissionMode } from '../config/schema.js'; export type { PermissionCategory, PermissionRiskLevel, PermissionDecisionSource, PermissionDecisionReasonCode, PermissionRequestAnalysis, PermissionCheckResult, } from './types.js'; /** * The slice of config the permission layer reads, `.permissions` and nothing * else. * * `getConfigSnapshot` returns the whole `GoodVibesConfig`, and this alias used * to be exactly that, so `PermissionConfigReader` declared a dependency on * every config domain in the product while its two consumers (both in this * file) read `getSnapshot().permissions`. A stand-in reader had to produce all * fifty-odd domains to satisfy it. Narrowing costs the real implementation * nothing: `createPermissionConfigReader` still hands back the full snapshot, * which remains assignable. */ type PermissionConfigSnapshot = Readonly, 'permissions'>>; export interface PermissionConfigReader { isAutoApproveEnabled(): boolean; getSnapshot(): PermissionConfigSnapshot; getWorkingDirectory(): string | null; /** * The `telemetry.decisionOtlp*` keys, as the exporter's own config shape. * * A method of its own rather than three more domains on * {@link PermissionConfigSnapshot}, so the narrowing above survives: a * stand-in reader that does not care about decision export omits this and * exports nothing, which is also the shipped default. */ getDecisionOtlpConfig?(): DecisionOtlpConfig; } export declare function createPermissionConfigReader(configManager: Pick): PermissionConfigReader; /** * PermissionManager - Controls tool execution approval. * * Approval logic (priority order): * 1. --no-worries-just-vibes flag OR mode='allow-all' -> auto-approve everything * 2. mode='custom' -> check per-tool config action ('allow'/'prompt'/'deny') * 3. mode='prompt' (default) -> auto-approve reads, prompt for writes/execute/delegate * 4. Session approval cache hit -> use cached decision * 5. Ask the shell-owned permission controller and block until user responds */ export declare class PermissionManager { private sessionApprovals; private readonly requestPermission; private readonly configReader; private readonly hookDispatcher; private readonly policyRuntimeState; private readonly featureFlags; private readonly userRuleStore; constructor(requestPermission: PermissionRequestHandler | undefined, configReader: PermissionConfigReader, policyRuntimeState: Pick, hookDispatcher?: Pick | null, featureFlags?: Pick | null, userRuleStore?: Pick | null); /** * check - Returns a Promise that resolves to true (approved) or false (denied). * Blocks orchestrator until the user responds when a prompt is needed. */ check(toolName: string, args: Record, attribution?: PermissionAttribution): Promise; /** * @param attribution When present, rides on the brokered ask so a surface can * render which background agent is asking. Only reaches the ask path * (prompt/session-cache-miss); auto-approve/deny short-circuits ignore it. */ checkDetailed(toolName: string, args: Record, attribution?: PermissionAttribution): Promise; /** * previewReadAccess, non-interactive answer to "would a `read` of `path` be * auto-allowed right now, WITHOUT prompting?" Returns 'allow' when it would be * auto-allowed and 'restricted' otherwise (a would-prompt/ask path or an * outright deny). Search / list / map tools call this per candidate file so * their results never surface CONTENT the read tool itself would gate behind * an ask/deny (e.g. the shipped credential-read defaults). * * It runs the SAME layered decision as {@link checkDetailed} up to the ask * boundary, the same mode logic, the same isGatedCredentialRead check (→ * matchesShippedCredentialReadPath), and the same policy evaluator + mapping, * so it can never drift from a parallel path matcher. It never prompts, caches, * records, or fires hooks; it is a pure read of current config + rules. */ previewReadAccess(rawPath: string): 'allow' | 'restricted'; /** * getMode, Returns the active session permission mode from config. * Surfaces (mode pill) and the orchestrator's standing plan-mode instruction * read this to reflect the current mode. Defaults to 'prompt' ("normal"). */ getMode(): PermissionMode; /** * getBackgroundAgentsMode, how background/subagent tool calls consult this * manager. 'inherit' (default): apply the session mode exactly like foreground. * 'allow-all': background agents are exempt (auto-approve). Read by the agent * runner before it gates a background tool call. */ getBackgroundAgentsMode(): BackgroundAgentsMode; /** Returns the permission category for a tool name. Unknown tools default to 'delegate'. */ getCategory(toolName: string, args?: Record): PermissionCategory; /** * getApprovalKey - Stable key for session-level "always approve" decisions. * Includes the most meaningful argument to distinguish different invocations. */ private getApprovalKey; private result; /** * A read whose path names a well-known credential store, which the shipped * default protects: it must not be SILENTLY auto-allowed. Returns false for * non-read categories and for paths that do not match a credential store. A * user can still override by approving (session cache), switching to allow-all, * or adding a user allow-rule. */ private isGatedCredentialRead; private evaluateRuntimePolicy; /** * Hand this evaluation's decision-log records to the OTLP exporter. * * `runtime/permissions/decision-otlp.ts` was fully built, attribute mapping, * both record shapes, the POST, the off-by-default guards, and called from * nowhere, so `telemetry.decisionOtlpEnabled` promised an export that could * not happen. This is the seam where a decision comes into existence: the * evaluator is constructed per evaluation, so its log holds exactly the * records this call produced. * * Fire-and-forget, and deliberately so. `exportDecisions` never throws and * reports its own failures, and a permission decision must not wait on a * collector: an unreachable endpoint would otherwise stall every tool call. * One record per request rather than a batch, because a batch would mean * holding decisions back from a collector to save round trips on a path that * is off unless an operator asked for it. */ private exportDecisionRecords; private mapEvaluatorDecision; private emitAndReturn; private fireHook; } //# sourceMappingURL=manager.d.ts.map