@rulvar/core 1.3.1 → 1.4.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/README.md +40 -0
- package/dist/index.d.ts +53 -11
- package/dist/index.js +93 -16
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# @rulvar/core
|
|
2
|
+
|
|
3
|
+
The rulvar engine in one dependency-light package: the L0 contracts and
|
|
4
|
+
SPI interfaces, the journal kernel behind the never-pay-twice invariant,
|
|
5
|
+
the model router with the capability and price registry, the agent
|
|
6
|
+
runtime, the tool system and MCP bus, the `ctx` primitives and run
|
|
7
|
+
engine, the dynamic orchestrator, the in-memory and JSONL reference
|
|
8
|
+
stores, and the typed event stream. Zero provider SDK dependencies:
|
|
9
|
+
adapters plug in from their own packages. Key exports: `createEngine`,
|
|
10
|
+
`defineWorkflow`, `tool`, `mcp`, `orchestrate`, `InMemoryStore`,
|
|
11
|
+
`JsonlFileStore`.
|
|
12
|
+
|
|
13
|
+
Part of [rulvar](https://rulvar.com), an embeddable TypeScript engine
|
|
14
|
+
for durable, budget-bounded multi-agent LLM workflows, where a completed
|
|
15
|
+
LLM call is never paid for twice. Full documentation:
|
|
16
|
+
[docs.rulvar.com](https://docs.rulvar.com).
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm add @rulvar/core
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Most applications start with the umbrella instead: `pnpm add
|
|
25
|
+
@rulvar/rulvar` bundles this engine with both first-class adapters and
|
|
26
|
+
the recommended model defaults. The a la carte path pairs the core with
|
|
27
|
+
exactly the pieces you need, for example
|
|
28
|
+
`pnpm add @rulvar/core @rulvar/anthropic @rulvar/store-sqlite`.
|
|
29
|
+
|
|
30
|
+
## Documentation
|
|
31
|
+
|
|
32
|
+
- [Quickstart](https://docs.rulvar.com/guide/quickstart)
|
|
33
|
+
- [Architecture](https://docs.rulvar.com/guide/architecture)
|
|
34
|
+
- [Workflows](https://docs.rulvar.com/guide/workflows) and
|
|
35
|
+
[The journal](https://docs.rulvar.com/guide/journal)
|
|
36
|
+
- [API reference](https://docs.rulvar.com/api/%40rulvar/core/)
|
|
37
|
+
|
|
38
|
+
## License
|
|
39
|
+
|
|
40
|
+
[Apache-2.0](https://github.com/o-stepper/rulvar/blob/main/LICENSE)
|
package/dist/index.d.ts
CHANGED
|
@@ -697,6 +697,14 @@ type RunMeta = {
|
|
|
697
697
|
workflowName?: string; /** Content hash of the body or of the compiled source. */
|
|
698
698
|
workflowHash?: string; /** TranscriptStore ref of the persisted CompiledWorkflow source. */
|
|
699
699
|
workflowSourceRef?: string;
|
|
700
|
+
/**
|
|
701
|
+
* The run's immutable USD ceiling (RunOptions.budgetUsd), recorded so
|
|
702
|
+
* resume restores the original invocation's bound. Absent when the
|
|
703
|
+
* run started without a ceiling. Stores must round-trip the field
|
|
704
|
+
* (the conformance kit checks); a store that drops it degrades a
|
|
705
|
+
* resumed run to uncapped.
|
|
706
|
+
*/
|
|
707
|
+
budgetUsd?: number;
|
|
700
708
|
};
|
|
701
709
|
type RunFilter = {
|
|
702
710
|
status?: string;
|
|
@@ -2867,8 +2875,10 @@ type PermissionHook = (toolName: string, input: unknown, ctx: ToolContext) => Ho
|
|
|
2867
2875
|
* position matches every tool WITHOUT declared risk: presets treat the
|
|
2868
2876
|
* undeclared state conservatively. Argv rules
|
|
2869
2877
|
* match through the real shell matcher; domain rules are
|
|
2870
|
-
* ADVISORY
|
|
2871
|
-
* change a verdict
|
|
2878
|
+
* ADVISORY for every tool in the current release: they never
|
|
2879
|
+
* change a verdict, and matches surface in the tool:end audit
|
|
2880
|
+
* fields (enforcement will live in a first-party fetch tool
|
|
2881
|
+
* when one ships).
|
|
2872
2882
|
*/
|
|
2873
2883
|
type RiskRuleValue = ToolRisk | "undeclared";
|
|
2874
2884
|
type PermissionRule = {
|
|
@@ -2928,8 +2938,8 @@ type PermissionVerdict = ({
|
|
|
2928
2938
|
input: unknown;
|
|
2929
2939
|
}) & {
|
|
2930
2940
|
/**
|
|
2931
|
-
* Advisory domain-rule matches: reported in
|
|
2932
|
-
*
|
|
2941
|
+
* Advisory domain-rule matches: reported in the tool:end
|
|
2942
|
+
* audit fields, never enforced in the current release.
|
|
2933
2943
|
*/
|
|
2934
2944
|
advisory?: PermissionRule[];
|
|
2935
2945
|
};
|
|
@@ -3893,12 +3903,23 @@ type AdaptiveEvents = {
|
|
|
3893
3903
|
coversToOrdinal: number;
|
|
3894
3904
|
renderSize: number;
|
|
3895
3905
|
} | {
|
|
3906
|
+
/**
|
|
3907
|
+
* Two emitted shapes share the discriminant: the cap-freeze form
|
|
3908
|
+
* carries { atCap: true, spentUsd, capUsd, finalizeReserveUsd },
|
|
3909
|
+
* and the per-wake digest form carries atCap plus the passive
|
|
3910
|
+
* WakeBudgetBlock fields (runSpentUsd .. softWarning).
|
|
3911
|
+
*/
|
|
3896
3912
|
type: "orchestrator:budget";
|
|
3897
|
-
|
|
3898
|
-
spentUsd
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3913
|
+
atCap: boolean;
|
|
3914
|
+
spentUsd?: number;
|
|
3915
|
+
capUsd?: number;
|
|
3916
|
+
finalizeReserveUsd?: number;
|
|
3917
|
+
runSpentUsd?: number;
|
|
3918
|
+
runCeilingUsd?: number;
|
|
3919
|
+
orchestratorSpentUsd?: number;
|
|
3920
|
+
orchestratorCapUsd?: number;
|
|
3921
|
+
orchestratorShare?: number;
|
|
3922
|
+
softWarning?: boolean;
|
|
3902
3923
|
} | {
|
|
3903
3924
|
type: "escalation:raised";
|
|
3904
3925
|
entryRef: number;
|
|
@@ -3920,7 +3941,12 @@ type AdaptiveEvents = {
|
|
|
3920
3941
|
spawnUnitsAfter: number;
|
|
3921
3942
|
} | {
|
|
3922
3943
|
type: "spawn:rejected";
|
|
3923
|
-
|
|
3944
|
+
/**
|
|
3945
|
+
* The journaled admission decision entry; absent for the
|
|
3946
|
+
* pre-admission config gates (orchestrate maxSpawns), which
|
|
3947
|
+
* reject before anything is journaled.
|
|
3948
|
+
*/
|
|
3949
|
+
entryRef?: number;
|
|
3924
3950
|
code: string;
|
|
3925
3951
|
agentType: string;
|
|
3926
3952
|
logicalTaskId?: string;
|
|
@@ -3971,6 +3997,12 @@ type AdaptiveEvents = {
|
|
|
3971
3997
|
frozenValue: Json;
|
|
3972
3998
|
liveValue: Json;
|
|
3973
3999
|
} | {
|
|
4000
|
+
/**
|
|
4001
|
+
* Declared for hosts; not emitted today. The compatibility scan
|
|
4002
|
+
* runs strictly before a run's event stream exists, so the
|
|
4003
|
+
* refusal travels only as the typed JournalCompatibilityError
|
|
4004
|
+
* (which carries the same fields).
|
|
4005
|
+
*/
|
|
3974
4006
|
type: "journal:compat";
|
|
3975
4007
|
code: "HASH_VERSION_TOO_OLD" | "HASH_VERSION_TOO_NEW";
|
|
3976
4008
|
found: number;
|
|
@@ -4773,7 +4805,14 @@ declare class ExternalRegistry {
|
|
|
4773
4805
|
private activity;
|
|
4774
4806
|
private quiesceListener?;
|
|
4775
4807
|
private quiesceScheduled;
|
|
4776
|
-
|
|
4808
|
+
private readonly emitEvent?;
|
|
4809
|
+
constructor(replayer: Replayer, emitEvent?: (body: WorkflowEventBody) => void);
|
|
4810
|
+
/**
|
|
4811
|
+
* Live resolution telemetry: applied when the attempt won the
|
|
4812
|
+
* first-closing-wins fold, superseded when it lost. Emitted for live
|
|
4813
|
+
* attempts only; folds of prior entries at resume re-emit nothing.
|
|
4814
|
+
*/
|
|
4815
|
+
private emitResolutionOutcome;
|
|
4777
4816
|
/** Wraps every non-suspension async operation (agents, steps). */
|
|
4778
4817
|
enter(): () => void;
|
|
4779
4818
|
/**
|
|
@@ -5568,6 +5607,9 @@ declare class InMemoryStore implements JournalStore {
|
|
|
5568
5607
|
private readonly runs;
|
|
5569
5608
|
private readonly metas;
|
|
5570
5609
|
private warned;
|
|
5610
|
+
constructor(options?: {
|
|
5611
|
+
quiet?: boolean;
|
|
5612
|
+
});
|
|
5571
5613
|
append(runId: string, e: JournalEntry): Promise<void>;
|
|
5572
5614
|
load(runId: string): Promise<JournalEntry[]>;
|
|
5573
5615
|
putMeta(m: RunMeta): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -2583,7 +2583,7 @@ async function resolveToolset(specs, session) {
|
|
|
2583
2583
|
if (specs === void 0 || specs.length === 0) return emptyToolset();
|
|
2584
2584
|
const tools = [];
|
|
2585
2585
|
for (const spec of specs) {
|
|
2586
|
-
if (typeof spec === "string") throw new ConfigError(`tools by registered name ('${spec}')
|
|
2586
|
+
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
2587
|
if (isToolDef(spec)) {
|
|
2588
2588
|
tools.push(spec);
|
|
2589
2589
|
continue;
|
|
@@ -5576,8 +5576,31 @@ var ExternalRegistry = class ExternalRegistry {
|
|
|
5576
5576
|
activity = 0;
|
|
5577
5577
|
quiesceListener;
|
|
5578
5578
|
quiesceScheduled = false;
|
|
5579
|
-
|
|
5579
|
+
emitEvent;
|
|
5580
|
+
constructor(replayer, emitEvent) {
|
|
5580
5581
|
this.replayer = replayer;
|
|
5582
|
+
this.emitEvent = emitEvent;
|
|
5583
|
+
}
|
|
5584
|
+
/**
|
|
5585
|
+
* Live resolution telemetry: applied when the attempt won the
|
|
5586
|
+
* first-closing-wins fold, superseded when it lost. Emitted for live
|
|
5587
|
+
* attempts only; folds of prior entries at resume re-emit nothing.
|
|
5588
|
+
*/
|
|
5589
|
+
emitResolutionOutcome(targetRef, by, outcome) {
|
|
5590
|
+
if (this.emitEvent === void 0) return;
|
|
5591
|
+
if (outcome.applied) this.emitEvent({
|
|
5592
|
+
type: "resolution:applied",
|
|
5593
|
+
targetRef,
|
|
5594
|
+
entryRef: outcome.seq,
|
|
5595
|
+
by
|
|
5596
|
+
});
|
|
5597
|
+
else this.emitEvent({
|
|
5598
|
+
type: "resolution:superseded",
|
|
5599
|
+
targetRef,
|
|
5600
|
+
entryRef: outcome.seq,
|
|
5601
|
+
supersededBy: outcome.supersededBy,
|
|
5602
|
+
reason: outcome.reason
|
|
5603
|
+
});
|
|
5581
5604
|
}
|
|
5582
5605
|
/** Wraps every non-suspension async operation (agents, steps). */
|
|
5583
5606
|
enter() {
|
|
@@ -5805,6 +5828,7 @@ var ExternalRegistry = class ExternalRegistry {
|
|
|
5805
5828
|
*/
|
|
5806
5829
|
async submitResolution(entryRef, attempt) {
|
|
5807
5830
|
const outcome = await this.replayer.resolveSuspended(entryRef, attempt);
|
|
5831
|
+
this.emitResolutionOutcome(entryRef, attempt.by, outcome);
|
|
5808
5832
|
if (outcome.applied) {
|
|
5809
5833
|
const waiter = this.waiters.get(entryRef);
|
|
5810
5834
|
if (waiter !== void 0) {
|
|
@@ -5838,6 +5862,7 @@ var ExternalRegistry = class ExternalRegistry {
|
|
|
5838
5862
|
by: "external",
|
|
5839
5863
|
value
|
|
5840
5864
|
});
|
|
5865
|
+
this.emitResolutionOutcome(waiter.entryRef, "external", outcome);
|
|
5841
5866
|
if (outcome.applied) {
|
|
5842
5867
|
this.waiters.delete(waiter.entryRef);
|
|
5843
5868
|
waiter.resolve(value);
|
|
@@ -5853,7 +5878,10 @@ function deepCopy(value) {
|
|
|
5853
5878
|
var InMemoryStore = class {
|
|
5854
5879
|
runs = /* @__PURE__ */ new Map();
|
|
5855
5880
|
metas = /* @__PURE__ */ new Map();
|
|
5856
|
-
warned
|
|
5881
|
+
warned;
|
|
5882
|
+
constructor(options) {
|
|
5883
|
+
this.warned = options?.quiet === true;
|
|
5884
|
+
}
|
|
5857
5885
|
append(runId, e) {
|
|
5858
5886
|
this.warnOnce();
|
|
5859
5887
|
const entries = this.runs.get(runId) ?? [];
|
|
@@ -5885,7 +5913,7 @@ var InMemoryStore = class {
|
|
|
5885
5913
|
warnOnce() {
|
|
5886
5914
|
if (this.warned) return;
|
|
5887
5915
|
this.warned = true;
|
|
5888
|
-
process.emitWarning("InMemoryStore keeps journals in process memory: nothing survives
|
|
5916
|
+
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
5917
|
code: "RULVAR_INMEMORY_STORE",
|
|
5890
5918
|
type: "RulvarWarning"
|
|
5891
5919
|
});
|
|
@@ -6518,6 +6546,12 @@ function liftRetainedParts(providerMetadata, adapter) {
|
|
|
6518
6546
|
}
|
|
6519
6547
|
//#endregion
|
|
6520
6548
|
//#region src/model/retry.ts
|
|
6549
|
+
/**
|
|
6550
|
+
* Captured at module load, before the InProcessRunner's nondeterminism
|
|
6551
|
+
* guard can patch the global: the engine's own jitter is journal
|
|
6552
|
+
* invisible and must never be blamed on workflow code.
|
|
6553
|
+
*/
|
|
6554
|
+
const nativeRandom = Math.random;
|
|
6521
6555
|
/** Appendix A committed defaults (M4 entry gate, PR #26). */
|
|
6522
6556
|
const DEFAULT_RETRY_POLICY = {
|
|
6523
6557
|
attempts: 3,
|
|
@@ -6553,7 +6587,7 @@ function retryClassOf(error) {
|
|
|
6553
6587
|
* equal-jitter: half the backoff is deterministic, half random, so a
|
|
6554
6588
|
* jittered delay never collapses to zero.
|
|
6555
6589
|
*/
|
|
6556
|
-
function retryDelayMs(policy, retryIndex, retryAfterMs, random =
|
|
6590
|
+
function retryDelayMs(policy, retryIndex, retryAfterMs, random = nativeRandom) {
|
|
6557
6591
|
if (retryAfterMs !== void 0) return retryAfterMs;
|
|
6558
6592
|
const { initialMs, factor, maxMs, jitter } = policy.backoff;
|
|
6559
6593
|
const base = Math.min(maxMs, initialMs * factor ** retryIndex);
|
|
@@ -7213,7 +7247,7 @@ function ruleMatches(rule, toolName, risk, input) {
|
|
|
7213
7247
|
}
|
|
7214
7248
|
/**
|
|
7215
7249
|
* Advisory domain-rule matches for the audit payload:
|
|
7216
|
-
* reported, never enforced
|
|
7250
|
+
* reported, never enforced in the current release.
|
|
7217
7251
|
*/
|
|
7218
7252
|
function advisoryMatches(chain, toolName) {
|
|
7219
7253
|
return [...chain.deny, ...chain.ask].filter((rule) => "domains" in rule && rule.tool === toolName);
|
|
@@ -9977,7 +10011,15 @@ function createCtx(internals) {
|
|
|
9977
10011
|
if (prior !== void 0) {
|
|
9978
10012
|
claimed.add(prior.seq);
|
|
9979
10013
|
const recorded = prior.value;
|
|
9980
|
-
if (recorded.reject !== void 0)
|
|
10014
|
+
if (recorded.reject !== void 0) {
|
|
10015
|
+
internals.events.emit({
|
|
10016
|
+
type: "spawn:rejected",
|
|
10017
|
+
entryRef: prior.seq,
|
|
10018
|
+
code: recorded.reject.code,
|
|
10019
|
+
agentType
|
|
10020
|
+
}, state.spanId, true);
|
|
10021
|
+
throw new AdmissionRejectedError(`lineage admission rejected agent spawn (${recorded.reject.code}; recorded verdict)`, { data: { reason: recorded.reject } });
|
|
10022
|
+
}
|
|
9981
10023
|
} else {
|
|
9982
10024
|
const evaluated = admission.evaluateLineage({
|
|
9983
10025
|
name: agentType,
|
|
@@ -10000,7 +10042,7 @@ function createCtx(internals) {
|
|
|
10000
10042
|
};
|
|
10001
10043
|
if (evaluated.decision.kind === "reject") decisionValue.reject = { code: evaluated.decision.reason.code };
|
|
10002
10044
|
else decisionValue.lineage = evaluated.decision.lineage;
|
|
10003
|
-
await internals.replayer.appendSinglePhase({
|
|
10045
|
+
const decisionEntry = await internals.replayer.appendSinglePhase({
|
|
10004
10046
|
scope: state.scope,
|
|
10005
10047
|
key: "",
|
|
10006
10048
|
kind: "decision",
|
|
@@ -10008,7 +10050,15 @@ function createCtx(internals) {
|
|
|
10008
10050
|
spanId: internals.spans.mint(state.spanId),
|
|
10009
10051
|
value: decisionValue
|
|
10010
10052
|
});
|
|
10011
|
-
if (evaluated.decision.kind === "reject")
|
|
10053
|
+
if (evaluated.decision.kind === "reject") {
|
|
10054
|
+
internals.events.emit({
|
|
10055
|
+
type: "spawn:rejected",
|
|
10056
|
+
entryRef: decisionEntry.seq,
|
|
10057
|
+
code: evaluated.decision.reason.code,
|
|
10058
|
+
agentType
|
|
10059
|
+
}, state.spanId);
|
|
10060
|
+
throw new AdmissionRejectedError(`lineage admission rejected agent spawn (${evaluated.decision.reason.code})`, { data: { reason: evaluated.decision.reason } });
|
|
10061
|
+
}
|
|
10012
10062
|
admission.registerLineageAdmit(evaluated.decision.lineage.logicalTaskId);
|
|
10013
10063
|
}
|
|
10014
10064
|
}
|
|
@@ -10112,7 +10162,12 @@ function createCtx(internals) {
|
|
|
10112
10162
|
data
|
|
10113
10163
|
}, toolSpanId)
|
|
10114
10164
|
});
|
|
10115
|
-
const
|
|
10165
|
+
const compiledChain = compilePermissionChain(internals.defaults.permissions, profile?.permissions);
|
|
10166
|
+
const readonlyDeny = { risk: ["write", "destructive"] };
|
|
10167
|
+
const chain = isolation === "readonly" ? {
|
|
10168
|
+
...compiledChain,
|
|
10169
|
+
deny: [...compiledChain.deny, readonlyDeny]
|
|
10170
|
+
} : compiledChain;
|
|
10116
10171
|
toolRuntime = {
|
|
10117
10172
|
defs: toolset.tools,
|
|
10118
10173
|
contracts: toolset.contracts,
|
|
@@ -11278,7 +11333,14 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
11278
11333
|
if (recovered !== void 0) return { handle: recovered.handle };
|
|
11279
11334
|
const recoveredRejection = rejectedByOrdinal.get(spawnOrdinal);
|
|
11280
11335
|
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)
|
|
11336
|
+
if (opts?.maxSpawns !== void 0 && spawnOrdinal >= opts.maxSpawns) {
|
|
11337
|
+
internals.events.emit({
|
|
11338
|
+
type: "spawn:rejected",
|
|
11339
|
+
code: "lifetime",
|
|
11340
|
+
agentType: params.agentType
|
|
11341
|
+
}, callingState.spanId);
|
|
11342
|
+
throw new AdmissionRejectedError(`orchestrate maxSpawns ${String(opts.maxSpawns)} reached`, { data: { reason: { code: "lifetime" } } });
|
|
11343
|
+
}
|
|
11282
11344
|
const scope = childScopeOf();
|
|
11283
11345
|
const profile = internals.defaults.profiles?.[params.agentType];
|
|
11284
11346
|
const profileModel = profile?.model;
|
|
@@ -11312,7 +11374,7 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
11312
11374
|
spec: params,
|
|
11313
11375
|
decision
|
|
11314
11376
|
};
|
|
11315
|
-
await internals.replayer.appendSinglePhase({
|
|
11377
|
+
const decisionEntry = await internals.replayer.appendSinglePhase({
|
|
11316
11378
|
scope: callingState.scope,
|
|
11317
11379
|
key: "",
|
|
11318
11380
|
kind: "decision",
|
|
@@ -11322,11 +11384,19 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
11322
11384
|
});
|
|
11323
11385
|
if (decision.verdict.kind === "reject") {
|
|
11324
11386
|
rejectedByOrdinal.set(spawnOrdinal, decision);
|
|
11387
|
+
internals.events.emit({
|
|
11388
|
+
type: "spawn:rejected",
|
|
11389
|
+
entryRef: decisionEntry.seq,
|
|
11390
|
+
code: decision.verdict.reason.code,
|
|
11391
|
+
agentType: params.agentType
|
|
11392
|
+
}, callingState.spanId);
|
|
11325
11393
|
throw new AdmissionRejectedError(`admission rejected spawn_agent '${params.agentType}' (${decision.verdict.reason.code})`, { data: { reason: decision.verdict.reason } });
|
|
11326
11394
|
}
|
|
11327
11395
|
if (decision.verdict.kind !== "admit") throw new ConfigError(`admission verdict '${decision.verdict.kind}' has no producer before M7 (DEF-5)`);
|
|
11328
11396
|
internals.events.emit({
|
|
11329
11397
|
type: "spawn:admitted",
|
|
11398
|
+
entryRef: decisionEntry.seq,
|
|
11399
|
+
verdict: decision.verdict.kind,
|
|
11330
11400
|
agentType: params.agentType,
|
|
11331
11401
|
logicalTaskId: decision.verdict.lineage.logicalTaskId,
|
|
11332
11402
|
spawnUnitsAfter: decision.verdict.spawnUnitsAfter
|
|
@@ -11740,8 +11810,12 @@ var InProcessRunner = class {
|
|
|
11740
11810
|
const priorRandom = Math.random;
|
|
11741
11811
|
let warnedNow = false;
|
|
11742
11812
|
let warnedRandom = false;
|
|
11813
|
+
const libraryCaller = () => {
|
|
11814
|
+
const caller = (/* @__PURE__ */ new Error()).stack?.split("\n")[3];
|
|
11815
|
+
return caller !== void 0 && caller.includes("node_modules");
|
|
11816
|
+
};
|
|
11743
11817
|
Date.now = function rulvarPatchedDateNow() {
|
|
11744
|
-
if (!warnedNow) {
|
|
11818
|
+
if (!warnedNow && !libraryCaller()) {
|
|
11745
11819
|
warnedNow = true;
|
|
11746
11820
|
process.emitWarning("bare Date.now() called inside a rulvar run; use ctx.now() so the value is journaled and stable on replay", {
|
|
11747
11821
|
code: "RULVAR_BARE_DATE_NOW",
|
|
@@ -11751,7 +11825,7 @@ var InProcessRunner = class {
|
|
|
11751
11825
|
return priorNow();
|
|
11752
11826
|
};
|
|
11753
11827
|
Math.random = function rulvarPatchedMathRandom() {
|
|
11754
|
-
if (!warnedRandom) {
|
|
11828
|
+
if (!warnedRandom && !libraryCaller()) {
|
|
11755
11829
|
warnedRandom = true;
|
|
11756
11830
|
process.emitWarning("bare Math.random() called inside a rulvar run; use ctx.random() so the value is journaled and stable on replay", {
|
|
11757
11831
|
code: "RULVAR_BARE_MATH_RANDOM",
|
|
@@ -11829,8 +11903,9 @@ function createEngine(options) {
|
|
|
11829
11903
|
});
|
|
11830
11904
|
const rootSpanId = spans.mint();
|
|
11831
11905
|
let budgetSeed;
|
|
11906
|
+
const ceilingUsd = opts?.budgetUsd ?? resumeCtx?.budgetUsd;
|
|
11832
11907
|
const makeBudget = () => new RunBudget({
|
|
11833
|
-
...
|
|
11908
|
+
...ceilingUsd === void 0 ? {} : { ceilingUsd },
|
|
11834
11909
|
lifetimeSpawnCap: options.budgetDefaults?.lifetimeSpawnCap ?? 500,
|
|
11835
11910
|
events: { emit: (body) => bus.emit(body, rootSpanId) },
|
|
11836
11911
|
priceUsd,
|
|
@@ -11889,7 +11964,7 @@ function createEngine(options) {
|
|
|
11889
11964
|
...options.budgetDefaults?.lineage === void 0 ? {} : { limits: options.budgetDefaults.lineage }
|
|
11890
11965
|
}
|
|
11891
11966
|
});
|
|
11892
|
-
const external = new ExternalRegistry(replayer);
|
|
11967
|
+
const external = new ExternalRegistry(replayer, (body) => bus.emit(body, rootSpanId));
|
|
11893
11968
|
let transcriptCounter = 0;
|
|
11894
11969
|
const internals = {
|
|
11895
11970
|
runId,
|
|
@@ -11950,6 +12025,7 @@ function createEngine(options) {
|
|
|
11950
12025
|
updatedAt: new Date(realNow()).toISOString(),
|
|
11951
12026
|
...opts?.name === void 0 ? {} : { name: opts.name },
|
|
11952
12027
|
...opts?.tags === void 0 ? {} : { tags: opts.tags },
|
|
12028
|
+
...ceilingUsd === void 0 ? {} : { budgetUsd: ceilingUsd },
|
|
11953
12029
|
workflowName: wf.name,
|
|
11954
12030
|
workflowHash: compiled === void 0 ? hashWorkflowBody(wf) : hashWorkflowSource(compiled.source),
|
|
11955
12031
|
...compiled === void 0 ? {} : { workflowSourceRef: workflowSourceRef(runId) }
|
|
@@ -12125,6 +12201,7 @@ function createEngine(options) {
|
|
|
12125
12201
|
strict: resumeOptions?.dryRun ?? false,
|
|
12126
12202
|
invalidate: resumeOptions?.invalidate ?? [],
|
|
12127
12203
|
...resumeOptions?.lease === void 0 ? {} : { lease: resumeOptions.lease },
|
|
12204
|
+
...typeof meta?.budgetUsd === "number" ? { budgetUsd: meta.budgetUsd } : {},
|
|
12128
12205
|
previewResolve
|
|
12129
12206
|
});
|
|
12130
12207
|
})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulvar/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.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",
|