@rulvar/core 1.3.2 → 1.5.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 +137 -14
  2. package/dist/index.js +245 -48
  3. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -602,6 +602,36 @@ type AbandonPayload = {
602
602
  retainCheckpoint?: boolean; /** Default false; counts against the pin cap (DEF-5). */
603
603
  retainWorktree?: boolean;
604
604
  };
605
+ /** One serving model's slice of a multi-model agent call's usage. */
606
+ interface UsageSlice {
607
+ servedBy: ModelRef;
608
+ usage: Usage;
609
+ }
610
+ /**
611
+ * The per-model slices of a terminal entry: the recorded split when the
612
+ * call spanned several models, else the whole usage attributed to
613
+ * `servedBy`. The fallback is what makes every journal written before the
614
+ * split shipped price exactly as it did before.
615
+ */
616
+ declare function entryUsageSlices(entry: JournalEntry): UsageSlice[];
617
+ /** A priced slice, plus the total and the gaps the price table did not cover. */
618
+ interface PricedUsage {
619
+ /** Total of every slice the price table covered. */
620
+ usd: number;
621
+ /** Covered slices with their prices; the basis of per-model attribution. */
622
+ priced: Array<UsageSlice & {
623
+ usd: number;
624
+ }>;
625
+ /** Slices with no price row: surfaced as unpriced, never a silent zero. */
626
+ unpriced: UsageSlice[];
627
+ }
628
+ /**
629
+ * The single pricing fold over one terminal entry, shared by the kernel
630
+ * ledger and the CostReport fold so a run's total and its per-model
631
+ * breakdown can never disagree. Each slice is priced at ITS OWN model's
632
+ * rate.
633
+ */
634
+ declare function priceEntryUsage(entry: JournalEntry, priceUsd: (servedBy: ModelRef, usage: Usage) => number | undefined): PricedUsage;
605
635
  /**
606
636
  * Final entry form (hashVersion 2).
607
637
  * All journaled values MUST be JSON-serializable; a violation raises a
@@ -627,6 +657,18 @@ type JournalEntry = {
627
657
  usage?: Usage; /** True when the stream was cut at the budget ceiling or by a stream failure. */
628
658
  usageApprox?: boolean; /** Who actually served (failover changes only this, never the key). */
629
659
  servedBy?: ModelRef;
660
+ /**
661
+ * Terminal agent entries whose phases were served by MORE THAN ONE
662
+ * model: usage split by the model that actually served each slice. The
663
+ * loop, extract, finalize, and summarize roles resolve independently,
664
+ * so a single agent call routinely spans models at different prices;
665
+ * pricing the whole call at `servedBy` bills the cheap extract at the
666
+ * loop model's rate. Absent when one model served the whole call, and
667
+ * on entries written before the split shipped: readers fall back to
668
+ * pricing `usage` at `servedBy`, which is exactly correct for those.
669
+ * Policy, never identity: it does not enter the content key.
670
+ */
671
+ usageByModel?: UsageSlice[];
630
672
  transcriptRef?: string;
631
673
  checkpointRef?: string;
632
674
  /**
@@ -697,6 +739,14 @@ type RunMeta = {
697
739
  workflowName?: string; /** Content hash of the body or of the compiled source. */
698
740
  workflowHash?: string; /** TranscriptStore ref of the persisted CompiledWorkflow source. */
699
741
  workflowSourceRef?: string;
742
+ /**
743
+ * The run's immutable USD ceiling (RunOptions.budgetUsd), recorded so
744
+ * resume restores the original invocation's bound. Absent when the
745
+ * run started without a ceiling. Stores must round-trip the field
746
+ * (the conformance kit checks); a store that drops it degrades a
747
+ * resumed run to uncapped.
748
+ */
749
+ budgetUsd?: number;
700
750
  };
701
751
  type RunFilter = {
702
752
  status?: string;
@@ -2010,6 +2060,8 @@ interface TerminalPatch {
2010
2060
  usage?: Usage;
2011
2061
  usageApprox?: boolean;
2012
2062
  servedBy?: ModelRef;
2063
+ /** Set only when the call spanned several serving models; see JournalEntry. */
2064
+ usageByModel?: UsageSlice[];
2013
2065
  transcriptRef?: string;
2014
2066
  checkpointRef?: string;
2015
2067
  /** Terminal agent entries: Artifact list. */
@@ -2304,6 +2356,14 @@ interface CheckpointState {
2304
2356
  turns: number;
2305
2357
  /** Usage accumulated so far (not yet journaled: terminals carry totals). */
2306
2358
  usage: Usage;
2359
+ /**
2360
+ * The same usage split by serving model, so a dangling redispatch
2361
+ * restores the per-model breakdown instead of collapsing every paid
2362
+ * turn onto the loop model. Absent on checkpoints written before the
2363
+ * split shipped: those restore the aggregate against the loop model,
2364
+ * exactly as they did then.
2365
+ */
2366
+ usageByModel?: UsageSlice[];
2307
2367
  toolCallsUsed: number;
2308
2368
  schemaAttempts: number;
2309
2369
  /** Compaction points; producers arrive with M4-T03. */
@@ -2633,6 +2693,14 @@ interface AgentResult<T> {
2633
2693
  * differs from the requested spec only under transport failover.
2634
2694
  */
2635
2695
  servedBy: ModelRef;
2696
+ /**
2697
+ * Present only when the call spanned MORE THAN ONE serving model (the
2698
+ * loop, extract, finalize, and summarize roles resolve independently):
2699
+ * usage split per model, so `costUsd` and every cost bucket price each
2700
+ * slice at its own rate. Absent for a single-model call, which
2701
+ * (usage, servedBy) already describes exactly.
2702
+ */
2703
+ usageByModel?: UsageSlice[];
2636
2704
  transcriptRef: string;
2637
2705
  artifacts?: Artifact[];
2638
2706
  error?: AgentError;
@@ -2867,8 +2935,10 @@ type PermissionHook = (toolName: string, input: unknown, ctx: ToolContext) => Ho
2867
2935
  * position matches every tool WITHOUT declared risk: presets treat the
2868
2936
  * undeclared state conservatively. Argv rules
2869
2937
  * match through the real shell matcher; domain rules are
2870
- * ADVISORY outside the first-party fetch tool: they never
2871
- * change a verdict in M5, and matches surface in audit events.
2938
+ * ADVISORY for every tool in the current release: they never
2939
+ * change a verdict, and matches surface in the tool:end audit
2940
+ * fields (enforcement will live in a first-party fetch tool
2941
+ * when one ships).
2872
2942
  */
2873
2943
  type RiskRuleValue = ToolRisk | "undeclared";
2874
2944
  type PermissionRule = {
@@ -2928,8 +2998,8 @@ type PermissionVerdict = ({
2928
2998
  input: unknown;
2929
2999
  }) & {
2930
3000
  /**
2931
- * Advisory domain-rule matches: reported in audit
2932
- * events, never enforced outside the first-party fetch tool.
3001
+ * Advisory domain-rule matches: reported in the tool:end
3002
+ * audit fields, never enforced in the current release.
2933
3003
  */
2934
3004
  advisory?: PermissionRule[];
2935
3005
  };
@@ -3258,6 +3328,8 @@ declare class RunBudget {
3258
3328
  private usageInternal;
3259
3329
  private agentsSpawnedInternal;
3260
3330
  private exhaustedInternal;
3331
+ /** Models already warned about; the warning fires once per model per run. */
3332
+ private readonly unpricedWarned;
3261
3333
  constructor(options: {
3262
3334
  ceilingUsd?: number;
3263
3335
  lifetimeSpawnCap?: number;
@@ -3893,12 +3965,23 @@ type AdaptiveEvents = {
3893
3965
  coversToOrdinal: number;
3894
3966
  renderSize: number;
3895
3967
  } | {
3968
+ /**
3969
+ * Two emitted shapes share the discriminant: the cap-freeze form
3970
+ * carries { atCap: true, spentUsd, capUsd, finalizeReserveUsd },
3971
+ * and the per-wake digest form carries atCap plus the passive
3972
+ * WakeBudgetBlock fields (runSpentUsd .. softWarning).
3973
+ */
3896
3974
  type: "orchestrator:budget";
3897
- entryRef: number;
3898
- spentUsd: number;
3899
- effectiveCapUsd: number;
3900
- reserveUsedUsd: number;
3901
- frozen: boolean;
3975
+ atCap: boolean;
3976
+ spentUsd?: number;
3977
+ capUsd?: number;
3978
+ finalizeReserveUsd?: number;
3979
+ runSpentUsd?: number;
3980
+ runCeilingUsd?: number;
3981
+ orchestratorSpentUsd?: number;
3982
+ orchestratorCapUsd?: number;
3983
+ orchestratorShare?: number;
3984
+ softWarning?: boolean;
3902
3985
  } | {
3903
3986
  type: "escalation:raised";
3904
3987
  entryRef: number;
@@ -3920,7 +4003,12 @@ type AdaptiveEvents = {
3920
4003
  spawnUnitsAfter: number;
3921
4004
  } | {
3922
4005
  type: "spawn:rejected";
3923
- entryRef: number;
4006
+ /**
4007
+ * The journaled admission decision entry; absent for the
4008
+ * pre-admission config gates (orchestrate maxSpawns), which
4009
+ * reject before anything is journaled.
4010
+ */
4011
+ entryRef?: number;
3924
4012
  code: string;
3925
4013
  agentType: string;
3926
4014
  logicalTaskId?: string;
@@ -3971,6 +4059,12 @@ type AdaptiveEvents = {
3971
4059
  frozenValue: Json;
3972
4060
  liveValue: Json;
3973
4061
  } | {
4062
+ /**
4063
+ * Declared for hosts; not emitted today. The compatibility scan
4064
+ * runs strictly before a run's event stream exists, so the
4065
+ * refusal travels only as the typed JournalCompatibilityError
4066
+ * (which carries the same fields).
4067
+ */
3974
4068
  type: "journal:compat";
3975
4069
  code: "HASH_VERSION_TOO_OLD" | "HASH_VERSION_TOO_NEW";
3976
4070
  found: number;
@@ -4773,7 +4867,14 @@ declare class ExternalRegistry {
4773
4867
  private activity;
4774
4868
  private quiesceListener?;
4775
4869
  private quiesceScheduled;
4776
- constructor(replayer: Replayer);
4870
+ private readonly emitEvent?;
4871
+ constructor(replayer: Replayer, emitEvent?: (body: WorkflowEventBody) => void);
4872
+ /**
4873
+ * Live resolution telemetry: applied when the attempt won the
4874
+ * first-closing-wins fold, superseded when it lost. Emitted for live
4875
+ * attempts only; folds of prior entries at resume re-emit nothing.
4876
+ */
4877
+ private emitResolutionOutcome;
4777
4878
  /** Wraps every non-suspension async operation (agents, steps). */
4778
4879
  enter(): () => void;
4779
4880
  /**
@@ -5114,12 +5215,27 @@ interface Workflow<A = unknown, R = unknown> {
5114
5215
  readonly name: string;
5115
5216
  readonly argsSchema?: SchemaSpec<A>;
5116
5217
  readonly errorPolicy: ErrorPolicy;
5218
+ /**
5219
+ * Workflow defaults: the third layer of the resolution chain, under the
5220
+ * call override and the agent profile and over the engine defaults.
5221
+ * A workflow that declares nothing contributes no layer and resolves
5222
+ * exactly as it did before. The layer follows the CALL TREE, not the
5223
+ * file: a child spawned through `ctx.workflow` contributes ITS OWN
5224
+ * defaults inside its scope, so nesting a cheap workflow under an
5225
+ * expensive one does the obvious thing.
5226
+ */
5227
+ readonly model?: ModelSpec;
5228
+ readonly routing?: Partial<Record<InvocationRole, ModelSpec>>;
5229
+ readonly effort?: Effort;
5117
5230
  readonly body: (ctx: Ctx<never>, args: A) => Promise<R>;
5118
5231
  }
5119
5232
  declare function defineWorkflow<A, R, P extends ErrorPolicy = "strict">(meta: {
5120
5233
  name: string;
5121
5234
  args?: SchemaSpec<A>;
5122
- errorPolicy?: P;
5235
+ errorPolicy?: P; /** Workflow defaults: resolution-chain layer 3. See Workflow. */
5236
+ model?: ModelSpec;
5237
+ routing?: Partial<Record<InvocationRole, ModelSpec>>;
5238
+ effort?: Effort;
5123
5239
  }, body: (ctx: Ctx<P>, args: A) => Promise<R>): Workflow<A, R>;
5124
5240
  /**
5125
5241
  * Span-aware event sink: bodies are stamped into the WorkflowEvent
@@ -5223,7 +5339,11 @@ interface RunInternals {
5223
5339
  * one ctx object while journaling under their own scope paths (I3:
5224
5340
  * structure from call-and-return only).
5225
5341
  */
5226
- declare function createCtx(internals: RunInternals): Ctx<ErrorPolicy>;
5342
+ declare function createCtx(internals: RunInternals, rootWorkflow?: {
5343
+ model?: ModelSpec;
5344
+ routing?: Partial<Record<InvocationRole, ModelSpec>>;
5345
+ effort?: Effort;
5346
+ }): Ctx<ErrorPolicy>;
5227
5347
  /**
5228
5348
  * Runs a workflow body against a fresh ctx: the engine core that
5229
5349
  * engine.run wraps with RunHandle, events, and outcome assembly (M1-T11).
@@ -5568,6 +5688,9 @@ declare class InMemoryStore implements JournalStore {
5568
5688
  private readonly runs;
5569
5689
  private readonly metas;
5570
5690
  private warned;
5691
+ constructor(options?: {
5692
+ quiet?: boolean;
5693
+ });
5571
5694
  append(runId: string, e: JournalEntry): Promise<void>;
5572
5695
  load(runId: string): Promise<JournalEntry[]>;
5573
5696
  putMeta(m: RunMeta): Promise<void>;
@@ -5977,4 +6100,4 @@ interface SandboxBridge {
5977
6100
  }
5978
6101
  declare function createSandboxBridge(ctx: Ctx<never>, options: SandboxBridgeOptions): SandboxBridge;
5979
6102
  //#endregion
5980
- export { AWAIT_SCHEMA, AbandonAttempt, AbandonFold, AbandonPayload, AbandonedSpendView, AbortClass, type AdaptiveEvents, AdmissionController, AdmissionDecision, AdmissionRejectedError, AdmissionStatsBefore, AdmitLineage, AdmitRejectReason, AdmitSpec, AdmitVerdict, AgentCallError, AgentError, type AgentEvents, AgentIdentityInput, AgentOpts, AgentProfile, AgentProfilePermissions, AgentResult, AgentResultMeta, AgentStatus, ApproachSignatureInputs, ApprovalDecision, ApprovalIdentityInput, Artifact, AttemptOutcomeClass, BUDGET_ABORT_REASON, BriefOpts, BudgetAccountView, BudgetDefaults, BudgetExhaustedError, 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, ChildIdentityInput, type ClaimClass, type ClaimOp, type ClaimStatus, ClaimValidationOptions, CollectOpts, CollectedTurn, CompactionConfig, CompiledPermissionChain, CompiledWorkflow, ConfigError, type CoreEvents, CostAttribution, CostReport, CreateEngineOptions, Ctx, DEFAULT_CHILD_BUDGET_FRACTION, DEFAULT_COMPACTION_THRESHOLD, DEFAULT_ESCALATION_LIMITS, 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, DebitResult, DeclaredLadder, DedupIndex, DedupNote, DerivedKey, DeriverRegistry, DispositionRule, DispositionTable, DonorCandidate, DonorRef, DroppedItem, EMIT_RESULT_TOOL, EMPTY_SCHEMA_HASH, EMPTY_TOOLSET_HASH, ESCALATE_TOOL_NAME, ESCALATION_REPORT_SCHEMA, ESCALATION_REQUEST_SCHEMA, EffectiveUsageLimits, Effort, Engine, EngineDefaults, EntryKind, EntryRef, EntryStatus, ErrorClass, ErrorCode, ErrorPolicy, EscalatedResult, EscalationDecision, EscalationDigest, EscalationKind, EscalationLimits, EscalationOptions, EscalationReport, EscalationRequest, EventBus, type EvidenceRef, ExtensionAppendInput, ExtensionDispatchSpec, ExternalIdentityInput, ExternalRegistry, ExtractNecessityInput, FINISH_SCHEMA, FINISH_TOOL_NAME, FailoverTarget, FailoverTrigger, FallbackField, FallbackTrigger, FileModelKnowledgeStore, FileModelKnowledgeStoreOptions, FileTranscriptStore, FinishInfo, Gate, GateAudit, type GateRecord, GitWorktreeProvider, GitWorktreeProviderOptions, GraftBoot, HashVersion, HookVerdict, INBOX_PROPOSAL_TTL_DAYS, IdentityInput, InMemoryStore, InMemoryTranscriptStore, InProcessRunner, InvalidResolutionError, InvocationRole, type IsolationProvider, type IsolationSpec, Issue$1 as Issue, JournalCompatSubCode, JournalCompatibilityError, JournalEntry, JournalMatcher, JournalMissError, JournalOperation, JournalOrderViolation, 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_DEPTH_CEILING, MatchResult, McpConfig, MechanicalGateProfile, MechanicalGateVerdict, type ModelCaps, ModelChoice, type ModelClaim, ModelEpochInputs, type ModelKnowledgeHandle, type ModelKnowledgeStore, ModelListConstraint, ModelRef, ModelRetry, ModelSpec, Msg, NoProgressDetector, NodeId, NodeLinkValue, NonSerializableValueError, ORCHESTRATE_WORKFLOW_NAME, OnEscalation, OperationDisposition, OrchestrateOptions, OrchestratorBudgetSpec, OrchestratorCapConfigError, OrchestratorExtension, OrchestratorExtensionIO, OrchestratorRuntime, Out, PARALLEL_AGENTS_SCHEMA, ParallelSiteCounter, Part, PendingExternal, PendingToolTurn, PermissionConfig, PermissionGate, PermissionHook, PermissionPreset, PermissionRule, PermissionVerdict, PhaseTarget, PipelineCollected, PipelineOpts, PlanInvariantError, PriceTable, type Pricing, type ProviderAdapter, QualityFloors, ROLE_EFFORT_DEFAULTS, ROOT_ACCOUNT, ROOT_SCOPE, RUN_PROFILES, RandIdentityInput, RandPayload, RefEntryAppender, RefEntryClassification, RefusalInfo, ReplayDisposition, ReplayMode, ReplayPlanHashMismatch, Replayer, ResolutionArbiter, ResolutionAttempt, ResolutionBy, ResolutionFold, ResolutionLayer, ResolutionOutcome, ResolutionPayload, ResolvedInvocation, ResolvedToolset, ResumeHandle, ResumeOptions, ResumePreview, ResumeReport, RetryClass, RetryPolicy, ReuseConfig, RiskRuleValue, Role, RulvarError, RulvarErrorCode, RunAgentOptions, RunBudget, RunEventSink, type RunFilter, RunHandle, RunInternals, type RunMeta, RunOptions, RunOutcome, RunProfile, RunStatus, RuntimeEventSink, SPAWN_AGENT_SCHEMA, SandboxBridge, SandboxBridgeOptions, SandboxError, SandboxHostToWorker, SandboxMethod, SandboxWorkerToHost, SchemaPair, SchemaSpec, SchemaValidationResult, ScopeSegment, ScriptRejected, ScriptRunner, ScrubNote, Semaphore, SerializationHook, Settled, 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, ToolInit, type ToolRisk, ToolRuntime, type ToolSource, type ToolSourceSession, ToolsOption, TranscriptSerializationHook, type TranscriptStore, TriggerClass, TtlState, Usage, UsageLimits, 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, agentErrorFromWire, agentErrorToWire, agentScope, applyClaimOps, applyStructuredOutputTier, approachSigCoarse, approachSigOf, archiveDeprecatedModelOps, atCompactionThreshold, 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, compileVerifiedLayer, costReportFromJournal, countsAgainstLimit, createCanonicalIdMinter, createCtx, createEngine, createSandboxBridge, currentOnlyKeyRing, decodeCheckpoint, defineWorkflow, deriveContentKey, deriverV1, deriverV2, digestOf, dispositionHook, emptyDigestBlocks, emptyToolset, encodeCheckpoint, escalateTool, evaluatePermission, evaluateReuse, executeWorkflow, exhaustionCodeOf, extractCandidate, failoverTriggerOf, fallbackTriggerOf, filterClaimsForRun, finalizeFires, foldTermination, formatRePrompt, formatScopePath, hashWorkflowBody, hashWorkflowSource, identityJcs, isEscalated, isSchemaPairSpec, isStandardSchemaSpec, isStrictCompatibleSchema, kMaxOf, knowledgeHash, ladderLengthOf, ladderRungChoice, lexShellCommand, liftRetainedParts, lineageWeightOf, makeOrchestratorWorkflow, maskSecrets, maskSecretsDeep, maskSecretsJson, matchArgvPattern, matchShellCommand, mcp, mergeUsageLimits, modelEpochOf, modelKnowledgeCard, modelSpecIdentity, needsSeparateExtract, nextFailover, nodeLinkKey, normalizeApproachTag, normalizeEntry, normalizeFallbacks, orchestrate, parallelScope, parseModelRef, parseScopePath, phiInitialOf, pipelineScope, planNodeScope, priceUsdOf, profileCard, profileRegistrySnapshotHash, projectHistory, projectIdentity, projectToJsonSchema, proposalStatement, providerOf, readTerminationInit, registryKeyRing, remeasureQueue, replayDisposition, resolveModelInvocation, resolvePricing, resolveToolset, retryClassOf, retryDelayMs, roleConfiguredInRouting, roundOneDisposition, runAgent, runProfile, scanJournalCompatibility, schemaHash, schemaHashOfSpec, selectStructuredOutputTier, shouldCompact, spawnDepthOf, summarizeInstruction, summarizeOutput, terminationConfigDrift, tierWithinCaps, toApprovalDecision, toJournalValue, tool, toolContract, toolsetHash, ttlState, validateEditorialCommit, validateEntryShape, validateEscalationLimits, validateEscalationReport, validateSchemaSpec, validateTerminationLimits, workflowScope, workflowSourceRef, wrapJournalStore, wrapTranscriptStore };
6103
+ export { AWAIT_SCHEMA, AbandonAttempt, AbandonFold, AbandonPayload, AbandonedSpendView, AbortClass, type AdaptiveEvents, AdmissionController, AdmissionDecision, AdmissionRejectedError, AdmissionStatsBefore, AdmitLineage, AdmitRejectReason, AdmitSpec, AdmitVerdict, AgentCallError, AgentError, type AgentEvents, AgentIdentityInput, AgentOpts, AgentProfile, AgentProfilePermissions, AgentResult, AgentResultMeta, AgentStatus, ApproachSignatureInputs, ApprovalDecision, ApprovalIdentityInput, Artifact, AttemptOutcomeClass, BUDGET_ABORT_REASON, BriefOpts, BudgetAccountView, BudgetDefaults, BudgetExhaustedError, 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, ChildIdentityInput, type ClaimClass, type ClaimOp, type ClaimStatus, ClaimValidationOptions, CollectOpts, CollectedTurn, CompactionConfig, CompiledPermissionChain, CompiledWorkflow, ConfigError, type CoreEvents, CostAttribution, CostReport, CreateEngineOptions, Ctx, DEFAULT_CHILD_BUDGET_FRACTION, DEFAULT_COMPACTION_THRESHOLD, DEFAULT_ESCALATION_LIMITS, 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, DebitResult, DeclaredLadder, DedupIndex, DedupNote, DerivedKey, DeriverRegistry, DispositionRule, DispositionTable, DonorCandidate, DonorRef, DroppedItem, EMIT_RESULT_TOOL, EMPTY_SCHEMA_HASH, EMPTY_TOOLSET_HASH, ESCALATE_TOOL_NAME, ESCALATION_REPORT_SCHEMA, ESCALATION_REQUEST_SCHEMA, EffectiveUsageLimits, Effort, Engine, EngineDefaults, EntryKind, EntryRef, EntryStatus, ErrorClass, ErrorCode, ErrorPolicy, EscalatedResult, EscalationDecision, EscalationDigest, EscalationKind, EscalationLimits, EscalationOptions, EscalationReport, EscalationRequest, EventBus, type EvidenceRef, ExtensionAppendInput, ExtensionDispatchSpec, ExternalIdentityInput, ExternalRegistry, ExtractNecessityInput, FINISH_SCHEMA, FINISH_TOOL_NAME, FailoverTarget, FailoverTrigger, FallbackField, FallbackTrigger, FileModelKnowledgeStore, FileModelKnowledgeStoreOptions, FileTranscriptStore, FinishInfo, Gate, GateAudit, type GateRecord, GitWorktreeProvider, GitWorktreeProviderOptions, GraftBoot, HashVersion, HookVerdict, INBOX_PROPOSAL_TTL_DAYS, IdentityInput, InMemoryStore, InMemoryTranscriptStore, InProcessRunner, InvalidResolutionError, InvocationRole, type IsolationProvider, type IsolationSpec, Issue$1 as Issue, JournalCompatSubCode, JournalCompatibilityError, JournalEntry, JournalMatcher, JournalMissError, JournalOperation, JournalOrderViolation, 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_DEPTH_CEILING, MatchResult, McpConfig, MechanicalGateProfile, MechanicalGateVerdict, type ModelCaps, ModelChoice, type ModelClaim, ModelEpochInputs, type ModelKnowledgeHandle, type ModelKnowledgeStore, ModelListConstraint, ModelRef, ModelRetry, ModelSpec, Msg, NoProgressDetector, NodeId, NodeLinkValue, NonSerializableValueError, ORCHESTRATE_WORKFLOW_NAME, OnEscalation, OperationDisposition, OrchestrateOptions, OrchestratorBudgetSpec, OrchestratorCapConfigError, OrchestratorExtension, OrchestratorExtensionIO, OrchestratorRuntime, Out, PARALLEL_AGENTS_SCHEMA, ParallelSiteCounter, Part, PendingExternal, PendingToolTurn, PermissionConfig, PermissionGate, PermissionHook, PermissionPreset, PermissionRule, PermissionVerdict, PhaseTarget, PipelineCollected, PipelineOpts, PlanInvariantError, PriceTable, PricedUsage, type Pricing, type ProviderAdapter, QualityFloors, ROLE_EFFORT_DEFAULTS, ROOT_ACCOUNT, ROOT_SCOPE, RUN_PROFILES, RandIdentityInput, RandPayload, RefEntryAppender, RefEntryClassification, RefusalInfo, ReplayDisposition, ReplayMode, ReplayPlanHashMismatch, Replayer, ResolutionArbiter, ResolutionAttempt, ResolutionBy, ResolutionFold, ResolutionLayer, ResolutionOutcome, ResolutionPayload, ResolvedInvocation, ResolvedToolset, ResumeHandle, ResumeOptions, ResumePreview, ResumeReport, RetryClass, RetryPolicy, ReuseConfig, RiskRuleValue, Role, RulvarError, RulvarErrorCode, RunAgentOptions, RunBudget, RunEventSink, type RunFilter, RunHandle, RunInternals, type RunMeta, RunOptions, RunOutcome, RunProfile, RunStatus, RuntimeEventSink, SPAWN_AGENT_SCHEMA, SandboxBridge, SandboxBridgeOptions, SandboxError, SandboxHostToWorker, SandboxMethod, SandboxWorkerToHost, SchemaPair, SchemaSpec, SchemaValidationResult, ScopeSegment, ScriptRejected, ScriptRunner, ScrubNote, Semaphore, SerializationHook, Settled, 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, 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, agentErrorFromWire, agentErrorToWire, agentScope, applyClaimOps, applyStructuredOutputTier, approachSigCoarse, approachSigOf, archiveDeprecatedModelOps, atCompactionThreshold, 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, compileVerifiedLayer, costReportFromJournal, countsAgainstLimit, createCanonicalIdMinter, createCtx, createEngine, createSandboxBridge, currentOnlyKeyRing, decodeCheckpoint, defineWorkflow, deriveContentKey, deriverV1, deriverV2, digestOf, dispositionHook, emptyDigestBlocks, emptyToolset, encodeCheckpoint, entryUsageSlices, escalateTool, evaluatePermission, evaluateReuse, executeWorkflow, exhaustionCodeOf, extractCandidate, failoverTriggerOf, fallbackTriggerOf, filterClaimsForRun, finalizeFires, foldTermination, formatRePrompt, formatScopePath, hashWorkflowBody, hashWorkflowSource, identityJcs, isEscalated, isSchemaPairSpec, isStandardSchemaSpec, isStrictCompatibleSchema, kMaxOf, knowledgeHash, ladderLengthOf, ladderRungChoice, lexShellCommand, liftRetainedParts, lineageWeightOf, makeOrchestratorWorkflow, maskSecrets, maskSecretsDeep, maskSecretsJson, matchArgvPattern, matchShellCommand, mcp, mergeUsageLimits, modelEpochOf, modelKnowledgeCard, modelSpecIdentity, needsSeparateExtract, nextFailover, nodeLinkKey, normalizeApproachTag, normalizeEntry, normalizeFallbacks, orchestrate, parallelScope, parseModelRef, parseScopePath, phiInitialOf, pipelineScope, planNodeScope, priceEntryUsage, priceUsdOf, profileCard, profileRegistrySnapshotHash, projectHistory, projectIdentity, projectToJsonSchema, proposalStatement, providerOf, readTerminationInit, registryKeyRing, remeasureQueue, replayDisposition, resolveModelInvocation, resolvePricing, resolveToolset, retryClassOf, retryDelayMs, roleConfiguredInRouting, roundOneDisposition, runAgent, runProfile, scanJournalCompatibility, schemaHash, schemaHashOfSpec, selectStructuredOutputTier, shouldCompact, spawnDepthOf, summarizeInstruction, summarizeOutput, terminationConfigDrift, tierWithinCaps, toApprovalDecision, toJournalValue, tool, toolContract, toolsetHash, ttlState, validateEditorialCommit, validateEntryShape, validateEscalationLimits, validateEscalationReport, validateSchemaSpec, validateTerminationLimits, workflowScope, workflowSourceRef, wrapJournalStore, wrapTranscriptStore };
package/dist/index.js CHANGED
@@ -1750,6 +1750,46 @@ async function validateSchemaSpec(spec, value) {
1750
1750
  /** 1 = round 1; 2 = current. */
1751
1751
  const CURRENT_HASH_VERSION = 2;
1752
1752
  /**
1753
+ * The per-model slices of a terminal entry: the recorded split when the
1754
+ * call spanned several models, else the whole usage attributed to
1755
+ * `servedBy`. The fallback is what makes every journal written before the
1756
+ * split shipped price exactly as it did before.
1757
+ */
1758
+ function entryUsageSlices(entry) {
1759
+ if (entry.usage === void 0) return [];
1760
+ if (entry.usageByModel !== void 0 && entry.usageByModel.length > 0) return entry.usageByModel;
1761
+ return entry.servedBy === void 0 ? [] : [{
1762
+ servedBy: entry.servedBy,
1763
+ usage: entry.usage
1764
+ }];
1765
+ }
1766
+ /**
1767
+ * The single pricing fold over one terminal entry, shared by the kernel
1768
+ * ledger and the CostReport fold so a run's total and its per-model
1769
+ * breakdown can never disagree. Each slice is priced at ITS OWN model's
1770
+ * rate.
1771
+ */
1772
+ function priceEntryUsage(entry, priceUsd) {
1773
+ const result = {
1774
+ usd: 0,
1775
+ priced: [],
1776
+ unpriced: []
1777
+ };
1778
+ for (const slice of entryUsageSlices(entry)) {
1779
+ const usd = priceUsd(slice.servedBy, slice.usage);
1780
+ if (usd === void 0) {
1781
+ result.unpriced.push(slice);
1782
+ continue;
1783
+ }
1784
+ result.usd += usd;
1785
+ result.priced.push({
1786
+ ...slice,
1787
+ usd
1788
+ });
1789
+ }
1790
+ return result;
1791
+ }
1792
+ /**
1753
1793
  * Round-1 normalization: hashVersion is taken from `hashVersion`, else
1754
1794
  * from the legacy `v` field, else 1. Stores are never rewritten;
1755
1795
  * normalization happens at read.
@@ -2583,7 +2623,7 @@ async function resolveToolset(specs, session) {
2583
2623
  if (specs === void 0 || specs.length === 0) return emptyToolset();
2584
2624
  const tools = [];
2585
2625
  for (const spec of specs) {
2586
- if (typeof spec === "string") throw new ConfigError(`tools by registered name ('${spec}') resolve only inside the worker sandbox; name-based tool registries exist for compiled scripts only (https://docs.rulvar.com/guide/planner)`);
2626
+ if (typeof spec === "string") throw new ConfigError(`tools by registered name ('${spec}') are not supported here: pass ToolDef or ToolSource values. Registered toolset names exist only for the dynamic orchestrator's spawn_agent toolsetRef (https://docs.rulvar.com/guide/tools)`);
2587
2627
  if (isToolDef(spec)) {
2588
2628
  tools.push(spec);
2589
2629
  continue;
@@ -5443,6 +5483,7 @@ var Replayer = class {
5443
5483
  if (patch.usage !== void 0) entry.usage = patch.usage;
5444
5484
  if (patch.usageApprox !== void 0) entry.usageApprox = patch.usageApprox;
5445
5485
  if (patch.servedBy !== void 0) entry.servedBy = patch.servedBy;
5486
+ if (patch.usageByModel !== void 0) entry.usageByModel = patch.usageByModel;
5446
5487
  if (patch.transcriptRef !== void 0) entry.transcriptRef = patch.transcriptRef;
5447
5488
  if (patch.checkpointRef !== void 0) entry.checkpointRef = patch.checkpointRef;
5448
5489
  if (patch.artifacts !== void 0) entry.artifacts = toJournalValue(patch.artifacts, "terminal artifacts");
@@ -5486,7 +5527,7 @@ var Replayer = class {
5486
5527
  usage.cacheReadTokens += entry.usage.cacheReadTokens;
5487
5528
  usage.cacheWriteTokens += entry.usage.cacheWriteTokens;
5488
5529
  reasoning += entry.usage.reasoningTokens ?? 0;
5489
- usd += this.priceUsd?.(entry.servedBy, entry.usage) ?? 0;
5530
+ if (this.priceUsd !== void 0) usd += priceEntryUsage(entry, this.priceUsd).usd;
5490
5531
  }
5491
5532
  if (reasoning > 0) usage.reasoningTokens = reasoning;
5492
5533
  return {
@@ -5576,8 +5617,31 @@ var ExternalRegistry = class ExternalRegistry {
5576
5617
  activity = 0;
5577
5618
  quiesceListener;
5578
5619
  quiesceScheduled = false;
5579
- constructor(replayer) {
5620
+ emitEvent;
5621
+ constructor(replayer, emitEvent) {
5580
5622
  this.replayer = replayer;
5623
+ this.emitEvent = emitEvent;
5624
+ }
5625
+ /**
5626
+ * Live resolution telemetry: applied when the attempt won the
5627
+ * first-closing-wins fold, superseded when it lost. Emitted for live
5628
+ * attempts only; folds of prior entries at resume re-emit nothing.
5629
+ */
5630
+ emitResolutionOutcome(targetRef, by, outcome) {
5631
+ if (this.emitEvent === void 0) return;
5632
+ if (outcome.applied) this.emitEvent({
5633
+ type: "resolution:applied",
5634
+ targetRef,
5635
+ entryRef: outcome.seq,
5636
+ by
5637
+ });
5638
+ else this.emitEvent({
5639
+ type: "resolution:superseded",
5640
+ targetRef,
5641
+ entryRef: outcome.seq,
5642
+ supersededBy: outcome.supersededBy,
5643
+ reason: outcome.reason
5644
+ });
5581
5645
  }
5582
5646
  /** Wraps every non-suspension async operation (agents, steps). */
5583
5647
  enter() {
@@ -5805,6 +5869,7 @@ var ExternalRegistry = class ExternalRegistry {
5805
5869
  */
5806
5870
  async submitResolution(entryRef, attempt) {
5807
5871
  const outcome = await this.replayer.resolveSuspended(entryRef, attempt);
5872
+ this.emitResolutionOutcome(entryRef, attempt.by, outcome);
5808
5873
  if (outcome.applied) {
5809
5874
  const waiter = this.waiters.get(entryRef);
5810
5875
  if (waiter !== void 0) {
@@ -5838,6 +5903,7 @@ var ExternalRegistry = class ExternalRegistry {
5838
5903
  by: "external",
5839
5904
  value
5840
5905
  });
5906
+ this.emitResolutionOutcome(waiter.entryRef, "external", outcome);
5841
5907
  if (outcome.applied) {
5842
5908
  this.waiters.delete(waiter.entryRef);
5843
5909
  waiter.resolve(value);
@@ -5853,7 +5919,10 @@ function deepCopy(value) {
5853
5919
  var InMemoryStore = class {
5854
5920
  runs = /* @__PURE__ */ new Map();
5855
5921
  metas = /* @__PURE__ */ new Map();
5856
- warned = false;
5922
+ warned;
5923
+ constructor(options) {
5924
+ this.warned = options?.quiet === true;
5925
+ }
5857
5926
  append(runId, e) {
5858
5927
  this.warnOnce();
5859
5928
  const entries = this.runs.get(runId) ?? [];
@@ -5885,7 +5954,7 @@ var InMemoryStore = class {
5885
5954
  warnOnce() {
5886
5955
  if (this.warned) return;
5887
5956
  this.warned = true;
5888
- process.emitWarning("InMemoryStore keeps journals in process memory: nothing survives the process and resume is disabled. Use JsonlFileStore (M2) or @rulvar/store-sqlite (M5) for durable runs.", {
5957
+ process.emitWarning("InMemoryStore keeps journals in process memory: nothing survives a process exit, and a run cannot be resumed from another process. Use JsonlFileStore (M2) or @rulvar/store-sqlite (M5) for durable runs.", {
5889
5958
  code: "RULVAR_INMEMORY_STORE",
5890
5959
  type: "RulvarWarning"
5891
5960
  });
@@ -6150,18 +6219,13 @@ function costReportFromJournal(entries, priceUsd) {
6150
6219
  for (const entry of entries) {
6151
6220
  if (entry.kind !== "resolution" && entry.kind !== "abandon" && abandonFold.isAbandoned(entry.ref ?? entry.seq)) continue;
6152
6221
  if (entry.status === "running" || entry.usage === void 0) continue;
6153
- const servedBy = entry.servedBy;
6154
- if (servedBy === void 0) continue;
6155
- const usd = priceUsd(servedBy, entry.usage);
6156
- if (usd === void 0) {
6157
- unpriced.push({
6158
- model: servedBy,
6159
- usage: entry.usage
6160
- });
6161
- continue;
6162
- }
6163
- byModel[servedBy] = (byModel[servedBy] ?? 0) + usd;
6164
- totalUsd += usd;
6222
+ const priced = priceEntryUsage(entry, priceUsd);
6223
+ for (const slice of priced.unpriced) unpriced.push({
6224
+ model: slice.servedBy,
6225
+ usage: slice.usage
6226
+ });
6227
+ for (const slice of priced.priced) byModel[slice.servedBy] = (byModel[slice.servedBy] ?? 0) + slice.usd;
6228
+ totalUsd += priced.usd;
6165
6229
  }
6166
6230
  return {
6167
6231
  totalUsd,
@@ -6518,6 +6582,12 @@ function liftRetainedParts(providerMetadata, adapter) {
6518
6582
  }
6519
6583
  //#endregion
6520
6584
  //#region src/model/retry.ts
6585
+ /**
6586
+ * Captured at module load, before the InProcessRunner's nondeterminism
6587
+ * guard can patch the global: the engine's own jitter is journal
6588
+ * invisible and must never be blamed on workflow code.
6589
+ */
6590
+ const nativeRandom = Math.random;
6521
6591
  /** Appendix A committed defaults (M4 entry gate, PR #26). */
6522
6592
  const DEFAULT_RETRY_POLICY = {
6523
6593
  attempts: 3,
@@ -6553,7 +6623,7 @@ function retryClassOf(error) {
6553
6623
  * equal-jitter: half the backoff is deterministic, half random, so a
6554
6624
  * jittered delay never collapses to zero.
6555
6625
  */
6556
- function retryDelayMs(policy, retryIndex, retryAfterMs, random = Math.random) {
6626
+ function retryDelayMs(policy, retryIndex, retryAfterMs, random = nativeRandom) {
6557
6627
  if (retryAfterMs !== void 0) return retryAfterMs;
6558
6628
  const { initialMs, factor, maxMs, jitter } = policy.backoff;
6559
6629
  const base = Math.min(maxMs, initialMs * factor ** retryIndex);
@@ -6788,7 +6858,13 @@ function resolveModelInvocation(options) {
6788
6858
  });
6789
6859
  const requestedEffort = merged.effort ?? ROLE_EFFORT_DEFAULTS[role];
6790
6860
  const { adapterId, model } = parseModelRef(merged.model);
6791
- const caps = options.capsOf(merged.model);
6861
+ let caps;
6862
+ try {
6863
+ caps = options.capsOf(merged.model);
6864
+ } catch (thrown) {
6865
+ if (thrown instanceof ConfigError) throw new ConfigError(`role '${role}': ${thrown.message}`);
6866
+ throw thrown;
6867
+ }
6792
6868
  const scrubs = [];
6793
6869
  let wireEffort = requestedEffort;
6794
6870
  if (wireEffort !== void 0 && !caps.reasoningEfforts.includes(wireEffort)) {
@@ -7213,7 +7289,7 @@ function ruleMatches(rule, toolName, risk, input) {
7213
7289
  }
7214
7290
  /**
7215
7291
  * Advisory domain-rule matches for the audit payload:
7216
- * reported, never enforced outside first-party fetch.
7292
+ * reported, never enforced in the current release.
7217
7293
  */
7218
7294
  function advisoryMatches(chain, toolName) {
7219
7295
  return [...chain.deny, ...chain.ask].filter((rule) => "domains" in rule && rule.tool === toolName);
@@ -7701,6 +7777,7 @@ async function runAgent(options) {
7701
7777
  }]
7702
7778
  }];
7703
7779
  let totalUsage = ZERO_USAGE$1;
7780
+ const usageByModel = /* @__PURE__ */ new Map();
7704
7781
  let turns = 0;
7705
7782
  let schemaAttempts = 0;
7706
7783
  let output = null;
@@ -7730,8 +7807,31 @@ async function runAgent(options) {
7730
7807
  toolCallsUsed = restored.toolCallsUsed;
7731
7808
  schemaAttempts = restored.schemaAttempts;
7732
7809
  compactionPoints.push(...restored.compaction);
7733
- options.budget?.onUsage(restored.usage, servedBy);
7810
+ const restoredSlices = restored.usageByModel ?? [{
7811
+ servedBy,
7812
+ usage: restored.usage
7813
+ }];
7814
+ for (const slice of restoredSlices) {
7815
+ usageByModel.set(slice.servedBy, addUsage(usageByModel.get(slice.servedBy) ?? ZERO_USAGE$1, slice.usage));
7816
+ options.budget?.onUsage(slice.usage, slice.servedBy);
7817
+ }
7734
7818
  }
7819
+ const usageSlices = () => [...usageByModel].map(([sliceServedBy, usage]) => ({
7820
+ servedBy: sliceServedBy,
7821
+ usage
7822
+ }));
7823
+ /**
7824
+ * Every slice priced at ITS OWN model's rate. An unpriced model
7825
+ * contributes zero here and surfaces through CostReport.unpriced, never
7826
+ * as a silent zero.
7827
+ */
7828
+ const priceRecordedUsage = () => {
7829
+ const price = options.priceUsd;
7830
+ if (price === void 0) return 0;
7831
+ let usd = 0;
7832
+ for (const [sliceServedBy, usage] of usageByModel) usd += price(sliceServedBy, usage) ?? 0;
7833
+ return usd;
7834
+ };
7735
7835
  const saveBoundary = async (pending) => {
7736
7836
  if (options.checkpoint === void 0) return;
7737
7837
  await options.checkpoint.save({
@@ -7739,6 +7839,7 @@ async function runAgent(options) {
7739
7839
  messages: [...messages],
7740
7840
  turns,
7741
7841
  usage: totalUsage,
7842
+ usageByModel: usageSlices(),
7742
7843
  toolCallsUsed,
7743
7844
  schemaAttempts,
7744
7845
  compaction: [...compactionPoints],
@@ -7852,7 +7953,7 @@ async function runAgent(options) {
7852
7953
  continue;
7853
7954
  }
7854
7955
  const request = validation.value;
7855
- const spentSoFar = options.priceUsd?.(servedBy, totalUsage) ?? 0;
7956
+ const spentSoFar = priceRecordedUsage();
7856
7957
  if (countsAgainstLimit(request.kind) && spentSoFar < options.escalation.minSpendUsd) {
7857
7958
  events?.emit({
7858
7959
  type: "tool:end",
@@ -7971,6 +8072,7 @@ async function runAgent(options) {
7971
8072
  invariantViolation = thrown instanceof Error ? thrown.message : String(thrown);
7972
8073
  }
7973
8074
  totalUsage = addUsage(totalUsage, usage);
8075
+ usageByModel.set(ref, addUsage(usageByModel.get(ref) ?? ZERO_USAGE$1, usage));
7974
8076
  const remainder = {
7975
8077
  inputTokens: Math.max(0, usage.inputTokens - reported.inputTokens),
7976
8078
  outputTokens: Math.max(0, usage.outputTokens - reported.outputTokens),
@@ -8550,7 +8652,7 @@ async function runAgent(options) {
8550
8652
  const blob = new TextEncoder().encode(JSON.stringify({ messages }));
8551
8653
  await options.transcript.put(transcriptRef, blob);
8552
8654
  }
8553
- const costUsd = options.priceUsd?.(servedBy, totalUsage) ?? 0;
8655
+ const costUsd = priceRecordedUsage();
8554
8656
  const result = {
8555
8657
  status,
8556
8658
  output: status === "ok" ? output : output ?? null,
@@ -8560,6 +8662,7 @@ async function runAgent(options) {
8560
8662
  servedBy,
8561
8663
  transcriptRef
8562
8664
  };
8665
+ if (usageByModel.size > 1) result.usageByModel = usageSlices();
8563
8666
  if (agentError !== void 0) result.error = agentError;
8564
8667
  if (escalationRequest !== void 0) result.escalationRequest = escalationRequest;
8565
8668
  if (abortClass !== void 0) result.abortClass = abortClass;
@@ -8626,6 +8729,8 @@ var RunBudget = class {
8626
8729
  usageInternal = { ...ZERO_USAGE };
8627
8730
  agentsSpawnedInternal = 0;
8628
8731
  exhaustedInternal = false;
8732
+ /** Models already warned about; the warning fires once per model per run. */
8733
+ unpricedWarned = /* @__PURE__ */ new Set();
8629
8734
  constructor(options) {
8630
8735
  if (options.ceilingUsd !== void 0) this.ceilingUsd = options.ceilingUsd;
8631
8736
  this.lifetimeSpawnCap = options.lifetimeSpawnCap ?? 500;
@@ -8830,7 +8935,16 @@ var RunBudget = class {
8830
8935
  };
8831
8936
  const reasoning = (this.usageInternal.reasoningTokens ?? 0) + (usage.reasoningTokens ?? 0);
8832
8937
  if (reasoning > 0) this.usageInternal.reasoningTokens = reasoning;
8833
- const usd = this.priceUsd?.(servedBy, usage) ?? 0;
8938
+ const priced = this.priceUsd?.(servedBy, usage);
8939
+ if (priced === void 0 && this.ceilingUsd !== void 0 && !this.unpricedWarned.has(servedBy)) {
8940
+ this.unpricedWarned.add(servedBy);
8941
+ this.events?.emit({
8942
+ type: "log",
8943
+ level: "warn",
8944
+ msg: `no price row for '${servedBy}': its usage does not debit the budget, so the ${this.ceilingUsd} USD run ceiling does NOT bound this model. Add it to createEngine({ pricing }) to cap it; its usage is reported under CostReport.unpriced`
8945
+ });
8946
+ }
8947
+ const usd = priced ?? 0;
8834
8948
  for (const account of this.chainOf(accountScope)) {
8835
8949
  account.spentUsd += usd;
8836
8950
  if (account.ceilingUsd !== void 0 && account.spentUsd >= account.ceilingUsd && !account.controller.signal.aborted) {
@@ -9535,11 +9649,22 @@ var AgentCallError = class extends Error {
9535
9649
  if (entryRef !== void 0) this.entryRef = entryRef;
9536
9650
  }
9537
9651
  };
9652
+ /** The workflow-defaults layer a Workflow value contributes, or nothing. */
9653
+ function workflowLayerOf(wf) {
9654
+ const layer = {};
9655
+ if (wf.model !== void 0) layer.model = wf.model;
9656
+ if (wf.routing !== void 0) layer.routing = wf.routing;
9657
+ if (wf.effort !== void 0) layer.effort = wf.effort;
9658
+ return Object.keys(layer).length === 0 ? void 0 : layer;
9659
+ }
9538
9660
  function defineWorkflow(meta, body) {
9539
9661
  const wf = {
9540
9662
  kind: "workflow",
9541
9663
  name: meta.name,
9542
9664
  errorPolicy: meta.errorPolicy ?? "strict",
9665
+ ...meta.model === void 0 ? {} : { model: meta.model },
9666
+ ...meta.routing === void 0 ? {} : { routing: meta.routing },
9667
+ ...meta.effort === void 0 ? {} : { effort: meta.effort },
9543
9668
  body
9544
9669
  };
9545
9670
  if (meta.args !== void 0) return {
@@ -9581,19 +9706,24 @@ function buildEscalationReport(request, result, worktreePatchRef) {
9581
9706
  * one ctx object while journaling under their own scope paths (I3:
9582
9707
  * structure from call-and-return only).
9583
9708
  */
9584
- function createCtx(internals) {
9709
+ function createCtx(internals, rootWorkflow) {
9585
9710
  const als = new AsyncLocalStorage();
9586
9711
  const sites = new ParallelSiteCounter();
9712
+ const rootWorkflowLayer = rootWorkflow === void 0 ? void 0 : workflowLayerOf(rootWorkflow);
9587
9713
  const rootState = {
9588
9714
  scope: "",
9589
- spanId: internals.rootSpanId
9715
+ spanId: internals.rootSpanId,
9716
+ ...rootWorkflowLayer === void 0 ? {} : { workflowLayer: rootWorkflowLayer }
9590
9717
  };
9591
9718
  const current = () => als.getStore() ?? rootState;
9592
9719
  const capsOf = (ref) => {
9593
9720
  const colon = ref.indexOf(":");
9594
9721
  const adapterId = ref.slice(0, colon);
9595
9722
  const adapter = internals.adapters.get(adapterId);
9596
- if (adapter === void 0) throw new ConfigError(`no adapter registered for '${adapterId}' (ModelRef '${ref}'); pass it to createEngine`);
9723
+ if (adapter === void 0) {
9724
+ const registered = [...internals.adapters.keys()].sort();
9725
+ throw new ConfigError(`no adapter registered for '${adapterId}' (ModelRef '${ref}'); registered: ${registered.length === 0 ? "(none)" : registered.join(", ")}. Pass the adapter to createEngine, or route this role to a registered adapter through defaults.routing`);
9726
+ }
9597
9727
  return adapter.caps(ref.slice(colon + 1));
9598
9728
  };
9599
9729
  const adapterOf = (resolved) => {
@@ -9686,6 +9816,7 @@ function createCtx(internals) {
9686
9816
  if (profile?.effort !== void 0) profileLayer.effort = profile.effort;
9687
9817
  const engineLayer = {};
9688
9818
  if (internals.defaults.routing !== void 0) engineLayer.routing = internals.defaults.routing;
9819
+ const workflowLayer = state.workflowLayer;
9689
9820
  const telemetryNamespace = { agentType };
9690
9821
  if (opts.label !== void 0) telemetryNamespace.label = opts.label;
9691
9822
  const withTelemetry = (resolved) => ({
@@ -9700,6 +9831,7 @@ function createCtx(internals) {
9700
9831
  role: primaryRole,
9701
9832
  call: callLayer,
9702
9833
  profile: profileLayer,
9834
+ workflow: workflowLayer,
9703
9835
  engine: engineLayer,
9704
9836
  capsOf,
9705
9837
  ...floorContext
@@ -9736,6 +9868,7 @@ function createCtx(internals) {
9736
9868
  role: "extract",
9737
9869
  call: callLayer,
9738
9870
  profile: profileLayer,
9871
+ workflow: workflowLayer,
9739
9872
  engine: engineLayer,
9740
9873
  capsOf,
9741
9874
  ...floorContext
@@ -9769,6 +9902,7 @@ function createCtx(internals) {
9769
9902
  role: "finalize",
9770
9903
  call: callLayer,
9771
9904
  profile: profileLayer,
9905
+ workflow: workflowLayer,
9772
9906
  engine: engineLayer,
9773
9907
  capsOf,
9774
9908
  ...floorContext
@@ -9789,6 +9923,7 @@ function createCtx(internals) {
9789
9923
  role: "summarize",
9790
9924
  call: callLayer,
9791
9925
  profile: profileLayer,
9926
+ workflow: workflowLayer,
9792
9927
  engine: engineLayer,
9793
9928
  capsOf,
9794
9929
  ...floorContext
@@ -9798,6 +9933,7 @@ function createCtx(internals) {
9798
9933
  role: "summarize",
9799
9934
  call: callLayer,
9800
9935
  profile: profileLayer,
9936
+ workflow: workflowLayer,
9801
9937
  engine: {
9802
9938
  ...engineLayer,
9803
9939
  model: loopResolved.ref
@@ -9817,6 +9953,7 @@ function createCtx(internals) {
9817
9953
  role,
9818
9954
  call: fallbackLayer,
9819
9955
  profile: profileLayer,
9956
+ workflow: workflowLayer,
9820
9957
  engine: engineLayer,
9821
9958
  capsOf,
9822
9959
  ...floorContext
@@ -9861,7 +9998,8 @@ function createCtx(internals) {
9861
9998
  cacheReadTokens: 0,
9862
9999
  cacheWriteTokens: 0
9863
10000
  };
9864
- const costUsd = terminal?.servedBy === void 0 ? 0 : internals.priceUsd(terminal.servedBy, usage) ?? 0;
10001
+ const replayPriced = terminal === void 0 ? void 0 : priceEntryUsage(terminal, (ref, sliceUsage) => internals.priceUsd(ref, sliceUsage));
10002
+ const costUsd = replayPriced?.usd ?? 0;
9865
10003
  const result = {
9866
10004
  status: matched.kind === "skip" ? "skipped" : terminal?.status ?? "ok",
9867
10005
  output: matched.kind === "skip" ? null : terminal?.value ?? null,
@@ -9918,7 +10056,11 @@ function createCtx(internals) {
9918
10056
  costUsd,
9919
10057
  entryRef: terminal?.seq ?? matched.running.seq
9920
10058
  }, spanId, true);
9921
- bump(internals.cost.byModel, terminal?.servedBy ?? loopResolved.ref, costUsd);
10059
+ for (const slice of replayPriced?.priced ?? []) bump(internals.cost.byModel, slice.servedBy, slice.usd);
10060
+ for (const slice of replayPriced?.unpriced ?? []) internals.cost.unpriced.push({
10061
+ model: slice.servedBy,
10062
+ usage: slice.usage
10063
+ });
9922
10064
  bump(internals.cost.byPhase, state.phase ?? "", costUsd);
9923
10065
  bump(internals.cost.byAgentType, agentType, costUsd);
9924
10066
  internals.cost.byRole.set(primaryRole, (internals.cost.byRole.get(primaryRole) ?? 0) + costUsd);
@@ -9977,7 +10119,15 @@ function createCtx(internals) {
9977
10119
  if (prior !== void 0) {
9978
10120
  claimed.add(prior.seq);
9979
10121
  const recorded = prior.value;
9980
- if (recorded.reject !== void 0) throw new AdmissionRejectedError(`lineage admission rejected agent spawn (${recorded.reject.code}; recorded verdict)`, { data: { reason: recorded.reject } });
10122
+ if (recorded.reject !== void 0) {
10123
+ internals.events.emit({
10124
+ type: "spawn:rejected",
10125
+ entryRef: prior.seq,
10126
+ code: recorded.reject.code,
10127
+ agentType
10128
+ }, state.spanId, true);
10129
+ throw new AdmissionRejectedError(`lineage admission rejected agent spawn (${recorded.reject.code}; recorded verdict)`, { data: { reason: recorded.reject } });
10130
+ }
9981
10131
  } else {
9982
10132
  const evaluated = admission.evaluateLineage({
9983
10133
  name: agentType,
@@ -10000,7 +10150,7 @@ function createCtx(internals) {
10000
10150
  };
10001
10151
  if (evaluated.decision.kind === "reject") decisionValue.reject = { code: evaluated.decision.reason.code };
10002
10152
  else decisionValue.lineage = evaluated.decision.lineage;
10003
- await internals.replayer.appendSinglePhase({
10153
+ const decisionEntry = await internals.replayer.appendSinglePhase({
10004
10154
  scope: state.scope,
10005
10155
  key: "",
10006
10156
  kind: "decision",
@@ -10008,7 +10158,15 @@ function createCtx(internals) {
10008
10158
  spanId: internals.spans.mint(state.spanId),
10009
10159
  value: decisionValue
10010
10160
  });
10011
- if (evaluated.decision.kind === "reject") throw new AdmissionRejectedError(`lineage admission rejected agent spawn (${evaluated.decision.reason.code})`, { data: { reason: evaluated.decision.reason } });
10161
+ if (evaluated.decision.kind === "reject") {
10162
+ internals.events.emit({
10163
+ type: "spawn:rejected",
10164
+ entryRef: decisionEntry.seq,
10165
+ code: evaluated.decision.reason.code,
10166
+ agentType
10167
+ }, state.spanId);
10168
+ throw new AdmissionRejectedError(`lineage admission rejected agent spawn (${evaluated.decision.reason.code})`, { data: { reason: evaluated.decision.reason } });
10169
+ }
10012
10170
  admission.registerLineageAdmit(evaluated.decision.lineage.logicalTaskId);
10013
10171
  }
10014
10172
  }
@@ -10112,7 +10270,12 @@ function createCtx(internals) {
10112
10270
  data
10113
10271
  }, toolSpanId)
10114
10272
  });
10115
- const chain = compilePermissionChain(internals.defaults.permissions, profile?.permissions);
10273
+ const compiledChain = compilePermissionChain(internals.defaults.permissions, profile?.permissions);
10274
+ const readonlyDeny = { risk: ["write", "destructive"] };
10275
+ const chain = isolation === "readonly" ? {
10276
+ ...compiledChain,
10277
+ deny: [...compiledChain.deny, readonlyDeny]
10278
+ } : compiledChain;
10116
10279
  toolRuntime = {
10117
10280
  defs: toolset.tools,
10118
10281
  contracts: toolset.contracts,
@@ -10297,6 +10460,7 @@ function createCtx(internals) {
10297
10460
  status: result.status === "skipped" ? "error" : result.status,
10298
10461
  usage: result.usage,
10299
10462
  servedBy: result.servedBy,
10463
+ ...result.usageByModel === void 0 ? {} : { usageByModel: result.usageByModel },
10300
10464
  transcriptRef: result.transcriptRef
10301
10465
  };
10302
10466
  if (result.status === "escalated" && result.escalation !== void 0) terminalPatch.escalation = result.escalation;
@@ -10347,14 +10511,23 @@ function createCtx(internals) {
10347
10511
  });
10348
10512
  }
10349
10513
  const usd = result.costUsd;
10350
- bump(internals.cost.byModel, loopResolved.ref, usd);
10514
+ for (const slice of result.usageByModel ?? [{
10515
+ servedBy: result.servedBy,
10516
+ usage: result.usage
10517
+ }]) {
10518
+ const priced = internals.priceUsd(slice.servedBy, slice.usage);
10519
+ if (priced === void 0) {
10520
+ internals.cost.unpriced.push({
10521
+ model: slice.servedBy,
10522
+ usage: slice.usage
10523
+ });
10524
+ continue;
10525
+ }
10526
+ bump(internals.cost.byModel, slice.servedBy, priced);
10527
+ }
10351
10528
  bump(internals.cost.byPhase, state.phase ?? "", usd);
10352
10529
  bump(internals.cost.byAgentType, agentType, usd);
10353
10530
  internals.cost.byRole.set(primaryRole, (internals.cost.byRole.get(primaryRole) ?? 0) + usd);
10354
- if (internals.priceUsd(loopResolved.ref, result.usage) === void 0) internals.cost.unpriced.push({
10355
- model: loopResolved.ref,
10356
- usage: result.usage
10357
- });
10358
10531
  if (result.error?.kind === "budget" || internals.budget.exhausted && result.status !== "ok") throw new BudgetExhaustedError("run budget ceiling reached during agent execution", { data: {
10359
10532
  scope: state.scope,
10360
10533
  entryRef: terminal.seq
@@ -10657,10 +10830,12 @@ function createCtx(internals) {
10657
10830
  site: `ctx.workflow('${name}')`
10658
10831
  });
10659
10832
  const signals = [state.signal ?? internals.runSignal, internals.budget.signalOf(childScope)].filter((signal) => signal !== void 0);
10833
+ const childLayer = workflowLayerOf(wf);
10660
10834
  const childState = {
10661
10835
  scope: childScope,
10662
10836
  spanId,
10663
- budgetScope: childScope
10837
+ budgetScope: childScope,
10838
+ ...childLayer === void 0 ? {} : { workflowLayer: childLayer }
10664
10839
  };
10665
10840
  if (signals.length === 1) childState.signal = signals[0];
10666
10841
  else if (signals.length > 1) childState.signal = AbortSignal.any(signals);
@@ -10831,7 +11006,7 @@ async function executeWorkflow(internals, wf, args) {
10831
11006
  const validation = await validateSchemaSpec(wf.argsSchema, args);
10832
11007
  if (!validation.valid) throw new ConfigError(`arguments for workflow '${wf.name}' do not validate: ` + validation.issues.map((issue) => issue.message).join("; "), { data: { issues: validation.issues.map((issue) => issue.message) } });
10833
11008
  }
10834
- const ctx = createCtx(internals);
11009
+ const ctx = createCtx(internals, wf);
10835
11010
  try {
10836
11011
  return await wf.body(ctx, args);
10837
11012
  } finally {
@@ -11278,7 +11453,14 @@ function makeOrchestratorWorkflow(goal, opts) {
11278
11453
  if (recovered !== void 0) return { handle: recovered.handle };
11279
11454
  const recoveredRejection = rejectedByOrdinal.get(spawnOrdinal);
11280
11455
  if (recoveredRejection !== void 0) throw new AdmissionRejectedError(`admission rejected spawn ordinal ${String(spawnOrdinal)} (recovered verdict)`, { data: { decision: recoveredRejection } });
11281
- if (opts?.maxSpawns !== void 0 && spawnOrdinal >= opts.maxSpawns) throw new AdmissionRejectedError(`orchestrate maxSpawns ${String(opts.maxSpawns)} reached`, { data: { reason: { code: "lifetime" } } });
11456
+ if (opts?.maxSpawns !== void 0 && spawnOrdinal >= opts.maxSpawns) {
11457
+ internals.events.emit({
11458
+ type: "spawn:rejected",
11459
+ code: "lifetime",
11460
+ agentType: params.agentType
11461
+ }, callingState.spanId);
11462
+ throw new AdmissionRejectedError(`orchestrate maxSpawns ${String(opts.maxSpawns)} reached`, { data: { reason: { code: "lifetime" } } });
11463
+ }
11282
11464
  const scope = childScopeOf();
11283
11465
  const profile = internals.defaults.profiles?.[params.agentType];
11284
11466
  const profileModel = profile?.model;
@@ -11312,7 +11494,7 @@ function makeOrchestratorWorkflow(goal, opts) {
11312
11494
  spec: params,
11313
11495
  decision
11314
11496
  };
11315
- await internals.replayer.appendSinglePhase({
11497
+ const decisionEntry = await internals.replayer.appendSinglePhase({
11316
11498
  scope: callingState.scope,
11317
11499
  key: "",
11318
11500
  kind: "decision",
@@ -11322,11 +11504,19 @@ function makeOrchestratorWorkflow(goal, opts) {
11322
11504
  });
11323
11505
  if (decision.verdict.kind === "reject") {
11324
11506
  rejectedByOrdinal.set(spawnOrdinal, decision);
11507
+ internals.events.emit({
11508
+ type: "spawn:rejected",
11509
+ entryRef: decisionEntry.seq,
11510
+ code: decision.verdict.reason.code,
11511
+ agentType: params.agentType
11512
+ }, callingState.spanId);
11325
11513
  throw new AdmissionRejectedError(`admission rejected spawn_agent '${params.agentType}' (${decision.verdict.reason.code})`, { data: { reason: decision.verdict.reason } });
11326
11514
  }
11327
11515
  if (decision.verdict.kind !== "admit") throw new ConfigError(`admission verdict '${decision.verdict.kind}' has no producer before M7 (DEF-5)`);
11328
11516
  internals.events.emit({
11329
11517
  type: "spawn:admitted",
11518
+ entryRef: decisionEntry.seq,
11519
+ verdict: decision.verdict.kind,
11330
11520
  agentType: params.agentType,
11331
11521
  logicalTaskId: decision.verdict.lineage.logicalTaskId,
11332
11522
  spawnUnitsAfter: decision.verdict.spawnUnitsAfter
@@ -11740,8 +11930,12 @@ var InProcessRunner = class {
11740
11930
  const priorRandom = Math.random;
11741
11931
  let warnedNow = false;
11742
11932
  let warnedRandom = false;
11933
+ const libraryCaller = () => {
11934
+ const caller = (/* @__PURE__ */ new Error()).stack?.split("\n")[3];
11935
+ return caller !== void 0 && caller.includes("node_modules");
11936
+ };
11743
11937
  Date.now = function rulvarPatchedDateNow() {
11744
- if (!warnedNow) {
11938
+ if (!warnedNow && !libraryCaller()) {
11745
11939
  warnedNow = true;
11746
11940
  process.emitWarning("bare Date.now() called inside a rulvar run; use ctx.now() so the value is journaled and stable on replay", {
11747
11941
  code: "RULVAR_BARE_DATE_NOW",
@@ -11751,7 +11945,7 @@ var InProcessRunner = class {
11751
11945
  return priorNow();
11752
11946
  };
11753
11947
  Math.random = function rulvarPatchedMathRandom() {
11754
- if (!warnedRandom) {
11948
+ if (!warnedRandom && !libraryCaller()) {
11755
11949
  warnedRandom = true;
11756
11950
  process.emitWarning("bare Math.random() called inside a rulvar run; use ctx.random() so the value is journaled and stable on replay", {
11757
11951
  code: "RULVAR_BARE_MATH_RANDOM",
@@ -11829,8 +12023,9 @@ function createEngine(options) {
11829
12023
  });
11830
12024
  const rootSpanId = spans.mint();
11831
12025
  let budgetSeed;
12026
+ const ceilingUsd = opts?.budgetUsd ?? resumeCtx?.budgetUsd;
11832
12027
  const makeBudget = () => new RunBudget({
11833
- ...opts?.budgetUsd === void 0 ? {} : { ceilingUsd: opts.budgetUsd },
12028
+ ...ceilingUsd === void 0 ? {} : { ceilingUsd },
11834
12029
  lifetimeSpawnCap: options.budgetDefaults?.lifetimeSpawnCap ?? 500,
11835
12030
  events: { emit: (body) => bus.emit(body, rootSpanId) },
11836
12031
  priceUsd,
@@ -11889,7 +12084,7 @@ function createEngine(options) {
11889
12084
  ...options.budgetDefaults?.lineage === void 0 ? {} : { limits: options.budgetDefaults.lineage }
11890
12085
  }
11891
12086
  });
11892
- const external = new ExternalRegistry(replayer);
12087
+ const external = new ExternalRegistry(replayer, (body) => bus.emit(body, rootSpanId));
11893
12088
  let transcriptCounter = 0;
11894
12089
  const internals = {
11895
12090
  runId,
@@ -11950,6 +12145,7 @@ function createEngine(options) {
11950
12145
  updatedAt: new Date(realNow()).toISOString(),
11951
12146
  ...opts?.name === void 0 ? {} : { name: opts.name },
11952
12147
  ...opts?.tags === void 0 ? {} : { tags: opts.tags },
12148
+ ...ceilingUsd === void 0 ? {} : { budgetUsd: ceilingUsd },
11953
12149
  workflowName: wf.name,
11954
12150
  workflowHash: compiled === void 0 ? hashWorkflowBody(wf) : hashWorkflowSource(compiled.source),
11955
12151
  ...compiled === void 0 ? {} : { workflowSourceRef: workflowSourceRef(runId) }
@@ -11983,7 +12179,7 @@ function createEngine(options) {
11983
12179
  const validation = await validateSchemaSpec(wf.argsSchema, args);
11984
12180
  if (!validation.valid) throw new ConfigError(`arguments for workflow '${wf.name}' do not validate: ` + validation.issues.map((issue) => issue.message).join("; "));
11985
12181
  }
11986
- const ctx = createCtx(internals);
12182
+ const ctx = createCtx(internals, wf.kind === "workflow" ? wf : void 0);
11987
12183
  const bodyPromise = (compiled === void 0 ? runner : options.runners?.sandbox).execute(wf, ctx, args);
11988
12184
  const raced = await Promise.race([bodyPromise.then((result) => ({
11989
12185
  kind: "done",
@@ -12125,6 +12321,7 @@ function createEngine(options) {
12125
12321
  strict: resumeOptions?.dryRun ?? false,
12126
12322
  invalidate: resumeOptions?.invalidate ?? [],
12127
12323
  ...resumeOptions?.lease === void 0 ? {} : { lease: resumeOptions.lease },
12324
+ ...typeof meta?.budgetUsd === "number" ? { budgetUsd: meta.budgetUsd } : {},
12128
12325
  previewResolve
12129
12326
  });
12130
12327
  })();
@@ -12478,4 +12675,4 @@ function createSandboxBridge(ctx, options) {
12478
12675
  };
12479
12676
  }
12480
12677
  //#endregion
12481
- 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_CHILD_BUDGET_FRACTION, DEFAULT_COMPACTION_THRESHOLD, DEFAULT_ESCALATION_LIMITS, 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, DedupIndex, EMIT_RESULT_TOOL, EMPTY_SCHEMA_HASH, EMPTY_TOOLSET_HASH, ESCALATE_TOOL_NAME, ESCALATION_REPORT_SCHEMA, ESCALATION_REQUEST_SCHEMA, EventBus, ExternalRegistry, FINISH_SCHEMA, FINISH_TOOL_NAME, FileModelKnowledgeStore, FileTranscriptStore, GitWorktreeProvider, INBOX_PROPOSAL_TTL_DAYS, InMemoryStore, InMemoryTranscriptStore, InProcessRunner, InvalidResolutionError, 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_DEPTH_CEILING, ModelRetry, NoProgressDetector, NonSerializableValueError, ORCHESTRATE_WORKFLOW_NAME, OrchestratorCapConfigError, PARALLEL_AGENTS_SCHEMA, ParallelSiteCounter, PlanInvariantError, ROLE_EFFORT_DEFAULTS, ROOT_ACCOUNT, ROOT_SCOPE, RUN_PROFILES, ReplayPlanHashMismatch, Replayer, ResolutionArbiter, ResolutionFold, RulvarError, RunBudget, SPAWN_AGENT_SCHEMA, SandboxError, ScriptRejected, Semaphore, SpanRegistry, TOOL_NAME_PATTERN, TerminationAccount, WAIT_FOR_EVENTS_SCHEMA, WAIT_FOR_EVENTS_TOOL_NAME, WAKE_SUMMARY_RENDER_BUDGET_CHARS, admissionReserveUsd, agentErrorFromWire, agentErrorToWire, agentScope, applyClaimOps, applyStructuredOutputTier, approachSigCoarse, approachSigOf, archiveDeprecatedModelOps, atCompactionThreshold, 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, compileVerifiedLayer, costReportFromJournal, countsAgainstLimit, createCanonicalIdMinter, createCtx, createEngine, createSandboxBridge, currentOnlyKeyRing, decodeCheckpoint, defineWorkflow, deriveContentKey, deriverV1, deriverV2, digestOf, dispositionHook, emptyDigestBlocks, emptyToolset, encodeCheckpoint, escalateTool, evaluatePermission, evaluateReuse, executeWorkflow, exhaustionCodeOf, extractCandidate, failoverTriggerOf, fallbackTriggerOf, filterClaimsForRun, finalizeFires, foldTermination, formatRePrompt, formatScopePath, hashWorkflowBody, hashWorkflowSource, identityJcs, isEscalated, isSchemaPairSpec, isStandardSchemaSpec, isStrictCompatibleSchema, kMaxOf, knowledgeHash, ladderLengthOf, ladderRungChoice, lexShellCommand, liftRetainedParts, lineageWeightOf, makeOrchestratorWorkflow, maskSecrets, maskSecretsDeep, maskSecretsJson, matchArgvPattern, matchShellCommand, mcp, mergeUsageLimits, modelEpochOf, modelKnowledgeCard, modelSpecIdentity, needsSeparateExtract, nextFailover, nodeLinkKey, normalizeApproachTag, normalizeEntry, normalizeFallbacks, orchestrate, parallelScope, parseModelRef, parseScopePath, phiInitialOf, pipelineScope, planNodeScope, priceUsdOf, profileCard, profileRegistrySnapshotHash, projectHistory, projectIdentity, projectToJsonSchema, proposalStatement, providerOf, readTerminationInit, registryKeyRing, remeasureQueue, replayDisposition, resolveModelInvocation, resolvePricing, resolveToolset, retryClassOf, retryDelayMs, roleConfiguredInRouting, roundOneDisposition, runAgent, runProfile, scanJournalCompatibility, schemaHash, schemaHashOfSpec, selectStructuredOutputTier, shouldCompact, spawnDepthOf, summarizeInstruction, summarizeOutput, terminationConfigDrift, tierWithinCaps, toApprovalDecision, toJournalValue, tool, toolContract, toolsetHash, ttlState, validateEditorialCommit, validateEntryShape, validateEscalationLimits, validateEscalationReport, validateSchemaSpec, validateTerminationLimits, workflowScope, workflowSourceRef, wrapJournalStore, wrapTranscriptStore };
12678
+ 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_CHILD_BUDGET_FRACTION, DEFAULT_COMPACTION_THRESHOLD, DEFAULT_ESCALATION_LIMITS, 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, DedupIndex, EMIT_RESULT_TOOL, EMPTY_SCHEMA_HASH, EMPTY_TOOLSET_HASH, ESCALATE_TOOL_NAME, ESCALATION_REPORT_SCHEMA, ESCALATION_REQUEST_SCHEMA, EventBus, ExternalRegistry, FINISH_SCHEMA, FINISH_TOOL_NAME, FileModelKnowledgeStore, FileTranscriptStore, GitWorktreeProvider, INBOX_PROPOSAL_TTL_DAYS, InMemoryStore, InMemoryTranscriptStore, InProcessRunner, InvalidResolutionError, 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_DEPTH_CEILING, ModelRetry, NoProgressDetector, NonSerializableValueError, ORCHESTRATE_WORKFLOW_NAME, OrchestratorCapConfigError, PARALLEL_AGENTS_SCHEMA, ParallelSiteCounter, PlanInvariantError, ROLE_EFFORT_DEFAULTS, ROOT_ACCOUNT, ROOT_SCOPE, RUN_PROFILES, ReplayPlanHashMismatch, Replayer, ResolutionArbiter, ResolutionFold, RulvarError, RunBudget, SPAWN_AGENT_SCHEMA, SandboxError, ScriptRejected, Semaphore, SpanRegistry, TOOL_NAME_PATTERN, TerminationAccount, WAIT_FOR_EVENTS_SCHEMA, WAIT_FOR_EVENTS_TOOL_NAME, WAKE_SUMMARY_RENDER_BUDGET_CHARS, admissionReserveUsd, agentErrorFromWire, agentErrorToWire, agentScope, applyClaimOps, applyStructuredOutputTier, approachSigCoarse, approachSigOf, archiveDeprecatedModelOps, atCompactionThreshold, 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, compileVerifiedLayer, costReportFromJournal, countsAgainstLimit, createCanonicalIdMinter, createCtx, createEngine, createSandboxBridge, currentOnlyKeyRing, decodeCheckpoint, defineWorkflow, deriveContentKey, deriverV1, deriverV2, digestOf, dispositionHook, emptyDigestBlocks, emptyToolset, encodeCheckpoint, entryUsageSlices, escalateTool, evaluatePermission, evaluateReuse, executeWorkflow, exhaustionCodeOf, extractCandidate, failoverTriggerOf, fallbackTriggerOf, filterClaimsForRun, finalizeFires, foldTermination, formatRePrompt, formatScopePath, hashWorkflowBody, hashWorkflowSource, identityJcs, isEscalated, isSchemaPairSpec, isStandardSchemaSpec, isStrictCompatibleSchema, kMaxOf, knowledgeHash, ladderLengthOf, ladderRungChoice, lexShellCommand, liftRetainedParts, lineageWeightOf, makeOrchestratorWorkflow, maskSecrets, maskSecretsDeep, maskSecretsJson, matchArgvPattern, matchShellCommand, mcp, mergeUsageLimits, modelEpochOf, modelKnowledgeCard, modelSpecIdentity, needsSeparateExtract, nextFailover, nodeLinkKey, normalizeApproachTag, normalizeEntry, normalizeFallbacks, orchestrate, parallelScope, parseModelRef, parseScopePath, phiInitialOf, pipelineScope, planNodeScope, priceEntryUsage, priceUsdOf, profileCard, profileRegistrySnapshotHash, projectHistory, projectIdentity, projectToJsonSchema, proposalStatement, providerOf, readTerminationInit, registryKeyRing, remeasureQueue, replayDisposition, resolveModelInvocation, resolvePricing, resolveToolset, retryClassOf, retryDelayMs, roleConfiguredInRouting, roundOneDisposition, runAgent, runProfile, scanJournalCompatibility, schemaHash, schemaHashOfSpec, selectStructuredOutputTier, shouldCompact, spawnDepthOf, summarizeInstruction, summarizeOutput, terminationConfigDrift, tierWithinCaps, toApprovalDecision, toJournalValue, tool, toolContract, toolsetHash, ttlState, validateEditorialCommit, validateEntryShape, validateEscalationLimits, validateEscalationReport, validateSchemaSpec, validateTerminationLimits, workflowScope, workflowSourceRef, wrapJournalStore, wrapTranscriptStore };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/core",
3
- "version": "1.3.2",
3
+ "version": "1.5.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",