@rulvar/core 1.56.0 → 1.58.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
@@ -1035,6 +1035,90 @@ declare function maskSecrets(text: string): string;
1035
1035
  declare function maskSecretsDeep<T>(value: T): T;
1036
1036
  /** Convenience for hosts: masks a Json value (alias of the deep walk). */
1037
1037
  declare function maskSecretsJson(value: Json): Json;
1038
+ /** A compiled masking policy: text and deep-JSON forms of one pattern set. */
1039
+ interface SecretMasker {
1040
+ maskText(text: string): string;
1041
+ maskDeep<T>(value: T): T;
1042
+ }
1043
+ /**
1044
+ * Compiles the redaction policy: the DEFAULT credential pattern set
1045
+ * plus host-defined patterns (RV-217), for the telemetry boundary
1046
+ * (events and traces; never the journal, where lossless encryption is
1047
+ * the right tool). String patterns compile as global regexes; RegExp
1048
+ * patterns are recompiled with the global flag when it is missing, so
1049
+ * replace-all semantics always hold. An invalid pattern is a typed
1050
+ * ConfigError at compile time, before anything runs under the policy.
1051
+ */
1052
+ declare function compileSecretMasker(patterns?: ReadonlyArray<RegExp | string>, site?: string): SecretMasker;
1053
+ //#endregion
1054
+ //#region src/l0/encryption.d.ts
1055
+ /**
1056
+ * The KMS seam. `keyId` is a stable routing id stamped into every
1057
+ * envelope (a KMS key ARN or alias, or a local rotation label); the
1058
+ * two methods are the exact shape of KMS GenerateDataKey and Decrypt.
1059
+ * Both are called only inside `createEnvelopeEncryption`.
1060
+ */
1061
+ interface DataKeyProvider {
1062
+ readonly keyId: string;
1063
+ generateDataKey(): Promise<{
1064
+ plaintext: Bytes;
1065
+ wrapped: Bytes;
1066
+ }>;
1067
+ unwrapDataKey(wrapped: Bytes): Promise<Bytes>;
1068
+ }
1069
+ /**
1070
+ * The local reference DataKeyProvider: the key-encryption key is
1071
+ * HKDF-SHA256(secret, info), data keys are random 32-byte AES keys,
1072
+ * and wrapping is AES-256-GCM under the KEK. `info` partitions one
1073
+ * master secret into unrelated KEKs (tenant-scoped keys: one provider
1074
+ * per tenant with `info: tenantId`); a provider with different
1075
+ * secret or info CANNOT unwrap this provider's keys. For production
1076
+ * KMS, implement the same interface over GenerateDataKey/Decrypt.
1077
+ */
1078
+ declare function localKeyProvider(options: {
1079
+ secret: string | Bytes; /** Stamped into envelopes; default 'local:v1'. */
1080
+ keyId?: string; /** KEK partition label (e.g. a tenant id); default ''. */
1081
+ info?: string;
1082
+ }): DataKeyProvider;
1083
+ /** The journal envelope marker; a stored entry's whole value is this. */
1084
+ declare const JOURNAL_ENVELOPE_MARKER = "__rulvarEnvelope";
1085
+ interface EnvelopeEncryption {
1086
+ /** Pass as `createEngine({ serialization })`. */
1087
+ hook: SerializationHook;
1088
+ /** The provider's routing id, stamped into every envelope. */
1089
+ keyId: string;
1090
+ /**
1091
+ * The CURRENT wrapped data key. Every write stamps it into the
1092
+ * envelope, so nothing else must be persisted; it is exposed for
1093
+ * hosts that keep a rotation ledger.
1094
+ */
1095
+ wrappedDataKey: Bytes;
1096
+ }
1097
+ interface EnvelopeEncryptionOptions {
1098
+ provider: DataKeyProvider;
1099
+ /**
1100
+ * Wrapped data keys from earlier sessions or rotations that this
1101
+ * process must still read. Unwrapped once at creation; an envelope
1102
+ * carrying an UNREGISTERED wrapped key fails typed at read, naming
1103
+ * this list.
1104
+ */
1105
+ historicalWrappedKeys?: readonly Bytes[];
1106
+ /**
1107
+ * What a NON-enveloped stored entry or blob means at read:
1108
+ * 'reject' (default, fail closed) or 'passthrough' (explicit
1109
+ * migration mode for stores with pre-encryption history).
1110
+ */
1111
+ plaintextReads?: "reject" | "passthrough";
1112
+ }
1113
+ /**
1114
+ * Builds the envelope-encryption SerializationHook. All DataKeyProvider
1115
+ * calls happen HERE (the hook itself is synchronous, on in-memory data
1116
+ * keys): a fresh data key is minted and wrapped for this instance, and
1117
+ * every historical wrapped key is unwrapped for the read path.
1118
+ */
1119
+ declare function createEnvelopeEncryption(options: EnvelopeEncryptionOptions): Promise<EnvelopeEncryption>;
1120
+ /** Guards against non-constant-time comparisons in host key checks. */
1121
+ declare function constantTimeEqual(a: Bytes, b: Bytes): boolean;
1038
1122
  //#endregion
1039
1123
  //#region src/l0/usage.d.ts
1040
1124
  /**
@@ -2280,12 +2364,21 @@ declare function replayDisposition(entry: JournalEntry, fold: AbandonFold, optio
2280
2364
  registry?: DeriverRegistry;
2281
2365
  terminal?: JournalEntry;
2282
2366
  invalidated?: ReadonlySet<number>;
2367
+ /**
2368
+ * True when the loaded journal carries a run settle with runStatus
2369
+ * 'ok' (the resume is a pure replay of a finished run): unstamped
2370
+ * limit entries then replay instead of re-running live. Terminal
2371
+ * settles other than ok keep the retry semantics.
2372
+ */
2373
+ runSettledOk?: boolean;
2283
2374
  }): ReplayDisposition;
2284
2375
  /**
2285
2376
  * Adapts the predicate to the matcher's disposition hook: two-phase
2286
2377
  * operations dispatch on their terminal, single-phase on themselves.
2287
2378
  */
2288
- declare function dispositionHook(fold: AbandonFold, registry: DeriverRegistry, invalidated?: ReadonlySet<number>): (op: JournalOperation) => ReplayDisposition;
2379
+ declare function dispositionHook(fold: AbandonFold, registry: DeriverRegistry, invalidated?: ReadonlySet<number>, options?: {
2380
+ runSettledOk?: boolean;
2381
+ }): (op: JournalOperation) => ReplayDisposition;
2289
2382
  //#endregion
2290
2383
  //#region src/journal/resolution.d.ts
2291
2384
  type ResolutionAttempt = {
@@ -5437,12 +5530,18 @@ interface CreateEngineOptions {
5437
5530
  */
5438
5531
  serialization?: SerializationHook;
5439
5532
  /**
5440
- * The default key-masking policy at the telemetry boundary. Default
5441
- * ON: key-shaped strings in every
5442
- * emitted WorkflowEvent are masked; never touches the journal.
5533
+ * The masking policy at the telemetry boundary. Default ON:
5534
+ * key-shaped strings in every emitted WorkflowEvent are masked;
5535
+ * never touches the journal (lossless encryption via `serialization`
5536
+ * is the persistence-side tool). `patterns` adds host-defined
5537
+ * redaction on top of the default credential set (RV-217): RegExp or
5538
+ * pattern strings, compiled once at construction, applied to every
5539
+ * string in every emitted event body. Feed the same patterns to the
5540
+ * OTel exporter for trace parity.
5443
5541
  */
5444
5542
  redaction?: {
5445
5543
  maskEvents?: boolean;
5544
+ patterns?: ReadonlyArray<RegExp | string>;
5446
5545
  };
5447
5546
  /**
5448
5547
  * Bare-nondeterminism detection over in-process workflow bodies
@@ -5456,6 +5555,21 @@ interface CreateEngineOptions {
5456
5555
  * runtime frames are classified exempt and stay silent.
5457
5556
  */
5458
5557
  determinism?: DeterminismConfig;
5558
+ /**
5559
+ * Metadata protection knobs (RV-217). `argsHashSalt` switches the
5560
+ * RunMeta.argsHash digest from plain sha256 to HMAC-SHA256 under the
5561
+ * salt: equal args stop correlating across deployments and
5562
+ * low-entropy args stop being recoverable from the digest. The salt
5563
+ * is deployment config, not a per-run secret: every engine (and the
5564
+ * CLI host config) resuming this store's runs must carry the SAME
5565
+ * salt, or the resume args gate refuses matching args. Runs recorded
5566
+ * before the salt keep their unsalted digests; the gate then simply
5567
+ * mismatches until forced, so introduce the salt on a fresh store or
5568
+ * accept --allow-args-change on legacy runs.
5569
+ */
5570
+ security?: {
5571
+ argsHashSalt?: string;
5572
+ };
5459
5573
  }
5460
5574
  interface RunOptions {
5461
5575
  /** Explicit id; otherwise the engine mints a ULID. */
@@ -5581,6 +5695,35 @@ interface Engine {
5581
5695
  pruneRun(runId: string, opts?: {
5582
5696
  lease?: Lease;
5583
5697
  }): Promise<number>;
5698
+ /**
5699
+ * Portable run export (RV-217): the meta record, every journal
5700
+ * entry, and every transcript blob, read through Engine.stores (the
5701
+ * one policy point), so an encrypted deployment exports PLAINTEXT
5702
+ * for a subject-access request or a store migration, without raw
5703
+ * store spelunking. Blobs are materialized in memory; export runs
5704
+ * one at a time, not catalogs.
5705
+ */
5706
+ exportRun(runId: string): Promise<RunExport>;
5707
+ /**
5708
+ * Imports a bundle produced by exportRun, under its ORIGINAL runId
5709
+ * (transcript refs and journal fields embed it; rewriting ids is
5710
+ * deliberately out of scope). Writes through Engine.stores, so an
5711
+ * encrypting target re-encrypts under its own policy. Refuses typed
5712
+ * when the run already exists in the target store, so an import can
5713
+ * never interleave with live history.
5714
+ */
5715
+ importRun(bundle: RunExport): Promise<void>;
5716
+ }
5717
+ /** The portable bundle exportRun produces and importRun consumes (RV-217). */
5718
+ interface RunExport {
5719
+ runId: string;
5720
+ /** Absent when the source store had no meta row for the run. */
5721
+ meta?: RunMeta;
5722
+ entries: JournalEntry[];
5723
+ blobs: Array<{
5724
+ ref: string;
5725
+ data: Bytes;
5726
+ }>;
5584
5727
  }
5585
5728
  /** Content hash of an in-process workflow body (run-to-definition binding). */
5586
5729
  declare function hashWorkflowBody(wf: Workflow<never, never> | Workflow<unknown, unknown>): string;
@@ -5604,7 +5747,9 @@ declare function workflowSourceRef(runId: string): string;
5604
5747
  * sensitive-derived metadata, not a value safe to publish (see the
5605
5748
  * `argsHash` field docs).
5606
5749
  */
5607
- declare function hashRunArgs(args: unknown): string | undefined;
5750
+ declare function hashRunArgs(args: unknown, options?: {
5751
+ salt?: string;
5752
+ }): string | undefined;
5608
5753
  /**
5609
5754
  * sha256 hex over the JCS canonical serialization of a run's result
5610
5755
  * value: the digest the engine records as `outputHash` on the journaled
@@ -7536,6 +7681,37 @@ declare function implementationAgentProfile(options?: AgentProfileTemplateOption
7536
7681
  */
7537
7682
  declare function reviewAgentProfile(options?: AgentProfileTemplateOptions): AgentProfile;
7538
7683
  //#endregion
7684
+ //#region src/engine/audit.d.ts
7685
+ type AuditCategory = "suspension" | "resolution" | "abandon" | "decision" | "termination-denied" | "run-settle";
7686
+ /** One reviewable authority event, in journal order. */
7687
+ interface AuditRecord {
7688
+ /** The journal seq of the entry behind this record. */
7689
+ seq: number;
7690
+ /** The entry's startedAt timestamp. */
7691
+ at: string;
7692
+ scope: string;
7693
+ category: AuditCategory;
7694
+ /**
7695
+ * The finer type: the suspension kind ('external' | 'approval') for
7696
+ * suspensions, the journaled decisionType for decisions.
7697
+ */
7698
+ type?: string;
7699
+ /** Who acted: a ResolutionBy for resolutions, 'engine' for decisions. */
7700
+ by?: string;
7701
+ /** The seq of the entry this record acts on (resolution/abandon target). */
7702
+ target?: number;
7703
+ /** One deterministic reviewable line. */
7704
+ summary: string;
7705
+ /** The journaled payload, verbatim (plaintext through Engine.stores). */
7706
+ value?: Json;
7707
+ }
7708
+ /**
7709
+ * Folds a loaded journal into the audit trail, in seq order. Pass the
7710
+ * FULL entry list (`Engine.stores.journal.load(runId)` or
7711
+ * `exportRun(runId).entries`); filtering is the reducer's job.
7712
+ */
7713
+ declare function reduceAuditTrail(entries: readonly JournalEntry[]): AuditRecord[];
7714
+ //#endregion
7539
7715
  //#region src/journal/scope.d.ts
7540
7716
  /**
7541
7717
  * Scope-path grammar (M1-T04): deterministic structural paths, independent
@@ -8105,7 +8281,7 @@ declare class EventBus {
8105
8281
  private readonly runId;
8106
8282
  private readonly spans;
8107
8283
  private readonly now;
8108
- private readonly maskEvents;
8284
+ private readonly mask;
8109
8285
  private readonly subscribers;
8110
8286
  private readonly listeners;
8111
8287
  private seq;
@@ -8122,6 +8298,12 @@ declare class EventBus {
8122
8298
  */
8123
8299
  maskEvents?: boolean;
8124
8300
  /**
8301
+ * The compiled masking policy applied when maskEvents is on
8302
+ * (RV-217): the default credential set plus host patterns. Absent
8303
+ * falls back to the default maskSecretsDeep.
8304
+ */
8305
+ mask?: (body: WorkflowEventBody) => WorkflowEventBody;
8306
+ /**
8125
8307
  * First seq value (default 0): the resumed-segment base that keeps
8126
8308
  * seq strictly increasing per run across segments (v1.22.0 review
8127
8309
  * P1-2).
@@ -8302,4 +8484,4 @@ interface SandboxBridge {
8302
8484
  declare const SANDBOX_AGENT_OPT_KEYS: readonly string[];
8303
8485
  declare function createSandboxBridge(ctx: Ctx<never>, options: SandboxBridgeOptions): SandboxBridge;
8304
8486
  //#endregion
8305
- 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, 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_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, 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, ErrorClass, ErrorCode, ErrorPolicy, EscalatedResult, EscalationDecision, EscalationDecisionAbortedError, EscalationDigest, EscalationKind, EscalationLimits, EscalationOptions, EscalationReport, EscalationRequest, EventBus, type EvidenceRef, type ExplorationSummary, ExtensionAppendInput, ExtensionDispatchSpec, ExternalIdentityInput, ExternalRegistry, ExtractNecessityInput, FINALIZE_SYNTHESIS_INSTRUCTION, FINISH_SCHEMA, FINISH_TOOL_NAME, FailRunError, FailoverTarget, FailoverTrigger, FallbackField, FallbackTrigger, FileModelKnowledgeStore, FileModelKnowledgeStoreOptions, FileTranscriptStore, FinishInfo, 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, 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_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, 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, PriceTable, PricedUsage, type Pricing, type PricingTier, ProgressReport, type ProviderAdapter, 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, 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, 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, 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, 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, compileVerifiedLayer, costReportFromJournal, countsAgainstLimit, createCanonicalIdMinter, createCtx, createEngine, createSandboxBridge, currentOnlyKeyRing, decodeCheckpoint, dedupeRepeatedClaims, defineWorkflow, deriveContentKey, deriverV1, deriverV2, digestOf, dispositionHook, emptyDigestBlocks, emptyToolset, encodeCheckpoint, entryUsageSlices, escalateTool, evaluatePermission, evaluateReuse, evidencePreservedValidator, executeWorkflow, exhaustionCodeOf, extractCandidate, failoverTriggerOf, fallbackTriggerOf, filterClaimsForRun, finalizeFires, foldTermination, formatRePrompt, formatScopePath, hasFencedWrites, hasMetaLookup, hashRunArgs, hashRunOutput, hashWorkflowBody, hashWorkflowSource, identityJcs, implementationAgentProfile, isEscalated, isSchemaPairSpec, isStandardSchemaSpec, isStrictCompatibleSchema, kMaxOf, knowledgeHash, ladderLengthOf, ladderRungChoice, lastRunSettle, latestProgressReport, lexShellCommand, liftRetainedParts, lineageWeightOf, makeOrchestratorWorkflow, maskSecrets, maskSecretsDeep, maskSecretsJson, matchArgvPattern, matchShellCommand, mcp, memoryQuotaLimiter, mergeQuotaDenial, mergeUsageLimits, metaMatchesFilter, minMatchesValidator, modelEpochOf, modelKnowledgeCard, modelSpecIdentity, needsSeparateExtract, nextFailover, nodeLinkKey, normalizeApproachTag, normalizeEntry, normalizeFallbacks, orchestrate, parallelScope, parseModelRef, parseScopePath, phiInitialOf, pipelineScope, planNodeScope, priceEntryUsage, priceUsdOf, profileCard, profileRegistrySnapshotHash, progressReportTool, projectHistory, projectIdentity, projectToJsonSchema, proposalStatement, providerOf, quotaActualTokens, quotaEstimateTokens, quotaRuleAdmission, quotaRuleMatches, readRunMeta, readTerminationInit, reconcileRunMeta, 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, selectStructuredOutputTier, shouldCompact, snapshotUsage, spawnDepthOf, summarizeInstruction, summarizeOutput, terminationConfigDrift, tierWithinCaps, toApprovalDecision, toJournalValue, tool, toolContract, toolsetHash, ttlState, usageViolations, validateEditorialCommit, validateEngineQuotaConfig, validateEntryShape, validateEscalationLimits, validateEscalationReport, validateQuotaRules, validateRetryPolicy, validateSchemaSpec, validateTerminationLimits, validateUsageLimits, workflowScope, workflowSourceRef, wrapJournalStore, wrapTranscriptStore };
8487
+ 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_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 ExplorationSummary, ExtensionAppendInput, ExtensionDispatchSpec, ExternalIdentityInput, ExternalRegistry, ExtractNecessityInput, FINALIZE_SYNTHESIS_INSTRUCTION, FINISH_SCHEMA, FINISH_TOOL_NAME, FailRunError, FailoverTarget, FailoverTrigger, FallbackField, FallbackTrigger, FileModelKnowledgeStore, FileModelKnowledgeStoreOptions, FileTranscriptStore, FinishInfo, 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, type IsolationProvider, type IsolationSpec, Issue$1 as Issue, JOURNAL_ENVELOPE_MARKER, 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_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, 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, PriceTable, PricedUsage, type Pricing, type PricingTier, ProgressReport, type ProviderAdapter, 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, 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, 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, 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, dispositionHook, emptyDigestBlocks, emptyToolset, encodeCheckpoint, entryUsageSlices, escalateTool, evaluatePermission, evaluateReuse, evidencePreservedValidator, executeWorkflow, exhaustionCodeOf, extractCandidate, failoverTriggerOf, fallbackTriggerOf, filterClaimsForRun, finalizeFires, foldTermination, formatRePrompt, formatScopePath, hasFencedWrites, hasMetaLookup, hashRunArgs, hashRunOutput, hashWorkflowBody, hashWorkflowSource, identityJcs, implementationAgentProfile, 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, parallelScope, parseModelRef, parseScopePath, phiInitialOf, pipelineScope, planNodeScope, 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, selectStructuredOutputTier, shouldCompact, snapshotUsage, spawnDepthOf, summarizeInstruction, summarizeOutput, terminationConfigDrift, tierWithinCaps, toApprovalDecision, toJournalValue, tool, toolContract, toolsetHash, ttlState, usageViolations, validateEditorialCommit, validateEngineQuotaConfig, validateEntryShape, validateEscalationLimits, validateEscalationReport, validateQuotaRules, validateRetryPolicy, validateSchemaSpec, validateTerminationLimits, validateUsageLimits, workflowScope, workflowSourceRef, wrapJournalStore, wrapTranscriptStore };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createHash, getRandomValues, randomUUID } from "node:crypto";
1
+ import { createCipheriv, createDecipheriv, createHash, createHmac, getRandomValues, hkdfSync, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
2
2
  import { appendFileSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
3
3
  import path, { dirname, join, resolve, sep } from "node:path";
4
4
  import { Client } from "@modelcontextprotocol/sdk/client";
@@ -491,6 +491,338 @@ function maskSecretsDeep(value) {
491
491
  function maskSecretsJson(value) {
492
492
  return maskSecretsDeep(value);
493
493
  }
494
+ /**
495
+ * Compiles the redaction policy: the DEFAULT credential pattern set
496
+ * plus host-defined patterns (RV-217), for the telemetry boundary
497
+ * (events and traces; never the journal, where lossless encryption is
498
+ * the right tool). String patterns compile as global regexes; RegExp
499
+ * patterns are recompiled with the global flag when it is missing, so
500
+ * replace-all semantics always hold. An invalid pattern is a typed
501
+ * ConfigError at compile time, before anything runs under the policy.
502
+ */
503
+ function compileSecretMasker(patterns = [], site = "redaction.patterns") {
504
+ const raw = patterns;
505
+ if (!Array.isArray(raw)) throw new ConfigError(`${site} must be an array of RegExp or string patterns`);
506
+ const compiled = raw.map((pattern, index) => {
507
+ const at = `${site}[${String(index)}]`;
508
+ if (pattern instanceof RegExp) return pattern.flags.includes("g") ? pattern : new RegExp(pattern.source, `${pattern.flags}g`);
509
+ if (typeof pattern !== "string" || pattern === "") throw new ConfigError(`${at} must be a RegExp or a nonempty pattern string`);
510
+ try {
511
+ return new RegExp(pattern, "g");
512
+ } catch (thrown) {
513
+ throw new ConfigError(`${at} is not a valid regular expression: ${thrown instanceof Error ? thrown.message : String(thrown)}`);
514
+ }
515
+ });
516
+ const maskText = (text) => {
517
+ let masked = maskSecrets(text);
518
+ for (const pattern of compiled) masked = masked.replace(pattern, MASKED_SECRET);
519
+ return masked;
520
+ };
521
+ const maskDeep = (value) => deepMap(value, maskText);
522
+ return {
523
+ maskText,
524
+ maskDeep
525
+ };
526
+ }
527
+ /** Shared deep string walk preserving identity when nothing changed. */
528
+ function deepMap(value, mapText) {
529
+ if (typeof value === "string") {
530
+ const masked = mapText(value);
531
+ return masked === value ? value : masked;
532
+ }
533
+ if (Array.isArray(value)) {
534
+ let changed = false;
535
+ const next = value.map((item) => {
536
+ const masked = deepMap(item, mapText);
537
+ if (masked !== item) changed = true;
538
+ return masked;
539
+ });
540
+ return changed ? next : value;
541
+ }
542
+ if (value !== null && typeof value === "object") {
543
+ let changed = false;
544
+ const next = {};
545
+ for (const [key, item] of Object.entries(value)) {
546
+ const masked = deepMap(item, mapText);
547
+ if (masked !== item) changed = true;
548
+ next[key] = masked;
549
+ }
550
+ return changed ? next : value;
551
+ }
552
+ return value;
553
+ }
554
+ //#endregion
555
+ //#region src/l0/encryption.ts
556
+ /**
557
+ * Envelope encryption over the serialization hook (RV-217): the
558
+ * reference implementation of "PII never persists in plaintext". The
559
+ * hook seam (l0/serialization.ts) is the single policy point between
560
+ * the engine and persistence; this module puts real cryptography on
561
+ * it, KMS-shaped.
562
+ *
563
+ * The envelope pattern, exactly as cloud KMS services frame it:
564
+ *
565
+ * - A DataKeyProvider is the KMS seam. It mints a fresh DATA key and
566
+ * returns it in two forms (plaintext for this process's memory,
567
+ * wrapped for storage), and it unwraps previously wrapped keys. The
568
+ * shipped `localKeyProvider` derives its key-encryption key from a
569
+ * host secret via HKDF-SHA256; an AWS KMS provider maps 1:1 onto
570
+ * GenerateDataKey and Decrypt (the guide shows the sketch), and
571
+ * tenant-scoped keys are providers constructed per tenant (the
572
+ * `info` input partitions one master secret into unrelated KEKs).
573
+ * - All provider calls are ASYNC and happen ONCE, in
574
+ * `createEnvelopeEncryption`, never per entry: the hook contract is
575
+ * synchronous, so the factory unwraps everything up front and the
576
+ * hooks run on in-memory data keys. Entries carry the WRAPPED key in
577
+ * every envelope, so decrypt needs only the provider registration,
578
+ * not a live KMS on the read path.
579
+ * - Payload encryption is AES-256-GCM with a random IV per write and
580
+ * the entry identity as ASSOCIATED DATA (`seq` and `key` for journal
581
+ * entries, the ref for transcript blobs), so a ciphertext moved to a
582
+ * different entry fails authentication instead of decrypting into
583
+ * the wrong place.
584
+ * - Journal entries keep the kernel ordering/identity fields plus the
585
+ * operational timestamps and spanId in plaintext (stores index and
586
+ * humans operate on them; none carry payload content); EVERYTHING
587
+ * else (value, error, usage, servedBy, cost attribution, refs) is
588
+ * inside the ciphertext. `fromStored(toStored(e))` reproduces the
589
+ * entry, so replay, content keys, and the folds are untouched.
590
+ * - Reads of NON-enveloped stored data fail closed by default
591
+ * (`plaintextReads: 'reject'`); `'passthrough'` is the explicit
592
+ * migration mode for stores with pre-encryption history.
593
+ *
594
+ * Docs: https://docs.rulvar.com/guide/data-protection
595
+ */
596
+ const HKDF_SALT = "rulvar-envelope-kek-v1";
597
+ const GCM_IV_BYTES = 12;
598
+ const GCM_TAG_BYTES = 16;
599
+ const DATA_KEY_BYTES = 32;
600
+ /**
601
+ * The local reference DataKeyProvider: the key-encryption key is
602
+ * HKDF-SHA256(secret, info), data keys are random 32-byte AES keys,
603
+ * and wrapping is AES-256-GCM under the KEK. `info` partitions one
604
+ * master secret into unrelated KEKs (tenant-scoped keys: one provider
605
+ * per tenant with `info: tenantId`); a provider with different
606
+ * secret or info CANNOT unwrap this provider's keys. For production
607
+ * KMS, implement the same interface over GenerateDataKey/Decrypt.
608
+ */
609
+ function localKeyProvider(options) {
610
+ const raw = options.secret;
611
+ if ((typeof raw !== "string" || raw === "") && !(raw instanceof Uint8Array)) throw new ConfigError("localKeyProvider secret must be a nonempty string or bytes");
612
+ if (raw instanceof Uint8Array && raw.length < 16) throw new ConfigError("localKeyProvider secret bytes must be at least 16 bytes");
613
+ const keyId = options.keyId ?? "local:v1";
614
+ if (typeof keyId !== "string" || keyId === "") throw new ConfigError("localKeyProvider keyId must be a nonempty string when given");
615
+ const kek = Buffer.from(hkdfSync("sha256", typeof raw === "string" ? Buffer.from(raw, "utf8") : Buffer.from(raw), Buffer.from(HKDF_SALT, "utf8"), Buffer.from(options.info ?? "", "utf8"), DATA_KEY_BYTES));
616
+ return {
617
+ keyId,
618
+ async generateDataKey() {
619
+ const plaintext = randomBytes(DATA_KEY_BYTES);
620
+ const iv = randomBytes(GCM_IV_BYTES);
621
+ const cipher = createCipheriv("aes-256-gcm", kek, iv);
622
+ const ct = Buffer.concat([cipher.update(plaintext), cipher.final()]);
623
+ const wrapped = Buffer.concat([
624
+ iv,
625
+ cipher.getAuthTag(),
626
+ ct
627
+ ]);
628
+ return {
629
+ plaintext: new Uint8Array(plaintext),
630
+ wrapped: new Uint8Array(wrapped)
631
+ };
632
+ },
633
+ async unwrapDataKey(wrapped) {
634
+ const buffer = Buffer.from(wrapped);
635
+ if (buffer.length <= 28) throw new ConfigError("localKeyProvider: the wrapped data key is truncated");
636
+ const iv = buffer.subarray(0, GCM_IV_BYTES);
637
+ const tag = buffer.subarray(GCM_IV_BYTES, 28);
638
+ const ct = buffer.subarray(28);
639
+ const decipher = createDecipheriv("aes-256-gcm", kek, iv);
640
+ decipher.setAuthTag(tag);
641
+ try {
642
+ const plaintext = Buffer.concat([decipher.update(ct), decipher.final()]);
643
+ return new Uint8Array(plaintext);
644
+ } catch {
645
+ throw new ConfigError(`localKeyProvider '${keyId}': cannot unwrap the data key (wrong secret, wrong info partition, or a corrupted wrapped key)`);
646
+ }
647
+ }
648
+ };
649
+ }
650
+ /** The journal envelope marker; a stored entry's whole value is this. */
651
+ const JOURNAL_ENVELOPE_MARKER = "__rulvarEnvelope";
652
+ /** The transcript blob envelope magic (version 1). */
653
+ const BLOB_MAGIC = Buffer.from("RVE1", "utf8");
654
+ /**
655
+ * Plaintext fields of a stored journal entry: the kernel
656
+ * ordering/identity fields the hook contract pins, plus the
657
+ * operational metadata stores index and operators read (timestamps,
658
+ * spanId). None carry payload content.
659
+ */
660
+ const CLEAR_FIELDS = [
661
+ "hashVersion",
662
+ "seq",
663
+ "ref",
664
+ "scope",
665
+ "key",
666
+ "ordinal",
667
+ "kind",
668
+ "status",
669
+ "spanId",
670
+ "startedAt",
671
+ "endedAt"
672
+ ];
673
+ function b64(bytes) {
674
+ return Buffer.from(bytes).toString("base64");
675
+ }
676
+ function isEnvelope(value) {
677
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
678
+ const keys = Object.keys(value);
679
+ if (keys.length !== 1 || keys[0] !== "__rulvarEnvelope") return false;
680
+ const env = value[JOURNAL_ENVELOPE_MARKER];
681
+ return typeof env === "object" && env !== null && env.v === 1 && typeof env.keyId === "string" && typeof env.wrapped === "string" && typeof env.iv === "string" && typeof env.tag === "string" && typeof env.data === "string";
682
+ }
683
+ /**
684
+ * Builds the envelope-encryption SerializationHook. All DataKeyProvider
685
+ * calls happen HERE (the hook itself is synchronous, on in-memory data
686
+ * keys): a fresh data key is minted and wrapped for this instance, and
687
+ * every historical wrapped key is unwrapped for the read path.
688
+ */
689
+ async function createEnvelopeEncryption(options) {
690
+ const provider = options.provider;
691
+ if (typeof provider !== "object" || provider === null || typeof provider.keyId !== "string" || provider.keyId === "" || typeof provider.generateDataKey !== "function" || typeof provider.unwrapDataKey !== "function") throw new ConfigError("createEnvelopeEncryption: provider must implement DataKeyProvider (keyId, generateDataKey, unwrapDataKey)");
692
+ const plaintextReads = options.plaintextReads ?? "reject";
693
+ if (plaintextReads !== "reject" && plaintextReads !== "passthrough") throw new ConfigError("createEnvelopeEncryption: plaintextReads must be 'reject' or 'passthrough'");
694
+ const realProvider = options.provider;
695
+ const current = await realProvider.generateDataKey();
696
+ if (current.plaintext.length !== DATA_KEY_BYTES) throw new ConfigError(`createEnvelopeEncryption: the provider returned a ${String(current.plaintext.length)}-byte data key; AES-256-GCM needs exactly ${String(DATA_KEY_BYTES)}`);
697
+ const ring = /* @__PURE__ */ new Map();
698
+ ring.set(b64(current.wrapped), Buffer.from(current.plaintext));
699
+ for (const wrapped of options.historicalWrappedKeys ?? []) {
700
+ const plaintext = await realProvider.unwrapDataKey(wrapped);
701
+ if (plaintext.length !== DATA_KEY_BYTES) throw new ConfigError(`createEnvelopeEncryption: a historical wrapped key unwrapped to ${String(plaintext.length)} bytes; AES-256-GCM needs exactly ${String(DATA_KEY_BYTES)}`);
702
+ ring.set(b64(wrapped), Buffer.from(plaintext));
703
+ }
704
+ const keyId = realProvider.keyId;
705
+ const currentWrappedB64 = b64(current.wrapped);
706
+ const currentKey = ring.get(currentWrappedB64);
707
+ const keyFor = (envelopeKeyId, wrappedB64, site) => {
708
+ const key = ring.get(wrappedB64);
709
+ if (key === void 0) throw new ConfigError(`${site}: the stored envelope carries a data key (keyId '${envelopeKeyId}') that this process has not registered; pass it in historicalWrappedKeys so the factory can unwrap it, and check the provider secret and info match the writing deployment`);
710
+ return key;
711
+ };
712
+ const encrypt = (key, aad, plaintext) => {
713
+ const iv = randomBytes(GCM_IV_BYTES);
714
+ const cipher = createCipheriv("aes-256-gcm", key, iv);
715
+ cipher.setAAD(aad);
716
+ const ct = Buffer.concat([cipher.update(plaintext), cipher.final()]);
717
+ return {
718
+ v: 1,
719
+ keyId,
720
+ wrapped: currentWrappedB64,
721
+ iv: iv.toString("base64"),
722
+ tag: cipher.getAuthTag().toString("base64"),
723
+ data: ct.toString("base64")
724
+ };
725
+ };
726
+ const decrypt = (key, aad, env, site) => {
727
+ const decipher = createDecipheriv("aes-256-gcm", key, Buffer.from(env.iv, "base64"));
728
+ decipher.setAAD(aad);
729
+ decipher.setAuthTag(Buffer.from(env.tag, "base64"));
730
+ try {
731
+ return Buffer.concat([decipher.update(Buffer.from(env.data, "base64")), decipher.final()]);
732
+ } catch {
733
+ throw new ConfigError(`${site}: envelope authentication failed (a tampered ciphertext, or a ciphertext moved to a different entry; the entry identity is associated data)`);
734
+ }
735
+ };
736
+ return {
737
+ hook: {
738
+ journal: {
739
+ toStored(e) {
740
+ const clear = {};
741
+ const rest = {};
742
+ for (const [field, fieldValue] of Object.entries(e)) if (CLEAR_FIELDS.includes(field)) clear[field] = fieldValue;
743
+ else rest[field] = fieldValue;
744
+ const aad = Buffer.from(`journal:${String(e.seq)}:${e.key}`, "utf8");
745
+ const envelope = encrypt(currentKey, aad, Buffer.from(JSON.stringify(rest), "utf8"));
746
+ return {
747
+ ...clear,
748
+ value: { [JOURNAL_ENVELOPE_MARKER]: envelope }
749
+ };
750
+ },
751
+ fromStored(e) {
752
+ if (!isEnvelope(e.value)) {
753
+ if (plaintextReads === "passthrough") return e;
754
+ throw new ConfigError(`envelope encryption: stored entry seq ${String(e.seq)} is not enveloped and plaintextReads is 'reject'; enable 'passthrough' only for a deliberate migration of pre-encryption history`);
755
+ }
756
+ const env = e.value[JOURNAL_ENVELOPE_MARKER];
757
+ const key = keyFor(env.keyId, env.wrapped, "envelope encryption (journal read)");
758
+ const aad = Buffer.from(`journal:${String(e.seq)}:${e.key}`, "utf8");
759
+ const rest = JSON.parse(decrypt(key, aad, env, "envelope encryption (journal read)").toString("utf8"));
760
+ const clear = {};
761
+ for (const field of CLEAR_FIELDS) if (e[field] !== void 0) clear[field] = e[field];
762
+ return {
763
+ ...clear,
764
+ ...rest
765
+ };
766
+ }
767
+ },
768
+ transcripts: {
769
+ toStored(ref, blob) {
770
+ const iv = randomBytes(GCM_IV_BYTES);
771
+ const cipher = createCipheriv("aes-256-gcm", currentKey, iv);
772
+ cipher.setAAD(Buffer.from(`blob:${ref}`, "utf8"));
773
+ const ct = Buffer.concat([cipher.update(Buffer.from(blob)), cipher.final()]);
774
+ const keyIdBytes = Buffer.from(keyId, "utf8");
775
+ const wrappedBytes = Buffer.from(currentWrappedB64, "base64");
776
+ const header = Buffer.alloc(4);
777
+ header.writeUInt16BE(keyIdBytes.length, 0);
778
+ header.writeUInt16BE(wrappedBytes.length, 2);
779
+ return new Uint8Array(Buffer.concat([
780
+ BLOB_MAGIC,
781
+ header,
782
+ keyIdBytes,
783
+ wrappedBytes,
784
+ iv,
785
+ cipher.getAuthTag(),
786
+ ct
787
+ ]));
788
+ },
789
+ fromStored(ref, blob) {
790
+ const buffer = Buffer.from(blob);
791
+ if (buffer.length < BLOB_MAGIC.length || !buffer.subarray(0, 4).equals(BLOB_MAGIC)) {
792
+ if (plaintextReads === "passthrough") return blob;
793
+ throw new ConfigError(`envelope encryption: stored blob '${ref}' is not enveloped and plaintextReads is 'reject'; enable 'passthrough' only for a deliberate migration`);
794
+ }
795
+ let offset = BLOB_MAGIC.length;
796
+ const keyIdLen = buffer.readUInt16BE(offset);
797
+ const wrappedLen = buffer.readUInt16BE(offset + 2);
798
+ offset += 4;
799
+ const envelopeKeyId = buffer.subarray(offset, offset + keyIdLen).toString("utf8");
800
+ offset += keyIdLen;
801
+ const wrappedB64 = buffer.subarray(offset, offset + wrappedLen).toString("base64");
802
+ offset += wrappedLen;
803
+ const iv = buffer.subarray(offset, offset + GCM_IV_BYTES);
804
+ const tag = buffer.subarray(offset + GCM_IV_BYTES, offset + GCM_IV_BYTES + GCM_TAG_BYTES);
805
+ const ct = buffer.subarray(offset + GCM_IV_BYTES + GCM_TAG_BYTES);
806
+ const decipher = createDecipheriv("aes-256-gcm", keyFor(envelopeKeyId, wrappedB64, `envelope encryption (blob '${ref}')`), iv);
807
+ decipher.setAAD(Buffer.from(`blob:${ref}`, "utf8"));
808
+ decipher.setAuthTag(tag);
809
+ try {
810
+ return new Uint8Array(Buffer.concat([decipher.update(ct), decipher.final()]));
811
+ } catch {
812
+ throw new ConfigError(`envelope encryption: blob '${ref}' authentication failed (tampered ciphertext or a ciphertext moved between refs; the ref is associated data)`);
813
+ }
814
+ }
815
+ }
816
+ },
817
+ keyId,
818
+ wrappedDataKey: new Uint8Array(current.wrapped)
819
+ };
820
+ }
821
+ /** Guards against non-constant-time comparisons in host key checks. */
822
+ function constantTimeEqual(a, b) {
823
+ if (a.length !== b.length) return false;
824
+ return timingSafeEqual(Buffer.from(a), Buffer.from(b));
825
+ }
494
826
  //#endregion
495
827
  //#region src/vendor/ulid.ts
496
828
  /**
@@ -3941,6 +4273,90 @@ function reviewAgentProfile(options = {}) {
3941
4273
  };
3942
4274
  }
3943
4275
  //#endregion
4276
+ //#region src/engine/audit.ts
4277
+ function record(entry, fields) {
4278
+ return {
4279
+ seq: entry.seq,
4280
+ at: entry.startedAt,
4281
+ scope: entry.scope,
4282
+ ...fields
4283
+ };
4284
+ }
4285
+ /**
4286
+ * Folds a loaded journal into the audit trail, in seq order. Pass the
4287
+ * FULL entry list (`Engine.stores.journal.load(runId)` or
4288
+ * `exportRun(runId).entries`); filtering is the reducer's job.
4289
+ */
4290
+ function reduceAuditTrail(entries) {
4291
+ const trail = [];
4292
+ for (const entry of entries) {
4293
+ if ((entry.kind === "external" || entry.kind === "approval") && entry.status === "suspended") {
4294
+ trail.push(record(entry, {
4295
+ category: "suspension",
4296
+ type: entry.kind,
4297
+ summary: `${entry.kind} suspension opened` + (entry.deadlineAt === void 0 ? "" : ` (deadline ${entry.deadlineAt})`),
4298
+ ...entry.value === void 0 ? {} : { value: entry.value }
4299
+ }));
4300
+ continue;
4301
+ }
4302
+ if (entry.kind === "resolution") {
4303
+ const payload = entry.resolution;
4304
+ if (payload === void 0) continue;
4305
+ trail.push(record(entry, {
4306
+ category: "resolution",
4307
+ by: payload.by,
4308
+ target: payload.target,
4309
+ summary: `suspension #${String(payload.target)} resolved by ${payload.by}` + (payload.decisionRef === void 0 ? "" : ` (class decision #${String(payload.decisionRef)})`),
4310
+ value: payload.value
4311
+ }));
4312
+ continue;
4313
+ }
4314
+ if (entry.kind === "abandon") {
4315
+ const payload = entry.abandon;
4316
+ if (payload === void 0) continue;
4317
+ trail.push(record(entry, {
4318
+ category: "abandon",
4319
+ target: payload.target,
4320
+ by: `decision #${String(payload.authorizedBy)}`,
4321
+ summary: `#${String(payload.target)} abandoned: ${payload.reason}`
4322
+ }));
4323
+ continue;
4324
+ }
4325
+ if (entry.kind === "termination.denied") {
4326
+ trail.push(record(entry, {
4327
+ category: "termination-denied",
4328
+ by: "engine",
4329
+ summary: "a termination-limit action was denied",
4330
+ ...entry.value === void 0 ? {} : { value: entry.value }
4331
+ }));
4332
+ continue;
4333
+ }
4334
+ if (entry.kind === "decision") {
4335
+ const value = entry.value;
4336
+ const decisionType = typeof value?.decisionType === "string" ? value.decisionType : void 0;
4337
+ if (decisionType === void 0) continue;
4338
+ if (decisionType === "run_settle") {
4339
+ trail.push(record(entry, {
4340
+ category: "run-settle",
4341
+ by: "engine",
4342
+ type: decisionType,
4343
+ summary: `run settled ${typeof value?.runStatus === "string" ? value.runStatus : "unknown"}`,
4344
+ ...entry.value === void 0 ? {} : { value: entry.value }
4345
+ }));
4346
+ continue;
4347
+ }
4348
+ trail.push(record(entry, {
4349
+ category: "decision",
4350
+ by: "engine",
4351
+ type: decisionType,
4352
+ summary: `engine decision ${decisionType}`,
4353
+ ...entry.value === void 0 ? {} : { value: entry.value }
4354
+ }));
4355
+ }
4356
+ }
4357
+ return trail;
4358
+ }
4359
+ //#endregion
3944
4360
  //#region src/journal/identity.ts
3945
4361
  /**
3946
4362
  * Content-addressed entry identity (M1-T04): IdentityInput records per
@@ -4340,11 +4756,13 @@ function buildAbandonFold(entries) {
4340
4756
  return isCovered(entry);
4341
4757
  } };
4342
4758
  }
4343
- function applyRule(rule, op) {
4759
+ function applyRule(rule, op, runSettledOk) {
4344
4760
  const terminal = op.terminal ?? op.running;
4345
4761
  switch (rule) {
4346
4762
  case "replay": return "replay";
4347
- case "memoize-limit": return terminal.memoizeOutcome ?? op.running.memoizeOutcome ?? false ? "replay" : "rerun";
4763
+ case "memoize-limit":
4764
+ if (terminal.memoizeOutcome ?? op.running.memoizeOutcome ?? false) return "replay";
4765
+ return runSettledOk ? "replay" : "rerun";
4348
4766
  case "memoize-task-error":
4349
4767
  if (!(terminal.memoizeOutcome ?? op.running.memoizeOutcome ?? false) || terminal.error === void 0) return "rerun";
4350
4768
  return classifyAgentError(agentErrorFromWireSafe(terminal)) === "task" ? "replay" : "rerun";
@@ -4381,17 +4799,18 @@ function replayDisposition(entry, fold, options) {
4381
4799
  if (options?.invalidated?.has(entry.seq) === true) return "rerun";
4382
4800
  const deriver = options?.registry?.get(entry.hashVersion) ?? deriverV2;
4383
4801
  const status = (options?.terminal ?? entry).status;
4384
- return applyRule(deriver.dispositionTable[status], op);
4802
+ return applyRule(deriver.dispositionTable[status], op, options?.runSettledOk ?? false);
4385
4803
  }
4386
4804
  /**
4387
4805
  * Adapts the predicate to the matcher's disposition hook: two-phase
4388
4806
  * operations dispatch on their terminal, single-phase on themselves.
4389
4807
  */
4390
- function dispositionHook(fold, registry, invalidated) {
4808
+ function dispositionHook(fold, registry, invalidated, options) {
4391
4809
  return (op) => replayDisposition(op.running, fold, {
4392
4810
  registry,
4393
4811
  ...op.terminal === void 0 ? {} : { terminal: op.terminal },
4394
- ...invalidated === void 0 ? {} : { invalidated }
4812
+ ...invalidated === void 0 ? {} : { invalidated },
4813
+ ...options?.runSettledOk === void 0 ? {} : { runSettledOk: options.runSettledOk }
4395
4814
  });
4396
4815
  }
4397
4816
  //#endregion
@@ -15946,7 +16365,7 @@ var EventBus = class {
15946
16365
  runId;
15947
16366
  spans;
15948
16367
  now;
15949
- maskEvents;
16368
+ mask;
15950
16369
  subscribers = /* @__PURE__ */ new Set();
15951
16370
  listeners = /* @__PURE__ */ new Set();
15952
16371
  seq;
@@ -15956,12 +16375,12 @@ var EventBus = class {
15956
16375
  this.runId = options.runId;
15957
16376
  this.spans = options.spans;
15958
16377
  this.now = options.now ?? realNow;
15959
- this.maskEvents = options.maskEvents ?? true;
16378
+ this.mask = options.maskEvents ?? true ? options.mask ?? maskSecretsDeep : void 0;
15960
16379
  this.seq = options.firstSeq ?? 0;
15961
16380
  }
15962
16381
  emit(body, spanId, replayed) {
15963
16382
  const parentSpanId = this.spans.parentOf(spanId);
15964
- const safeBody = this.maskEvents ? maskSecretsDeep(body) : body;
16383
+ const safeBody = this.mask === void 0 ? body : this.mask(body);
15965
16384
  const event = {
15966
16385
  runId: this.runId,
15967
16386
  seq: this.seq++,
@@ -16561,9 +16980,12 @@ function liftRunCompletion(candidate) {
16561
16980
  * sensitive-derived metadata, not a value safe to publish (see the
16562
16981
  * `argsHash` field docs).
16563
16982
  */
16564
- function hashRunArgs(args) {
16983
+ function hashRunArgs(args, options) {
16565
16984
  if (args === void 0) return;
16566
- return createHash("sha256").update(jcsSerialize(args), "utf8").digest("hex");
16985
+ const canonical = jcsSerialize(args);
16986
+ const salt = options?.salt;
16987
+ if (salt === void 0) return createHash("sha256").update(canonical, "utf8").digest("hex");
16988
+ return createHmac("sha256", Buffer.from(salt, "utf8")).update(canonical, "utf8").digest("hex");
16567
16989
  }
16568
16990
  /**
16569
16991
  * sha256 hex over the JCS canonical serialization of a run's result
@@ -16592,6 +17014,7 @@ function createEngine(options) {
16592
17014
  const journal = options.serialization?.journal === void 0 ? rawJournal : wrapJournalStore(rawJournal, options.serialization.journal);
16593
17015
  const transcripts = options.serialization?.transcripts === void 0 ? rawTranscripts : wrapTranscriptStore(rawTranscripts, options.serialization.transcripts);
16594
17016
  const maskEvents = options.redaction?.maskEvents ?? true;
17017
+ const eventMasker = options.redaction?.patterns === void 0 ? void 0 : compileSecretMasker(options.redaction.patterns, "createEngine redaction.patterns");
16595
17018
  const defaults = options.defaults ?? {};
16596
17019
  if (defaults.retry !== void 0) validateRetryPolicy(defaults.retry, "createEngine defaults.retry");
16597
17020
  if (options.concurrency?.perRun !== void 0) requirePositiveInteger(options.concurrency.perRun, "createEngine concurrency.perRun");
@@ -16615,6 +17038,8 @@ function createEngine(options) {
16615
17038
  }
16616
17039
  validateDeterminismConfig(options.determinism);
16617
17040
  validateEngineQuotaConfig(options.quota);
17041
+ if (options.security?.argsHashSalt !== void 0 && (typeof options.security.argsHashSalt !== "string" || options.security.argsHashSalt === "")) throw new ConfigError("createEngine security.argsHashSalt must be a nonempty string when given");
17042
+ const argsHashSalt = options.security?.argsHashSalt;
16618
17043
  const quotaRuntime = options.quota === void 0 ? void 0 : {
16619
17044
  limiter: options.quota.limiter,
16620
17045
  ...options.quota.tenant === void 0 ? {} : { tenant: options.quota.tenant },
@@ -16654,6 +17079,7 @@ function createEngine(options) {
16654
17079
  spans,
16655
17080
  now: realNow,
16656
17081
  maskEvents,
17082
+ ...eventMasker === void 0 ? {} : { mask: (body) => eventMasker.maskDeep(body) },
16657
17083
  firstSeq: telemetryBase
16658
17084
  });
16659
17085
  const rootSpanId = spans.mint();
@@ -16684,8 +17110,9 @@ function createEngine(options) {
16684
17110
  strict: resumeCtx?.strict ?? false
16685
17111
  });
16686
17112
  for (const seqToInvalidate of invalidated) replayer.invalidate(seqToInvalidate);
16687
- replayer.setDisposition(dispositionHook(replayer.fold.abandonFold, registry, replayer.invalidatedSeqs));
16688
- replayer.setAliasDisposition(dispositionHook({ isAbandoned: () => false }, registry, replayer.invalidatedSeqs));
17113
+ const runSettledOk = resumeCtx !== void 0 && lastRunSettle(resumeCtx.priorEntries)?.runStatus === "ok";
17114
+ replayer.setDisposition(dispositionHook(replayer.fold.abandonFold, registry, replayer.invalidatedSeqs, { runSettledOk }));
17115
+ replayer.setAliasDisposition(dispositionHook({ isAbandoned: () => false }, registry, replayer.invalidatedSeqs, { runSettledOk }));
16689
17116
  if (resumeCtx !== void 0) {
16690
17117
  const prior = replayer.ledger();
16691
17118
  budgetSeed = {
@@ -16779,7 +17206,7 @@ function createEngine(options) {
16779
17206
  if (resumeCtx === void 0) {
16780
17207
  argsBinding.argsProvided = args !== void 0;
16781
17208
  try {
16782
- const argsHash = hashRunArgs(args);
17209
+ const argsHash = hashRunArgs(args, argsHashSalt === void 0 ? void 0 : { salt: argsHashSalt });
16783
17210
  if (argsHash !== void 0) argsBinding.argsHash = argsHash;
16784
17211
  } catch {}
16785
17212
  } else {
@@ -17040,6 +17467,42 @@ function createEngine(options) {
17040
17467
  preview
17041
17468
  };
17042
17469
  }
17470
+ /** Portable export through the policy point (RV-217). */
17471
+ async function exportRun(runId) {
17472
+ const entries = await journal.load(runId);
17473
+ const meta = await readRunMeta(journal, runId);
17474
+ const blobs = [];
17475
+ for (const ref of await transcripts.list(runId)) {
17476
+ const data = await transcripts.get(ref);
17477
+ if (data !== null) blobs.push({
17478
+ ref,
17479
+ data
17480
+ });
17481
+ }
17482
+ if (entries.length === 0 && meta === void 0 && blobs.length === 0) throw new ConfigError(`exportRun: run '${runId}' does not exist in this engine's stores`);
17483
+ return {
17484
+ runId,
17485
+ ...meta === void 0 ? {} : { meta },
17486
+ entries,
17487
+ blobs
17488
+ };
17489
+ }
17490
+ /** Import under the original runId; refuses an existing run (RV-217). */
17491
+ async function importRun(bundle) {
17492
+ const raw = bundle;
17493
+ if (typeof raw !== "object" || raw === null || typeof raw.runId !== "string" || raw.runId === "" || !Array.isArray(raw.entries) || !Array.isArray(raw.blobs)) throw new ConfigError("importRun: the bundle must be a RunExport (runId, entries, blobs)");
17494
+ const runId = bundle.runId;
17495
+ const existingMeta = await readRunMeta(journal, runId);
17496
+ const existingEntries = await journal.load(runId);
17497
+ const existingBlobs = await transcripts.list(runId);
17498
+ if (existingMeta !== void 0 || existingEntries.length > 0 || existingBlobs.length > 0) throw new ConfigError(`importRun: run '${runId}' already exists in the target stores; an import never interleaves with live history (delete the run first if replacement is intended)`);
17499
+ for (const entry of bundle.entries) await journal.append(runId, entry);
17500
+ for (const blob of bundle.blobs) await transcripts.put(blob.ref, blob.data);
17501
+ if (bundle.meta !== void 0) await journal.putMeta({
17502
+ ...bundle.meta,
17503
+ runId
17504
+ });
17505
+ }
17043
17506
  /** Retention cascade (OQ-20 executed at M8-T04): blobs, then journal. */
17044
17507
  async function deleteRun(runId, opts) {
17045
17508
  const refs = await transcripts.list(runId);
@@ -17104,6 +17567,8 @@ function createEngine(options) {
17104
17567
  },
17105
17568
  deleteRun,
17106
17569
  pruneRun,
17570
+ exportRun,
17571
+ importRun,
17107
17572
  profileCard: (names) => {
17108
17573
  const registered = defaults.profiles ?? {};
17109
17574
  if (names === void 0) return profileCard(registered, defaults.toolsets);
@@ -17401,4 +17866,4 @@ function createSandboxBridge(ctx, options) {
17401
17866
  };
17402
17867
  }
17403
17868
  //#endregion
17404
- 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_CHILD_RESULT_PAGE_CHARS, DEFAULT_CITATION_PATTERN, 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, DedupIndex, DeterminismError, EMIT_RESULT_TOOL, EMPTY_SCHEMA_HASH, EMPTY_TOOLSET_HASH, ESCALATE_TOOL_NAME, ESCALATION_REPORT_SCHEMA, ESCALATION_REQUEST_SCHEMA, EVENT_SEGMENT_STRIDE, EscalationDecisionAbortedError, EventBus, ExternalRegistry, FINALIZE_SYNTHESIS_INSTRUCTION, FINISH_SCHEMA, FINISH_TOOL_NAME, FailRunError, FileModelKnowledgeStore, FileTranscriptStore, GET_CHILD_RESULT_SCHEMA, GET_CHILD_RESULT_TOOL_NAME, GitWorktreeProvider, IMPLEMENTATION_PROFILE_LIMITS, 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_CHILD_RESULT_PAGE_CHARS, MAX_DEPTH_CEILING, ModelRetry, NoProgressDetector, NonSerializableValueError, ORCHESTRATE_WORKFLOW_NAME, OrchestratorCapConfigError, PARALLEL_AGENTS_SCHEMA, PROGRESS_REPORT_TOOL_NAME, ParallelSiteCounter, PlanInvariantError, QUOTA_WINDOW_MS, READ_CHILD_ARTIFACT_SCHEMA, READ_CHILD_ARTIFACT_TOOL_NAME, RESEARCH_PROFILE_LIMITS, REVIEW_PROFILE_LIMITS, ROLE_EFFORT_DEFAULTS, ROOT_ACCOUNT, ROOT_SCOPE, RUN_PROFILES, RUN_SETTLE_DECISION_TYPE, ReplayPlanHashMismatch, Replayer, ResolutionArbiter, ResolutionFold, RulvarError, RunBudget, SANDBOX_AGENT_OPT_KEYS, SPAWN_AGENT_SCHEMA, SandboxError, ScriptRejected, Semaphore, SpanRegistry, TOOL_NAME_PATTERN, TerminationAccount, WAIT_FOR_EVENTS_SCHEMA, WAIT_FOR_EVENTS_TOOL_NAME, WAKE_SUMMARY_RENDER_BUDGET_CHARS, 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, compileVerifiedLayer, costReportFromJournal, countsAgainstLimit, createCanonicalIdMinter, createCtx, createEngine, createSandboxBridge, currentOnlyKeyRing, decodeCheckpoint, dedupeRepeatedClaims, defineWorkflow, deriveContentKey, deriverV1, deriverV2, digestOf, dispositionHook, emptyDigestBlocks, emptyToolset, encodeCheckpoint, entryUsageSlices, escalateTool, evaluatePermission, evaluateReuse, evidencePreservedValidator, executeWorkflow, exhaustionCodeOf, extractCandidate, failoverTriggerOf, fallbackTriggerOf, filterClaimsForRun, finalizeFires, foldTermination, formatRePrompt, formatScopePath, hasFencedWrites, hasMetaLookup, hashRunArgs, hashRunOutput, hashWorkflowBody, hashWorkflowSource, identityJcs, implementationAgentProfile, isEscalated, isSchemaPairSpec, isStandardSchemaSpec, isStrictCompatibleSchema, kMaxOf, knowledgeHash, ladderLengthOf, ladderRungChoice, lastRunSettle, latestProgressReport, lexShellCommand, liftRetainedParts, lineageWeightOf, makeOrchestratorWorkflow, maskSecrets, maskSecretsDeep, maskSecretsJson, matchArgvPattern, matchShellCommand, mcp, memoryQuotaLimiter, mergeQuotaDenial, mergeUsageLimits, metaMatchesFilter, minMatchesValidator, modelEpochOf, modelKnowledgeCard, modelSpecIdentity, needsSeparateExtract, nextFailover, nodeLinkKey, normalizeApproachTag, normalizeEntry, normalizeFallbacks, orchestrate, parallelScope, parseModelRef, parseScopePath, phiInitialOf, pipelineScope, planNodeScope, priceEntryUsage, priceUsdOf, profileCard, profileRegistrySnapshotHash, progressReportTool, projectHistory, projectIdentity, projectToJsonSchema, proposalStatement, providerOf, quotaActualTokens, quotaEstimateTokens, quotaRuleAdmission, quotaRuleMatches, readRunMeta, readTerminationInit, reconcileRunMeta, 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, selectStructuredOutputTier, shouldCompact, snapshotUsage, spawnDepthOf, summarizeInstruction, summarizeOutput, terminationConfigDrift, tierWithinCaps, toApprovalDecision, toJournalValue, tool, toolContract, toolsetHash, ttlState, usageViolations, validateEditorialCommit, validateEngineQuotaConfig, validateEntryShape, validateEscalationLimits, validateEscalationReport, validateQuotaRules, validateRetryPolicy, validateSchemaSpec, validateTerminationLimits, validateUsageLimits, workflowScope, workflowSourceRef, wrapJournalStore, wrapTranscriptStore };
17869
+ 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_CHILD_RESULT_PAGE_CHARS, DEFAULT_CITATION_PATTERN, 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, DedupIndex, DeterminismError, EMIT_RESULT_TOOL, EMPTY_SCHEMA_HASH, EMPTY_TOOLSET_HASH, ESCALATE_TOOL_NAME, ESCALATION_REPORT_SCHEMA, ESCALATION_REQUEST_SCHEMA, EVENT_SEGMENT_STRIDE, EscalationDecisionAbortedError, EventBus, ExternalRegistry, FINALIZE_SYNTHESIS_INSTRUCTION, FINISH_SCHEMA, FINISH_TOOL_NAME, FailRunError, FileModelKnowledgeStore, FileTranscriptStore, GET_CHILD_RESULT_SCHEMA, GET_CHILD_RESULT_TOOL_NAME, GitWorktreeProvider, IMPLEMENTATION_PROFILE_LIMITS, INBOX_PROPOSAL_TTL_DAYS, InMemoryStore, InMemoryTranscriptStore, InProcessRunner, InvalidResolutionError, JOURNAL_ENVELOPE_MARKER, JournalCompatibilityError, JournalMatcher, JournalMissError, JournalOrderViolation, JsonlFileStore, KB_ACTIVE_CLAIMS_CAP, KB_CARD_RENDER_BUDGET_CHARS, KeyedLimiter, KnowledgeCasError, LARGE_VALUE_WARN_BYTES, LEGACY_LTID_PREFIX, LEGACY_SIGNATURE_INPUTS, LINEAGE_SIG_VERSION, LeaseHeldError, LineageIndex, MASKED_SECRET, MAX_CHILD_RESULT_PAGE_CHARS, MAX_DEPTH_CEILING, ModelRetry, NoProgressDetector, NonSerializableValueError, ORCHESTRATE_WORKFLOW_NAME, OrchestratorCapConfigError, PARALLEL_AGENTS_SCHEMA, PROGRESS_REPORT_TOOL_NAME, ParallelSiteCounter, PlanInvariantError, QUOTA_WINDOW_MS, READ_CHILD_ARTIFACT_SCHEMA, READ_CHILD_ARTIFACT_TOOL_NAME, RESEARCH_PROFILE_LIMITS, REVIEW_PROFILE_LIMITS, ROLE_EFFORT_DEFAULTS, ROOT_ACCOUNT, ROOT_SCOPE, RUN_PROFILES, RUN_SETTLE_DECISION_TYPE, ReplayPlanHashMismatch, Replayer, ResolutionArbiter, ResolutionFold, RulvarError, RunBudget, SANDBOX_AGENT_OPT_KEYS, SPAWN_AGENT_SCHEMA, SandboxError, ScriptRejected, Semaphore, SpanRegistry, TOOL_NAME_PATTERN, TerminationAccount, WAIT_FOR_EVENTS_SCHEMA, WAIT_FOR_EVENTS_TOOL_NAME, WAKE_SUMMARY_RENDER_BUDGET_CHARS, 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, dispositionHook, emptyDigestBlocks, emptyToolset, encodeCheckpoint, entryUsageSlices, escalateTool, evaluatePermission, evaluateReuse, evidencePreservedValidator, executeWorkflow, exhaustionCodeOf, extractCandidate, failoverTriggerOf, fallbackTriggerOf, filterClaimsForRun, finalizeFires, foldTermination, formatRePrompt, formatScopePath, hasFencedWrites, hasMetaLookup, hashRunArgs, hashRunOutput, hashWorkflowBody, hashWorkflowSource, identityJcs, implementationAgentProfile, 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, parallelScope, parseModelRef, parseScopePath, phiInitialOf, pipelineScope, planNodeScope, 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, selectStructuredOutputTier, shouldCompact, snapshotUsage, spawnDepthOf, summarizeInstruction, summarizeOutput, terminationConfigDrift, tierWithinCaps, toApprovalDecision, toJournalValue, tool, toolContract, toolsetHash, ttlState, usageViolations, validateEditorialCommit, validateEngineQuotaConfig, validateEntryShape, validateEscalationLimits, validateEscalationReport, validateQuotaRules, validateRetryPolicy, validateSchemaSpec, validateTerminationLimits, validateUsageLimits, workflowScope, workflowSourceRef, wrapJournalStore, wrapTranscriptStore };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/core",
3
- "version": "1.56.0",
3
+ "version": "1.58.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",