@rulvar/core 1.84.0 → 1.86.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.
package/dist/index.d.ts CHANGED
@@ -3547,6 +3547,42 @@ interface ExplorationSummary {
3547
3547
  toolUnitsUsed?: number;
3548
3548
  }
3549
3549
  /**
3550
+ * The tool budget pressure snapshot (RV304, the seventh comparison
3551
+ * experiment): how close one agent invocation came to its tool budget,
3552
+ * visible BEFORE the terminal 'limit' a starved worker would settle
3553
+ * with. Attached to the full AgentResult and to the live `agent:end`
3554
+ * event whenever maxToolCalls, toolUnits, or toolBudgetExtension is
3555
+ * configured. Live telemetry only, exactly like transportRetries: never
3556
+ * journaled, absent on a replayed result.
3557
+ */
3558
+ interface ToolBudgetSummary {
3559
+ /** Executed tool calls (the loop's own counter). */
3560
+ used: number;
3561
+ /**
3562
+ * The effective executed-call cap at the end: maxToolCalls plus every
3563
+ * granted extension. Absent when only toolUnits bounds the loop.
3564
+ */
3565
+ cap?: number;
3566
+ /** Weighted units spent; present when toolUnits is configured. */
3567
+ unitsUsed?: number;
3568
+ /** The weighted budget; present when toolUnits is configured. */
3569
+ unitsMax?: number;
3570
+ /**
3571
+ * Extension grants used, restored grants included; present exactly
3572
+ * when toolBudgetExtension is configured (RV301).
3573
+ */
3574
+ extensionsGranted?: number;
3575
+ /**
3576
+ * Notice thresholds (fractions of the cap) whose notices entered the
3577
+ * conversation; present when at least one fired.
3578
+ */
3579
+ noticesFired?: number[];
3580
+ /** Present and true when the finalization reserve summary turn ran. */
3581
+ finalizationReserveUsed?: boolean;
3582
+ /** The tool budget limiter that ended the loop, on that 'limit' only. */
3583
+ limiter?: "maxToolCalls" | "toolUnits";
3584
+ }
3585
+ /**
3550
3586
  * Agent lifecycle. One logical agent dispatch emits EXACTLY ONE
3551
3587
  * `agent:start`/`agent:end` pair on its span (the start carries the
3552
3588
  * primary role), and each model invocation phase inside the span
@@ -3632,6 +3668,12 @@ type AgentEvents = {
3632
3668
  * terminal error payload.
3633
3669
  */
3634
3670
  exploration?: ExplorationSummary;
3671
+ /**
3672
+ * The tool budget pressure snapshot (RV304). Present live whenever
3673
+ * a tool budget limiter or the extension was configured; live
3674
+ * telemetry only, absent on replay.
3675
+ */
3676
+ toolBudget?: ToolBudgetSummary;
3635
3677
  } | {
3636
3678
  type: "agent:error";
3637
3679
  agentType: string;
@@ -4041,6 +4083,29 @@ interface UsageLimits {
4041
4083
  finalizationReserve?: {
4042
4084
  maxOutputTokens?: number;
4043
4085
  };
4086
+ /**
4087
+ * The adaptive tool budget (RV301, the seventh comparison experiment):
4088
+ * when maxToolCalls expires but the run still has money and the agent
4089
+ * still makes progress, the runtime grants `increment` more executed
4090
+ * calls instead of ending the invocation, up to `maxExtensions` grants.
4091
+ * A grant is admitted only when the remaining chain budget (the same
4092
+ * arithmetic the per-turn output clamp reads) is above zero, or above
4093
+ * `minHeadroomUsd` when declared, and, unless `requireNewEvidence` is
4094
+ * set to false, only when at least one novel tool result digest arrived
4095
+ * since the previous grant (the exploration guard's evidence chain).
4096
+ * Each grant is announced to the model as a plain user message with the
4097
+ * exact new counts, so pacing stays possible; on resume the grants
4098
+ * re-derive conservatively from the restored executed-call count.
4099
+ * Extends maxToolCalls only, never toolUnits. Off by default: the
4100
+ * grant notices enter the conversation, so enabling it changes
4101
+ * recorded model requests.
4102
+ */
4103
+ toolBudgetExtension?: {
4104
+ /** Executed calls added per grant. */increment: number; /** Hard bound on grants per invocation. */
4105
+ maxExtensions: number; /** Grant only at or above this remaining chain headroom, in USD. */
4106
+ minHeadroomUsd?: number; /** Default true: a grant needs new evidence since the last one. */
4107
+ requireNewEvidence?: boolean;
4108
+ };
4044
4109
  }
4045
4110
  declare const DEFAULT_MAX_TURNS = 32;
4046
4111
  declare const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 12e4;
@@ -4064,6 +4129,12 @@ interface EffectiveUsageLimits {
4064
4129
  finalizationReserve?: {
4065
4130
  maxOutputTokens?: number;
4066
4131
  };
4132
+ toolBudgetExtension?: {
4133
+ increment: number;
4134
+ maxExtensions: number;
4135
+ minHeadroomUsd?: number;
4136
+ requireNewEvidence?: boolean;
4137
+ };
4067
4138
  }
4068
4139
  /**
4069
4140
  * Limits merge per spawn: AgentOpts.limits over profile limits over engine
@@ -4196,6 +4267,13 @@ interface AgentResult<T> {
4196
4267
  */
4197
4268
  exploration?: ExplorationSummary;
4198
4269
  /**
4270
+ * The tool budget pressure snapshot (RV304): present live whenever
4271
+ * maxToolCalls, toolUnits, or toolBudgetExtension is configured. Live
4272
+ * telemetry only, exactly like transportRetries: never journaled,
4273
+ * absent on a replayed result.
4274
+ */
4275
+ toolBudget?: ToolBudgetSummary;
4276
+ /**
4199
4277
  * The structured terminal partial (RV-210 close-out): the LAST
4200
4278
  * successful `report_progress` call of the invocation, present only on
4201
4279
  * a 'limit' terminal (cap expiry or an engine-decided abort) whose
@@ -4270,6 +4348,13 @@ interface BudgetHooks {
4270
4348
  * or free output).
4271
4349
  */
4272
4350
  maxAffordableOutputTokens?: (servedBy: ModelRef, estimatedInputTokens: number) => number | undefined;
4351
+ /**
4352
+ * The remaining chain headroom in USD (RV301): the same arithmetic
4353
+ * the output bound above reads, before pricing. Undefined = no
4354
+ * ceiling anywhere on the chain. The tool budget extension admits a
4355
+ * grant against it.
4356
+ */
4357
+ remainingUsd?: () => number | undefined;
4273
4358
  /** Live usage accounting; layer 3 may respond by aborting `signal`. */
4274
4359
  onUsage(usage: Usage, servedBy: ModelRef): void;
4275
4360
  /** Layer 3: the ceiling AbortSignal. */
@@ -5122,6 +5207,14 @@ declare class RunBudget {
5122
5207
  * onUsage covers that hole), or when output is free. Zero or negative
5123
5208
  * means the turn cannot be dispatched within the budget.
5124
5209
  */
5210
+ /**
5211
+ * The tightest chain headroom of `accountScope` in plain USD (RV301):
5212
+ * exactly the remaining money the output clamp below prices, before
5213
+ * any pricing. Undefined when every account on the chain is uncapped;
5214
+ * never negative. The tool budget extension admits a grant against
5215
+ * this number.
5216
+ */
5217
+ remainingUsd(accountScope?: string): number | undefined;
5125
5218
  maxAffordableOutputTokens(servedBy: ModelRef, estimatedInputTokens: number, accountScope?: string): number | undefined;
5126
5219
  /**
5127
5220
  * Live accounting; spend propagates from `accountScope` to every
@@ -9766,6 +9859,11 @@ interface AgentInvocationRow {
9766
9859
  costUsd: number;
9767
9860
  usageApprox: boolean;
9768
9861
  retryCount: number;
9862
+ /**
9863
+ * The tool budget pressure snapshot (RV304), carried through from the
9864
+ * live agent:end. Absent on replayed rows and unbounded loops.
9865
+ */
9866
+ toolBudget?: ToolBudgetSummary;
9769
9867
  replayed: boolean;
9770
9868
  /** True when the span's agent:end never arrived. */
9771
9869
  open: boolean;
@@ -9889,4 +9987,4 @@ interface SandboxBridge {
9889
9987
  declare const SANDBOX_AGENT_OPT_KEYS: readonly string[];
9890
9988
  declare function createSandboxBridge(ctx: Ctx<never>, options: SandboxBridgeOptions): SandboxBridge;
9891
9989
  //#endregion
9892
- export { AWAIT_SCHEMA, AbandonAttempt, AbandonFold, AbandonPayload, AbandonedSpendView, AbortClass, type AdaptiveEvents, AdmissionController, AdmissionDecision, AdmissionRejectedError, AdmissionStatsBefore, AdmitLineage, AdmitRejectReason, AdmitSpec, AdmitVerdict, AgentCallError, AgentError, type AgentEvents, AgentIdentityInput, type AgentInvocationRow, AgentOpts, AgentProfile, AgentProfilePermissions, AgentProfileTemplateOptions, AgentResult, AgentResultMeta, AgentStatus, 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, ChildIdentityInput, ChildResultPage, type ClaimClass, type ClaimOp, type ClaimStatus, ClaimValidationOptions, CollectOpts, CollectedTurn, CompactionConfig, CompiledPermissionChain, CompiledWorkflow, ConfigError, type CoreEvents, CostAttribution, CostAttributionFacts, CostReport, CreateEngineOptions, type CriticalPath, Ctx, DEFAULT_CHILD_BUDGET_FRACTION, DEFAULT_CHILD_RESULT_PAGE_CHARS, DEFAULT_CITATION_PATTERN, DEFAULT_CITATION_SAMPLE, DEFAULT_COMPACTION_THRESHOLD, DEFAULT_ESCALATION_LIMITS, DEFAULT_EVIDENCE_MIN_SHARE, DEFAULT_FINISH_MAX_REPAIRS, DEFAULT_FLAT_RESERVE_USD, DEFAULT_MAX_CHILDREN_PER_NODE, DEFAULT_MAX_DEPTH, DEFAULT_MAX_OSCILLATIONS_PER_KEY, DEFAULT_MAX_PINNED_WORKTREES, 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, 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, EntryKind, EntryRef, EntryStatus, EnvelopeEncryption, EnvelopeEncryptionOptions, ErrorClass, ErrorCode, ErrorPolicy, EscalatedResult, EscalationDecision, EscalationDecisionAbortedError, EscalationDigest, EscalationKind, EscalationLimits, EscalationOptions, EscalationReport, EscalationRequest, EventBus, type EvidenceRef, type ExecutorRegistry, type ExplorationSummary, ExtensionAppendInput, ExtensionDispatchSpec, ExternalIdentityInput, ExternalRegistry, ExtractNecessityInput, FINALIZE_SYNTHESIS_INSTRUCTION, FINISH_SCHEMA, FINISH_TOOL_NAME, FailRunError, FailoverTarget, FailoverTrigger, FallbackField, FallbackTrigger, FencedCodeMode, FileModelKnowledgeStore, FileModelKnowledgeStoreOptions, FileTranscriptStore, 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, IdentityInput, InMemoryStore, InMemoryTranscriptStore, InProcessRunner, IncrementalSynthesisResult, InvalidResolutionError, InvocationRole, type InvocationTable, InvoiceExport, 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, 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, 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, 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, type PhaseRow, PhaseTarget, PipelineCollected, PipelineOpts, PlanInvariantError, PreflightAdmissionRow, PreflightFinding, PreflightInput, PreflightOrchestratorSpec, PreflightReport, PreflightSpawnReport, PreflightSpawnSpec, PreflightToolCeiling, PriceTable, 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, StructuredOutputTier, SuspendedAppend, SuspensionState, TOOL_NAME_PATTERN, type TaskClass, TaskDigest, TaskSpec, TerminalPatch, TerminationAccount, TerminationAccountSnapshot, TerminationDeniedValue, TerminationDeniedWriter, TerminationInitValue, TerminationLimits, TerminationResource, ToolCallRequest, ToolChoice, type ToolContext, ToolContextSeed, ToolContract, type ToolDef, type ToolEvents, type ToolExecutor, type ToolExecutorProvider, ToolInit, type ToolRisk, ToolRuntime, type ToolSource, type ToolSourceSession, ToolsOption, 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, admissionReserveUsd, affordableOutputTokens, agentErrorFromWire, agentErrorToWire, agentResultWire, agentScope, applyClaimOps, applyStructuredOutputTier, approachSigCoarse, approachSigOf, archiveDeprecatedModelOps, assertFencedWrites, atCompactionThreshold, auditRun, auditRuns, buildAbandonFold, buildAdapterRegistry, buildCostReport, buildDeriverRegistry, buildOrchestratorTools, buildTerminationInitValue, buildToolContext, canRideLoopTurn, canonicalIsolationTag, canonicalizeLadder, canonicalizeSchema, capIssues, capsHashOf, checkFloors, checkpointRefFor, childCoveragePrefix, claimExpired, claimExpiry, claimIssues, claimOpIssues, classifyAgentError, classifyAttemptOutcome, collectDeclaredLadders, compactMessages, 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, entryUsageSlices, escalateTool, evaluatePermission, evaluateReuse, evidencePreservedValidator, executeWorkflow, exhaustionCodeOf, extractCandidate, failoverTriggerOf, fallbackTriggerOf, filterClaimsForRun, finalizeFires, finishContract, foldTermination, formatRePrompt, formatScopePath, hasFencedWrites, hasMetaLookup, hashRunArgs, hashRunOutput, hashWorkflowBody, hashWorkflowSource, headingStructureValidator, identityJcs, implementationAgentProfile, invoiceFromJournal, isEscalated, isSchemaPairSpec, isStandardSchemaSpec, isStrictCompatibleSchema, 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, parallelScope, parseModelRef, parseScopePath, phiInitialOf, pipelineScope, planNodeScope, preflightEstimate, priceEntryUsage, priceUsdOf, profileCard, profileRegistrySnapshotHash, progressReportTool, projectHistory, projectIdentity, projectToJsonSchema, proposalStatement, providerOf, quotaActualTokens, quotaEstimateTokens, quotaRuleAdmission, 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, snapshotUsage, spawnDepthOf, stripFencedBlocks, summarizeInstruction, summarizeOutput, terminationConfigDrift, tierWithinCaps, toApprovalDecision, toJournalValue, tool, toolContract, toolsetHash, ttlState, usageViolations, validateEditorialCommit, validateEngineQuotaConfig, validateEntryShape, validateEscalationLimits, validateEscalationReport, validateQuotaRules, validateRetryPolicy, validateSchemaSpec, validateTerminationLimits, validateUsageLimits, wordCountValidator, workflowScope, workflowSourceRef, wrapJournalStore, wrapTranscriptStore };
9990
+ export { AWAIT_SCHEMA, AbandonAttempt, AbandonFold, AbandonPayload, AbandonedSpendView, AbortClass, type AdaptiveEvents, AdmissionController, AdmissionDecision, AdmissionRejectedError, AdmissionStatsBefore, AdmitLineage, AdmitRejectReason, AdmitSpec, AdmitVerdict, AgentCallError, AgentError, type AgentEvents, AgentIdentityInput, type AgentInvocationRow, AgentOpts, AgentProfile, AgentProfilePermissions, AgentProfileTemplateOptions, AgentResult, AgentResultMeta, AgentStatus, 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, ChildIdentityInput, ChildResultPage, type ClaimClass, type ClaimOp, type ClaimStatus, ClaimValidationOptions, CollectOpts, CollectedTurn, CompactionConfig, CompiledPermissionChain, CompiledWorkflow, ConfigError, type CoreEvents, CostAttribution, CostAttributionFacts, CostReport, CreateEngineOptions, type CriticalPath, Ctx, DEFAULT_CHILD_BUDGET_FRACTION, DEFAULT_CHILD_RESULT_PAGE_CHARS, DEFAULT_CITATION_PATTERN, DEFAULT_CITATION_SAMPLE, DEFAULT_COMPACTION_THRESHOLD, DEFAULT_ESCALATION_LIMITS, DEFAULT_EVIDENCE_MIN_SHARE, DEFAULT_FINISH_MAX_REPAIRS, DEFAULT_FLAT_RESERVE_USD, DEFAULT_MAX_CHILDREN_PER_NODE, DEFAULT_MAX_DEPTH, DEFAULT_MAX_OSCILLATIONS_PER_KEY, DEFAULT_MAX_PINNED_WORKTREES, 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, 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, EntryKind, EntryRef, EntryStatus, EnvelopeEncryption, EnvelopeEncryptionOptions, ErrorClass, ErrorCode, ErrorPolicy, EscalatedResult, EscalationDecision, EscalationDecisionAbortedError, EscalationDigest, EscalationKind, EscalationLimits, EscalationOptions, EscalationReport, EscalationRequest, EventBus, type EvidenceRef, type ExecutorRegistry, type ExplorationSummary, ExtensionAppendInput, ExtensionDispatchSpec, ExternalIdentityInput, ExternalRegistry, ExtractNecessityInput, FINALIZE_SYNTHESIS_INSTRUCTION, FINISH_SCHEMA, FINISH_TOOL_NAME, FailRunError, FailoverTarget, FailoverTrigger, FallbackField, FallbackTrigger, FencedCodeMode, FileModelKnowledgeStore, FileModelKnowledgeStoreOptions, FileTranscriptStore, 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, IdentityInput, InMemoryStore, InMemoryTranscriptStore, InProcessRunner, IncrementalSynthesisResult, InvalidResolutionError, InvocationRole, type InvocationTable, InvoiceExport, 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, 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, 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, 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, type PhaseRow, PhaseTarget, PipelineCollected, PipelineOpts, PlanInvariantError, PreflightAdmissionRow, PreflightFinding, PreflightInput, PreflightOrchestratorSpec, PreflightReport, PreflightSpawnReport, PreflightSpawnSpec, PreflightToolCeiling, PriceTable, 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, StructuredOutputTier, SuspendedAppend, SuspensionState, TOOL_NAME_PATTERN, type TaskClass, TaskDigest, TaskSpec, 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, 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, admissionReserveUsd, affordableOutputTokens, agentErrorFromWire, agentErrorToWire, agentResultWire, agentScope, applyClaimOps, applyStructuredOutputTier, approachSigCoarse, approachSigOf, archiveDeprecatedModelOps, assertFencedWrites, atCompactionThreshold, auditRun, auditRuns, buildAbandonFold, buildAdapterRegistry, buildCostReport, buildDeriverRegistry, buildOrchestratorTools, buildTerminationInitValue, buildToolContext, canRideLoopTurn, canonicalIsolationTag, canonicalizeLadder, canonicalizeSchema, capIssues, capsHashOf, checkFloors, checkpointRefFor, childCoveragePrefix, claimExpired, claimExpiry, claimIssues, claimOpIssues, classifyAgentError, classifyAttemptOutcome, collectDeclaredLadders, compactMessages, 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, entryUsageSlices, escalateTool, evaluatePermission, evaluateReuse, evidencePreservedValidator, executeWorkflow, exhaustionCodeOf, extractCandidate, failoverTriggerOf, fallbackTriggerOf, filterClaimsForRun, finalizeFires, finishContract, foldTermination, formatRePrompt, formatScopePath, hasFencedWrites, hasMetaLookup, hashRunArgs, hashRunOutput, hashWorkflowBody, hashWorkflowSource, headingStructureValidator, identityJcs, implementationAgentProfile, invoiceFromJournal, isEscalated, isSchemaPairSpec, isStandardSchemaSpec, isStrictCompatibleSchema, 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, parallelScope, parseModelRef, parseScopePath, phiInitialOf, pipelineScope, planNodeScope, preflightEstimate, priceEntryUsage, priceUsdOf, profileCard, profileRegistrySnapshotHash, progressReportTool, projectHistory, projectIdentity, projectToJsonSchema, proposalStatement, providerOf, quotaActualTokens, quotaEstimateTokens, quotaRuleAdmission, 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, snapshotUsage, spawnDepthOf, stripFencedBlocks, summarizeInstruction, summarizeOutput, terminationConfigDrift, tierWithinCaps, toApprovalDecision, toJournalValue, tool, toolContract, toolsetHash, ttlState, usageViolations, validateEditorialCommit, validateEngineQuotaConfig, validateEntryShape, validateEscalationLimits, validateEscalationReport, validateQuotaRules, validateRetryPolicy, validateSchemaSpec, validateTerminationLimits, validateUsageLimits, wordCountValidator, workflowScope, workflowSourceRef, wrapJournalStore, wrapTranscriptStore };
package/dist/index.js CHANGED
@@ -9021,6 +9021,8 @@ function mergeUsageLimits(call, profile, engine) {
9021
9021
  if (toolUnits !== void 0) merged.toolUnits = toolUnits;
9022
9022
  const finalizationReserve = pick("finalizationReserve");
9023
9023
  if (finalizationReserve !== void 0) merged.finalizationReserve = finalizationReserve;
9024
+ const toolBudgetExtension = pick("toolBudgetExtension");
9025
+ if (toolBudgetExtension !== void 0) merged.toolBudgetExtension = toolBudgetExtension;
9024
9026
  return merged;
9025
9027
  }
9026
9028
  /**
@@ -9066,6 +9068,15 @@ function validateUsageLimits(limits, site) {
9066
9068
  const { maxOutputTokens } = reserve;
9067
9069
  if (maxOutputTokens !== void 0) requirePositiveInteger(maxOutputTokens, `${site}.finalizationReserve.maxOutputTokens`);
9068
9070
  }
9071
+ if (limits.toolBudgetExtension !== void 0) {
9072
+ const extension = limits.toolBudgetExtension;
9073
+ if (typeof extension !== "object" || extension === null || Array.isArray(extension)) throw new ConfigError(`${site}.toolBudgetExtension must be { increment, maxExtensions, minHeadroomUsd?, requireNewEvidence? }`);
9074
+ const { increment, maxExtensions, minHeadroomUsd, requireNewEvidence } = extension;
9075
+ requirePositiveInteger(increment, `${site}.toolBudgetExtension.increment`);
9076
+ requirePositiveInteger(maxExtensions, `${site}.toolBudgetExtension.maxExtensions`);
9077
+ if (minHeadroomUsd !== void 0 && (typeof minHeadroomUsd !== "number" || !Number.isFinite(minHeadroomUsd) || minHeadroomUsd < 0)) throw new ConfigError(`${site}.toolBudgetExtension.minHeadroomUsd must be a finite nonnegative USD amount, got ${typeof minHeadroomUsd === "number" ? String(minHeadroomUsd) : typeof minHeadroomUsd}`);
9078
+ if (requireNewEvidence !== void 0 && typeof requireNewEvidence !== "boolean") throw new ConfigError(`${site}.toolBudgetExtension.requireNewEvidence must be a boolean; got ${typeof requireNewEvidence}`);
9079
+ }
9069
9080
  }
9070
9081
  //#endregion
9071
9082
  //#region src/model/failover.ts
@@ -9646,9 +9657,13 @@ const DEFAULT_MODEL_RETRY_ATTEMPTS = 2;
9646
9657
  */
9647
9658
  /** The docs anchor cited by guard denials and the guard abort. */
9648
9659
  const GUARD_DOCS_URL = "https://docs.rulvar.com/guide/agents#exploration-guards";
9649
- /** True when any exploration guard field asks for tracking. */
9660
+ /**
9661
+ * True when any exploration guard field asks for tracking. The tool
9662
+ * budget extension (RV301) counts too: its requireNewEvidence admission
9663
+ * reads the guard's evidence chain.
9664
+ */
9650
9665
  function explorationTrackingEnabled(limits) {
9651
- return limits.maxRepeatedToolSignature !== void 0 || limits.maxNoNewEvidenceCalls !== void 0 || limits.toolBudgetNotices === true || limits.maxCallsPerTool !== void 0 || limits.toolUnits !== void 0;
9666
+ return limits.maxRepeatedToolSignature !== void 0 || limits.maxNoNewEvidenceCalls !== void 0 || limits.toolBudgetNotices === true || limits.maxCallsPerTool !== void 0 || limits.toolUnits !== void 0 || limits.toolBudgetExtension !== void 0;
9652
9667
  }
9653
9668
  function digestOf$1(value) {
9654
9669
  try {
@@ -9775,6 +9790,16 @@ var ExplorationGuard = class {
9775
9790
  unitsExhausted() {
9776
9791
  return this.config.toolUnits !== void 0 && this.unitsUsed >= this.config.toolUnits.max;
9777
9792
  }
9793
+ /**
9794
+ * Distinct successful result digests seen this invocation: the
9795
+ * extension's requireNewEvidence admission compares snapshots of this
9796
+ * count (RV301). A result JCS cannot digest never counts, so a grant
9797
+ * fails closed there, unlike the guards, which fail open: a denied
9798
+ * grant only restores the pre-extension expiry.
9799
+ */
9800
+ evidenceCount() {
9801
+ return this.seenDigests.size;
9802
+ }
9778
9803
  /** The abort message for a tripped no-new-evidence guard. */
9779
9804
  describeTrip() {
9780
9805
  return `exploration guard: ${String(this.noNewEvidenceStreak)} consecutive tool calls returned no new evidence (maxNoNewEvidenceCalls ${String(this.config.maxNoNewEvidenceCalls ?? this.noNewEvidenceStreak)}; every result was already seen this invocation). The executed work is kept; narrow the scope, vary the queries, or raise the limit (${GUARD_DOCS_URL}).`;
@@ -9813,6 +9838,15 @@ function toolBudgetNoticeText(used, max) {
9813
9838
  const remaining = Math.max(0, max - used);
9814
9839
  return `Tool budget notice: ${String(used)} of ${String(max)} tool calls used; ${String(remaining)} remaining. Prioritize the highest value calls and finish with what you have.`;
9815
9840
  }
9841
+ /**
9842
+ * The model-visible extension notice (RV301): announces one grant with
9843
+ * the exact new counts. Deterministic for given counts, like the budget
9844
+ * notice above.
9845
+ */
9846
+ function toolBudgetExtensionNoticeText(grant, maxExtensions, used, cap) {
9847
+ const remaining = Math.max(0, cap - used);
9848
+ return `Tool budget extended: grant ${String(grant)} of ${String(maxExtensions)}; ${String(used)} of ${String(cap)} tool calls used, ${String(remaining)} remaining. The budget headroom permits continued work; prioritize the highest value calls.`;
9849
+ }
9816
9850
  //#endregion
9817
9851
  //#region src/runtime/no-progress.ts
9818
9852
  /**
@@ -10124,7 +10158,7 @@ async function streamTurn(adapter, req, options) {
10124
10158
  if (sawFinish || wireError !== void 0) break;
10125
10159
  }
10126
10160
  } catch (thrown) {
10127
- if (!combined.aborted) wireError = {
10161
+ if (!combined.aborted) wireError = thrown instanceof RulvarError ? thrown.toWire() : {
10128
10162
  code: "agent",
10129
10163
  message: thrown instanceof Error ? thrown.message : String(thrown),
10130
10164
  retryable: true,
@@ -10601,13 +10635,32 @@ async function runAgent(options) {
10601
10635
  const noProgress = new NoProgressDetector(limits.noProgressTurns);
10602
10636
  const guard = explorationTrackingEnabled(limits) ? new ExplorationGuard(limits) : void 0;
10603
10637
  /**
10638
+ * The adaptive tool budget (RV301, the seventh comparison experiment):
10639
+ * the run that motivated it starved two mandatory workers at a fixed
10640
+ * 84-call cap while 38% of the USD ceiling sat unspent. A grant at the
10641
+ * expiry converts that headroom into `increment` more executed calls,
10642
+ * bounded by `maxExtensions`, admitted only with money remaining and
10643
+ * (by default) new evidence since the last grant. Grants re-derive
10644
+ * conservatively from the restored executed-call count on resume, so
10645
+ * nothing new is journaled or checkpointed.
10646
+ */
10647
+ const extension = limits.toolBudgetExtension;
10648
+ let extensionGrants = 0;
10649
+ let extensionEvidenceAtLastGrant = 0;
10650
+ const pendingExtensionNotices = [];
10651
+ const effectiveMaxToolCalls = () => limits.maxToolCalls === void 0 ? void 0 : extension === void 0 ? limits.maxToolCalls : limits.maxToolCalls + extensionGrants * extension.increment;
10652
+ /** The limiter that ended the loop; rides the RV304 pressure snapshot. */
10653
+ let limitLimiter;
10654
+ /** True once the finalization reserve summary turn actually ran. */
10655
+ let reserveSummaryRan = false;
10656
+ /**
10604
10657
  * The exact limiter behind a tool-budget expiry, with its counts: the
10605
10658
  * wording rides the finalization-reserve instruction and the 'limit'
10606
10659
  * terminal's errorMessage (P1.1 criterion: the terminal names the
10607
10660
  * limiter, never a bare status).
10608
10661
  */
10609
10662
  const toolBudgetDetail = (limiter) => {
10610
- if (limiter === "maxToolCalls") return `maxToolCalls (${String(toolCallsUsed)}/${String(limits.maxToolCalls ?? 0)})`;
10663
+ if (limiter === "maxToolCalls") return `maxToolCalls (${String(toolCallsUsed)}/${String(effectiveMaxToolCalls() ?? 0)})`;
10611
10664
  const max = limits.toolUnits?.max ?? 0;
10612
10665
  const used = guard === void 0 ? max : guard.summary(toolCallsUsed).toolUnitsUsed ?? max;
10613
10666
  return `toolUnits (${String(used)}/${String(max)})`;
@@ -10639,6 +10692,44 @@ async function runAgent(options) {
10639
10692
  }]
10640
10693
  });
10641
10694
  };
10695
+ /**
10696
+ * One extension grant (RV301), attempted exactly at a maxToolCalls
10697
+ * expiry inside the dispatch walk. The notice text is queued rather
10698
+ * than pushed: a user message may not interleave a tool batch, so the
10699
+ * queue flushes with the budget notices after the batch's results
10700
+ * join the history.
10701
+ */
10702
+ const tryToolBudgetGrant = () => {
10703
+ if (extension === void 0 || limits.maxToolCalls === void 0) return false;
10704
+ if (extensionGrants >= extension.maxExtensions) return false;
10705
+ if (extension.requireNewEvidence !== false) {
10706
+ if ((guard?.evidenceCount() ?? 0) <= extensionEvidenceAtLastGrant) return false;
10707
+ }
10708
+ const remaining = options.budget?.remainingUsd?.();
10709
+ if (remaining !== void 0) {
10710
+ const floor = extension.minHeadroomUsd ?? 0;
10711
+ if (floor > 0 ? remaining < floor : remaining <= 0) return false;
10712
+ }
10713
+ extensionGrants += 1;
10714
+ extensionEvidenceAtLastGrant = guard?.evidenceCount() ?? 0;
10715
+ const cap = limits.maxToolCalls + extensionGrants * extension.increment;
10716
+ pendingExtensionNotices.push(toolBudgetExtensionNoticeText(extensionGrants, extension.maxExtensions, toolCallsUsed, cap));
10717
+ events?.emit({
10718
+ type: "log",
10719
+ level: "info",
10720
+ msg: `tool budget extended (grant ${String(extensionGrants)}/${String(extension.maxExtensions)}): maxToolCalls now ${String(cap)}`
10721
+ });
10722
+ return true;
10723
+ };
10724
+ const flushExtensionNotices = () => {
10725
+ for (const text of pendingExtensionNotices.splice(0)) messages.push({
10726
+ role: "user",
10727
+ parts: [{
10728
+ type: "text",
10729
+ text
10730
+ }]
10731
+ });
10732
+ };
10642
10733
  const modelRetryCounts = /* @__PURE__ */ new Map();
10643
10734
  let lastTurnUsage = {
10644
10735
  inputTokens: 0,
@@ -10671,6 +10762,10 @@ async function runAgent(options) {
10671
10762
  });
10672
10763
  guard?.restore(messages);
10673
10764
  if (limits.toolBudgetNotices === true && limits.maxToolCalls !== void 0) for (const threshold of crossedNoticeThresholds(toolCallsUsed, limits.maxToolCalls)) firedNotices.add(threshold);
10765
+ if (extension !== void 0 && limits.maxToolCalls !== void 0 && toolCallsUsed > limits.maxToolCalls) {
10766
+ extensionGrants = Math.min(extension.maxExtensions, Math.ceil((toolCallsUsed - limits.maxToolCalls) / extension.increment));
10767
+ extensionEvidenceAtLastGrant = guard?.evidenceCount() ?? 0;
10768
+ }
10674
10769
  }
10675
10770
  const usageSlices = () => [...usageByPhaseModel.values()].map(({ role, servedBy: sliceServedBy, usage }) => ({
10676
10771
  servedBy: sliceServedBy,
@@ -10757,7 +10852,12 @@ async function runAgent(options) {
10757
10852
  };
10758
10853
  let terminalAdmitted = false;
10759
10854
  for (const [index, call] of calls.entries()) {
10760
- const expiredLimiter = limits.maxToolCalls !== void 0 && toolCallsUsed >= limits.maxToolCalls ? "maxToolCalls" : guard !== void 0 && guard.unitsExhausted() ? "toolUnits" : void 0;
10855
+ const expiryOf = () => {
10856
+ const cap = effectiveMaxToolCalls();
10857
+ return cap !== void 0 && toolCallsUsed >= cap ? "maxToolCalls" : guard !== void 0 && guard.unitsExhausted() ? "toolUnits" : void 0;
10858
+ };
10859
+ let expiredLimiter = expiryOf();
10860
+ if (expiredLimiter === "maxToolCalls" && call.name !== options.terminalTool?.name && tryToolBudgetGrant()) expiredLimiter = expiryOf();
10761
10861
  if (expiredLimiter !== void 0) {
10762
10862
  const tail = calls.slice(index);
10763
10863
  const terminalName = options.terminalTool?.name;
@@ -11012,6 +11112,7 @@ async function runAgent(options) {
11012
11112
  await saveBoundary();
11013
11113
  } else if (limitHit) {
11014
11114
  status = "limit";
11115
+ if (limiter !== void 0) limitLimiter = limiter;
11015
11116
  if (guardTrip === true && guard !== void 0) {
11016
11117
  abortClass = "exploration";
11017
11118
  agentError = {
@@ -11031,6 +11132,7 @@ async function runAgent(options) {
11031
11132
  };
11032
11133
  }
11033
11134
  } else {
11135
+ flushExtensionNotices();
11034
11136
  maybePushBudgetNotice();
11035
11137
  await saveBoundary();
11036
11138
  }
@@ -11474,6 +11576,7 @@ async function runAgent(options) {
11474
11576
  }
11475
11577
  if (limitHit) {
11476
11578
  status = "limit";
11579
+ if (limiter !== void 0) limitLimiter = limiter;
11477
11580
  if (guardTrip === true && guard !== void 0) {
11478
11581
  abortClass = "exploration";
11479
11582
  agentError = {
@@ -11494,6 +11597,7 @@ async function runAgent(options) {
11494
11597
  }
11495
11598
  break;
11496
11599
  }
11600
+ flushExtensionNotices();
11497
11601
  maybePushBudgetNotice();
11498
11602
  if (options.summarize !== void 0 && !compactionDisabled && shouldCompact({
11499
11603
  lastTurnUsage,
@@ -11741,6 +11845,7 @@ async function runAgent(options) {
11741
11845
  });
11742
11846
  }
11743
11847
  if (reserveDispatch !== void 0) {
11848
+ reserveSummaryRan = true;
11744
11849
  const { outcome, target: reserveTarget } = reserveDispatch;
11745
11850
  servedBy = reserveTarget.resolved.ref;
11746
11851
  usageApprox = usageApprox || outcome.usageApprox;
@@ -12043,6 +12148,20 @@ async function runAgent(options) {
12043
12148
  if (abortClass !== void 0) result.abortClass = abortClass;
12044
12149
  if (errorMessage !== void 0) result.errorMessage = errorMessage;
12045
12150
  if (guard !== void 0) result.exploration = guard.summary(toolCallsUsed);
12151
+ if (limits.maxToolCalls !== void 0 || limits.toolUnits !== void 0 || extension !== void 0) {
12152
+ const toolBudget = { used: toolCallsUsed };
12153
+ const cap = effectiveMaxToolCalls();
12154
+ if (cap !== void 0) toolBudget.cap = cap;
12155
+ if (limits.toolUnits !== void 0) {
12156
+ toolBudget.unitsUsed = guard?.summary(toolCallsUsed).toolUnitsUsed ?? 0;
12157
+ toolBudget.unitsMax = limits.toolUnits.max;
12158
+ }
12159
+ if (extension !== void 0) toolBudget.extensionsGranted = extensionGrants;
12160
+ if (firedNotices.size > 0) toolBudget.noticesFired = [...firedNotices].sort((a, b) => a - b);
12161
+ if (reserveSummaryRan) toolBudget.finalizationReserveUsed = true;
12162
+ if (limitLimiter !== void 0) toolBudget.limiter = limitLimiter;
12163
+ result.toolBudget = toolBudget;
12164
+ }
12046
12165
  if (limitPartial !== void 0) result.partial = limitPartial;
12047
12166
  const terminalName = options.terminalTool?.name;
12048
12167
  const schemaRejectedTerminalExchanges = terminalName === void 0 ? 0 : messages.reduce((count, message) => count + message.parts.filter((part) => part.type === "tool-result" && part.name === terminalName && part.isError === true && part.result?.error === terminalSchemaRejectionMessage(terminalName)).length, 0);
@@ -12449,17 +12568,28 @@ var RunBudget = class {
12449
12568
  * onUsage covers that hole), or when output is free. Zero or negative
12450
12569
  * means the turn cannot be dispatched within the budget.
12451
12570
  */
12452
- maxAffordableOutputTokens(servedBy, estimatedInputTokens, accountScope = "run") {
12453
- const pricing = this.pricingOf?.(servedBy);
12454
- if (pricing === void 0) return;
12455
- let remainingUsd;
12571
+ /**
12572
+ * The tightest chain headroom of `accountScope` in plain USD (RV301):
12573
+ * exactly the remaining money the output clamp below prices, before
12574
+ * any pricing. Undefined when every account on the chain is uncapped;
12575
+ * never negative. The tool budget extension admits a grant against
12576
+ * this number.
12577
+ */
12578
+ remainingUsd(accountScope = "run") {
12579
+ let remaining;
12456
12580
  for (const account of this.chainOf(accountScope)) {
12457
12581
  if (account.ceilingUsd === void 0) continue;
12458
12582
  const headroom = account.ceilingUsd - account.spentUsd - account.synthesisReserveUsd;
12459
- remainingUsd = remainingUsd === void 0 ? headroom : Math.min(remainingUsd, headroom);
12583
+ remaining = remaining === void 0 ? headroom : Math.min(remaining, headroom);
12460
12584
  }
12585
+ return remaining === void 0 ? void 0 : Math.max(0, remaining);
12586
+ }
12587
+ maxAffordableOutputTokens(servedBy, estimatedInputTokens, accountScope = "run") {
12588
+ const pricing = this.pricingOf?.(servedBy);
12589
+ if (pricing === void 0) return;
12590
+ const remainingUsd = this.remainingUsd(accountScope);
12461
12591
  if (remainingUsd === void 0) return;
12462
- return affordableOutputTokens(pricing, Math.max(0, remainingUsd), estimatedInputTokens);
12592
+ return affordableOutputTokens(pricing, remainingUsd, estimatedInputTokens);
12463
12593
  }
12464
12594
  /**
12465
12595
  * Live accounting; spend propagates from `accountScope` to every
@@ -14177,6 +14307,7 @@ function createCtx(internals, rootWorkflow) {
14177
14307
  budget: {
14178
14308
  beforeTurn: () => internals.budget.beforeTurn(budgetAccount),
14179
14309
  maxAffordableOutputTokens: (servedBy, estimatedInputTokens) => internals.budget.maxAffordableOutputTokens(servedBy, estimatedInputTokens, budgetAccount),
14310
+ remainingUsd: () => internals.budget.remainingUsd(budgetAccount),
14180
14311
  onUsage: (usage, servedBy) => internals.budget.onUsage(usage, servedBy, budgetAccount),
14181
14312
  signal: budgetAccount === "run" ? internals.budget.signal : AbortSignal.any([internals.budget.signal, internals.budget.signalOf(budgetAccount)].filter((signal) => signal !== void 0))
14182
14313
  },
@@ -14428,7 +14559,8 @@ function createCtx(internals, rootWorkflow) {
14428
14559
  entryRef: terminal.seq,
14429
14560
  ...resultUsageApprox ? { usageApprox: true } : {},
14430
14561
  ...result.transportRetries !== void 0 && result.transportRetries > 0 ? { retryCount: result.transportRetries } : {},
14431
- ...result.exploration === void 0 ? {} : { exploration: result.exploration }
14562
+ ...result.exploration === void 0 ? {} : { exploration: result.exploration },
14563
+ ...result.toolBudget === void 0 ? {} : { toolBudget: result.toolBudget }
14432
14564
  }, spanId);
14433
14565
  if (result.status === "escalated" && result.escalation !== void 0) {
14434
14566
  let decision = flavorBDecision;
@@ -18003,9 +18135,20 @@ function orchestrate(engine, goal, opts, runOptions) {
18003
18135
  * regardless of mix. A free tool (unit cost 0) lifts the units bound
18004
18136
  * entirely for calls of that tool.
18005
18137
  */
18138
+ /**
18139
+ * maxToolCalls at the fully extended cap (RV301): the projections
18140
+ * assume every grant lands, because quota demand and the checkpoint
18141
+ * loss window must hold at the worst case, not the base cap.
18142
+ */
18143
+ function extendedMaxToolCalls(limits) {
18144
+ if (limits.maxToolCalls === void 0) return;
18145
+ const extension = limits.toolBudgetExtension;
18146
+ return extension === void 0 ? limits.maxToolCalls : limits.maxToolCalls + extension.maxExtensions * extension.increment;
18147
+ }
18006
18148
  function overallExecutedCeiling(limits, toolCeilings) {
18007
18149
  const overall = toolCeilings.reduce((best, row) => row.ceiling === null ? best : best === null ? row.ceiling : Math.max(best, row.ceiling), null);
18008
- return limits.maxToolCalls !== void 0 && (overall === null || limits.maxToolCalls < overall) ? limits.maxToolCalls : overall;
18150
+ const callCap = extendedMaxToolCalls(limits);
18151
+ return callCap !== void 0 && (overall === null || callCap < overall) ? callCap : overall;
18009
18152
  }
18010
18153
  /**
18011
18154
  * The provider-call ceiling of one whole loop: maxTurns bounded by the
@@ -18054,9 +18197,10 @@ function toolCeilingsOf(limits) {
18054
18197
  ceiling: Math.floor(limits.toolUnits.max / cost)
18055
18198
  });
18056
18199
  }
18057
- if (limits.maxToolCalls !== void 0) terms.push({
18200
+ const callCap = extendedMaxToolCalls(limits);
18201
+ if (callCap !== void 0) terms.push({
18058
18202
  boundBy: "maxToolCalls",
18059
- ceiling: limits.maxToolCalls
18203
+ ceiling: callCap
18060
18204
  });
18061
18205
  if (terms.length === 0) {
18062
18206
  rows.push({
@@ -18289,6 +18433,21 @@ function preflightEstimate(input) {
18289
18433
  message: `spawn '${label}': the whole executed-call budget (${String(executedToolCallCeiling)} calls) fits into one parallel tool batch, and checkpoints write once per completed tool turn: the cap can be reached before any checkpoint exists, and a kill mid-batch re-pays every executed call on resume`,
18290
18434
  spawn: label
18291
18435
  });
18436
+ if (limits.toolBudgetExtension !== void 0) if (limits.maxToolCalls === void 0) say({
18437
+ severity: "warning",
18438
+ code: "inert-tool-budget-extension",
18439
+ message: `spawn '${label}' sets toolBudgetExtension without maxToolCalls: the extension only ever raises the executed-call cap, so there is nothing to extend`,
18440
+ spawn: label
18441
+ });
18442
+ else {
18443
+ const { increment, maxExtensions } = limits.toolBudgetExtension;
18444
+ say({
18445
+ severity: "info",
18446
+ code: "tool-budget-extension-exposure",
18447
+ message: `spawn '${label}': toolBudgetExtension can raise maxToolCalls ${String(limits.maxToolCalls)} by up to ${String(maxExtensions * increment)} extra calls (${String(maxExtensions)} grants of ${String(increment)}); every grant is admitted only under remaining budget headroom, and the projections already assume the fully extended cap`,
18448
+ spawn: label
18449
+ });
18450
+ }
18292
18451
  if (limits.finalizationReserve !== void 0 && limits.maxToolCalls === void 0 && limits.toolUnits === void 0) say({
18293
18452
  severity: "warning",
18294
18453
  code: "inert-finalization-reserve",
@@ -19183,6 +19342,7 @@ function reduceInvocationTable(events) {
19183
19342
  row.costUsd = event.costUsd;
19184
19343
  row.usageApprox = event.usageApprox === true;
19185
19344
  row.retryCount = event.retryCount ?? 0;
19345
+ if (event.toolBudget !== void 0) row.toolBudget = event.toolBudget;
19186
19346
  totalCostUsd += event.costUsd;
19187
19347
  break;
19188
19348
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/core",
3
- "version": "1.84.0",
3
+ "version": "1.86.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",
@@ -22,10 +22,10 @@
22
22
  "access": "public"
23
23
  },
24
24
  "devDependencies": {
25
- "@types/node": "^22.20.0",
26
- "arktype": "^2.2.2",
27
- "fast-check": "^4.8.0",
28
- "tsdown": "^0.22.3",
25
+ "@types/node": "^22.20.1",
26
+ "arktype": "^2.2.3",
27
+ "fast-check": "^4.9.0",
28
+ "tsdown": "^0.22.14",
29
29
  "typescript": "~6.0.3",
30
30
  "valibot": "^1.4.2",
31
31
  "zod": "^4.4.3"