@rulvar/core 1.169.0 → 1.171.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.ts +71 -12
  2. package/dist/index.js +455 -412
  3. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -764,7 +764,13 @@ interface ProviderCallRecord {
764
764
  /** The invocation phase that paid the call. */
765
765
  role: InvocationRole;
766
766
  servedBy: ModelRef;
767
- /** 1-based try number on the serving target; retries increment it. */
767
+ /**
768
+ * 1-based DISPATCHED try number on the serving target; transport
769
+ * retries increment it, a pre-wire quota denial never does (RV1601),
770
+ * so the recorded attempts of one (role, target) series are always
771
+ * dense from 1 and an attempt=2 row proves a prior dispatched try
772
+ * with its own record.
773
+ */
768
774
  attempt: number;
769
775
  /**
770
776
  * 'ok' = a terminal finish; 'error' = a wire failure after dispatch
@@ -4067,6 +4073,19 @@ interface EngineQuotaConfig {
4067
4073
  */
4068
4074
  reserveContinuations?: boolean;
4069
4075
  /**
4076
+ * The denial retry budget (RV1601): how many pre-wire quota denials
4077
+ * one dispatch tolerates per serving target before the denial takes
4078
+ * the exhaustion path (failover when the chain names a rate-limit
4079
+ * trigger, else the typed rate-limit terminal). Denials stopped
4080
+ * consuming `RetryPolicy.attempts` in RV1601: that budget counts
4081
+ * DISPATCHED tries only, so a busy window can no longer exhaust the
4082
+ * transport budget before the wire ever opens (the eighteenth
4083
+ * comparison benchmark measured 21 denials riding the transport
4084
+ * namespaces). Each denied turn still waits the limiter's own
4085
+ * `retryAfterMs` first. Default {@link DEFAULT_MAX_QUOTA_DENIALS}.
4086
+ */
4087
+ maxDenials?: number;
4088
+ /**
4070
4089
  * The drift telemetry opt-in (the v1.71 experiment review, P0.5
4071
4090
  * resized): the SAME rule declaration `preflightEstimate` takes as
4072
4091
  * `quotaRules`, mirrored here so the engine can hold it against what
@@ -4086,6 +4105,14 @@ interface EngineQuotaConfig {
4086
4105
  */
4087
4106
  declaredRules?: readonly QuotaRule[];
4088
4107
  }
4108
+ /**
4109
+ * The default {@link EngineQuotaConfig.maxDenials}: generous next to the
4110
+ * transport default of 3 tries because a denial is a WAIT, not a
4111
+ * failure signal, yet finite because nothing else bounds the pre-wire
4112
+ * loop (the per-agent timeout is checked between turns, not inside a
4113
+ * dispatch).
4114
+ */
4115
+ declare const DEFAULT_MAX_QUOTA_DENIALS = 8;
4089
4116
  /** The resolved engine-side quota runtime threaded into every run. */
4090
4117
  interface EngineQuotaRuntime {
4091
4118
  limiter: QuotaLimiter;
@@ -4093,6 +4120,8 @@ interface EngineQuotaRuntime {
4093
4120
  onLimiterError: "deny" | "allow";
4094
4121
  /** Pre-wire continuation admission (RV1013); see {@link EngineQuotaConfig}. */
4095
4122
  reserveContinuations: boolean;
4123
+ /** The per-target denial retry budget (RV1601); see {@link EngineQuotaConfig}. */
4124
+ maxDenials: number;
4096
4125
  /** The declared rule mirror for drift telemetry; see {@link EngineQuotaConfig}. */
4097
4126
  declaredRules?: readonly QuotaRule[];
4098
4127
  }
@@ -4856,9 +4885,14 @@ interface AgentResult<T> {
4856
4885
  abortClass?: AbortClass;
4857
4886
  /**
4858
4887
  * Transport retries across the span's phase activations, present only
4859
- * when greater than zero. Live telemetry only: the ctx layer surfaces
4860
- * it as `agent:end` retryCount; it is never journaled, so a replayed
4861
- * result omits it (absent means "zero or unknown").
4888
+ * when greater than zero. Counts retries of DISPATCHED attempts only
4889
+ * (RV1601): a pre-wire quota denial never increments it, so this
4890
+ * number can be read against the provider ledger without correction
4891
+ * (the eighteenth comparison benchmark exported 21 denials under this
4892
+ * name over an invoice with zero provider error rows). Live telemetry
4893
+ * only: the ctx layer surfaces it as `agent:end` retryCount; it is
4894
+ * never journaled, so a replayed result omits it (absent means "zero
4895
+ * or unknown").
4862
4896
  */
4863
4897
  transportRetries?: number;
4864
4898
  /**
@@ -5166,12 +5200,14 @@ interface RunAgentOptions<S extends SchemaSpec = JsonSchema> {
5166
5200
  * failover takeovers alike, in every phase). A denial becomes a
5167
5201
  * synthetic rate-limit-class WireError the retry and failover
5168
5202
  * engine treats exactly like a provider 429, except no wire call
5169
- * was paid: retryAfterMs drives the interruptible backoff, attempts
5170
- * stay bounded by RetryPolicy, and exhaustion fails over (the
5171
- * takeover reserves under its own model). Granted reservations are
5172
- * reconciled with the attempt's actual usage after the outcome
5173
- * settles. Live-only by construction: replayed calls never reach
5174
- * this seam, and nothing here is journaled.
5203
+ * was paid: retryAfterMs drives the interruptible backoff, denied
5204
+ * turns stay bounded by their OWN `maxDenials` budget (RV1601;
5205
+ * RetryPolicy.attempts counts dispatched tries only), and
5206
+ * exhaustion of either budget fails over (the takeover reserves
5207
+ * under its own model). Granted reservations are reconciled with
5208
+ * the attempt's actual usage after the outcome settles. Live-only
5209
+ * by construction: replayed calls never reach this seam, and
5210
+ * nothing here is journaled.
5175
5211
  */
5176
5212
  quota?: {
5177
5213
  reserve: (request: QuotaReservationRequest) => Promise<QuotaDecision>;
@@ -5179,7 +5215,8 @@ interface RunAgentOptions<S extends SchemaSpec = JsonSchema> {
5179
5215
  requests?: number;
5180
5216
  }) => Promise<void>; /** Limiter infrastructure failure policy; a denial is unaffected. */
5181
5217
  onLimiterError: "deny" | "allow"; /** Pre-wire continuation admission (RV1013); default post-hoc. */
5182
- reserveContinuations?: boolean; /** Cancels an unused admission; absent = window age-out. */
5218
+ reserveContinuations?: boolean; /** The per-target denial retry budget (RV1601); default 8. */
5219
+ maxDenials?: number; /** Cancels an unused admission; absent = window age-out. */
5183
5220
  release?: (reservationId: string) => Promise<void>;
5184
5221
  };
5185
5222
  /** The resolved toolset; absent = no tools declared. */
@@ -10839,6 +10876,28 @@ interface McpConfig {
10839
10876
  listMs?: number;
10840
10877
  callMs?: number;
10841
10878
  };
10879
+ /**
10880
+ * streamable-http only (RV1516): headers injected into EVERY wire
10881
+ * request through a wrapped fetch. The hook form is awaited before
10882
+ * each send, so it IS the refresh point: rotate a token in the hook
10883
+ * and the next request carries it, with no reconnect and no
10884
+ * library-invented 401 retry (transport failures surface exactly as
10885
+ * before; the engine's RetryPolicy owns retries).
10886
+ */
10887
+ http?: {
10888
+ headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
10889
+ };
10890
+ /**
10891
+ * What a listChanged notification means for THIS source (RV1516).
10892
+ * 'rekey' is the documented default: the session cache invalidates
10893
+ * and subsequently spawned agents import the changed list under a new
10894
+ * toolsetHash. 'refuse' fails closed instead: the notification
10895
+ * poisons the source, every later tools() call refuses typed, and
10896
+ * only close() (a deliberate host reset) clears it. In-flight spawn
10897
+ * snapshots are untouched either way. Composes with the toolset
10898
+ * attestation: refuse at the source vs refuse at the spawn.
10899
+ */
10900
+ drift?: "rekey" | "refuse";
10842
10901
  }
10843
10902
  /**
10844
10903
  * The ToolSource returned by {@link mcp}: the frozen ToolSource seam
@@ -12653,4 +12712,4 @@ interface SandboxBridge {
12653
12712
  declare const SANDBOX_AGENT_OPT_KEYS: readonly string[];
12654
12713
  declare function createSandboxBridge(ctx: Ctx<never>, options: SandboxBridgeOptions): SandboxBridge;
12655
12714
  //#endregion
12656
- export { AWAIT_SCHEMA, AbandonAttempt, AbandonFold, AbandonPayload, AbandonedSpendView, AbortClass, AcceptanceChildSummary, type AdaptiveEvents, AdmissionController, AdmissionDecision, AdmissionRejectedError, AdmissionStatsBefore, AdmitLineage, AdmitRejectReason, AdmitSpec, AdmitVerdict, AgentCallError, AgentError, type AgentEvents, AgentIdentityInput, type AgentInvocationRow, AgentOpts, AgentProfile, AgentProfilePermissions, AgentProfileTemplateOptions, AgentResult, AgentResultMeta, AgentStatus, type AppliedPricingRow, ApproachSignatureInputs, ApprovalDecision, ApprovalIdentityInput, Artifact, AttemptOutcomeClass, AuditCategory, AuditRecord, AuditRunsOptions, BUDGET_ABORT_REASON, BaseAppend, BriefOpts, BudgetAccountView, BudgetDefaults, BudgetExhaustedError, BudgetExhaustionDiagnostics, BudgetHooks, BudgetReserve, type Bytes, CANCEL_AGENT_SCHEMA, CHECKPOINT_FORMAT_V1, CLAIM_STATEMENT_MAX_CHARS, CLAIM_TTL_DAYS, COMPACTION_SUMMARY_PREFIX, CURRENT_HASH_VERSION, CacheHint, CacheTtl, CanUseTool, CanonicalId, CanonicalIdentity, CanonicalLadderSpec, CanonicalModelSpec, ChatEvent, ChatRequest, CheckpointState, ChildArtifactPage, ChildExecutionFacts, ChildIdentityInput, ChildResultPage, CitationTarget, type ClaimClass, ClaimContradictionFinding, type ClaimOp, ClaimPair, ClaimPairOptions, ClaimPairsFold, ClaimPoolReading, type ClaimStatus, ClaimValidationOptions, CollectOpts, CollectedTurn, CompactionConfig, CompiledPermissionChain, CompiledWorkflow, ConfigError, Contradiction, ContradictionClaim, ContradictionOptions, ContradictionSource, type CoreEvents, CostAttribution, CostAttributionFacts, type CostBasis, CostReport, CreateEngineOptions, type CriticalPath, Ctx, DEFAULT_ANCHOR_PATTERN, DEFAULT_ARTIFACT_PATTERN, DEFAULT_CHILD_BUDGET_FRACTION, DEFAULT_CHILD_RESULT_PAGE_CHARS, DEFAULT_CITATION_PATTERN, DEFAULT_CITATION_SAMPLE, DEFAULT_CLAIM_JUDGE_MAX_TURNS, DEFAULT_COMPACTION_THRESHOLD, DEFAULT_ESCALATION_LIMITS, DEFAULT_EVIDENCE_CALLS_PER_ENTRY, DEFAULT_EVIDENCE_GRADE_PHRASES, DEFAULT_EVIDENCE_MIN_SHARE, DEFAULT_EVIDENCE_OVERHEAD_CALLS, DEFAULT_FINISH_MAX_REPAIRS, DEFAULT_FLAT_RESERVE_USD, DEFAULT_MAX_CHILDREN_PER_NODE, DEFAULT_MAX_CLAIM_PAIRS, DEFAULT_MAX_CONTRADICTIONS, DEFAULT_MAX_DEPTH, DEFAULT_MAX_EXCERPT_CHARS, DEFAULT_MAX_OSCILLATIONS_PER_KEY, DEFAULT_MAX_PAIR_EXCERPT_CHARS, DEFAULT_MAX_PINNED_WORKTREES, DEFAULT_MAX_POOL_PER_PAIR, DEFAULT_MAX_REVISIONS_PER_RUN, DEFAULT_MAX_TOTAL_SPAWNS, DEFAULT_MAX_TURNS, DEFAULT_MODEL_RETRY_ATTEMPTS, DEFAULT_NO_PROGRESS_TURNS, DEFAULT_PER_RUN_CONCURRENCY, DEFAULT_RETRY_POLICY, DEFAULT_STREAM_IDLE_TIMEOUT_MS, DEFAULT_SYNTHESIS_MAX_TURNS, DEFAULT_SYNTHESIS_NOTE_MAX_TURNS, DataKeyProvider, DebitResult, DeclaredLadder, DedupIndex, DedupNote, DedupedClaims, DerivedKey, DeriverRegistry, type DeterminismConfig, DeterminismError, type DeterminismEvents, type DeterminismMode, DispositionRule, DispositionTable, DocumentedRates, DonorCandidate, DonorRef, DroppedItem, EMIT_RESULT_TOOL, EMPTY_SCHEMA_HASH, EMPTY_TOOLSET_HASH, ESCALATE_TOOL_NAME, ESCALATION_REPORT_SCHEMA, ESCALATION_REQUEST_SCHEMA, EVENT_SEGMENT_STRIDE, EffectiveUsageLimits, Effort, Engine, EngineDefaults, EngineQuotaConfig, EngineQuotaRuntime, EntryBillingFold, EntryBillingUnit, EntryKind, EntryRef, EntryStatus, EnvelopeEncryption, EnvelopeEncryptionOptions, ErrorClass, ErrorCode, ErrorPolicy, EscalatedResult, EscalationDecision, EscalationDecisionAbortedError, EscalationDigest, EscalationKind, EscalationLimits, EscalationOptions, EscalationReport, EscalationRequest, EventBus, EvidenceContract, type EvidenceRef, type ExecKeyDerivation, type ExecutorRegistry, type ExplorationSummary, ExtensionAppendInput, ExtensionDispatchSpec, ExternalIdentityInput, ExternalRegistry, ExtractNecessityInput, FINALIZE_SYNTHESIS_INSTRUCTION, FINISH_SCHEMA, FINISH_SECTIONAL_SCHEMA, FINISH_TOOL_NAME, FailRunError, FailoverTarget, FailoverTrigger, FallbackField, FallbackTrigger, FencedCodeMode, FileModelKnowledgeStore, FileModelKnowledgeStoreOptions, FileTranscriptStore, type FinalizationWindowBudget, FinishContract, FinishContractCitations, FinishContractGoldenReject, FinishContractManifest, FinishInfo, FinishSelfTestFailure, FinishSelfTestFixtures, FinishSelfTestReport, FinishValidationChild, FinishValidationInput, FinishValidationSpec, FinishValidationVerdict, FinishValidator, GET_CHILD_RESULT_SCHEMA, GET_CHILD_RESULT_TOOL_NAME, Gate, GateAudit, type GateRecord, GitWorktreeProvider, GitWorktreeProviderOptions, GraftBoot, HashVersion, HookVerdict, IMPLEMENTATION_PROFILE_LIMITS, INBOX_PROPOSAL_TTL_DAYS, IN_FLIGHT_EXPOSURE_REFUSAL_PREFIX, IdentityInput, InMemoryStore, InMemoryTranscriptStore, InProcessRunner, IncrementalSynthesisResult, InvalidResolutionError, InvocationRole, type InvocationTable, InvoiceCardinality, InvoiceExport, InvoicePricingProvenance, InvoiceReconciliation, InvoiceRow, type IsolatedExecContext, type IsolatedExecRequest, type IsolatedExecutorTag, type IsolationProvider, type IsolationSpec, Issue$1 as Issue, JOURNAL_ENVELOPE_MARKER, JournalCompatSubCode, JournalCompatibilityError, JournalEntry, JournalMatcher, JournalMissError, JournalOperation, JournalOrderViolation, type JournalPricingSnapshot, JournalSerializationContext, JournalSerializationHook, type JournalStore, type Json, JsonSchema, JsonlFileStore, KB_ACTIVE_CLAIMS_CAP, KB_CARD_RENDER_BUDGET_CHARS, type KbProposal, type KbProposalTrigger, KeyDeriver, KeyRing, KeyedLimiter, KnowledgeCasError, type KnowledgeSnapshot, LARGE_VALUE_WARN_BYTES, LEGACY_LTID_PREFIX, LEGACY_SIGNATURE_INPUTS, LINEAGE_SIG_VERSION, LadderSpec, type LeasableStore, type Lease, LeaseHeldError, Ledger, LineageCounters, LineageIndex, LineageRef, LineageRelation, LineageStats, LogicalTaskId, MASKED_SECRET, MAX_CHILD_RESULT_PAGE_CHARS, MAX_DEPTH_CEILING, MAX_RUN_ID_LENGTH, MAX_TIMER_DELAY_MS, MatchResult, McpConfig, McpToolSource, MechanicalGateProfile, MechanicalGateVerdict, MemoryQuotaLimiter, type MetaLookupStore, type ModelCaps, ModelChoice, type ModelClaim, ModelEpochInputs, type ModelKnowledgeHandle, type ModelKnowledgeStore, ModelListConstraint, ModelRef, ModelRetry, ModelSpec, Msg, NoProgressDetector, NodeId, NodeLinkValue, NonSerializableValueError, ORCHESTRATE_WORKFLOW_NAME, OnEscalation, OperationDisposition, OrchestrateAcceptance, OrchestrateClaimConsistency, OrchestrateClaimConsistencyMeta, OrchestrateContradictions, OrchestrateContradictionsMeta, OrchestrateOptions, OrchestrateSynthesis, OrchestrateSynthesisSkipReason, OrchestratorBudgetSpec, OrchestratorCapConfigError, OrchestratorExtension, OrchestratorExtensionIO, OrchestratorRuntime, Out, PARALLEL_AGENTS_SCHEMA, PROGRESS_REPORT_TOOL_NAME, ParallelSiteCounter, Part, PendingExternal, PendingToolTurn, PermissionConfig, PermissionGate, PermissionHook, PermissionPreset, PermissionRule, PermissionVerdict, PersistedTerminalRefusal, PersistedTerminalResult, type PhaseRow, PhaseTarget, type PinnedPricingSegment, PipelineCollected, PipelineOpts, PlanInvariantError, type PostFanInBreakdown, PreflightAdmissionRow, PreflightFinding, PreflightInput, PreflightOrchestratorSpec, PreflightReport, PreflightSpawnReport, PreflightSpawnSpec, PreflightToolCeiling, PriceTable, PricedComponent, PricedComponents, PricedUsage, type Pricing, type PricingTier, ProgressReport, type ProviderAdapter, ProviderCallRecord, QUOTA_WINDOW_MS, QualityFloors, QuotaCounters, type QuotaDecision, type QuotaEstimate, type QuotaLimiter, type QuotaReservationRequest, QuotaRule, QuotaWindowSnapshot, READ_CHILD_ARTIFACT_SCHEMA, READ_CHILD_ARTIFACT_TOOL_NAME, RESEARCH_PROFILE_LIMITS, REVIEW_PROFILE_LIMITS, ROLE_EFFORT_DEFAULTS, ROOT_ACCOUNT, ROOT_SCOPE, RUN_PROFILES, RUN_SETTLE_DECISION_TYPE, RandIdentityInput, RandPayload, RateLimitObservation, ReconcileOptions, ReconcileResult, RefEntryAppender, RefEntryClassification, RefusalInfo, RepeatedClaim, ReplayDisposition, ReplayMode, ReplayPlanHashMismatch, Replayer, RepositoryResearchToolset, RepositoryResearchToolsetOptions, ResearchAgentProfileOptions, ResearchAgentProfileResult, ResearchEvidenceEntry, ResolutionArbiter, ResolutionAttempt, ResolutionBy, ResolutionFold, ResolutionLayer, ResolutionOutcome, ResolutionPayload, ResolvedInvocation, ResolvedToolset, ResumeHandle, ResumeOptions, ResumePreview, ResumeReport, RetryClass, RetryPolicy, ReuseConfig, RiskRuleValue, Role, RulvarError, RulvarErrorCode, RunAgentOptions, RunAuditVerdict, RunBudget, RunEventSink, RunExport, type RunFilter, RunHandle, RunInternals, type RunMeta, RunOptions, RunOutcome, RunProfile, RunStateAudit, RunStatus, RuntimeEventSink, SANDBOX_AGENT_OPT_KEYS, SPAWN_AGENT_SCHEMA, SandboxBridge, SandboxBridgeOptions, SandboxError, SandboxHostToWorker, SandboxMethod, SandboxWorkerToHost, SchemaPair, SchemaSpec, SchemaValidationResult, ScopeSegment, ScriptRejected, ScriptRunner, ScrubNote, SecretMasker, SectionMatchMode, Semaphore, SerializationHook, Settled, SettlementError, ShellPatternRules, ShellSegment, ShellVerdict, SinglePhaseAppend, SpanMinter, SpanRegistry, SpawnAdmissionValue, SpawnAgentParams, SpawnKey, SpawnLineage, SpawnLineageOpt, SpawnOrigin, SpawnRecord, Spend, Stage, type StandardJSONSchemaV1, type StandardSchemaV1, StepIdentityInput, type StreamHooks, StructuredOutputTier, SupersededError, SuspendedAppend, SuspensionState, TOOL_NAME_PATTERN, type TaskClass, TaskDigest, TaskSpec, type TerminalEnvelope, TerminalOutcomeFacts, TerminalPatch, TerminationAccount, TerminationAccountSnapshot, TerminationDeniedValue, TerminationDeniedWriter, TerminationInitValue, TerminationLimits, TerminationResource, type ToolBudgetSummary, ToolCallRequest, ToolChoice, type ToolContext, ToolContextSeed, ToolContract, type ToolDef, type ToolEvents, type ToolExecutor, type ToolExecutorProvider, ToolInit, type ToolRisk, ToolRuntime, type ToolSource, type ToolSourceSession, ToolsOption, ToolsetAttestation, TranscriptSerializationHook, type TranscriptStore, TriggerClass, TtlState, Usage, UsageLimits, UsageSlice, VerifiedRecommendation, WAIT_FOR_EVENTS_SCHEMA, WAIT_FOR_EVENTS_TOOL_NAME, WAKE_SUMMARY_RENDER_BUDGET_CHARS, WakeBudgetBlock, WakeDigest, WakeTrigger, WireError, Workflow, WorkflowCallOpts, type WorkflowEvent, type WorkflowEventBody, WorkflowRegistry, accountSpendFromJournal, admissionReserveUsd, affordableOutputTokens, agentErrorFromWire, agentErrorToWire, agentResultWire, agentScope, applyClaimOps, applyStructuredOutputTier, approachSigCoarse, approachSigOf, archiveDeprecatedModelOps, assertFencedWrites, assertSafeRunId, atCompactionThreshold, attestToolset, auditRun, auditRuns, buildAbandonFold, buildAdapterRegistry, buildCostReport, buildDeriverRegistry, buildOrchestratorTools, buildTerminationInitValue, buildToolContext, canRideLoopTurn, canonicalIsolationTag, canonicalizeLadder, canonicalizeSchema, capIssues, capsHashOf, checkFloors, checkpointRefFor, childCoveragePrefix, citationTargetsValidator, citedValueValidator, claimExpired, claimExpiry, claimIssues, claimOpIssues, classifyAgentError, classifyAttemptOutcome, collectDeclaredLadders, compactMessages, compareRates, compilePermissionChain, compilePermissionPreset, compileSecretMasker, compileVerifiedLayer, constantTimeEqual, costReportFromJournal, countsAgainstLimit, createCanonicalIdMinter, createCtx, createEngine, createEnvelopeEncryption, createSandboxBridge, currentOnlyKeyRing, decodeCheckpoint, dedupeRepeatedClaims, defineWorkflow, deriveContentKey, deriverV1, deriverV2, digestOf, dispatchProjectionReserveUsd, dispositionHook, emptyDigestBlocks, emptyToolset, encodeCheckpoint, enforceToolsetAttestation, entryUsageSlices, escalateTool, evaluatePermission, evaluateReuse, evidenceGradeValidator, evidencePreservedValidator, executeWorkflow, executionFactsOf, exhaustionCodeOf, extractCandidate, failoverTriggerOf, fallbackTriggerOf, filterClaimsForRun, finalizeFires, findContradictions, finishContract, foldLedger, foldTermination, formatCharacterValidator, formatRePrompt, formatScopePath, hasFencedWrites, hasMetaLookup, hashRunArgs, hashRunOutput, hashWorkflowBody, hashWorkflowSource, headingStructureValidator, identityJcs, implementationAgentProfile, invoiceFromJournal, isEscalated, isSchemaPairSpec, isStandardSchemaSpec, isStrictCompatibleSchema, journalPricingSnapshot, kMaxOf, knowledgeHash, ladderLengthOf, ladderRungChoice, lastRunSettle, latestProgressReport, lexShellCommand, liftRetainedParts, lineageWeightOf, localKeyProvider, makeOrchestratorWorkflow, maskSecrets, maskSecretsDeep, maskSecretsJson, matchArgvPattern, matchShellCommand, mcp, memoryQuotaLimiter, mergeQuotaDenial, mergeUsageLimits, metaMatchesFilter, minMatchesValidator, modelEpochOf, modelKnowledgeCard, modelSpecIdentity, needsSeparateExtract, nextFailover, nodeLinkKey, normalizeApproachTag, normalizeEntry, normalizeFallbacks, orchestrate, orchestratorAdmissionEstCostUsd, pairDraftClaims, parallelScope, parseModelRef, parseScopePath, persistedTerminalEnvelope, phiInitialOf, pipelineScope, planNodeScope, preflightEstimate, priceComponentsOf, priceEntryBilling, priceEntryUsage, priceUsdOf, profileCard, profileRegistrySnapshotHash, progressReportTool, projectHistory, projectIdentity, projectToJsonSchema, proposalStatement, providerOf, quotaActualRequestsDelta, quotaActualTokens, quotaEstimateTokens, quotaRuleAdmission, quotaRuleKey, quotaRuleMatches, readRunMeta, readTerminationInit, reconcileRunMeta, reduceAuditTrail, reduceCriticalPath, reduceInvocationTable, registryKeyRing, remeasureQueue, replayDisposition, repositoryResearchToolset, requiredFieldsValidator, requiredSectionsValidator, researchAgentProfile, resolveModelInvocation, resolvePricing, resolveToolset, retryClassOf, retryDelayMs, reviewAgentProfile, roleConfiguredInRouting, roundOneDisposition, runAgent, runProfile, sanitizeTerminalText, sanitizeTokenCount, sanitizeUsage, sanitizeUsageDelta, scanJournalCompatibility, schemaHash, schemaHashOfSpec, sectionCitationsValidator, selectStructuredOutputTier, selfTestFinishValidation, shouldCompact, snapshotQuotaRules, snapshotUsage, spawnDepthOf, spliceSections, stripFencedBlocks, sumUsage, summarizeInstruction, summarizeOutput, terminalEnvelopeOf, terminationConfigDrift, tierWithinCaps, toApprovalDecision, toJournalValue, tool, toolContract, toolContractHash, toolsetHash, ttlState, usageViolations, validateDetachedResolution, validateEditorialCommit, validateEngineQuotaConfig, validateEntryShape, validateEscalationLimits, validateEscalationReport, validateQuotaRules, validateRetryPolicy, validateSchemaSpec, validateTerminationLimits, validateToolsetAttestation, validateUsageLimits, wordCountValidator, workflowScope, workflowSourceRef, wrapJournalStore, wrapTranscriptStore };
12715
+ export { AWAIT_SCHEMA, AbandonAttempt, AbandonFold, AbandonPayload, AbandonedSpendView, AbortClass, AcceptanceChildSummary, type AdaptiveEvents, AdmissionController, AdmissionDecision, AdmissionRejectedError, AdmissionStatsBefore, AdmitLineage, AdmitRejectReason, AdmitSpec, AdmitVerdict, AgentCallError, AgentError, type AgentEvents, AgentIdentityInput, type AgentInvocationRow, AgentOpts, AgentProfile, AgentProfilePermissions, AgentProfileTemplateOptions, AgentResult, AgentResultMeta, AgentStatus, type AppliedPricingRow, ApproachSignatureInputs, ApprovalDecision, ApprovalIdentityInput, Artifact, AttemptOutcomeClass, AuditCategory, AuditRecord, AuditRunsOptions, BUDGET_ABORT_REASON, BaseAppend, BriefOpts, BudgetAccountView, BudgetDefaults, BudgetExhaustedError, BudgetExhaustionDiagnostics, BudgetHooks, BudgetReserve, type Bytes, CANCEL_AGENT_SCHEMA, CHECKPOINT_FORMAT_V1, CLAIM_STATEMENT_MAX_CHARS, CLAIM_TTL_DAYS, COMPACTION_SUMMARY_PREFIX, CURRENT_HASH_VERSION, CacheHint, CacheTtl, CanUseTool, CanonicalId, CanonicalIdentity, CanonicalLadderSpec, CanonicalModelSpec, ChatEvent, ChatRequest, CheckpointState, ChildArtifactPage, ChildExecutionFacts, ChildIdentityInput, ChildResultPage, CitationTarget, type ClaimClass, ClaimContradictionFinding, type ClaimOp, ClaimPair, ClaimPairOptions, ClaimPairsFold, ClaimPoolReading, type ClaimStatus, ClaimValidationOptions, CollectOpts, CollectedTurn, CompactionConfig, CompiledPermissionChain, CompiledWorkflow, ConfigError, Contradiction, ContradictionClaim, ContradictionOptions, ContradictionSource, type CoreEvents, CostAttribution, CostAttributionFacts, type CostBasis, CostReport, CreateEngineOptions, type CriticalPath, Ctx, DEFAULT_ANCHOR_PATTERN, DEFAULT_ARTIFACT_PATTERN, DEFAULT_CHILD_BUDGET_FRACTION, DEFAULT_CHILD_RESULT_PAGE_CHARS, DEFAULT_CITATION_PATTERN, DEFAULT_CITATION_SAMPLE, DEFAULT_CLAIM_JUDGE_MAX_TURNS, DEFAULT_COMPACTION_THRESHOLD, DEFAULT_ESCALATION_LIMITS, DEFAULT_EVIDENCE_CALLS_PER_ENTRY, DEFAULT_EVIDENCE_GRADE_PHRASES, DEFAULT_EVIDENCE_MIN_SHARE, DEFAULT_EVIDENCE_OVERHEAD_CALLS, DEFAULT_FINISH_MAX_REPAIRS, DEFAULT_FLAT_RESERVE_USD, DEFAULT_MAX_CHILDREN_PER_NODE, DEFAULT_MAX_CLAIM_PAIRS, DEFAULT_MAX_CONTRADICTIONS, DEFAULT_MAX_DEPTH, DEFAULT_MAX_EXCERPT_CHARS, DEFAULT_MAX_OSCILLATIONS_PER_KEY, DEFAULT_MAX_PAIR_EXCERPT_CHARS, DEFAULT_MAX_PINNED_WORKTREES, DEFAULT_MAX_POOL_PER_PAIR, DEFAULT_MAX_QUOTA_DENIALS, DEFAULT_MAX_REVISIONS_PER_RUN, DEFAULT_MAX_TOTAL_SPAWNS, DEFAULT_MAX_TURNS, DEFAULT_MODEL_RETRY_ATTEMPTS, DEFAULT_NO_PROGRESS_TURNS, DEFAULT_PER_RUN_CONCURRENCY, DEFAULT_RETRY_POLICY, DEFAULT_STREAM_IDLE_TIMEOUT_MS, DEFAULT_SYNTHESIS_MAX_TURNS, DEFAULT_SYNTHESIS_NOTE_MAX_TURNS, DataKeyProvider, DebitResult, DeclaredLadder, DedupIndex, DedupNote, DedupedClaims, DerivedKey, DeriverRegistry, type DeterminismConfig, DeterminismError, type DeterminismEvents, type DeterminismMode, DispositionRule, DispositionTable, DocumentedRates, DonorCandidate, DonorRef, DroppedItem, EMIT_RESULT_TOOL, EMPTY_SCHEMA_HASH, EMPTY_TOOLSET_HASH, ESCALATE_TOOL_NAME, ESCALATION_REPORT_SCHEMA, ESCALATION_REQUEST_SCHEMA, EVENT_SEGMENT_STRIDE, EffectiveUsageLimits, Effort, Engine, EngineDefaults, EngineQuotaConfig, EngineQuotaRuntime, EntryBillingFold, EntryBillingUnit, EntryKind, EntryRef, EntryStatus, EnvelopeEncryption, EnvelopeEncryptionOptions, ErrorClass, ErrorCode, ErrorPolicy, EscalatedResult, EscalationDecision, EscalationDecisionAbortedError, EscalationDigest, EscalationKind, EscalationLimits, EscalationOptions, EscalationReport, EscalationRequest, EventBus, EvidenceContract, type EvidenceRef, type ExecKeyDerivation, type ExecutorRegistry, type ExplorationSummary, ExtensionAppendInput, ExtensionDispatchSpec, ExternalIdentityInput, ExternalRegistry, ExtractNecessityInput, FINALIZE_SYNTHESIS_INSTRUCTION, FINISH_SCHEMA, FINISH_SECTIONAL_SCHEMA, FINISH_TOOL_NAME, FailRunError, FailoverTarget, FailoverTrigger, FallbackField, FallbackTrigger, FencedCodeMode, FileModelKnowledgeStore, FileModelKnowledgeStoreOptions, FileTranscriptStore, type FinalizationWindowBudget, FinishContract, FinishContractCitations, FinishContractGoldenReject, FinishContractManifest, FinishInfo, FinishSelfTestFailure, FinishSelfTestFixtures, FinishSelfTestReport, FinishValidationChild, FinishValidationInput, FinishValidationSpec, FinishValidationVerdict, FinishValidator, GET_CHILD_RESULT_SCHEMA, GET_CHILD_RESULT_TOOL_NAME, Gate, GateAudit, type GateRecord, GitWorktreeProvider, GitWorktreeProviderOptions, GraftBoot, HashVersion, HookVerdict, IMPLEMENTATION_PROFILE_LIMITS, INBOX_PROPOSAL_TTL_DAYS, IN_FLIGHT_EXPOSURE_REFUSAL_PREFIX, IdentityInput, InMemoryStore, InMemoryTranscriptStore, InProcessRunner, IncrementalSynthesisResult, InvalidResolutionError, InvocationRole, type InvocationTable, InvoiceCardinality, InvoiceExport, InvoicePricingProvenance, InvoiceReconciliation, InvoiceRow, type IsolatedExecContext, type IsolatedExecRequest, type IsolatedExecutorTag, type IsolationProvider, type IsolationSpec, Issue$1 as Issue, JOURNAL_ENVELOPE_MARKER, JournalCompatSubCode, JournalCompatibilityError, JournalEntry, JournalMatcher, JournalMissError, JournalOperation, JournalOrderViolation, type JournalPricingSnapshot, JournalSerializationContext, JournalSerializationHook, type JournalStore, type Json, JsonSchema, JsonlFileStore, KB_ACTIVE_CLAIMS_CAP, KB_CARD_RENDER_BUDGET_CHARS, type KbProposal, type KbProposalTrigger, KeyDeriver, KeyRing, KeyedLimiter, KnowledgeCasError, type KnowledgeSnapshot, LARGE_VALUE_WARN_BYTES, LEGACY_LTID_PREFIX, LEGACY_SIGNATURE_INPUTS, LINEAGE_SIG_VERSION, LadderSpec, type LeasableStore, type Lease, LeaseHeldError, Ledger, LineageCounters, LineageIndex, LineageRef, LineageRelation, LineageStats, LogicalTaskId, MASKED_SECRET, MAX_CHILD_RESULT_PAGE_CHARS, MAX_DEPTH_CEILING, MAX_RUN_ID_LENGTH, MAX_TIMER_DELAY_MS, MatchResult, McpConfig, McpToolSource, MechanicalGateProfile, MechanicalGateVerdict, MemoryQuotaLimiter, type MetaLookupStore, type ModelCaps, ModelChoice, type ModelClaim, ModelEpochInputs, type ModelKnowledgeHandle, type ModelKnowledgeStore, ModelListConstraint, ModelRef, ModelRetry, ModelSpec, Msg, NoProgressDetector, NodeId, NodeLinkValue, NonSerializableValueError, ORCHESTRATE_WORKFLOW_NAME, OnEscalation, OperationDisposition, OrchestrateAcceptance, OrchestrateClaimConsistency, OrchestrateClaimConsistencyMeta, OrchestrateContradictions, OrchestrateContradictionsMeta, OrchestrateOptions, OrchestrateSynthesis, OrchestrateSynthesisSkipReason, OrchestratorBudgetSpec, OrchestratorCapConfigError, OrchestratorExtension, OrchestratorExtensionIO, OrchestratorRuntime, Out, PARALLEL_AGENTS_SCHEMA, PROGRESS_REPORT_TOOL_NAME, ParallelSiteCounter, Part, PendingExternal, PendingToolTurn, PermissionConfig, PermissionGate, PermissionHook, PermissionPreset, PermissionRule, PermissionVerdict, PersistedTerminalRefusal, PersistedTerminalResult, type PhaseRow, PhaseTarget, type PinnedPricingSegment, PipelineCollected, PipelineOpts, PlanInvariantError, type PostFanInBreakdown, PreflightAdmissionRow, PreflightFinding, PreflightInput, PreflightOrchestratorSpec, PreflightReport, PreflightSpawnReport, PreflightSpawnSpec, PreflightToolCeiling, PriceTable, PricedComponent, PricedComponents, PricedUsage, type Pricing, type PricingTier, ProgressReport, type ProviderAdapter, ProviderCallRecord, QUOTA_WINDOW_MS, QualityFloors, QuotaCounters, type QuotaDecision, type QuotaEstimate, type QuotaLimiter, type QuotaReservationRequest, QuotaRule, QuotaWindowSnapshot, READ_CHILD_ARTIFACT_SCHEMA, READ_CHILD_ARTIFACT_TOOL_NAME, RESEARCH_PROFILE_LIMITS, REVIEW_PROFILE_LIMITS, ROLE_EFFORT_DEFAULTS, ROOT_ACCOUNT, ROOT_SCOPE, RUN_PROFILES, RUN_SETTLE_DECISION_TYPE, RandIdentityInput, RandPayload, RateLimitObservation, ReconcileOptions, ReconcileResult, RefEntryAppender, RefEntryClassification, RefusalInfo, RepeatedClaim, ReplayDisposition, ReplayMode, ReplayPlanHashMismatch, Replayer, RepositoryResearchToolset, RepositoryResearchToolsetOptions, ResearchAgentProfileOptions, ResearchAgentProfileResult, ResearchEvidenceEntry, ResolutionArbiter, ResolutionAttempt, ResolutionBy, ResolutionFold, ResolutionLayer, ResolutionOutcome, ResolutionPayload, ResolvedInvocation, ResolvedToolset, ResumeHandle, ResumeOptions, ResumePreview, ResumeReport, RetryClass, RetryPolicy, ReuseConfig, RiskRuleValue, Role, RulvarError, RulvarErrorCode, RunAgentOptions, RunAuditVerdict, RunBudget, RunEventSink, RunExport, type RunFilter, RunHandle, RunInternals, type RunMeta, RunOptions, RunOutcome, RunProfile, RunStateAudit, RunStatus, RuntimeEventSink, SANDBOX_AGENT_OPT_KEYS, SPAWN_AGENT_SCHEMA, SandboxBridge, SandboxBridgeOptions, SandboxError, SandboxHostToWorker, SandboxMethod, SandboxWorkerToHost, SchemaPair, SchemaSpec, SchemaValidationResult, ScopeSegment, ScriptRejected, ScriptRunner, ScrubNote, SecretMasker, SectionMatchMode, Semaphore, SerializationHook, Settled, SettlementError, ShellPatternRules, ShellSegment, ShellVerdict, SinglePhaseAppend, SpanMinter, SpanRegistry, SpawnAdmissionValue, SpawnAgentParams, SpawnKey, SpawnLineage, SpawnLineageOpt, SpawnOrigin, SpawnRecord, Spend, Stage, type StandardJSONSchemaV1, type StandardSchemaV1, StepIdentityInput, type StreamHooks, StructuredOutputTier, SupersededError, SuspendedAppend, SuspensionState, TOOL_NAME_PATTERN, type TaskClass, TaskDigest, TaskSpec, type TerminalEnvelope, TerminalOutcomeFacts, TerminalPatch, TerminationAccount, TerminationAccountSnapshot, TerminationDeniedValue, TerminationDeniedWriter, TerminationInitValue, TerminationLimits, TerminationResource, type ToolBudgetSummary, ToolCallRequest, ToolChoice, type ToolContext, ToolContextSeed, ToolContract, type ToolDef, type ToolEvents, type ToolExecutor, type ToolExecutorProvider, ToolInit, type ToolRisk, ToolRuntime, type ToolSource, type ToolSourceSession, ToolsOption, ToolsetAttestation, TranscriptSerializationHook, type TranscriptStore, TriggerClass, TtlState, Usage, UsageLimits, UsageSlice, VerifiedRecommendation, WAIT_FOR_EVENTS_SCHEMA, WAIT_FOR_EVENTS_TOOL_NAME, WAKE_SUMMARY_RENDER_BUDGET_CHARS, WakeBudgetBlock, WakeDigest, WakeTrigger, WireError, Workflow, WorkflowCallOpts, type WorkflowEvent, type WorkflowEventBody, WorkflowRegistry, accountSpendFromJournal, admissionReserveUsd, affordableOutputTokens, agentErrorFromWire, agentErrorToWire, agentResultWire, agentScope, applyClaimOps, applyStructuredOutputTier, approachSigCoarse, approachSigOf, archiveDeprecatedModelOps, assertFencedWrites, assertSafeRunId, atCompactionThreshold, attestToolset, auditRun, auditRuns, buildAbandonFold, buildAdapterRegistry, buildCostReport, buildDeriverRegistry, buildOrchestratorTools, buildTerminationInitValue, buildToolContext, canRideLoopTurn, canonicalIsolationTag, canonicalizeLadder, canonicalizeSchema, capIssues, capsHashOf, checkFloors, checkpointRefFor, childCoveragePrefix, citationTargetsValidator, citedValueValidator, claimExpired, claimExpiry, claimIssues, claimOpIssues, classifyAgentError, classifyAttemptOutcome, collectDeclaredLadders, compactMessages, compareRates, compilePermissionChain, compilePermissionPreset, compileSecretMasker, compileVerifiedLayer, constantTimeEqual, costReportFromJournal, countsAgainstLimit, createCanonicalIdMinter, createCtx, createEngine, createEnvelopeEncryption, createSandboxBridge, currentOnlyKeyRing, decodeCheckpoint, dedupeRepeatedClaims, defineWorkflow, deriveContentKey, deriverV1, deriverV2, digestOf, dispatchProjectionReserveUsd, dispositionHook, emptyDigestBlocks, emptyToolset, encodeCheckpoint, enforceToolsetAttestation, entryUsageSlices, escalateTool, evaluatePermission, evaluateReuse, evidenceGradeValidator, evidencePreservedValidator, executeWorkflow, executionFactsOf, exhaustionCodeOf, extractCandidate, failoverTriggerOf, fallbackTriggerOf, filterClaimsForRun, finalizeFires, findContradictions, finishContract, foldLedger, foldTermination, formatCharacterValidator, formatRePrompt, formatScopePath, hasFencedWrites, hasMetaLookup, hashRunArgs, hashRunOutput, hashWorkflowBody, hashWorkflowSource, headingStructureValidator, identityJcs, implementationAgentProfile, invoiceFromJournal, isEscalated, isSchemaPairSpec, isStandardSchemaSpec, isStrictCompatibleSchema, journalPricingSnapshot, kMaxOf, knowledgeHash, ladderLengthOf, ladderRungChoice, lastRunSettle, latestProgressReport, lexShellCommand, liftRetainedParts, lineageWeightOf, localKeyProvider, makeOrchestratorWorkflow, maskSecrets, maskSecretsDeep, maskSecretsJson, matchArgvPattern, matchShellCommand, mcp, memoryQuotaLimiter, mergeQuotaDenial, mergeUsageLimits, metaMatchesFilter, minMatchesValidator, modelEpochOf, modelKnowledgeCard, modelSpecIdentity, needsSeparateExtract, nextFailover, nodeLinkKey, normalizeApproachTag, normalizeEntry, normalizeFallbacks, orchestrate, orchestratorAdmissionEstCostUsd, pairDraftClaims, parallelScope, parseModelRef, parseScopePath, persistedTerminalEnvelope, phiInitialOf, pipelineScope, planNodeScope, preflightEstimate, priceComponentsOf, priceEntryBilling, priceEntryUsage, priceUsdOf, profileCard, profileRegistrySnapshotHash, progressReportTool, projectHistory, projectIdentity, projectToJsonSchema, proposalStatement, providerOf, quotaActualRequestsDelta, quotaActualTokens, quotaEstimateTokens, quotaRuleAdmission, quotaRuleKey, quotaRuleMatches, readRunMeta, readTerminationInit, reconcileRunMeta, reduceAuditTrail, reduceCriticalPath, reduceInvocationTable, registryKeyRing, remeasureQueue, replayDisposition, repositoryResearchToolset, requiredFieldsValidator, requiredSectionsValidator, researchAgentProfile, resolveModelInvocation, resolvePricing, resolveToolset, retryClassOf, retryDelayMs, reviewAgentProfile, roleConfiguredInRouting, roundOneDisposition, runAgent, runProfile, sanitizeTerminalText, sanitizeTokenCount, sanitizeUsage, sanitizeUsageDelta, scanJournalCompatibility, schemaHash, schemaHashOfSpec, sectionCitationsValidator, selectStructuredOutputTier, selfTestFinishValidation, shouldCompact, snapshotQuotaRules, snapshotUsage, spawnDepthOf, spliceSections, stripFencedBlocks, sumUsage, summarizeInstruction, summarizeOutput, terminalEnvelopeOf, terminationConfigDrift, tierWithinCaps, toApprovalDecision, toJournalValue, tool, toolContract, toolContractHash, toolsetHash, ttlState, usageViolations, validateDetachedResolution, validateEditorialCommit, validateEngineQuotaConfig, validateEntryShape, validateEscalationLimits, validateEscalationReport, validateQuotaRules, validateRetryPolicy, validateSchemaSpec, validateTerminationLimits, validateToolsetAttestation, validateUsageLimits, wordCountValidator, workflowScope, workflowSourceRef, wrapJournalStore, wrapTranscriptStore };
package/dist/index.js CHANGED
@@ -3894,6 +3894,9 @@ function validateBounds(cfg) {
3894
3894
  const value = cfg.timeouts?.[key];
3895
3895
  if (value !== void 0 && (!Number.isFinite(value) || value <= 0)) throw new ConfigError(`mcp: 'timeouts.${key}' must be a positive finite number of milliseconds, got ${String(value)}`);
3896
3896
  }
3897
+ if (cfg.drift !== void 0 && cfg.drift !== "rekey" && cfg.drift !== "refuse") throw new ConfigError(`mcp: 'drift' must be 'rekey' or 'refuse', got '${String(cfg.drift)}'`);
3898
+ const headers = cfg.http?.headers;
3899
+ if (headers !== void 0 && typeof headers !== "function" && typeof headers !== "object") throw new ConfigError("mcp: 'http.headers' must be a record of header values or a (possibly async) function returning one");
3897
3900
  }
3898
3901
  function validateConfig(cfg) {
3899
3902
  validateBounds(cfg);
@@ -3905,6 +3908,7 @@ function validateConfig(cfg) {
3905
3908
  if (cfg.command === void 0) throw new ConfigError("mcp: the stdio transport requires 'command'");
3906
3909
  forbid("url");
3907
3910
  forbid("server");
3911
+ forbid("http");
3908
3912
  return;
3909
3913
  case "streamable-http":
3910
3914
  if (cfg.url === void 0) throw new ConfigError("mcp: the streamable-http transport requires 'url'");
@@ -3917,6 +3921,7 @@ function validateConfig(cfg) {
3917
3921
  forbid("command");
3918
3922
  forbid("args");
3919
3923
  forbid("url");
3924
+ forbid("http");
3920
3925
  return;
3921
3926
  default: throw new ConfigError(`mcp: unknown transport '${String(cfg.transport)}'`);
3922
3927
  }
@@ -3937,6 +3942,23 @@ function mapContent(result) {
3937
3942
  text: block.text ?? ""
3938
3943
  } : block);
3939
3944
  }
3945
+ /**
3946
+ * Wraps fetch so EVERY wire request of the streamable-http transport
3947
+ * consults the declared headers before send (RV1516): the hook form is
3948
+ * the per-request refresh point for rotating tokens, so no reconnect
3949
+ * and no library-invented 401 retry exists or is needed.
3950
+ */
3951
+ function perRequestHeaders(headersOption) {
3952
+ return async (url, init) => {
3953
+ const extra = typeof headersOption === "function" ? await headersOption() : headersOption;
3954
+ const headers = new Headers(init?.headers);
3955
+ for (const [name, value] of Object.entries(extra ?? {})) headers.set(name, value);
3956
+ return fetch(url, {
3957
+ ...init,
3958
+ headers
3959
+ });
3960
+ };
3961
+ }
3940
3962
  function errorText(result) {
3941
3963
  const text = (result.content ?? []).filter((block) => block.type === "text").map((block) => block.text ?? "").join("");
3942
3964
  return text === "" ? "MCP tool reported an error" : text;
@@ -3959,6 +3981,7 @@ function mcp(cfg) {
3959
3981
  let cache;
3960
3982
  let generation = 0;
3961
3983
  let inFlight;
3984
+ let poisoned = false;
3962
3985
  const connect = async () => {
3963
3986
  const client = new Client({
3964
3987
  name: "rulvar",
@@ -3972,7 +3995,8 @@ function mcp(cfg) {
3972
3995
  });
3973
3996
  await client.connect(transport);
3974
3997
  } else if (cfg.transport === "streamable-http") {
3975
- const transport = new StreamableHTTPClientTransport(new URL(cfg.url ?? ""));
3998
+ const declaredHeaders = cfg.http?.headers;
3999
+ const transport = new StreamableHTTPClientTransport(new URL(cfg.url ?? ""), declaredHeaders === void 0 ? void 0 : { fetch: perRequestHeaders(declaredHeaders) });
3976
4000
  await client.connect(transport);
3977
4001
  } else {
3978
4002
  const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
@@ -4003,6 +4027,7 @@ function mcp(cfg) {
4003
4027
  client.setNotificationHandler(ToolListChangedNotificationSchema, () => {
4004
4028
  generation += 1;
4005
4029
  cache = void 0;
4030
+ if (cfg.drift === "refuse") poisoned = true;
4006
4031
  });
4007
4032
  return client;
4008
4033
  };
@@ -4058,6 +4083,7 @@ function mcp(cfg) {
4058
4083
  return {
4059
4084
  id: sourceIdOf(cfg),
4060
4085
  tools: async () => {
4086
+ if (poisoned) throw new ConfigError(`mcp: the tool list of '${sourceIdOf(cfg)}' changed after import (listChanged) and drift policy 'refuse' holds the source closed; close() and re-create the source (and re-record any toolset attestation) to import the changed list deliberately`);
4061
4087
  if (cache !== void 0) return cache;
4062
4088
  if (inFlight !== void 0) return inFlight;
4063
4089
  const fetch = (async () => {
@@ -4084,6 +4110,7 @@ function mcp(cfg) {
4084
4110
  const pending = clientPromise;
4085
4111
  clientPromise = void 0;
4086
4112
  cache = void 0;
4113
+ poisoned = false;
4087
4114
  if (pending === void 0) return;
4088
4115
  let client;
4089
4116
  try {
@@ -8982,118 +9009,441 @@ function compareRates(seed, page) {
8982
9009
  return findings;
8983
9010
  }
8984
9011
  //#endregion
8985
- //#region src/model/failover.ts
8986
- /** Normalizes the author-facing ModelChoice.fallbacks list. */
8987
- function normalizeFallbacks(refs) {
8988
- return (refs ?? []).map((model) => ({ model }));
8989
- }
9012
+ //#region src/model/quota.ts
8990
9013
  /**
8991
- * Maps a retry class to its failover trigger once retries exhaust.
8992
- * Overloaded (529) is transport-class for failover purposes; a
8993
- * non-retryable error never fails over.
9014
+ * Quota rules and the in-process reference QuotaLimiter (RV-215).
9015
+ * The rule model is shared by every reference implementation
9016
+ * (memoryQuotaLimiter here, SqliteQuotaLimiter in
9017
+ * @rulvar/store-sqlite): fixed one-minute windows aligned to the
9018
+ * epoch, admission at reservation time, reconciliation to actual
9019
+ * usage inside the same window. The hard guarantee is on
9020
+ * `requestsPerMinute` (every wire attempt is exactly one request);
9021
+ * `tokensPerMinute` admits on the heuristic estimate and settles to
9022
+ * actual usage, so token windows are approximate at admission and
9023
+ * exact at settlement.
9024
+ *
9025
+ * Docs: https://docs.rulvar.com/guide/model-routing
8994
9026
  */
8995
- function failoverTriggerOf(retryClass) {
8996
- if (retryClass === void 0) return;
8997
- return retryClass === "rate-limit" ? "rate-limit" : "transport";
9027
+ /**
9028
+ * Captured at module load, before the InProcessRunner's
9029
+ * nondeterminism guard can patch the global: the limiter's clock is
9030
+ * engine infrastructure on the live-only dispatch path and must never
9031
+ * be blamed on workflow code.
9032
+ */
9033
+ const nativeNow = Date.now;
9034
+ /** The fixed accounting window every PerMinute cap counts over. */
9035
+ const QUOTA_WINDOW_MS = 6e4;
9036
+ /**
9037
+ * Validates a quota rule set as a typed ConfigError before any
9038
+ * limiter can admit under it: a non-array or empty set, a rule
9039
+ * without a cap, a malformed dimension, or a malformed cap all fail
9040
+ * loud at construction. Shared by every reference implementation.
9041
+ */
9042
+ function validateQuotaRules(rules, site = "quota rules") {
9043
+ const raw = rules;
9044
+ if (!Array.isArray(raw)) throw new ConfigError(`${site} must be an array of QuotaRule objects`);
9045
+ if (raw.length === 0) throw new ConfigError(`${site} must contain at least one rule`);
9046
+ raw.forEach((entry, index) => {
9047
+ const at = `${site}[${String(index)}]`;
9048
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) throw new ConfigError(`${at} must be a QuotaRule object`);
9049
+ const rule = entry;
9050
+ for (const dimension of [
9051
+ "provider",
9052
+ "model",
9053
+ "tenant"
9054
+ ]) {
9055
+ const value = rule[dimension];
9056
+ if (value !== void 0 && (typeof value !== "string" || value === "")) throw new ConfigError(`${at}.${dimension} must be a nonempty string when given`);
9057
+ }
9058
+ if (rule.requestsPerMinute === void 0 && rule.tokensPerMinute === void 0) throw new ConfigError(`${at} must set requestsPerMinute or tokensPerMinute (or both)`);
9059
+ for (const cap of ["requestsPerMinute", "tokensPerMinute"]) if (rule[cap] !== void 0) requirePositiveInteger$2(rule[cap], `${at}.${cap}`);
9060
+ });
8998
9061
  }
8999
9062
  /**
9000
- * The next target index past `from` that serves `trigger`, or undefined
9001
- * when the chain is exhausted. Index 0 is the primary; the chain never
9002
- * moves backwards (sticky failover).
9063
+ * The canonical content key of one rule (RV608, promoted from the
9064
+ * store limiters): a fixed-field-order JSON of the rule, identical
9065
+ * across processes and hosts for identical rules. It is the bucket key
9066
+ * of both store references, the input of
9067
+ * `quotaRulesFingerprint`, and the CANONICAL ORDER every reference
9068
+ * limiter folds denials in, so equal rule sets produce byte-identical
9069
+ * refusal objects regardless of array permutation.
9003
9070
  */
9004
- function nextFailover(targets, trigger, from) {
9005
- for (let index = from + 1; index < targets.length; index += 1) {
9006
- const on = targets[index]?.on;
9007
- if (on === void 0 || on.includes(trigger)) return index;
9008
- }
9071
+ function quotaRuleKey(rule) {
9072
+ return JSON.stringify({
9073
+ provider: rule.provider ?? null,
9074
+ model: rule.model ?? null,
9075
+ tenant: rule.tenant ?? null,
9076
+ requestsPerMinute: rule.requestsPerMinute ?? null,
9077
+ tokensPerMinute: rule.tokensPerMinute ?? null
9078
+ });
9009
9079
  }
9010
9080
  /**
9011
- * Classifies a terminal agent outcome for the degenerate fallback:
9012
- * schema-mismatch errors are
9013
- * 'schema-exhausted'; any other error is 'error'; limit terminals (the
9014
- * no-progress abort included) are 'limit'; cancelled, escalated, and
9015
- * skipped never trigger.
9081
+ * Validates a rule set and returns the immutable snapshot every
9082
+ * reference limiter admits under (RV608): a fresh array of fresh
9083
+ * objects carrying ONLY the known rule fields, each frozen, the array
9084
+ * frozen. The caller's array and objects stay untouched and unshared,
9085
+ * so ordinary JavaScript after the constructor (a pushed rule, a
9086
+ * reassigned cap) can no longer change a decision, a bucket key, or a
9087
+ * recorded fingerprint.
9088
+ *
9089
+ * A set containing two rules with the same canonical content key is
9090
+ * refused typed (RV704): the memory reference buckets by rule INDEX
9091
+ * (each copy counts independently, the full cap admits) while the
9092
+ * store references bucket by rule KEY (one shared bucket is debited
9093
+ * once per matching copy, half the cap admits), so the same duplicated
9094
+ * configuration admitted differently per storage. Refusing it at the
9095
+ * shared construction chokepoint is what keeps equal configurations
9096
+ * equal on every storage.
9016
9097
  */
9017
- function fallbackTriggerOf(outcome) {
9018
- if (outcome.status === "error") return outcome.error?.kind === "schema-mismatch" ? "schema-exhausted" : "error";
9019
- if (outcome.status === "limit") return "limit";
9098
+ function snapshotQuotaRules(rules, site = "quota rules") {
9099
+ validateQuotaRules(rules, site);
9100
+ const firstIndexByKey = /* @__PURE__ */ new Map();
9101
+ rules.forEach((rule, index) => {
9102
+ const key = quotaRuleKey(rule);
9103
+ const first = firstIndexByKey.get(key);
9104
+ if (first !== void 0) throw new ConfigError(`${site}[${String(index)}] duplicates ${site}[${String(first)}] (rule key ${key}): identical rules occupy independent buckets in memory but share one key-debited bucket on keyed storage, so one configuration would admit differently per store; delete the duplicate`);
9105
+ firstIndexByKey.set(key, index);
9106
+ });
9107
+ return Object.freeze(rules.map((rule) => Object.freeze({
9108
+ ...rule.provider === void 0 ? {} : { provider: rule.provider },
9109
+ ...rule.model === void 0 ? {} : { model: rule.model },
9110
+ ...rule.tenant === void 0 ? {} : { tenant: rule.tenant },
9111
+ ...rule.requestsPerMinute === void 0 ? {} : { requestsPerMinute: rule.requestsPerMinute },
9112
+ ...rule.tokensPerMinute === void 0 ? {} : { tokensPerMinute: rule.tokensPerMinute }
9113
+ })));
9020
9114
  }
9021
- //#endregion
9022
- //#region src/model/projector.ts
9023
- /** The provider family of an adapter: `provider` when set, else `id`. */
9024
- function providerOf(adapter) {
9025
- return adapter.provider ?? adapter.id;
9115
+ /** True when every dimension the rule pins matches the request. */
9116
+ function quotaRuleMatches(rule, request) {
9117
+ return (rule.provider === void 0 || rule.provider === request.provider) && (rule.model === void 0 || rule.model === request.model) && (rule.tenant === void 0 || rule.tenant === request.tenant);
9118
+ }
9119
+ /** The tokens a reservation is admitted under: input estimate plus the output cap. */
9120
+ function quotaEstimateTokens(request) {
9121
+ return request.estimate.inputTokens + (request.estimate.maxOutputTokens ?? 0);
9122
+ }
9123
+ /** The tokens a settled attempt actually consumed. */
9124
+ function quotaActualTokens(usage) {
9125
+ return usage.inputTokens + usage.outputTokens;
9026
9126
  }
9027
9127
  /**
9028
- * Projects the canonical history into the target provider's view:
9029
- * provider-raw parts of a DIFFERENT provider are omitted; everything
9030
- * else (text, images, tool calls, tool results, compaction content)
9031
- * passes through untouched. Messages whose parts all belong to another
9032
- * provider vanish entirely rather than ride as empty messages.
9128
+ * The request-count settlement delta of one reservation (RV905): the
9129
+ * reservation admitted ONE wire request, and `actual.requests` names
9130
+ * how many the attempt actually made (an adapter absorbing
9131
+ * provider-side continuations dispatches several inside one reserved
9132
+ * call). Non-integer, non-positive, or absent actuals settle as the
9133
+ * single reserved request (delta 0); a settlement only ever ADDS, the
9134
+ * calls already happened. Shared by every reference limiter so the
9135
+ * three implementations cannot disagree about the arithmetic.
9033
9136
  */
9034
- function projectHistory(messages, targetProvider) {
9035
- const projected = [];
9036
- for (const msg of messages) {
9037
- const parts = msg.parts.filter((part) => part.type !== "provider-raw" || part.provider === targetProvider);
9038
- if (parts.length === 0 && msg.parts.length > 0) continue;
9039
- projected.push(parts.length === msg.parts.length ? msg : {
9040
- ...msg,
9041
- parts
9042
- });
9043
- }
9044
- return projected;
9137
+ function quotaActualRequestsDelta(actual) {
9138
+ const requests = actual?.requests;
9139
+ return typeof requests === "number" && Number.isInteger(requests) && requests > 1 ? requests - 1 : 0;
9045
9140
  }
9046
9141
  /**
9047
- * Lifts the adapter-shipped retention payload of one finished turn into
9048
- * provider-raw parts (the retention transport). Reads
9049
- * providerMetadata[<adapter id>].retainedParts and tags each block with
9050
- * the adapter's provider family. Returns [] when the adapter shipped
9051
- * nothing.
9142
+ * One rule's admission verdict against its current-window counters,
9143
+ * the pure decision both reference implementations share. A denial
9144
+ * carries the window remainder as retryAfterMs, except when the
9145
+ * estimate alone can never fit the token cap: that denial says
9146
+ * retryAfterMs 0 (retry immediately), so the caller's bounded
9147
+ * attempts exhaust without waiting and failover gets its chance.
9052
9148
  */
9053
- function liftRetainedParts(providerMetadata, adapter) {
9054
- const namespace = providerMetadata?.[adapter.id];
9055
- if (typeof namespace !== "object" || namespace === null) return [];
9056
- const retained = namespace.retainedParts;
9057
- if (!Array.isArray(retained)) return [];
9058
- const blocks = retained;
9059
- const provider = providerOf(adapter);
9060
- return blocks.map((block) => ({
9061
- type: "provider-raw",
9062
- provider,
9063
- block
9064
- }));
9149
+ function quotaRuleAdmission(rule, counters, estimate, msUntilWindowEnd) {
9150
+ if (rule.requestsPerMinute !== void 0 && counters.requests + estimate.requests > rule.requestsPerMinute) return {
9151
+ admit: false,
9152
+ retryAfterMs: msUntilWindowEnd,
9153
+ reason: `requestsPerMinute ${String(rule.requestsPerMinute)} exhausted`
9154
+ };
9155
+ if (rule.tokensPerMinute !== void 0) {
9156
+ if (estimate.tokens > rule.tokensPerMinute) return {
9157
+ admit: false,
9158
+ retryAfterMs: 0,
9159
+ reason: `the estimate of ${String(estimate.tokens)} tokens can never fit tokensPerMinute ${String(rule.tokensPerMinute)}`
9160
+ };
9161
+ if (counters.tokens + estimate.tokens > rule.tokensPerMinute) return {
9162
+ admit: false,
9163
+ retryAfterMs: msUntilWindowEnd,
9164
+ reason: `tokensPerMinute ${String(rule.tokensPerMinute)} exhausted`
9165
+ };
9166
+ }
9167
+ return { admit: true };
9065
9168
  }
9066
- //#endregion
9067
- //#region src/model/retry.ts
9068
9169
  /**
9069
- * Transport RetryPolicy (M4-T05): retries live UNDER the journal. A
9070
- * retried-then-successful call is exactly one journal entry with one
9071
- * usage total, transport retries never count as lineage attempts
9072
- * (DEF-3), the provider-supplied retryAfterMs replaces the computed
9073
- * delay, and task-class failures never retry by construction (a
9074
- * non-retryable WireError has no retry class).
9075
- *
9076
- * Full contract: https://docs.rulvar.com/guide/model-routing; the
9077
- * Appendix A defaults were committed at the M4 entry gate.
9170
+ * Folds one more failing rule into the decision the caller returns:
9171
+ * the wait is the LONGEST failing horizon (every matching rule must
9172
+ * admit), and the FIRST failing rule names the denial.
9078
9173
  */
9174
+ function mergeQuotaDenial(current, next) {
9175
+ if (current === void 0) return {
9176
+ retryAfterMs: next.retryAfterMs,
9177
+ reason: next.reason
9178
+ };
9179
+ return next.retryAfterMs > current.retryAfterMs ? {
9180
+ retryAfterMs: next.retryAfterMs,
9181
+ reason: current.reason
9182
+ } : current;
9183
+ }
9079
9184
  /**
9080
- * Captured at module load, before the InProcessRunner's nondeterminism
9081
- * guard can patch the global: the engine's own jitter is journal
9082
- * invisible and must never be blamed on workflow code.
9185
+ * The in-process reference QuotaLimiter: fixed epoch-aligned
9186
+ * one-minute windows over the shared rule model. Coordinates every
9187
+ * engine that shares THIS instance inside one process; processes
9188
+ * coordinate through a shared-storage implementation of the same SPI
9189
+ * (SqliteQuotaLimiter in @rulvar/store-sqlite) instead.
9083
9190
  */
9084
- const nativeRandom = Math.random;
9085
- /** Appendix A committed defaults (M4 entry gate, PR #26). */
9086
- const DEFAULT_RETRY_POLICY = {
9087
- attempts: 3,
9088
- backoff: {
9089
- initialMs: 500,
9090
- factor: 2,
9091
- maxMs: 8e3,
9092
- jitter: true
9093
- },
9094
- retryOn: [
9095
- "transport",
9096
- "rate-limit",
9191
+ function memoryQuotaLimiter(rules, options = {}) {
9192
+ const frozen = snapshotQuotaRules(rules, "memoryQuotaLimiter rules");
9193
+ const ordered = frozen.map((rule, index) => ({
9194
+ rule,
9195
+ index,
9196
+ key: quotaRuleKey(rule)
9197
+ })).sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
9198
+ const now = options.now ?? (() => nativeNow());
9199
+ const buckets = /* @__PURE__ */ new Map();
9200
+ const reservations = /* @__PURE__ */ new Map();
9201
+ let nextReservation = 0;
9202
+ const windowStartAt = (at) => at - at % QUOTA_WINDOW_MS;
9203
+ const bucketFor = (ruleIndex, windowStart) => {
9204
+ let bucket = buckets.get(ruleIndex);
9205
+ if (bucket === void 0 || bucket.windowStart !== windowStart) {
9206
+ bucket = {
9207
+ windowStart,
9208
+ requests: 0,
9209
+ tokens: 0
9210
+ };
9211
+ buckets.set(ruleIndex, bucket);
9212
+ }
9213
+ return bucket;
9214
+ };
9215
+ const prune = (windowStart) => {
9216
+ for (const [id, reservation] of reservations) if (reservation.windowStart < windowStart) reservations.delete(id);
9217
+ };
9218
+ return {
9219
+ reserve(request) {
9220
+ const at = now();
9221
+ const windowStart = windowStartAt(at);
9222
+ prune(windowStart);
9223
+ const estimateTokens = quotaEstimateTokens(request);
9224
+ const msUntilWindowEnd = windowStart + QUOTA_WINDOW_MS - at;
9225
+ const matched = [];
9226
+ let denial;
9227
+ for (const { rule, index } of ordered) {
9228
+ if (!quotaRuleMatches(rule, request)) continue;
9229
+ matched.push(index);
9230
+ const verdict = quotaRuleAdmission(rule, bucketFor(index, windowStart), {
9231
+ requests: request.estimate.requests,
9232
+ tokens: estimateTokens
9233
+ }, msUntilWindowEnd);
9234
+ if (!verdict.admit) denial = mergeQuotaDenial(denial, verdict);
9235
+ }
9236
+ if (denial !== void 0) return Promise.resolve({
9237
+ granted: false,
9238
+ ...denial
9239
+ });
9240
+ for (const index of matched) {
9241
+ const bucket = bucketFor(index, windowStart);
9242
+ bucket.requests += request.estimate.requests;
9243
+ bucket.tokens += estimateTokens;
9244
+ }
9245
+ nextReservation += 1;
9246
+ const reservationId = `mq-${String(nextReservation)}`;
9247
+ reservations.set(reservationId, {
9248
+ windowStart,
9249
+ estimateTokens,
9250
+ requests: request.estimate.requests,
9251
+ ruleIndexes: matched
9252
+ });
9253
+ return Promise.resolve({
9254
+ granted: true,
9255
+ reservationId
9256
+ });
9257
+ },
9258
+ reconcile(reservationId, usage, actual) {
9259
+ const reservation = reservations.get(reservationId);
9260
+ if (reservation === void 0) return Promise.resolve();
9261
+ reservations.delete(reservationId);
9262
+ const windowStart = windowStartAt(now());
9263
+ if (reservation.windowStart !== windowStart) return Promise.resolve();
9264
+ const delta = quotaActualTokens(usage) - reservation.estimateTokens;
9265
+ const requestsDelta = quotaActualRequestsDelta(actual);
9266
+ for (const index of reservation.ruleIndexes) {
9267
+ const bucket = buckets.get(index);
9268
+ if (bucket !== void 0 && bucket.windowStart === windowStart) {
9269
+ bucket.tokens = Math.max(0, bucket.tokens + delta);
9270
+ bucket.requests += requestsDelta;
9271
+ }
9272
+ }
9273
+ return Promise.resolve();
9274
+ },
9275
+ release(reservationId) {
9276
+ const reservation = reservations.get(reservationId);
9277
+ if (reservation === void 0) return Promise.resolve();
9278
+ reservations.delete(reservationId);
9279
+ const windowStart = windowStartAt(now());
9280
+ if (reservation.windowStart !== windowStart) return Promise.resolve();
9281
+ for (const index of reservation.ruleIndexes) {
9282
+ const bucket = buckets.get(index);
9283
+ if (bucket !== void 0 && bucket.windowStart === windowStart) {
9284
+ bucket.requests = Math.max(0, bucket.requests - reservation.requests);
9285
+ bucket.tokens = Math.max(0, bucket.tokens - reservation.estimateTokens);
9286
+ }
9287
+ }
9288
+ return Promise.resolve();
9289
+ },
9290
+ snapshot() {
9291
+ const windowStart = windowStartAt(now());
9292
+ return frozen.map((rule, index) => {
9293
+ const bucket = buckets.get(index);
9294
+ const current = bucket !== void 0 && bucket.windowStart === windowStart;
9295
+ return {
9296
+ rule,
9297
+ windowStart,
9298
+ requests: current ? bucket.requests : 0,
9299
+ tokens: current ? bucket.tokens : 0
9300
+ };
9301
+ });
9302
+ }
9303
+ };
9304
+ }
9305
+ /**
9306
+ * The default {@link EngineQuotaConfig.maxDenials}: generous next to the
9307
+ * transport default of 3 tries because a denial is a WAIT, not a
9308
+ * failure signal, yet finite because nothing else bounds the pre-wire
9309
+ * loop (the per-agent timeout is checked between turns, not inside a
9310
+ * dispatch).
9311
+ */
9312
+ const DEFAULT_MAX_QUOTA_DENIALS = 8;
9313
+ /**
9314
+ * Validates createEngine's quota config as a typed ConfigError before
9315
+ * any run could dispatch under a malformed limiter (the intake
9316
+ * discipline every engine option follows).
9317
+ */
9318
+ function validateEngineQuotaConfig(config, site = "createEngine quota") {
9319
+ if (config === void 0) return;
9320
+ const raw = config;
9321
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) throw new ConfigError(`${site} must be an object with a limiter`);
9322
+ const candidate = raw;
9323
+ const limiter = candidate.limiter;
9324
+ if (typeof limiter !== "object" || limiter === null || typeof limiter.reserve !== "function" || typeof limiter.reconcile !== "function") throw new ConfigError(`${site}.limiter must implement QuotaLimiter (reserve and reconcile functions)`);
9325
+ if (candidate.tenant !== void 0 && (typeof candidate.tenant !== "string" || candidate.tenant === "")) throw new ConfigError(`${site}.tenant must be a nonempty string when given`);
9326
+ if (candidate.onLimiterError !== void 0 && candidate.onLimiterError !== "deny" && candidate.onLimiterError !== "allow") throw new ConfigError(`${site}.onLimiterError must be 'deny' or 'allow' when given`);
9327
+ const reserveContinuations = candidate.reserveContinuations;
9328
+ if (reserveContinuations !== void 0 && typeof reserveContinuations !== "boolean") throw new ConfigError(`${site}.reserveContinuations must be a boolean when given`);
9329
+ const maxDenials = candidate.maxDenials;
9330
+ if (maxDenials !== void 0 && (typeof maxDenials !== "number" || !Number.isInteger(maxDenials) || maxDenials < 1)) throw new ConfigError(`${site}.maxDenials must be a positive integer when given`);
9331
+ const declared = candidate.declaredRules;
9332
+ if (declared !== void 0) validateQuotaRules(declared, `${site}.declaredRules`);
9333
+ }
9334
+ //#endregion
9335
+ //#region src/model/failover.ts
9336
+ /** Normalizes the author-facing ModelChoice.fallbacks list. */
9337
+ function normalizeFallbacks(refs) {
9338
+ return (refs ?? []).map((model) => ({ model }));
9339
+ }
9340
+ /**
9341
+ * Maps a retry class to its failover trigger once retries exhaust.
9342
+ * Overloaded (529) is transport-class for failover purposes; a
9343
+ * non-retryable error never fails over.
9344
+ */
9345
+ function failoverTriggerOf(retryClass) {
9346
+ if (retryClass === void 0) return;
9347
+ return retryClass === "rate-limit" ? "rate-limit" : "transport";
9348
+ }
9349
+ /**
9350
+ * The next target index past `from` that serves `trigger`, or undefined
9351
+ * when the chain is exhausted. Index 0 is the primary; the chain never
9352
+ * moves backwards (sticky failover).
9353
+ */
9354
+ function nextFailover(targets, trigger, from) {
9355
+ for (let index = from + 1; index < targets.length; index += 1) {
9356
+ const on = targets[index]?.on;
9357
+ if (on === void 0 || on.includes(trigger)) return index;
9358
+ }
9359
+ }
9360
+ /**
9361
+ * Classifies a terminal agent outcome for the degenerate fallback:
9362
+ * schema-mismatch errors are
9363
+ * 'schema-exhausted'; any other error is 'error'; limit terminals (the
9364
+ * no-progress abort included) are 'limit'; cancelled, escalated, and
9365
+ * skipped never trigger.
9366
+ */
9367
+ function fallbackTriggerOf(outcome) {
9368
+ if (outcome.status === "error") return outcome.error?.kind === "schema-mismatch" ? "schema-exhausted" : "error";
9369
+ if (outcome.status === "limit") return "limit";
9370
+ }
9371
+ //#endregion
9372
+ //#region src/model/projector.ts
9373
+ /** The provider family of an adapter: `provider` when set, else `id`. */
9374
+ function providerOf(adapter) {
9375
+ return adapter.provider ?? adapter.id;
9376
+ }
9377
+ /**
9378
+ * Projects the canonical history into the target provider's view:
9379
+ * provider-raw parts of a DIFFERENT provider are omitted; everything
9380
+ * else (text, images, tool calls, tool results, compaction content)
9381
+ * passes through untouched. Messages whose parts all belong to another
9382
+ * provider vanish entirely rather than ride as empty messages.
9383
+ */
9384
+ function projectHistory(messages, targetProvider) {
9385
+ const projected = [];
9386
+ for (const msg of messages) {
9387
+ const parts = msg.parts.filter((part) => part.type !== "provider-raw" || part.provider === targetProvider);
9388
+ if (parts.length === 0 && msg.parts.length > 0) continue;
9389
+ projected.push(parts.length === msg.parts.length ? msg : {
9390
+ ...msg,
9391
+ parts
9392
+ });
9393
+ }
9394
+ return projected;
9395
+ }
9396
+ /**
9397
+ * Lifts the adapter-shipped retention payload of one finished turn into
9398
+ * provider-raw parts (the retention transport). Reads
9399
+ * providerMetadata[<adapter id>].retainedParts and tags each block with
9400
+ * the adapter's provider family. Returns [] when the adapter shipped
9401
+ * nothing.
9402
+ */
9403
+ function liftRetainedParts(providerMetadata, adapter) {
9404
+ const namespace = providerMetadata?.[adapter.id];
9405
+ if (typeof namespace !== "object" || namespace === null) return [];
9406
+ const retained = namespace.retainedParts;
9407
+ if (!Array.isArray(retained)) return [];
9408
+ const blocks = retained;
9409
+ const provider = providerOf(adapter);
9410
+ return blocks.map((block) => ({
9411
+ type: "provider-raw",
9412
+ provider,
9413
+ block
9414
+ }));
9415
+ }
9416
+ //#endregion
9417
+ //#region src/model/retry.ts
9418
+ /**
9419
+ * Transport RetryPolicy (M4-T05): retries live UNDER the journal. A
9420
+ * retried-then-successful call is exactly one journal entry with one
9421
+ * usage total, transport retries never count as lineage attempts
9422
+ * (DEF-3), the provider-supplied retryAfterMs replaces the computed
9423
+ * delay, and task-class failures never retry by construction (a
9424
+ * non-retryable WireError has no retry class).
9425
+ *
9426
+ * Full contract: https://docs.rulvar.com/guide/model-routing; the
9427
+ * Appendix A defaults were committed at the M4 entry gate.
9428
+ */
9429
+ /**
9430
+ * Captured at module load, before the InProcessRunner's nondeterminism
9431
+ * guard can patch the global: the engine's own jitter is journal
9432
+ * invisible and must never be blamed on workflow code.
9433
+ */
9434
+ const nativeRandom = Math.random;
9435
+ /** Appendix A committed defaults (M4 entry gate, PR #26). */
9436
+ const DEFAULT_RETRY_POLICY = {
9437
+ attempts: 3,
9438
+ backoff: {
9439
+ initialMs: 500,
9440
+ factor: 2,
9441
+ maxMs: 8e3,
9442
+ jitter: true
9443
+ },
9444
+ retryOn: [
9445
+ "transport",
9446
+ "rate-limit",
9097
9447
  "overloaded"
9098
9448
  ]
9099
9449
  };
@@ -11552,6 +11902,8 @@ async function runAgent(options) {
11552
11902
  for (;;) {
11553
11903
  const target = site.chain[site.cursor.index] ?? site.chain[0];
11554
11904
  let tries = 0;
11905
+ let denialTurns = 0;
11906
+ const maxDenials = options.quota?.maxDenials ?? 8;
11555
11907
  inner: for (;;) {
11556
11908
  let reservationId;
11557
11909
  const segmentReservations = [];
@@ -11792,14 +12144,16 @@ async function runAgent(options) {
11792
12144
  reportedLimits: limited.reportedLimits
11793
12145
  });
11794
12146
  }
11795
- tries += 1;
12147
+ if (outcome.quotaDenied === true) denialTurns += 1;
12148
+ else tries += 1;
11796
12149
  const retryClass = outcome.aborted === "idle" ? "transport" : outcome.wireError === void 0 ? void 0 : retryClassOf(outcome.wireError);
11797
12150
  if (retryClass === void 0) return {
11798
12151
  outcome,
11799
12152
  target
11800
12153
  };
11801
12154
  usageApprox = usageApprox || outcome.usageApprox;
11802
- if (retryOn.includes(retryClass) && tries < retryPolicy.attempts) {
12155
+ const retryBudgetLeft = outcome.quotaDenied === true ? denialTurns < maxDenials : tries < retryPolicy.attempts;
12156
+ if (retryOn.includes(retryClass) && retryBudgetLeft) {
11803
12157
  const abortedBefore = abortKind();
11804
12158
  if (abortedBefore !== void 0) return {
11805
12159
  outcome: abortedOutcome(abortedBefore),
@@ -11807,7 +12161,7 @@ async function runAgent(options) {
11807
12161
  };
11808
12162
  const retryAfter = (outcome.wireError?.data)?.retryAfterMs;
11809
12163
  if (outcome.wireError !== void 0) {
11810
- transportRetries += 1;
12164
+ if (outcome.quotaDenied !== true) transportRetries += 1;
11811
12165
  events?.emit({
11812
12166
  type: "agent:error",
11813
12167
  agentType,
@@ -11816,7 +12170,7 @@ async function runAgent(options) {
11816
12170
  willRetry: true
11817
12171
  });
11818
12172
  }
11819
- await backoffWait(retryDelayMs(retryPolicy, tries - 1, typeof retryAfter === "number" ? retryAfter : void 0, retryRandom));
12173
+ await backoffWait(retryDelayMs(retryPolicy, outcome.quotaDenied === true ? denialTurns - 1 : tries - 1, typeof retryAfter === "number" ? retryAfter : void 0, retryRandom));
11820
12174
  const abortedAfter = abortKind();
11821
12175
  if (abortedAfter !== void 0) return {
11822
12176
  outcome: abortedOutcome(abortedAfter),
@@ -14329,319 +14683,6 @@ function ladderRungChoice(ladder, index) {
14329
14683
  };
14330
14684
  }
14331
14685
  //#endregion
14332
- //#region src/model/quota.ts
14333
- /**
14334
- * Quota rules and the in-process reference QuotaLimiter (RV-215).
14335
- * The rule model is shared by every reference implementation
14336
- * (memoryQuotaLimiter here, SqliteQuotaLimiter in
14337
- * @rulvar/store-sqlite): fixed one-minute windows aligned to the
14338
- * epoch, admission at reservation time, reconciliation to actual
14339
- * usage inside the same window. The hard guarantee is on
14340
- * `requestsPerMinute` (every wire attempt is exactly one request);
14341
- * `tokensPerMinute` admits on the heuristic estimate and settles to
14342
- * actual usage, so token windows are approximate at admission and
14343
- * exact at settlement.
14344
- *
14345
- * Docs: https://docs.rulvar.com/guide/model-routing
14346
- */
14347
- /**
14348
- * Captured at module load, before the InProcessRunner's
14349
- * nondeterminism guard can patch the global: the limiter's clock is
14350
- * engine infrastructure on the live-only dispatch path and must never
14351
- * be blamed on workflow code.
14352
- */
14353
- const nativeNow = Date.now;
14354
- /** The fixed accounting window every PerMinute cap counts over. */
14355
- const QUOTA_WINDOW_MS = 6e4;
14356
- /**
14357
- * Validates a quota rule set as a typed ConfigError before any
14358
- * limiter can admit under it: a non-array or empty set, a rule
14359
- * without a cap, a malformed dimension, or a malformed cap all fail
14360
- * loud at construction. Shared by every reference implementation.
14361
- */
14362
- function validateQuotaRules(rules, site = "quota rules") {
14363
- const raw = rules;
14364
- if (!Array.isArray(raw)) throw new ConfigError(`${site} must be an array of QuotaRule objects`);
14365
- if (raw.length === 0) throw new ConfigError(`${site} must contain at least one rule`);
14366
- raw.forEach((entry, index) => {
14367
- const at = `${site}[${String(index)}]`;
14368
- if (typeof entry !== "object" || entry === null || Array.isArray(entry)) throw new ConfigError(`${at} must be a QuotaRule object`);
14369
- const rule = entry;
14370
- for (const dimension of [
14371
- "provider",
14372
- "model",
14373
- "tenant"
14374
- ]) {
14375
- const value = rule[dimension];
14376
- if (value !== void 0 && (typeof value !== "string" || value === "")) throw new ConfigError(`${at}.${dimension} must be a nonempty string when given`);
14377
- }
14378
- if (rule.requestsPerMinute === void 0 && rule.tokensPerMinute === void 0) throw new ConfigError(`${at} must set requestsPerMinute or tokensPerMinute (or both)`);
14379
- for (const cap of ["requestsPerMinute", "tokensPerMinute"]) if (rule[cap] !== void 0) requirePositiveInteger$2(rule[cap], `${at}.${cap}`);
14380
- });
14381
- }
14382
- /**
14383
- * The canonical content key of one rule (RV608, promoted from the
14384
- * store limiters): a fixed-field-order JSON of the rule, identical
14385
- * across processes and hosts for identical rules. It is the bucket key
14386
- * of both store references, the input of
14387
- * `quotaRulesFingerprint`, and the CANONICAL ORDER every reference
14388
- * limiter folds denials in, so equal rule sets produce byte-identical
14389
- * refusal objects regardless of array permutation.
14390
- */
14391
- function quotaRuleKey(rule) {
14392
- return JSON.stringify({
14393
- provider: rule.provider ?? null,
14394
- model: rule.model ?? null,
14395
- tenant: rule.tenant ?? null,
14396
- requestsPerMinute: rule.requestsPerMinute ?? null,
14397
- tokensPerMinute: rule.tokensPerMinute ?? null
14398
- });
14399
- }
14400
- /**
14401
- * Validates a rule set and returns the immutable snapshot every
14402
- * reference limiter admits under (RV608): a fresh array of fresh
14403
- * objects carrying ONLY the known rule fields, each frozen, the array
14404
- * frozen. The caller's array and objects stay untouched and unshared,
14405
- * so ordinary JavaScript after the constructor (a pushed rule, a
14406
- * reassigned cap) can no longer change a decision, a bucket key, or a
14407
- * recorded fingerprint.
14408
- *
14409
- * A set containing two rules with the same canonical content key is
14410
- * refused typed (RV704): the memory reference buckets by rule INDEX
14411
- * (each copy counts independently, the full cap admits) while the
14412
- * store references bucket by rule KEY (one shared bucket is debited
14413
- * once per matching copy, half the cap admits), so the same duplicated
14414
- * configuration admitted differently per storage. Refusing it at the
14415
- * shared construction chokepoint is what keeps equal configurations
14416
- * equal on every storage.
14417
- */
14418
- function snapshotQuotaRules(rules, site = "quota rules") {
14419
- validateQuotaRules(rules, site);
14420
- const firstIndexByKey = /* @__PURE__ */ new Map();
14421
- rules.forEach((rule, index) => {
14422
- const key = quotaRuleKey(rule);
14423
- const first = firstIndexByKey.get(key);
14424
- if (first !== void 0) throw new ConfigError(`${site}[${String(index)}] duplicates ${site}[${String(first)}] (rule key ${key}): identical rules occupy independent buckets in memory but share one key-debited bucket on keyed storage, so one configuration would admit differently per store; delete the duplicate`);
14425
- firstIndexByKey.set(key, index);
14426
- });
14427
- return Object.freeze(rules.map((rule) => Object.freeze({
14428
- ...rule.provider === void 0 ? {} : { provider: rule.provider },
14429
- ...rule.model === void 0 ? {} : { model: rule.model },
14430
- ...rule.tenant === void 0 ? {} : { tenant: rule.tenant },
14431
- ...rule.requestsPerMinute === void 0 ? {} : { requestsPerMinute: rule.requestsPerMinute },
14432
- ...rule.tokensPerMinute === void 0 ? {} : { tokensPerMinute: rule.tokensPerMinute }
14433
- })));
14434
- }
14435
- /** True when every dimension the rule pins matches the request. */
14436
- function quotaRuleMatches(rule, request) {
14437
- return (rule.provider === void 0 || rule.provider === request.provider) && (rule.model === void 0 || rule.model === request.model) && (rule.tenant === void 0 || rule.tenant === request.tenant);
14438
- }
14439
- /** The tokens a reservation is admitted under: input estimate plus the output cap. */
14440
- function quotaEstimateTokens(request) {
14441
- return request.estimate.inputTokens + (request.estimate.maxOutputTokens ?? 0);
14442
- }
14443
- /** The tokens a settled attempt actually consumed. */
14444
- function quotaActualTokens(usage) {
14445
- return usage.inputTokens + usage.outputTokens;
14446
- }
14447
- /**
14448
- * The request-count settlement delta of one reservation (RV905): the
14449
- * reservation admitted ONE wire request, and `actual.requests` names
14450
- * how many the attempt actually made (an adapter absorbing
14451
- * provider-side continuations dispatches several inside one reserved
14452
- * call). Non-integer, non-positive, or absent actuals settle as the
14453
- * single reserved request (delta 0); a settlement only ever ADDS, the
14454
- * calls already happened. Shared by every reference limiter so the
14455
- * three implementations cannot disagree about the arithmetic.
14456
- */
14457
- function quotaActualRequestsDelta(actual) {
14458
- const requests = actual?.requests;
14459
- return typeof requests === "number" && Number.isInteger(requests) && requests > 1 ? requests - 1 : 0;
14460
- }
14461
- /**
14462
- * One rule's admission verdict against its current-window counters,
14463
- * the pure decision both reference implementations share. A denial
14464
- * carries the window remainder as retryAfterMs, except when the
14465
- * estimate alone can never fit the token cap: that denial says
14466
- * retryAfterMs 0 (retry immediately), so the caller's bounded
14467
- * attempts exhaust without waiting and failover gets its chance.
14468
- */
14469
- function quotaRuleAdmission(rule, counters, estimate, msUntilWindowEnd) {
14470
- if (rule.requestsPerMinute !== void 0 && counters.requests + estimate.requests > rule.requestsPerMinute) return {
14471
- admit: false,
14472
- retryAfterMs: msUntilWindowEnd,
14473
- reason: `requestsPerMinute ${String(rule.requestsPerMinute)} exhausted`
14474
- };
14475
- if (rule.tokensPerMinute !== void 0) {
14476
- if (estimate.tokens > rule.tokensPerMinute) return {
14477
- admit: false,
14478
- retryAfterMs: 0,
14479
- reason: `the estimate of ${String(estimate.tokens)} tokens can never fit tokensPerMinute ${String(rule.tokensPerMinute)}`
14480
- };
14481
- if (counters.tokens + estimate.tokens > rule.tokensPerMinute) return {
14482
- admit: false,
14483
- retryAfterMs: msUntilWindowEnd,
14484
- reason: `tokensPerMinute ${String(rule.tokensPerMinute)} exhausted`
14485
- };
14486
- }
14487
- return { admit: true };
14488
- }
14489
- /**
14490
- * Folds one more failing rule into the decision the caller returns:
14491
- * the wait is the LONGEST failing horizon (every matching rule must
14492
- * admit), and the FIRST failing rule names the denial.
14493
- */
14494
- function mergeQuotaDenial(current, next) {
14495
- if (current === void 0) return {
14496
- retryAfterMs: next.retryAfterMs,
14497
- reason: next.reason
14498
- };
14499
- return next.retryAfterMs > current.retryAfterMs ? {
14500
- retryAfterMs: next.retryAfterMs,
14501
- reason: current.reason
14502
- } : current;
14503
- }
14504
- /**
14505
- * The in-process reference QuotaLimiter: fixed epoch-aligned
14506
- * one-minute windows over the shared rule model. Coordinates every
14507
- * engine that shares THIS instance inside one process; processes
14508
- * coordinate through a shared-storage implementation of the same SPI
14509
- * (SqliteQuotaLimiter in @rulvar/store-sqlite) instead.
14510
- */
14511
- function memoryQuotaLimiter(rules, options = {}) {
14512
- const frozen = snapshotQuotaRules(rules, "memoryQuotaLimiter rules");
14513
- const ordered = frozen.map((rule, index) => ({
14514
- rule,
14515
- index,
14516
- key: quotaRuleKey(rule)
14517
- })).sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
14518
- const now = options.now ?? (() => nativeNow());
14519
- const buckets = /* @__PURE__ */ new Map();
14520
- const reservations = /* @__PURE__ */ new Map();
14521
- let nextReservation = 0;
14522
- const windowStartAt = (at) => at - at % QUOTA_WINDOW_MS;
14523
- const bucketFor = (ruleIndex, windowStart) => {
14524
- let bucket = buckets.get(ruleIndex);
14525
- if (bucket === void 0 || bucket.windowStart !== windowStart) {
14526
- bucket = {
14527
- windowStart,
14528
- requests: 0,
14529
- tokens: 0
14530
- };
14531
- buckets.set(ruleIndex, bucket);
14532
- }
14533
- return bucket;
14534
- };
14535
- const prune = (windowStart) => {
14536
- for (const [id, reservation] of reservations) if (reservation.windowStart < windowStart) reservations.delete(id);
14537
- };
14538
- return {
14539
- reserve(request) {
14540
- const at = now();
14541
- const windowStart = windowStartAt(at);
14542
- prune(windowStart);
14543
- const estimateTokens = quotaEstimateTokens(request);
14544
- const msUntilWindowEnd = windowStart + QUOTA_WINDOW_MS - at;
14545
- const matched = [];
14546
- let denial;
14547
- for (const { rule, index } of ordered) {
14548
- if (!quotaRuleMatches(rule, request)) continue;
14549
- matched.push(index);
14550
- const verdict = quotaRuleAdmission(rule, bucketFor(index, windowStart), {
14551
- requests: request.estimate.requests,
14552
- tokens: estimateTokens
14553
- }, msUntilWindowEnd);
14554
- if (!verdict.admit) denial = mergeQuotaDenial(denial, verdict);
14555
- }
14556
- if (denial !== void 0) return Promise.resolve({
14557
- granted: false,
14558
- ...denial
14559
- });
14560
- for (const index of matched) {
14561
- const bucket = bucketFor(index, windowStart);
14562
- bucket.requests += request.estimate.requests;
14563
- bucket.tokens += estimateTokens;
14564
- }
14565
- nextReservation += 1;
14566
- const reservationId = `mq-${String(nextReservation)}`;
14567
- reservations.set(reservationId, {
14568
- windowStart,
14569
- estimateTokens,
14570
- requests: request.estimate.requests,
14571
- ruleIndexes: matched
14572
- });
14573
- return Promise.resolve({
14574
- granted: true,
14575
- reservationId
14576
- });
14577
- },
14578
- reconcile(reservationId, usage, actual) {
14579
- const reservation = reservations.get(reservationId);
14580
- if (reservation === void 0) return Promise.resolve();
14581
- reservations.delete(reservationId);
14582
- const windowStart = windowStartAt(now());
14583
- if (reservation.windowStart !== windowStart) return Promise.resolve();
14584
- const delta = quotaActualTokens(usage) - reservation.estimateTokens;
14585
- const requestsDelta = quotaActualRequestsDelta(actual);
14586
- for (const index of reservation.ruleIndexes) {
14587
- const bucket = buckets.get(index);
14588
- if (bucket !== void 0 && bucket.windowStart === windowStart) {
14589
- bucket.tokens = Math.max(0, bucket.tokens + delta);
14590
- bucket.requests += requestsDelta;
14591
- }
14592
- }
14593
- return Promise.resolve();
14594
- },
14595
- release(reservationId) {
14596
- const reservation = reservations.get(reservationId);
14597
- if (reservation === void 0) return Promise.resolve();
14598
- reservations.delete(reservationId);
14599
- const windowStart = windowStartAt(now());
14600
- if (reservation.windowStart !== windowStart) return Promise.resolve();
14601
- for (const index of reservation.ruleIndexes) {
14602
- const bucket = buckets.get(index);
14603
- if (bucket !== void 0 && bucket.windowStart === windowStart) {
14604
- bucket.requests = Math.max(0, bucket.requests - reservation.requests);
14605
- bucket.tokens = Math.max(0, bucket.tokens - reservation.estimateTokens);
14606
- }
14607
- }
14608
- return Promise.resolve();
14609
- },
14610
- snapshot() {
14611
- const windowStart = windowStartAt(now());
14612
- return frozen.map((rule, index) => {
14613
- const bucket = buckets.get(index);
14614
- const current = bucket !== void 0 && bucket.windowStart === windowStart;
14615
- return {
14616
- rule,
14617
- windowStart,
14618
- requests: current ? bucket.requests : 0,
14619
- tokens: current ? bucket.tokens : 0
14620
- };
14621
- });
14622
- }
14623
- };
14624
- }
14625
- /**
14626
- * Validates createEngine's quota config as a typed ConfigError before
14627
- * any run could dispatch under a malformed limiter (the intake
14628
- * discipline every engine option follows).
14629
- */
14630
- function validateEngineQuotaConfig(config, site = "createEngine quota") {
14631
- if (config === void 0) return;
14632
- const raw = config;
14633
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) throw new ConfigError(`${site} must be an object with a limiter`);
14634
- const candidate = raw;
14635
- const limiter = candidate.limiter;
14636
- if (typeof limiter !== "object" || limiter === null || typeof limiter.reserve !== "function" || typeof limiter.reconcile !== "function") throw new ConfigError(`${site}.limiter must implement QuotaLimiter (reserve and reconcile functions)`);
14637
- if (candidate.tenant !== void 0 && (typeof candidate.tenant !== "string" || candidate.tenant === "")) throw new ConfigError(`${site}.tenant must be a nonempty string when given`);
14638
- if (candidate.onLimiterError !== void 0 && candidate.onLimiterError !== "deny" && candidate.onLimiterError !== "allow") throw new ConfigError(`${site}.onLimiterError must be 'deny' or 'allow' when given`);
14639
- const reserveContinuations = candidate.reserveContinuations;
14640
- if (reserveContinuations !== void 0 && typeof reserveContinuations !== "boolean") throw new ConfigError(`${site}.reserveContinuations must be a boolean when given`);
14641
- const declared = candidate.declaredRules;
14642
- if (declared !== void 0) validateQuotaRules(declared, `${site}.declaredRules`);
14643
- }
14644
- //#endregion
14645
14686
  //#region src/runtime/usage-limits.ts
14646
14687
  /**
14647
14688
  * UsageLimits (M1-T06): normative limit vocabulary and the per-spawn merge.
@@ -16587,7 +16628,8 @@ function createCtx(internals, rootWorkflow) {
16587
16628
  }),
16588
16629
  reconcile: (reservationId, usage, actual) => quota.limiter.reconcile(reservationId, usage, actual),
16589
16630
  onLimiterError: quota.onLimiterError,
16590
- reserveContinuations: quota.reserveContinuations
16631
+ reserveContinuations: quota.reserveContinuations,
16632
+ maxDenials: quota.maxDenials
16591
16633
  };
16592
16634
  const limiterRelease = quota.limiter.release?.bind(quota.limiter);
16593
16635
  if (limiterRelease !== void 0) runAgentOptions.quota.release = limiterRelease;
@@ -24078,6 +24120,7 @@ function createEngine(options) {
24078
24120
  ...options.quota.tenant === void 0 ? {} : { tenant: options.quota.tenant },
24079
24121
  onLimiterError: options.quota.onLimiterError ?? "deny",
24080
24122
  reserveContinuations: options.quota.reserveContinuations ?? false,
24123
+ maxDenials: options.quota.maxDenials ?? 8,
24081
24124
  ...options.quota.declaredRules === void 0 ? {} : { declaredRules: options.quota.declaredRules }
24082
24125
  };
24083
24126
  const knowledgeStore = options.stores?.modelKnowledge;
@@ -25103,4 +25146,4 @@ function createSandboxBridge(ctx, options) {
25103
25146
  };
25104
25147
  }
25105
25148
  //#endregion
25106
- export { AWAIT_SCHEMA, AdmissionController, AdmissionRejectedError, AgentCallError, BUDGET_ABORT_REASON, BudgetExhaustedError, CANCEL_AGENT_SCHEMA, CHECKPOINT_FORMAT_V1, CLAIM_STATEMENT_MAX_CHARS, CLAIM_TTL_DAYS, COMPACTION_SUMMARY_PREFIX, CURRENT_HASH_VERSION, ConfigError, DEFAULT_ANCHOR_PATTERN, DEFAULT_ARTIFACT_PATTERN, DEFAULT_CHILD_BUDGET_FRACTION, DEFAULT_CHILD_RESULT_PAGE_CHARS, DEFAULT_CITATION_PATTERN, DEFAULT_CITATION_SAMPLE, DEFAULT_CLAIM_JUDGE_MAX_TURNS, DEFAULT_COMPACTION_THRESHOLD, DEFAULT_ESCALATION_LIMITS, DEFAULT_EVIDENCE_CALLS_PER_ENTRY, DEFAULT_EVIDENCE_GRADE_PHRASES, DEFAULT_EVIDENCE_MIN_SHARE, DEFAULT_EVIDENCE_OVERHEAD_CALLS, DEFAULT_FINISH_MAX_REPAIRS, DEFAULT_FLAT_RESERVE_USD, DEFAULT_MAX_CHILDREN_PER_NODE, DEFAULT_MAX_CLAIM_PAIRS, DEFAULT_MAX_CONTRADICTIONS, DEFAULT_MAX_DEPTH, DEFAULT_MAX_EXCERPT_CHARS, DEFAULT_MAX_OSCILLATIONS_PER_KEY, DEFAULT_MAX_PAIR_EXCERPT_CHARS, DEFAULT_MAX_PINNED_WORKTREES, DEFAULT_MAX_POOL_PER_PAIR, DEFAULT_MAX_REVISIONS_PER_RUN, DEFAULT_MAX_TOTAL_SPAWNS, DEFAULT_MAX_TURNS, DEFAULT_MODEL_RETRY_ATTEMPTS, DEFAULT_NO_PROGRESS_TURNS, DEFAULT_PER_RUN_CONCURRENCY, DEFAULT_RETRY_POLICY, DEFAULT_STREAM_IDLE_TIMEOUT_MS, DEFAULT_SYNTHESIS_MAX_TURNS, DEFAULT_SYNTHESIS_NOTE_MAX_TURNS, DedupIndex, DeterminismError, EMIT_RESULT_TOOL, EMPTY_SCHEMA_HASH, EMPTY_TOOLSET_HASH, ESCALATE_TOOL_NAME, ESCALATION_REPORT_SCHEMA, ESCALATION_REQUEST_SCHEMA, EVENT_SEGMENT_STRIDE, EscalationDecisionAbortedError, EventBus, ExternalRegistry, FINALIZE_SYNTHESIS_INSTRUCTION, FINISH_SCHEMA, FINISH_SECTIONAL_SCHEMA, FINISH_TOOL_NAME, FailRunError, FileModelKnowledgeStore, FileTranscriptStore, GET_CHILD_RESULT_SCHEMA, GET_CHILD_RESULT_TOOL_NAME, GitWorktreeProvider, IMPLEMENTATION_PROFILE_LIMITS, INBOX_PROPOSAL_TTL_DAYS, IN_FLIGHT_EXPOSURE_REFUSAL_PREFIX, InMemoryStore, InMemoryTranscriptStore, InProcessRunner, InvalidResolutionError, JOURNAL_ENVELOPE_MARKER, JournalCompatibilityError, JournalMatcher, JournalMissError, JournalOrderViolation, JsonlFileStore, KB_ACTIVE_CLAIMS_CAP, KB_CARD_RENDER_BUDGET_CHARS, KeyedLimiter, KnowledgeCasError, LARGE_VALUE_WARN_BYTES, LEGACY_LTID_PREFIX, LEGACY_SIGNATURE_INPUTS, LINEAGE_SIG_VERSION, LeaseHeldError, LineageIndex, MASKED_SECRET, MAX_CHILD_RESULT_PAGE_CHARS, MAX_DEPTH_CEILING, MAX_RUN_ID_LENGTH, MAX_TIMER_DELAY_MS, ModelRetry, NoProgressDetector, NonSerializableValueError, ORCHESTRATE_WORKFLOW_NAME, OrchestratorCapConfigError, PARALLEL_AGENTS_SCHEMA, PROGRESS_REPORT_TOOL_NAME, ParallelSiteCounter, PlanInvariantError, QUOTA_WINDOW_MS, READ_CHILD_ARTIFACT_SCHEMA, READ_CHILD_ARTIFACT_TOOL_NAME, RESEARCH_PROFILE_LIMITS, REVIEW_PROFILE_LIMITS, ROLE_EFFORT_DEFAULTS, ROOT_ACCOUNT, ROOT_SCOPE, RUN_PROFILES, RUN_SETTLE_DECISION_TYPE, ReplayPlanHashMismatch, Replayer, ResolutionArbiter, ResolutionFold, RulvarError, RunBudget, SANDBOX_AGENT_OPT_KEYS, SPAWN_AGENT_SCHEMA, SandboxError, ScriptRejected, Semaphore, SettlementError, SpanRegistry, SupersededError, TOOL_NAME_PATTERN, TerminationAccount, WAIT_FOR_EVENTS_SCHEMA, WAIT_FOR_EVENTS_TOOL_NAME, WAKE_SUMMARY_RENDER_BUDGET_CHARS, accountSpendFromJournal, admissionReserveUsd, affordableOutputTokens, agentErrorFromWire, agentErrorToWire, agentResultWire, agentScope, applyClaimOps, applyStructuredOutputTier, approachSigCoarse, approachSigOf, archiveDeprecatedModelOps, assertFencedWrites, assertSafeRunId, atCompactionThreshold, attestToolset, auditRun, auditRuns, buildAbandonFold, buildAdapterRegistry, buildCostReport, buildDeriverRegistry, buildOrchestratorTools, buildTerminationInitValue, buildToolContext, canRideLoopTurn, canonicalIsolationTag, canonicalizeLadder, canonicalizeSchema, capIssues, capsHashOf, checkFloors, checkpointRefFor, childCoveragePrefix, citationTargetsValidator, citedValueValidator, claimExpired, claimExpiry, claimIssues, claimOpIssues, classifyAgentError, classifyAttemptOutcome, collectDeclaredLadders, compactMessages, compareRates, compilePermissionChain, compilePermissionPreset, compileSecretMasker, compileVerifiedLayer, constantTimeEqual, costReportFromJournal, countsAgainstLimit, createCanonicalIdMinter, createCtx, createEngine, createEnvelopeEncryption, createSandboxBridge, currentOnlyKeyRing, decodeCheckpoint, dedupeRepeatedClaims, defineWorkflow, deriveContentKey, deriverV1, deriverV2, digestOf, dispatchProjectionReserveUsd, dispositionHook, emptyDigestBlocks, emptyToolset, encodeCheckpoint, enforceToolsetAttestation, entryUsageSlices, escalateTool, evaluatePermission, evaluateReuse, evidenceGradeValidator, evidencePreservedValidator, executeWorkflow, executionFactsOf, exhaustionCodeOf, extractCandidate, failoverTriggerOf, fallbackTriggerOf, filterClaimsForRun, finalizeFires, findContradictions, finishContract, foldLedger, foldTermination, formatCharacterValidator, formatRePrompt, formatScopePath, hasFencedWrites, hasMetaLookup, hashRunArgs, hashRunOutput, hashWorkflowBody, hashWorkflowSource, headingStructureValidator, identityJcs, implementationAgentProfile, invoiceFromJournal, isEscalated, isSchemaPairSpec, isStandardSchemaSpec, isStrictCompatibleSchema, journalPricingSnapshot, kMaxOf, knowledgeHash, ladderLengthOf, ladderRungChoice, lastRunSettle, latestProgressReport, lexShellCommand, liftRetainedParts, lineageWeightOf, localKeyProvider, makeOrchestratorWorkflow, maskSecrets, maskSecretsDeep, maskSecretsJson, matchArgvPattern, matchShellCommand, mcp, memoryQuotaLimiter, mergeQuotaDenial, mergeUsageLimits, metaMatchesFilter, minMatchesValidator, modelEpochOf, modelKnowledgeCard, modelSpecIdentity, needsSeparateExtract, nextFailover, nodeLinkKey, normalizeApproachTag, normalizeEntry, normalizeFallbacks, orchestrate, orchestratorAdmissionEstCostUsd, pairDraftClaims, parallelScope, parseModelRef, parseScopePath, persistedTerminalEnvelope, phiInitialOf, pipelineScope, planNodeScope, preflightEstimate, priceComponentsOf, priceEntryBilling, priceEntryUsage, priceUsdOf, profileCard, profileRegistrySnapshotHash, progressReportTool, projectHistory, projectIdentity, projectToJsonSchema, proposalStatement, providerOf, quotaActualRequestsDelta, quotaActualTokens, quotaEstimateTokens, quotaRuleAdmission, quotaRuleKey, quotaRuleMatches, readRunMeta, readTerminationInit, reconcileRunMeta, reduceAuditTrail, reduceCriticalPath, reduceInvocationTable, registryKeyRing, remeasureQueue, replayDisposition, repositoryResearchToolset, requiredFieldsValidator, requiredSectionsValidator, researchAgentProfile, resolveModelInvocation, resolvePricing, resolveToolset, retryClassOf, retryDelayMs, reviewAgentProfile, roleConfiguredInRouting, roundOneDisposition, runAgent, runProfile, sanitizeTerminalText, sanitizeTokenCount, sanitizeUsage, sanitizeUsageDelta, scanJournalCompatibility, schemaHash, schemaHashOfSpec, sectionCitationsValidator, selectStructuredOutputTier, selfTestFinishValidation, shouldCompact, snapshotQuotaRules, snapshotUsage, spawnDepthOf, spliceSections, stripFencedBlocks, sumUsage, summarizeInstruction, summarizeOutput, terminalEnvelopeOf, terminationConfigDrift, tierWithinCaps, toApprovalDecision, toJournalValue, tool, toolContract, toolContractHash, toolsetHash, ttlState, usageViolations, validateDetachedResolution, validateEditorialCommit, validateEngineQuotaConfig, validateEntryShape, validateEscalationLimits, validateEscalationReport, validateQuotaRules, validateRetryPolicy, validateSchemaSpec, validateTerminationLimits, validateToolsetAttestation, validateUsageLimits, wordCountValidator, workflowScope, workflowSourceRef, wrapJournalStore, wrapTranscriptStore };
25149
+ export { AWAIT_SCHEMA, AdmissionController, AdmissionRejectedError, AgentCallError, BUDGET_ABORT_REASON, BudgetExhaustedError, CANCEL_AGENT_SCHEMA, CHECKPOINT_FORMAT_V1, CLAIM_STATEMENT_MAX_CHARS, CLAIM_TTL_DAYS, COMPACTION_SUMMARY_PREFIX, CURRENT_HASH_VERSION, ConfigError, DEFAULT_ANCHOR_PATTERN, DEFAULT_ARTIFACT_PATTERN, DEFAULT_CHILD_BUDGET_FRACTION, DEFAULT_CHILD_RESULT_PAGE_CHARS, DEFAULT_CITATION_PATTERN, DEFAULT_CITATION_SAMPLE, DEFAULT_CLAIM_JUDGE_MAX_TURNS, DEFAULT_COMPACTION_THRESHOLD, DEFAULT_ESCALATION_LIMITS, DEFAULT_EVIDENCE_CALLS_PER_ENTRY, DEFAULT_EVIDENCE_GRADE_PHRASES, DEFAULT_EVIDENCE_MIN_SHARE, DEFAULT_EVIDENCE_OVERHEAD_CALLS, DEFAULT_FINISH_MAX_REPAIRS, DEFAULT_FLAT_RESERVE_USD, DEFAULT_MAX_CHILDREN_PER_NODE, DEFAULT_MAX_CLAIM_PAIRS, DEFAULT_MAX_CONTRADICTIONS, DEFAULT_MAX_DEPTH, DEFAULT_MAX_EXCERPT_CHARS, DEFAULT_MAX_OSCILLATIONS_PER_KEY, DEFAULT_MAX_PAIR_EXCERPT_CHARS, DEFAULT_MAX_PINNED_WORKTREES, DEFAULT_MAX_POOL_PER_PAIR, DEFAULT_MAX_QUOTA_DENIALS, DEFAULT_MAX_REVISIONS_PER_RUN, DEFAULT_MAX_TOTAL_SPAWNS, DEFAULT_MAX_TURNS, DEFAULT_MODEL_RETRY_ATTEMPTS, DEFAULT_NO_PROGRESS_TURNS, DEFAULT_PER_RUN_CONCURRENCY, DEFAULT_RETRY_POLICY, DEFAULT_STREAM_IDLE_TIMEOUT_MS, DEFAULT_SYNTHESIS_MAX_TURNS, DEFAULT_SYNTHESIS_NOTE_MAX_TURNS, DedupIndex, DeterminismError, EMIT_RESULT_TOOL, EMPTY_SCHEMA_HASH, EMPTY_TOOLSET_HASH, ESCALATE_TOOL_NAME, ESCALATION_REPORT_SCHEMA, ESCALATION_REQUEST_SCHEMA, EVENT_SEGMENT_STRIDE, EscalationDecisionAbortedError, EventBus, ExternalRegistry, FINALIZE_SYNTHESIS_INSTRUCTION, FINISH_SCHEMA, FINISH_SECTIONAL_SCHEMA, FINISH_TOOL_NAME, FailRunError, FileModelKnowledgeStore, FileTranscriptStore, GET_CHILD_RESULT_SCHEMA, GET_CHILD_RESULT_TOOL_NAME, GitWorktreeProvider, IMPLEMENTATION_PROFILE_LIMITS, INBOX_PROPOSAL_TTL_DAYS, IN_FLIGHT_EXPOSURE_REFUSAL_PREFIX, InMemoryStore, InMemoryTranscriptStore, InProcessRunner, InvalidResolutionError, JOURNAL_ENVELOPE_MARKER, JournalCompatibilityError, JournalMatcher, JournalMissError, JournalOrderViolation, JsonlFileStore, KB_ACTIVE_CLAIMS_CAP, KB_CARD_RENDER_BUDGET_CHARS, KeyedLimiter, KnowledgeCasError, LARGE_VALUE_WARN_BYTES, LEGACY_LTID_PREFIX, LEGACY_SIGNATURE_INPUTS, LINEAGE_SIG_VERSION, LeaseHeldError, LineageIndex, MASKED_SECRET, MAX_CHILD_RESULT_PAGE_CHARS, MAX_DEPTH_CEILING, MAX_RUN_ID_LENGTH, MAX_TIMER_DELAY_MS, ModelRetry, NoProgressDetector, NonSerializableValueError, ORCHESTRATE_WORKFLOW_NAME, OrchestratorCapConfigError, PARALLEL_AGENTS_SCHEMA, PROGRESS_REPORT_TOOL_NAME, ParallelSiteCounter, PlanInvariantError, QUOTA_WINDOW_MS, READ_CHILD_ARTIFACT_SCHEMA, READ_CHILD_ARTIFACT_TOOL_NAME, RESEARCH_PROFILE_LIMITS, REVIEW_PROFILE_LIMITS, ROLE_EFFORT_DEFAULTS, ROOT_ACCOUNT, ROOT_SCOPE, RUN_PROFILES, RUN_SETTLE_DECISION_TYPE, ReplayPlanHashMismatch, Replayer, ResolutionArbiter, ResolutionFold, RulvarError, RunBudget, SANDBOX_AGENT_OPT_KEYS, SPAWN_AGENT_SCHEMA, SandboxError, ScriptRejected, Semaphore, SettlementError, SpanRegistry, SupersededError, TOOL_NAME_PATTERN, TerminationAccount, WAIT_FOR_EVENTS_SCHEMA, WAIT_FOR_EVENTS_TOOL_NAME, WAKE_SUMMARY_RENDER_BUDGET_CHARS, accountSpendFromJournal, admissionReserveUsd, affordableOutputTokens, agentErrorFromWire, agentErrorToWire, agentResultWire, agentScope, applyClaimOps, applyStructuredOutputTier, approachSigCoarse, approachSigOf, archiveDeprecatedModelOps, assertFencedWrites, assertSafeRunId, atCompactionThreshold, attestToolset, auditRun, auditRuns, buildAbandonFold, buildAdapterRegistry, buildCostReport, buildDeriverRegistry, buildOrchestratorTools, buildTerminationInitValue, buildToolContext, canRideLoopTurn, canonicalIsolationTag, canonicalizeLadder, canonicalizeSchema, capIssues, capsHashOf, checkFloors, checkpointRefFor, childCoveragePrefix, citationTargetsValidator, citedValueValidator, claimExpired, claimExpiry, claimIssues, claimOpIssues, classifyAgentError, classifyAttemptOutcome, collectDeclaredLadders, compactMessages, compareRates, compilePermissionChain, compilePermissionPreset, compileSecretMasker, compileVerifiedLayer, constantTimeEqual, costReportFromJournal, countsAgainstLimit, createCanonicalIdMinter, createCtx, createEngine, createEnvelopeEncryption, createSandboxBridge, currentOnlyKeyRing, decodeCheckpoint, dedupeRepeatedClaims, defineWorkflow, deriveContentKey, deriverV1, deriverV2, digestOf, dispatchProjectionReserveUsd, dispositionHook, emptyDigestBlocks, emptyToolset, encodeCheckpoint, enforceToolsetAttestation, entryUsageSlices, escalateTool, evaluatePermission, evaluateReuse, evidenceGradeValidator, evidencePreservedValidator, executeWorkflow, executionFactsOf, exhaustionCodeOf, extractCandidate, failoverTriggerOf, fallbackTriggerOf, filterClaimsForRun, finalizeFires, findContradictions, finishContract, foldLedger, foldTermination, formatCharacterValidator, formatRePrompt, formatScopePath, hasFencedWrites, hasMetaLookup, hashRunArgs, hashRunOutput, hashWorkflowBody, hashWorkflowSource, headingStructureValidator, identityJcs, implementationAgentProfile, invoiceFromJournal, isEscalated, isSchemaPairSpec, isStandardSchemaSpec, isStrictCompatibleSchema, journalPricingSnapshot, kMaxOf, knowledgeHash, ladderLengthOf, ladderRungChoice, lastRunSettle, latestProgressReport, lexShellCommand, liftRetainedParts, lineageWeightOf, localKeyProvider, makeOrchestratorWorkflow, maskSecrets, maskSecretsDeep, maskSecretsJson, matchArgvPattern, matchShellCommand, mcp, memoryQuotaLimiter, mergeQuotaDenial, mergeUsageLimits, metaMatchesFilter, minMatchesValidator, modelEpochOf, modelKnowledgeCard, modelSpecIdentity, needsSeparateExtract, nextFailover, nodeLinkKey, normalizeApproachTag, normalizeEntry, normalizeFallbacks, orchestrate, orchestratorAdmissionEstCostUsd, pairDraftClaims, parallelScope, parseModelRef, parseScopePath, persistedTerminalEnvelope, phiInitialOf, pipelineScope, planNodeScope, preflightEstimate, priceComponentsOf, priceEntryBilling, priceEntryUsage, priceUsdOf, profileCard, profileRegistrySnapshotHash, progressReportTool, projectHistory, projectIdentity, projectToJsonSchema, proposalStatement, providerOf, quotaActualRequestsDelta, quotaActualTokens, quotaEstimateTokens, quotaRuleAdmission, quotaRuleKey, quotaRuleMatches, readRunMeta, readTerminationInit, reconcileRunMeta, reduceAuditTrail, reduceCriticalPath, reduceInvocationTable, registryKeyRing, remeasureQueue, replayDisposition, repositoryResearchToolset, requiredFieldsValidator, requiredSectionsValidator, researchAgentProfile, resolveModelInvocation, resolvePricing, resolveToolset, retryClassOf, retryDelayMs, reviewAgentProfile, roleConfiguredInRouting, roundOneDisposition, runAgent, runProfile, sanitizeTerminalText, sanitizeTokenCount, sanitizeUsage, sanitizeUsageDelta, scanJournalCompatibility, schemaHash, schemaHashOfSpec, sectionCitationsValidator, selectStructuredOutputTier, selfTestFinishValidation, shouldCompact, snapshotQuotaRules, snapshotUsage, spawnDepthOf, spliceSections, stripFencedBlocks, sumUsage, summarizeInstruction, summarizeOutput, terminalEnvelopeOf, terminationConfigDrift, tierWithinCaps, toApprovalDecision, toJournalValue, tool, toolContract, toolContractHash, toolsetHash, ttlState, usageViolations, validateDetachedResolution, validateEditorialCommit, validateEngineQuotaConfig, validateEntryShape, validateEscalationLimits, validateEscalationReport, validateQuotaRules, validateRetryPolicy, validateSchemaSpec, validateTerminationLimits, validateToolsetAttestation, validateUsageLimits, wordCountValidator, workflowScope, workflowSourceRef, wrapJournalStore, wrapTranscriptStore };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/core",
3
- "version": "1.169.0",
3
+ "version": "1.171.0",
4
4
  "description": "Rulvar core: L0 contracts, journal kernel, ctx primitives, agent runtime, model router, tool system, dynamic orchestrator, InMemory and JSONL stores, event stream.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",