import { Context, Service } from '@deepseek-ai/cordis'; import type { Agent } from '@deepseek-ai/dsh-agent'; import type { Session } from '@deepseek-ai/dsh-session'; import Schema from '@deepseek-ai/schemastery'; import type { ToolDefinition, ToolExecution, ToolGuard } from '@deepseek-ai/dsh-tools'; import type { ApprovalRequest } from '@deepseek-ai/dsh-user-approval'; import { type AutoReviewConfig } from './auto-review.js'; import { type HumanApprovalAnswerer } from './approval-routing.js'; import { type ApprovalDecisionInput, type ApprovalDispatchResult, type ApprovalDispatchSnapshot, type ApprovalProposalInput, type ApprovalProposalLookupInput, type ApprovalProposalRecoveryInput, type ApprovalProposalRecoveryResult, type ApprovalDispatchRouteV2, type ApprovalProposalRouteStates, type ApprovalProposalResult, type AuditEvent, type BudgetReservationResult, type ApprovalProposalSnapshot, type EmergencyStopState } from './ledger.js'; import type { PolicyDecision, PolicyInitiator, PolicyRequest, PolicyResource, PolicyRule, PolicySubject } from './types.js'; export interface PolicyBudgetConfig { id: string; metric: string; limit: number; periodMs: number; scope: 'global' | 'subject' | 'workspace'; } export interface Config { databasePath: string; toolDefaultEffect?: 'deny' | 'allow'; autoReview?: AutoReviewConfig; rules?: PolicyRule[]; budgets?: PolicyBudgetConfig[]; proposalMaintenanceIntervalMs?: number; } export interface PolicyBudgetReservationInput { budgetId: string; subject: PolicySubject; amount: number; idempotencyKey: string; } export interface AuthorizationOptions { idempotencyKey?: string; auditDetails?: unknown; } export interface AssistantPolicyServiceOptions { now?: () => number; } export interface OwnerRequestedChangeContext { readonly route: ApprovalDispatchRouteV2; readonly text: string; readonly receiptDigest: string; } export interface OwnerRequestedChangeInput { readonly execution: ToolExecution; readonly resourceKind: 'memory' | 'wiki'; readonly operation: 'add' | 'replace' | 'create' | 'update'; readonly policyIdempotencyKey: string; readonly resolveOwnerRequest: () => Readonly | undefined; /** Host-owned current record/page snapshot for replace and update. */ readonly resolveCurrentTarget?: () => unknown; } /** Read-only, Host-only record of the active policy configuration. */ export interface PolicyHostConfiguration { readonly rules: readonly PolicyRule[]; readonly toolDefaultEffect: 'deny' | 'allow'; readonly budgets: readonly PolicyBudgetConfig[]; readonly autoReview: Readonly | null; } declare module '@deepseek-ai/cordis' { interface Context { assistantPolicy: AssistantPolicyService; } } export type NativeFullReviewerReconciliation = 'not-applicable' | 'ready' | 'unavailable'; export declare class AssistantPolicyService extends Service { static Config: Schema; private readonly policy; private readonly ledger; private readonly budgets; private readonly toolDefaultEffect; private readonly configuration; private readonly initiators; private readonly nativeFullAdoptions; private readonly nativeFullUnsettled; private readonly reviewerEventRegistration; private readonly policyContext; private readonly preauthorizedTools; private readonly humanApprovalRouter; private readonly ownerReviewAbort; private ownerRequestedScope; private active; /** Host channel composition only; the channel retains all owner/call checks. */ registerHumanApprovalAnswerer(answerer: HumanApprovalAnswerer): () => void; /** Query the mounted Policy's exact live handoff, including across package copies. */ isAutoReviewEscalation(request: Readonly): boolean; constructor(ctx: Context, input: Config, options?: AssistantPolicyServiceOptions); /** * Publish the fixed live Auto permission preset when the host supports it. * * In 0.2.0-rc.2 "auto" is a reserved preset name: it cannot appear in the * static table (the service construction throws), and is instead exposed only * while an integration registers the host's fixed bundle through * `permissionPresets.registerAuto(admit)`. The host owns the bundle * (danger-full-access sandbox + "ask" approval on 0.2.0-rc.2) and invokes the * synchronous `admit` gate immediately before selecting or restoring Auto; * per-call risk gating is unrelated and stays in tools/pre-execute. This * adapter therefore only proves the integration is still alive, and hands the * disposer returned by registerAuto() to OUR injected fiber so Auto is * withdrawn the moment this plugin (or the permissionPresets generation) goes * away. Older 0.1.5-rc.3 hosts lack this API and expose their configured * static Auto without dynamic registration. */ private publishAutoPreset; /** * Join or initiate the one process-wide durability barrier for an exact * legacy native-full session. `ready` means its reviewer state crossed the * shared flush barrier; `not-applicable` means no compatibility event was * required; `unavailable` is fail-closed after persistence/compensation * could not establish a usable terminal state. */ reconcileNativeFullReviewer(session: Session): Promise; /** Versioned capability for owner-scoped context tools added after the original registration API. */ contextToolPreauthorizationVersion: () => 1; /** Exact current-owner goal checkpoints may opt into a separate admission contract. */ checkpointToolPreauthorizationVersion: () => 1; /** Exact Skills definitions may authorize only their live isolated benchmark delegation. */ delegatedSkillToolPreauthorizationVersion: () => 1; /** * Register one trusted Host-owned, finite authorization predicate for an * exact live tool definition. The independent monotonic policy guard still * evaluates every execution. A denialReason marks bounds that interactive * approval cannot extend, so a rejected predicate denies without prompting. */ registerPreauthorizedTool(caller: Context, definition: ToolDefinition, authorize: (execution: ToolExecution) => boolean, options?: { denialReason: string; }): () => void; /** * Fail closed unless a live exact tool definition has an active trusted * predicate that accepts this still-live execution. */ isPreauthorizedTool(execution: ToolExecution): boolean; private preauthorizedToolDecision; private nativeFullCandidate; private ensureNativeFullReviewer; private assertActive; /** Returns a detached Host snapshot; it never exposes ledger or maintenance configuration. */ inspectHostConfiguration(): Readonly; evaluate(request: PolicyRequest): PolicyDecision; authorize(request: PolicyRequest, options?: AuthorizationOptions): PolicyDecision; /** Read-only declaration lookup for consumers that must prove metric compatibility before reserving. */ getBudgetConfig(budgetId: string): Readonly | undefined; reserve(input: PolicyBudgetReservationInput): BudgetReservationResult; finalize(reservationId: string, actualAmount: number): BudgetReservationResult; release(reservationId: string): BudgetReservationResult; /** * Crash-recovery settlement keyed by the reservation's idempotency key. * Releases only a still-open reservation; a finalized charge is never * unwound (invalid-state), a missing row is a no-op (undefined). */ releaseByIdempotencyKey(idempotencyKey: string): BudgetReservationResult | undefined; propose(input: ApprovalProposalInput): ApprovalProposalResult; decideProposal(input: ApprovalDecisionInput): ApprovalProposalResult; expireProposals(limit?: number): number; /** * Read one proposal's current status without deciding it. Policy deliberately * never calls back into a domain, so a domain reconciler polls this seam and * then commits the decided outcome through its own approval gate. */ getProposal(proposalId: string): ApprovalProposalSnapshot | undefined; /** * Read-only recovery for the cross-database window where Policy committed a * proposal but the owning domain did not persist its proposal ID. */ getProposalByIdempotencyKey(input: ApprovalProposalLookupInput): ApprovalProposalSnapshot | undefined; /** * Atomic cross-database recovery/creation under an absolute deadline. Once the * deadline passes without a proposal, Policy tombstones the idempotency key so * another process cannot create an orphan approval dispatch later. */ recoverOrCreateProposal(input: ApprovalProposalRecoveryInput): ApprovalProposalRecoveryResult; /** One synchronous domain commit can use a freshly reviewed owner request for one exact proposal. */ runOwnerRequestedChange(input: OwnerRequestedChangeInput, commit: () => T): Promise; listPendingApprovalDispatches(limit?: number, after?: Readonly): ApprovalDispatchSnapshot[]; /** Host-only facts for the current owner route; no diff, summary or reason. */ listProposalStatesForRoute(route: ApprovalDispatchRouteV2, limit?: number): ApprovalProposalRouteStates; markApprovalDispatchEnqueued(proposalId: string, expectedVersion: number): ApprovalDispatchResult; setEmergencyStop(input: { enabled: boolean; actor: string; reason: string; }): EmergencyStopState; getEmergencyStop(): EmergencyStopState; queryAudit(options?: { afterSequence?: number; limit?: number; }): AuditEvent[]; health(): { emergencyStop: boolean; lastAuditSequence: number; }; bindInitiator(agent: Agent, initiator: PolicyInitiator, principal?: string): () => void; authorizeAgent(agent: Agent | undefined, action: string, resource: PolicyResource, options?: AuthorizationOptions): PolicyDecision; /** * Evaluate an agent action against the same live identity and initiator * context as authorization, without auditing or consuming a budget. */ evaluateAgent(agent: Agent | undefined, action: string, resource: PolicyResource): PolicyDecision; authorizeToolExecution(execution: Readonly): PolicyDecision; private auditAgentIdentityFailure; private consumeAuthorizationBudget; private reserveConfiguredBudget; private auditToolIdentityFailure; } export declare function createPolicyToolGuard(service: AssistantPolicyService): ToolGuard; export declare class AssistantPolicyError extends Error { readonly code: 'budget-not-configured' | 'missing-workspace'; constructor(code: 'budget-not-configured' | 'missing-workspace', message: string); } export declare const Config: Schema; //# sourceMappingURL=service.d.ts.map