@rulvar/core 1.225.0 → 1.227.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 +200 -8
- package/dist/index.js +263 -18
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -5225,6 +5225,17 @@ interface BudgetHooks {
|
|
|
5225
5225
|
*/
|
|
5226
5226
|
remainingUsd?: () => number | undefined;
|
|
5227
5227
|
/**
|
|
5228
|
+
* Layer 2b asked of the IN-FLIGHT EXPOSURE ceiling (RV2503), wired
|
|
5229
|
+
* only when the cap is configured: the output tokens the exposure
|
|
5230
|
+
* room still affords for this prompt. The dispatch clamps to it too,
|
|
5231
|
+
* so a turn whose full plan overshoots the exposure line is SHORTENED
|
|
5232
|
+
* rather than refused while the budget can still pay for it. An
|
|
5233
|
+
* answer below the serving model's output floor is ignored, so a
|
|
5234
|
+
* genuine exposure exhaustion still refuses through
|
|
5235
|
+
* `admitTurnExposure` with its own typed reason.
|
|
5236
|
+
*/
|
|
5237
|
+
maxExposureOutputTokens?: (servedBy: ModelRef, estimatedInputTokens: number) => number | undefined;
|
|
5238
|
+
/**
|
|
5228
5239
|
* The in-flight exposure admission (RV711), wired only when the cap
|
|
5229
5240
|
* is configured. Called synchronously right before each provider
|
|
5230
5241
|
* dispatch attempt with the attempt's own request estimate: the
|
|
@@ -6270,6 +6281,8 @@ declare class RunBudget {
|
|
|
6270
6281
|
* reservation surface is inert and reserveTurnExposure never binds.
|
|
6271
6282
|
*/
|
|
6272
6283
|
readonly maxInFlightExposureUsd?: number;
|
|
6284
|
+
/** The opt-in lone-dispatch clamp (RV2503); see maxExposureOutputTokens. */
|
|
6285
|
+
private readonly clampTurnToExposure;
|
|
6273
6286
|
private readonly lifetimeSpawnCap;
|
|
6274
6287
|
private readonly events?;
|
|
6275
6288
|
private readonly priceUsd?;
|
|
@@ -6334,7 +6347,8 @@ declare class RunBudget {
|
|
|
6334
6347
|
private readonly invalidPriceWarned;
|
|
6335
6348
|
constructor(options: {
|
|
6336
6349
|
ceilingUsd?: number; /** The opt-in in-flight exposure cap (RV711); see reserveTurnExposure. */
|
|
6337
|
-
maxInFlightExposureUsd?: number;
|
|
6350
|
+
maxInFlightExposureUsd?: number; /** The opt-in lone-dispatch clamp (RV2503); see maxExposureOutputTokens. */
|
|
6351
|
+
clampTurnToExposure?: boolean;
|
|
6338
6352
|
lifetimeSpawnCap?: number;
|
|
6339
6353
|
events?: RuntimeEventSink;
|
|
6340
6354
|
priceUsd?: (servedBy: ModelRef, usage: Usage) => number | undefined; /** Raw price-row resolution for the layer-2b output bound. */
|
|
@@ -6637,6 +6651,53 @@ declare class RunBudget {
|
|
|
6637
6651
|
remainingUsd(accountScope?: string): number | undefined;
|
|
6638
6652
|
maxAffordableOutputTokens(servedBy: ModelRef, estimatedInputTokens: number, accountScope?: string): number | undefined;
|
|
6639
6653
|
/**
|
|
6654
|
+
* The same layer-2b question asked of the IN-FLIGHT EXPOSURE ceiling
|
|
6655
|
+
* (RV2503): the output tokens `cap - spent - live estimates` still
|
|
6656
|
+
* affords from `servedBy` for an estimated prompt, priced by the
|
|
6657
|
+
* settlement function like every other estimate here.
|
|
6658
|
+
*
|
|
6659
|
+
* The clamp above has always existed for the budget ceiling while
|
|
6660
|
+
* {@link reserveTurnExposure} only ever answered yes or no, so a
|
|
6661
|
+
* turn whose FULL planned output overshot the exposure line was
|
|
6662
|
+
* refused outright even when a shorter one fit and the budget could
|
|
6663
|
+
* pay for it. The 1.226.0 comparison run died exactly there: it held
|
|
6664
|
+
* 0.8642 USD of budget, the exposure ceiling had 0.5642 USD of room,
|
|
6665
|
+
* the mandatory repair turn was estimated at 0.7066 USD against an
|
|
6666
|
+
* 18000 token output plan, and the dispatch was refused before any
|
|
6667
|
+
* provider call. The same turn, re-issued after the operator raised
|
|
6668
|
+
* the ceiling, wrote 12840 output tokens and cost 0.4788 USD: it fit
|
|
6669
|
+
* the ceiling that refused it, and a clamp to the ~13253 tokens the
|
|
6670
|
+
* room afforded would have let it run.
|
|
6671
|
+
*
|
|
6672
|
+
* Answered ONLY for a dispatch that is alone in flight, which is the
|
|
6673
|
+
* whole difference between a refusal that means something and one
|
|
6674
|
+
* that means nothing. With siblings live the refusal is TRANSIENT:
|
|
6675
|
+
* RV1902 parks on it and the turn runs at its full planned length
|
|
6676
|
+
* the moment one of them releases, so shortening it would trade a
|
|
6677
|
+
* complete answer for a truncated one and buy nothing. With nothing
|
|
6678
|
+
* live the refusal is PERMANENT (RV2003's sweep wakes such a waiter
|
|
6679
|
+
* 'drained' precisely because no hold will ever return), and the
|
|
6680
|
+
* only choices left are a shorter turn or no turn at all. The
|
|
6681
|
+
* concurrent-wave bound of RV711 is therefore untouched.
|
|
6682
|
+
*
|
|
6683
|
+
* Opt-in through `RunOptions.clampTurnToExposure`, so the drained
|
|
6684
|
+
* refusal terminals RV1902, RV2002 and RV2003 built out of live
|
|
6685
|
+
* parity deaths keep their shapes until a host asks for this one.
|
|
6686
|
+
*
|
|
6687
|
+
* Undefined when the clamp is not armed, when the cap is not
|
|
6688
|
+
* configured, when anything is in flight, or when the model has no
|
|
6689
|
+
* price row, so a run that declares nothing keeps every byte of its
|
|
6690
|
+
* historical path. Zero or
|
|
6691
|
+
* negative when the room cannot even pay for the prompt, the same
|
|
6692
|
+
* convention {@link maxAffordableOutputTokens} inherits from
|
|
6693
|
+
* `affordableOutputTokens`; the caller decides what a sub-floor
|
|
6694
|
+
* answer means, and the loop deliberately ignores one so a true
|
|
6695
|
+
* exposure exhaustion still refuses through
|
|
6696
|
+
* {@link reserveTurnExposure} with its own typed reason instead of
|
|
6697
|
+
* an output-floor verdict.
|
|
6698
|
+
*/
|
|
6699
|
+
maxExposureOutputTokens(servedBy: ModelRef, estimatedInputTokens: number): number | undefined;
|
|
6700
|
+
/**
|
|
6640
6701
|
* Live accounting; spend propagates from `accountScope` to every
|
|
6641
6702
|
* ancestor. Crossing a ceiling severs the crossing account's subtree
|
|
6642
6703
|
* via its layer-3 AbortSignal (overshoot bounded by one turn per
|
|
@@ -7630,6 +7691,33 @@ interface RunOptions {
|
|
|
7630
7691
|
*/
|
|
7631
7692
|
maxInFlightExposureUsd?: number;
|
|
7632
7693
|
/**
|
|
7694
|
+
* Layer 2b against the exposure ceiling (RV2503), opt-in and
|
|
7695
|
+
* meaningful only beside `maxInFlightExposureUsd`. Armed, a dispatch
|
|
7696
|
+
* with NOTHING else in flight has its planned output clamped to the
|
|
7697
|
+
* tokens the remaining exposure room affords instead of being
|
|
7698
|
+
* refused outright, exactly as the budget ceiling has always clamped
|
|
7699
|
+
* it. The 1.226.0 comparison run is the case: nothing was live, the
|
|
7700
|
+
* budget still held 0.8642 USD, the mandatory repair turn's FULL
|
|
7701
|
+
* 18000 token plan priced 0.7066 USD against 0.5642 USD of room, and
|
|
7702
|
+
* the dispatch was refused before any provider call; the same work,
|
|
7703
|
+
* re-issued after an operator raised the ceiling, wrote 12840 output
|
|
7704
|
+
* tokens for 0.4788 USD. A refusal with nothing live buys nothing,
|
|
7705
|
+
* because no hold will ever release to fund the full plan.
|
|
7706
|
+
*
|
|
7707
|
+
* Deliberately scoped and deliberately off by default. With siblings
|
|
7708
|
+
* in flight the refusal is transient and the RV1902/RV2002 waits
|
|
7709
|
+
* park on it, so the wave keeps the full-length turn RV711 promised
|
|
7710
|
+
* and nothing here applies. When the room cannot even fund the
|
|
7711
|
+
* serving model's output floor, the clamp stands aside and the
|
|
7712
|
+
* dispatch refuses through the usual typed `in-flight-exposure`
|
|
7713
|
+
* path, so the drained-refusal terminals (RV1902, RV2002, RV2003)
|
|
7714
|
+
* keep their shapes. Absent, every byte of dispatch behavior is
|
|
7715
|
+
* historical. Like `strictPricing`, this is a per-segment posture: it
|
|
7716
|
+
* is not recorded in RunMeta and a resumed segment carries only what
|
|
7717
|
+
* its own options declare.
|
|
7718
|
+
*/
|
|
7719
|
+
clampTurnToExposure?: boolean;
|
|
7720
|
+
/**
|
|
7633
7721
|
* The opt-in strict pre-egress pricing gate (RV1508): every paid
|
|
7634
7722
|
* dispatch must resolve a well-formed price row for its serving
|
|
7635
7723
|
* model BEFORE the wire call, or the dispatch refuses typed
|
|
@@ -7988,6 +8076,15 @@ interface FinishValidationInput {
|
|
|
7988
8076
|
* evidence the children actually produced.
|
|
7989
8077
|
*/
|
|
7990
8078
|
readonly children?: readonly FinishValidationChild[];
|
|
8079
|
+
/**
|
|
8080
|
+
* The id of the run being judged (RV2501). Optional in the TYPE only
|
|
8081
|
+
* so hand built inputs stay source compatible; the orchestrator
|
|
8082
|
+
* runtime always supplies it, at every gate that judges a finish
|
|
8083
|
+
* (the validator-bound finish, the contract draft gate, and the
|
|
8084
|
+
* skipWhenDraftValid pre-pass), so a validator can accept the run's
|
|
8085
|
+
* own id as the artifact a claim about THIS run points at.
|
|
8086
|
+
*/
|
|
8087
|
+
readonly runId?: string;
|
|
7991
8088
|
}
|
|
7992
8089
|
/** The verdict of one validator over one finish attempt. */
|
|
7993
8090
|
type FinishValidationVerdict = {
|
|
@@ -8281,7 +8378,22 @@ declare const DEFAULT_ARTIFACT_PATTERN = "(?:run[ -]?[0-9A-HJKMNP-TV-Z]{6,26}|[\
|
|
|
8281
8378
|
* paragraphs away no longer satisfies the grade. Purely textual: what
|
|
8282
8379
|
* the referenced artifact contains is
|
|
8283
8380
|
* {@link citedValueValidator}'s question, and whether it exists on
|
|
8284
|
-
* disk is the host's.
|
|
8381
|
+
* disk is the host's.
|
|
8382
|
+
*
|
|
8383
|
+
* The run's OWN id is an artifact (RV2501). `DEFAULT_ARTIFACT_PATTERN`
|
|
8384
|
+
* only ever matched the literal word `run` followed by a ULID, so the
|
|
8385
|
+
* escape the verdict advertised was unreachable for every run whose id
|
|
8386
|
+
* the engine did not mint in that exact shape: the comparison run's
|
|
8387
|
+
* `comparison-rulvar-v12260-aug09-...` matched nothing, its synthesis
|
|
8388
|
+
* had no artifact it could name, and a document that told the truth
|
|
8389
|
+
* about the run it was part of could not be written at all. When
|
|
8390
|
+
* {@link FinishValidationInput.runId} is supplied (the orchestrator
|
|
8391
|
+
* runtime always supplies it), a sentence carrying that id verbatim as
|
|
8392
|
+
* a whole token satisfies the grade, and the verdict names the id so
|
|
8393
|
+
* the repair instruction is executable rather than aspirational. An id
|
|
8394
|
+
* shorter than `MIN_RUN_ID_ARTIFACT_CHARS` (six) is ignored, and
|
|
8395
|
+
* without an id the verdict is byte identical to the historical one.
|
|
8396
|
+
* Default name 'evidence-grade'.
|
|
8285
8397
|
*/
|
|
8286
8398
|
declare function evidenceGradeValidator(options?: {
|
|
8287
8399
|
/** Overrides {@link DEFAULT_EVIDENCE_GRADE_PHRASES}; matched case-insensitively. */phrases?: readonly string[]; /** Overrides {@link DEFAULT_ARTIFACT_PATTERN}. */
|
|
@@ -8314,6 +8426,27 @@ interface CitationTarget {
|
|
|
8314
8426
|
* ({@link citationTargetsValidator} judges every citation with no such
|
|
8315
8427
|
* precondition).
|
|
8316
8428
|
*
|
|
8429
|
+
* One span class is IDENTITY, not assertion (RV2502, the 1.226.0
|
|
8430
|
+
* comparison run): a span naming the artefact under review says which
|
|
8431
|
+
* commit, run, or release the document is about, and asserts nothing
|
|
8432
|
+
* about any cited line. That run's synthesis wrote its frozen commit
|
|
8433
|
+
* sha beside source citations and the validator demanded the sha appear
|
|
8434
|
+
* in the cited source, an impossible repair, in the same verdict that
|
|
8435
|
+
* demanded three real value fixes; two granted repairs burned and the
|
|
8436
|
+
* finish was rejected. Three shapes are structural and always excluded:
|
|
8437
|
+
* a commit sha (12 to 64 hex characters, long enough that ordinary hex
|
|
8438
|
+
* literals stay judged), a release version (`1.2.3`, `v1.2.3`, with an
|
|
8439
|
+
* optional prerelease or build tail), and the run's own id when the
|
|
8440
|
+
* runtime supplies `runId`. Host vocabulary is declared: `notValues`
|
|
8441
|
+
* lists spans this document writes as identity, verdict words like
|
|
8442
|
+
* `conditionally ready` among them.
|
|
8443
|
+
*
|
|
8444
|
+
* The run-id exclusion is what makes the bundle self consistent
|
|
8445
|
+
* (RV2501, RV2202): the evidence grade instructs a failing model to
|
|
8446
|
+
* write this run's id inside the offending sentence, and before RV2502
|
|
8447
|
+
* doing so beside a citation traded an evidence-grade failure for a
|
|
8448
|
+
* cited-value one. The two repair instructions now compose.
|
|
8449
|
+
*
|
|
8317
8450
|
* `resolve` is host code and must be PURE over a snapshot the host
|
|
8318
8451
|
* froze before the run, exactly like every other finish validator: a
|
|
8319
8452
|
* resolver that reads the filesystem live would make a verdict depend
|
|
@@ -8326,6 +8459,12 @@ declare function citedValueValidator(options: {
|
|
|
8326
8459
|
resolve: (target: CitationTarget) => string | undefined; /** Lines AFTER the cited one that may carry the value; default 0. */
|
|
8327
8460
|
window?: number; /** Overrides {@link DEFAULT_CITATION_PATTERN}; must capture `path:line`. */
|
|
8328
8461
|
pattern?: string;
|
|
8462
|
+
/**
|
|
8463
|
+
* Spans this host writes as IDENTITY rather than as a value asserted
|
|
8464
|
+
* about a citation (RV2502), matched whole and case sensitively.
|
|
8465
|
+
* Commit shas, versions, and the run's own id need no declaration.
|
|
8466
|
+
*/
|
|
8467
|
+
notValues?: readonly string[];
|
|
8329
8468
|
name?: string;
|
|
8330
8469
|
}): FinishValidator;
|
|
8331
8470
|
/**
|
|
@@ -8606,16 +8745,26 @@ declare function pairRunFactClaims(draftText: string, sheet: RunFactsSheet, opti
|
|
|
8606
8745
|
*
|
|
8607
8746
|
* - `'full'`: every citing sentence the draft carries had at least one
|
|
8608
8747
|
* judged pair, nothing was cut by a bound, no declared critical
|
|
8609
|
-
* anchor was missed, and the judge (when needed) settled ok.
|
|
8610
|
-
*
|
|
8611
|
-
* nothing
|
|
8612
|
-
*
|
|
8748
|
+
* anchor was missed, and the judge (when needed) settled ok.
|
|
8749
|
+
* - `'vacuous'` (RV2508): the draft carried NO citing sentence, so the
|
|
8750
|
+
* configured pass verified nothing. This used to grade `'full'` on
|
|
8751
|
+
* the reasoning that saying `'partial'` would imply a subset was
|
|
8752
|
+
* chosen, which is true and beside the point: `'full'` is the
|
|
8753
|
+
* strongest word in the vocabulary and it was standing over a
|
|
8754
|
+
* denominator of zero, the same silent green the grade exists to
|
|
8755
|
+
* abolish, at its extreme.
|
|
8613
8756
|
* - `'partial'`: the pass verified a strict subset: the pair bound
|
|
8614
8757
|
* truncated the fold, a run-facts bound truncated the run-claim
|
|
8615
8758
|
* pairs, or citing sentences exist that no judged pair covers.
|
|
8616
8759
|
* - `'critical-uncovered'`: at least one DECLARED critical anchor got
|
|
8617
8760
|
* no judged pair; stronger than `'partial'` because the caller named
|
|
8618
8761
|
* exactly these claims as the ones that must not go unverified.
|
|
8762
|
+
* - `'judge-declined'` (RV2508): the judge invocation was refused
|
|
8763
|
+
* ADMISSION and never dispatched (RV2106), so nothing was judged at
|
|
8764
|
+
* all. It ranks with a failed judge and above everything the counts
|
|
8765
|
+
* could say, because those counts describe a pass that did not
|
|
8766
|
+
* happen; before this the flag was invisible to the grade and a
|
|
8767
|
+
* declined judge over a citation-free draft graded `'full'`.
|
|
8619
8768
|
* - `'judge-failed'`: the judge invocation did not settle ok, so
|
|
8620
8769
|
* nothing was judged at all; every other reading of the meta is
|
|
8621
8770
|
* moot.
|
|
@@ -8624,7 +8773,7 @@ declare function pairRunFactClaims(draftText: string, sheet: RunFactsSheet, opti
|
|
|
8624
8773
|
* and total over metas written BEFORE the grade shipped, so a consumer
|
|
8625
8774
|
* can grade a persisted outcome from an older engine.
|
|
8626
8775
|
*/
|
|
8627
|
-
type ClaimCoverageGrade = "full" | "partial" | "critical-uncovered" | "judge-failed";
|
|
8776
|
+
type ClaimCoverageGrade = "full" | "vacuous" | "partial" | "critical-uncovered" | "judge-declined" | "judge-failed";
|
|
8628
8777
|
/** The subset of the claim-consistency meta the grade derives from. */
|
|
8629
8778
|
interface ClaimCoverageInput {
|
|
8630
8779
|
/** Draft sentences carrying at least one parsable anchor. */
|
|
@@ -8639,6 +8788,12 @@ interface ClaimCoverageInput {
|
|
|
8639
8788
|
runFactPairsTruncated?: true;
|
|
8640
8789
|
/** True when the judge invocation did not settle ok. */
|
|
8641
8790
|
judgeFailed?: true;
|
|
8791
|
+
/**
|
|
8792
|
+
* True when the judge invocation was refused ADMISSION and never
|
|
8793
|
+
* dispatched (RV2106). The orchestrator already spreads the flag into
|
|
8794
|
+
* the meta it grades, so nothing at the call site changes.
|
|
8795
|
+
*/
|
|
8796
|
+
judgeDeclined?: true;
|
|
8642
8797
|
}
|
|
8643
8798
|
/** Derives the {@link ClaimCoverageGrade} of a claim-consistency meta. */
|
|
8644
8799
|
declare function claimCoverageOf(meta: ClaimCoverageInput): ClaimCoverageGrade;
|
|
@@ -10296,6 +10451,39 @@ interface OrchestrateSynthesis {
|
|
|
10296
10451
|
*/
|
|
10297
10452
|
carryDraftGaps?: boolean;
|
|
10298
10453
|
/**
|
|
10454
|
+
* The no-regression floor under the synthesis (RV2505, the 1.226.0
|
|
10455
|
+
* comparison run). That run's coordination draft satisfied the FULL
|
|
10456
|
+
* declared contract, `skipWhenDraftValid` was off because the
|
|
10457
|
+
* operator wanted the composing pass anyway, and the synthesis then
|
|
10458
|
+
* failed the same bundle three times and died mid repair: the run
|
|
10459
|
+
* settled with NO result at all, having paid for four workers, the
|
|
10460
|
+
* draft that would have passed, and three rejected compositions.
|
|
10461
|
+
* With `true`, a synthesis that fails terminally does not throw away
|
|
10462
|
+
* a draft the contract accepts. The failure is caught at the
|
|
10463
|
+
* post-fan-in chokepoint, the coordination draft is judged by the
|
|
10464
|
+
* same `finishValidation.validators` that bind the synthesis finish,
|
|
10465
|
+
* and a draft every validator accepts becomes the run result under a
|
|
10466
|
+
* journaled 'orchestrator_synthesis_regressed' decision (the failure
|
|
10467
|
+
* message, the validator names, the draft hash, the contract
|
|
10468
|
+
* generation) plus a warn 'orchestrator synthesis regressed' log; the
|
|
10469
|
+
* envelope carries `synthesisRegressed`. A draft that fails too
|
|
10470
|
+
* journals 'orchestrator_synthesis_fallback_declined' naming ITS
|
|
10471
|
+
* failing validators and the original failure rethrows untouched, so
|
|
10472
|
+
* the decline is auditable instead of silent. Deterministic by
|
|
10473
|
+
* construction: only the declared contract judges, never a quality
|
|
10474
|
+
* heuristic, and the verdict is a pure function of the draft, so a
|
|
10475
|
+
* resume re-derives it without re-running the paid invocation.
|
|
10476
|
+
* Requires `finishValidation` (a ConfigError at intake otherwise:
|
|
10477
|
+
* without a contract there is nothing to judge either document by),
|
|
10478
|
+
* which transitively limits it to mode 'single'. Orthogonal to
|
|
10479
|
+
* `skipWhenDraftValid`: that gate decides whether to PAY for the
|
|
10480
|
+
* synthesis, this floor decides what to do when the paid one comes
|
|
10481
|
+
* back worse than the draft, and with both on a valid draft skips
|
|
10482
|
+
* before there is anything to regress. Default false: no catch, no
|
|
10483
|
+
* decision entry, no envelope field, byte for byte.
|
|
10484
|
+
*/
|
|
10485
|
+
fallbackToValidDraft?: boolean;
|
|
10486
|
+
/**
|
|
10299
10487
|
* The structured evidence index (RV808b): a deterministic per-child
|
|
10300
10488
|
* citation map in the 'single' synthesis prompt, so the composing
|
|
10301
10489
|
* model can target its reads instead of re-reading the whole
|
|
@@ -13277,7 +13465,11 @@ interface PreflightInput {
|
|
|
13277
13465
|
* Mirrors FinishValidationSpec.maxRepairs (default
|
|
13278
13466
|
* {@link DEFAULT_FINISH_MAX_REPAIRS}): with zero, the first
|
|
13279
13467
|
* rejection is final and there is no repair exchange to fund, so
|
|
13280
|
-
* the repair-reserve-unfunded warning stays silent.
|
|
13468
|
+
* the repair-reserve-unfunded warning stays silent. It also SIZES
|
|
13469
|
+
* the mandatory synthesis tail (RV2504): every granted repair can
|
|
13470
|
+
* write to the output allowance, so the tail
|
|
13471
|
+
* `synthesis-reserve-below-cap-composition` prices is one
|
|
13472
|
+
* composition plus this many turns, whatever the turn reserve says.
|
|
13281
13473
|
*/
|
|
13282
13474
|
maxRepairs?: number;
|
|
13283
13475
|
/**
|
package/dist/index.js
CHANGED
|
@@ -10926,11 +10926,16 @@ function applyOutputBudget(req, target, budget) {
|
|
|
10926
10926
|
const floor = outputFloorOf(target);
|
|
10927
10927
|
if (req.maxOutputTokens !== void 0 && req.maxOutputTokens < floor) throw new ConfigError(`the per-turn output cap ${String(req.maxOutputTokens)} is below the ${String(floor)} token output floor of ${target.resolved.ref}; the provider would reject every dispatch, so raise limits.maxOutputTokensPerTurn to at least the floor`);
|
|
10928
10928
|
const hook = budget?.maxAffordableOutputTokens;
|
|
10929
|
-
|
|
10930
|
-
|
|
10929
|
+
const exposureHook = budget?.maxExposureOutputTokens;
|
|
10930
|
+
if (hook === void 0 && exposureHook === void 0) return req;
|
|
10931
|
+
const estimatedInput = estimateInputTokens(req.messages);
|
|
10932
|
+
const budgetAffordable = hook?.(target.resolved.ref, estimatedInput);
|
|
10933
|
+
const exposureAffordable = exposureHook?.(target.resolved.ref, estimatedInput);
|
|
10934
|
+
const usableExposure = exposureAffordable !== void 0 && exposureAffordable >= floor ? exposureAffordable : void 0;
|
|
10935
|
+
const affordable = budgetAffordable === void 0 ? usableExposure : usableExposure === void 0 ? budgetAffordable : Math.min(budgetAffordable, usableExposure);
|
|
10931
10936
|
if (affordable === void 0) return req;
|
|
10932
10937
|
if (affordable < floor) {
|
|
10933
|
-
const zeroInputAffordable = hook(target.resolved.ref, 0);
|
|
10938
|
+
const zeroInputAffordable = hook?.(target.resolved.ref, 0);
|
|
10934
10939
|
if (zeroInputAffordable !== void 0 && zeroInputAffordable < floor) throw new BudgetExhaustedError(floor === 1 ? `the remaining budget cannot afford one output token from ${target.resolved.ref}; the turn was not dispatched` : `the remaining budget cannot afford the ${String(floor)} token output floor of ${target.resolved.ref}; the turn was not dispatched`, { data: { reason: "output-floor" } });
|
|
10935
10940
|
return {
|
|
10936
10941
|
...req,
|
|
@@ -13572,7 +13577,12 @@ async function runAgent(options) {
|
|
|
13572
13577
|
* model, and a turn that cannot afford one output token is denied before
|
|
13573
13578
|
* dispatch. Layer 3: the AbortSignal ceiling severing live streams, with
|
|
13574
13579
|
* partial usage written usageApprox.
|
|
13575
|
-
* B0 is immutable
|
|
13580
|
+
* B0 is immutable WITHIN a segment: no API tops up a live run's ceiling
|
|
13581
|
+
* (RV2511 corrects the older "immutable after start", which RV2208 made
|
|
13582
|
+
* false). The one thing that can change it is `ResumeOptions.run`, an
|
|
13583
|
+
* explicit host decision journaled as its own decision entry, and it
|
|
13584
|
+
* takes effect only by opening a NEW segment: a live run can never
|
|
13585
|
+
* raise the bound it is already being measured against.
|
|
13576
13586
|
*
|
|
13577
13587
|
* The account tree: the run root plus one
|
|
13578
13588
|
* sub-account per admitted child workflow (and, from M7, the orchestrator
|
|
@@ -13677,6 +13687,8 @@ var RunBudget = class {
|
|
|
13677
13687
|
* reservation surface is inert and reserveTurnExposure never binds.
|
|
13678
13688
|
*/
|
|
13679
13689
|
maxInFlightExposureUsd;
|
|
13690
|
+
/** The opt-in lone-dispatch clamp (RV2503); see maxExposureOutputTokens. */
|
|
13691
|
+
clampTurnToExposure = false;
|
|
13680
13692
|
lifetimeSpawnCap;
|
|
13681
13693
|
events;
|
|
13682
13694
|
priceUsd;
|
|
@@ -13745,6 +13757,7 @@ var RunBudget = class {
|
|
|
13745
13757
|
requireValidCeiling(options.maxInFlightExposureUsd, "maxInFlightExposureUsd");
|
|
13746
13758
|
this.maxInFlightExposureUsd = options.maxInFlightExposureUsd;
|
|
13747
13759
|
}
|
|
13760
|
+
this.clampTurnToExposure = options.clampTurnToExposure === true;
|
|
13748
13761
|
this.lifetimeSpawnCap = options.lifetimeSpawnCap ?? 500;
|
|
13749
13762
|
if (options.events !== void 0) this.events = options.events;
|
|
13750
13763
|
if (options.priceUsd !== void 0) this.priceUsd = options.priceUsd;
|
|
@@ -14327,6 +14340,59 @@ var RunBudget = class {
|
|
|
14327
14340
|
return affordableOutputTokens(pricing, remainingUsd, estimatedInputTokens);
|
|
14328
14341
|
}
|
|
14329
14342
|
/**
|
|
14343
|
+
* The same layer-2b question asked of the IN-FLIGHT EXPOSURE ceiling
|
|
14344
|
+
* (RV2503): the output tokens `cap - spent - live estimates` still
|
|
14345
|
+
* affords from `servedBy` for an estimated prompt, priced by the
|
|
14346
|
+
* settlement function like every other estimate here.
|
|
14347
|
+
*
|
|
14348
|
+
* The clamp above has always existed for the budget ceiling while
|
|
14349
|
+
* {@link reserveTurnExposure} only ever answered yes or no, so a
|
|
14350
|
+
* turn whose FULL planned output overshot the exposure line was
|
|
14351
|
+
* refused outright even when a shorter one fit and the budget could
|
|
14352
|
+
* pay for it. The 1.226.0 comparison run died exactly there: it held
|
|
14353
|
+
* 0.8642 USD of budget, the exposure ceiling had 0.5642 USD of room,
|
|
14354
|
+
* the mandatory repair turn was estimated at 0.7066 USD against an
|
|
14355
|
+
* 18000 token output plan, and the dispatch was refused before any
|
|
14356
|
+
* provider call. The same turn, re-issued after the operator raised
|
|
14357
|
+
* the ceiling, wrote 12840 output tokens and cost 0.4788 USD: it fit
|
|
14358
|
+
* the ceiling that refused it, and a clamp to the ~13253 tokens the
|
|
14359
|
+
* room afforded would have let it run.
|
|
14360
|
+
*
|
|
14361
|
+
* Answered ONLY for a dispatch that is alone in flight, which is the
|
|
14362
|
+
* whole difference between a refusal that means something and one
|
|
14363
|
+
* that means nothing. With siblings live the refusal is TRANSIENT:
|
|
14364
|
+
* RV1902 parks on it and the turn runs at its full planned length
|
|
14365
|
+
* the moment one of them releases, so shortening it would trade a
|
|
14366
|
+
* complete answer for a truncated one and buy nothing. With nothing
|
|
14367
|
+
* live the refusal is PERMANENT (RV2003's sweep wakes such a waiter
|
|
14368
|
+
* 'drained' precisely because no hold will ever return), and the
|
|
14369
|
+
* only choices left are a shorter turn or no turn at all. The
|
|
14370
|
+
* concurrent-wave bound of RV711 is therefore untouched.
|
|
14371
|
+
*
|
|
14372
|
+
* Opt-in through `RunOptions.clampTurnToExposure`, so the drained
|
|
14373
|
+
* refusal terminals RV1902, RV2002 and RV2003 built out of live
|
|
14374
|
+
* parity deaths keep their shapes until a host asks for this one.
|
|
14375
|
+
*
|
|
14376
|
+
* Undefined when the clamp is not armed, when the cap is not
|
|
14377
|
+
* configured, when anything is in flight, or when the model has no
|
|
14378
|
+
* price row, so a run that declares nothing keeps every byte of its
|
|
14379
|
+
* historical path. Zero or
|
|
14380
|
+
* negative when the room cannot even pay for the prompt, the same
|
|
14381
|
+
* convention {@link maxAffordableOutputTokens} inherits from
|
|
14382
|
+
* `affordableOutputTokens`; the caller decides what a sub-floor
|
|
14383
|
+
* answer means, and the loop deliberately ignores one so a true
|
|
14384
|
+
* exposure exhaustion still refuses through
|
|
14385
|
+
* {@link reserveTurnExposure} with its own typed reason instead of
|
|
14386
|
+
* an output-floor verdict.
|
|
14387
|
+
*/
|
|
14388
|
+
maxExposureOutputTokens(servedBy, estimatedInputTokens) {
|
|
14389
|
+
const cap = this.maxInFlightExposureUsd;
|
|
14390
|
+
if (!this.clampTurnToExposure || cap === void 0 || this.inFlightExposureUsd > 0) return;
|
|
14391
|
+
const pricing = this.pricingOf?.(servedBy);
|
|
14392
|
+
if (pricing === void 0) return;
|
|
14393
|
+
return affordableOutputTokens(pricing, Math.max(0, cap - this.root.spentUsd), estimatedInputTokens);
|
|
14394
|
+
}
|
|
14395
|
+
/**
|
|
14330
14396
|
* Live accounting; spend propagates from `accountScope` to every
|
|
14331
14397
|
* ancestor. Crossing a ceiling severs the crossing account's subtree
|
|
14332
14398
|
* via its layer-3 AbortSignal (overshoot bounded by one turn per
|
|
@@ -18132,6 +18198,7 @@ function createCtx(internals, rootWorkflow) {
|
|
|
18132
18198
|
remainingUsd: () => internals.budget.remainingUsd(budgetAccount),
|
|
18133
18199
|
...internals.budget.strictPricing === void 0 ? {} : { assertPricedDispatch: (servedBy) => internals.budget.assertPricedDispatch(servedBy) },
|
|
18134
18200
|
...internals.budget.maxInFlightExposureUsd === void 0 ? {} : {
|
|
18201
|
+
maxExposureOutputTokens: (servedBy, estimatedInputTokens) => internals.budget.maxExposureOutputTokens(servedBy, estimatedInputTokens),
|
|
18135
18202
|
admitTurnExposure: (servedBy, estimatedInputTokens, plannedOutputTokens) => internals.budget.reserveTurnExposure(servedBy, estimatedInputTokens, plannedOutputTokens, `agent:${running.seq}`),
|
|
18136
18203
|
awaitExposureRelease: (signal) => internals.budget.awaitExposureRelease(signal),
|
|
18137
18204
|
liveExposureUsd: () => internals.budget.liveExposureUsd
|
|
@@ -19954,6 +20021,29 @@ const MAX_LISTED_CITATIONS = 20;
|
|
|
19954
20021
|
*/
|
|
19955
20022
|
const MAX_NAMED_OFFENDING_SENTENCES = 5;
|
|
19956
20023
|
const MAX_OFFENDING_SENTENCE_CHARS = 240;
|
|
20024
|
+
/**
|
|
20025
|
+
* The shortest run id {@link evidenceGradeValidator} will accept as an
|
|
20026
|
+
* artifact (RV2501). The floor mirrors the id half of
|
|
20027
|
+
* {@link DEFAULT_ARTIFACT_PATTERN}: a two character id would satisfy
|
|
20028
|
+
* nearly every sentence by accident, which is the fail-open the empty
|
|
20029
|
+
* pattern guard exists to prevent. The id is additionally matched as a
|
|
20030
|
+
* whole identifier, so it cannot be credited from inside a longer
|
|
20031
|
+
* word, and only inside the sentence making the claim.
|
|
20032
|
+
*/
|
|
20033
|
+
const MIN_RUN_ID_ARTIFACT_CHARS = 6;
|
|
20034
|
+
/**
|
|
20035
|
+
* True when `value` occurs in `haystack` as a whole IDENTIFIER
|
|
20036
|
+
* (RV2501). Deliberately not {@link containsToken}: that boundary
|
|
20037
|
+
* class carries the dot, so an id written at the end of a sentence is
|
|
20038
|
+
* followed by the sentence period and would never be credited, which
|
|
20039
|
+
* is the most natural place to write one. Word characters alone bound
|
|
20040
|
+
* an id, so `x<id>y` is refused while `` `<id>` `` and `<id>.` are
|
|
20041
|
+
* credited. The value is matched literally (metacharacters escaped).
|
|
20042
|
+
*/
|
|
20043
|
+
function containsIdentifier(haystack, value) {
|
|
20044
|
+
const escaped = value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
20045
|
+
return new RegExp(`(?<!\\w)${escaped}(?!\\w)`, "u").test(haystack);
|
|
20046
|
+
}
|
|
19957
20047
|
function listCitations(values) {
|
|
19958
20048
|
return values.length <= MAX_LISTED_CITATIONS ? values.join(", ") : `${values.slice(0, MAX_LISTED_CITATIONS).join(", ")} and ${String(values.length - MAX_LISTED_CITATIONS)} more`;
|
|
19959
20049
|
}
|
|
@@ -20228,7 +20318,22 @@ const DEFAULT_ARTIFACT_PATTERN = "(?:run[ -]?[0-9A-HJKMNP-TV-Z]{6,26}|[\\w./-]+\
|
|
|
20228
20318
|
* paragraphs away no longer satisfies the grade. Purely textual: what
|
|
20229
20319
|
* the referenced artifact contains is
|
|
20230
20320
|
* {@link citedValueValidator}'s question, and whether it exists on
|
|
20231
|
-
* disk is the host's.
|
|
20321
|
+
* disk is the host's.
|
|
20322
|
+
*
|
|
20323
|
+
* The run's OWN id is an artifact (RV2501). `DEFAULT_ARTIFACT_PATTERN`
|
|
20324
|
+
* only ever matched the literal word `run` followed by a ULID, so the
|
|
20325
|
+
* escape the verdict advertised was unreachable for every run whose id
|
|
20326
|
+
* the engine did not mint in that exact shape: the comparison run's
|
|
20327
|
+
* `comparison-rulvar-v12260-aug09-...` matched nothing, its synthesis
|
|
20328
|
+
* had no artifact it could name, and a document that told the truth
|
|
20329
|
+
* about the run it was part of could not be written at all. When
|
|
20330
|
+
* {@link FinishValidationInput.runId} is supplied (the orchestrator
|
|
20331
|
+
* runtime always supplies it), a sentence carrying that id verbatim as
|
|
20332
|
+
* a whole token satisfies the grade, and the verdict names the id so
|
|
20333
|
+
* the repair instruction is executable rather than aspirational. An id
|
|
20334
|
+
* shorter than `MIN_RUN_ID_ARTIFACT_CHARS` (six) is ignored, and
|
|
20335
|
+
* without an id the verdict is byte identical to the historical one.
|
|
20336
|
+
* Default name 'evidence-grade'.
|
|
20232
20337
|
*/
|
|
20233
20338
|
function evidenceGradeValidator(options) {
|
|
20234
20339
|
const phrases = options?.phrases === void 0 ? [...DEFAULT_EVIDENCE_GRADE_PHRASES] : requireNonEmptyStrings(options.phrases, "evidenceGradeValidator phrases");
|
|
@@ -20246,10 +20351,11 @@ function evidenceGradeValidator(options) {
|
|
|
20246
20351
|
validate: (input) => {
|
|
20247
20352
|
const unsupported = [];
|
|
20248
20353
|
const offenders = [];
|
|
20354
|
+
const runId = typeof input.runId === "string" && input.runId.trim().length >= MIN_RUN_ID_ARTIFACT_CHARS ? input.runId.trim() : void 0;
|
|
20249
20355
|
for (const sentence of sentencesOf(input.text)) {
|
|
20250
20356
|
const haystack = sentence.toLowerCase();
|
|
20251
20357
|
const found = lowered.filter((phrase) => haystack.includes(phrase));
|
|
20252
|
-
if (found.length === 0 || new RegExp(artifactPattern, "").test(sentence)) continue;
|
|
20358
|
+
if (found.length === 0 || new RegExp(artifactPattern, "").test(sentence) || runId !== void 0 && containsIdentifier(sentence, runId)) continue;
|
|
20253
20359
|
offenders.push(sentence);
|
|
20254
20360
|
for (const phrase of found) if (!unsupported.includes(phrase)) unsupported.push(phrase);
|
|
20255
20361
|
}
|
|
@@ -20262,7 +20368,7 @@ function evidenceGradeValidator(options) {
|
|
|
20262
20368
|
return {
|
|
20263
20369
|
ok: false,
|
|
20264
20370
|
reasons: [
|
|
20265
|
-
`evidence-grade claims cite no run or repro artifact in their own sentence: ${listCitations(unsupported)}; give each such claim a file:line citation in its own sentence, or state its run id in a SEPARATE sentence carrying no source citation (a run id written beside a path:line citation is not in the cited window and trades this failure for a cited-value one)`,
|
|
20371
|
+
runId === void 0 ? `evidence-grade claims cite no run or repro artifact in their own sentence: ${listCitations(unsupported)}; give each such claim a file:line citation in its own sentence, or state its run id in a SEPARATE sentence carrying no source citation (a run id written beside a path:line citation is not in the cited window and trades this failure for a cited-value one)` : `evidence-grade claims cite no run or repro artifact in their own sentence: ${listCitations(unsupported)}; write this run's id ${runId} inside each such sentence, or give the claim a file:line citation instead (the id may share a sentence with a source citation: cited-value reads a run id as identity, not as a value asserted about the cited line)`,
|
|
20266
20372
|
...named,
|
|
20267
20373
|
...overflow > 0 ? [`and ${String(overflow)} more offending sentences`] : []
|
|
20268
20374
|
]
|
|
@@ -20273,6 +20379,15 @@ function evidenceGradeValidator(options) {
|
|
|
20273
20379
|
/** Splits a citation into its path and line halves at the LAST colon. */
|
|
20274
20380
|
const CITATION_TAIL$1 = /^(.*):(\d+)$/u;
|
|
20275
20381
|
/**
|
|
20382
|
+
* A commit sha span (RV2502). Twelve hex characters is the floor: real
|
|
20383
|
+
* abbreviations run 7 to 12 and full shas 40, while shorter hex words
|
|
20384
|
+
* (`deadbeef`) are ordinary literals a document may legitimately assert
|
|
20385
|
+
* about a cited line, so they stay judged.
|
|
20386
|
+
*/
|
|
20387
|
+
const COMMIT_SHA_SPAN = /^[0-9a-f]{12,64}$/u;
|
|
20388
|
+
/** A release version span (RV2502), with an optional tail. */
|
|
20389
|
+
const VERSION_SPAN = /^v?\d+\.\d+\.\d+(?:[-+][0-9A-Za-z][0-9A-Za-z.-]*)?$/u;
|
|
20390
|
+
/**
|
|
20276
20391
|
* True when `value` occurs in `haystack` as a WHOLE token (RV1402, the
|
|
20277
20392
|
* seventeenth comparison experiment P0-1). Substring matching credited
|
|
20278
20393
|
* an asserted `3` against a line that says `30`, so the validator
|
|
@@ -20308,6 +20423,27 @@ function containsToken(haystack, value) {
|
|
|
20308
20423
|
* ({@link citationTargetsValidator} judges every citation with no such
|
|
20309
20424
|
* precondition).
|
|
20310
20425
|
*
|
|
20426
|
+
* One span class is IDENTITY, not assertion (RV2502, the 1.226.0
|
|
20427
|
+
* comparison run): a span naming the artefact under review says which
|
|
20428
|
+
* commit, run, or release the document is about, and asserts nothing
|
|
20429
|
+
* about any cited line. That run's synthesis wrote its frozen commit
|
|
20430
|
+
* sha beside source citations and the validator demanded the sha appear
|
|
20431
|
+
* in the cited source, an impossible repair, in the same verdict that
|
|
20432
|
+
* demanded three real value fixes; two granted repairs burned and the
|
|
20433
|
+
* finish was rejected. Three shapes are structural and always excluded:
|
|
20434
|
+
* a commit sha (12 to 64 hex characters, long enough that ordinary hex
|
|
20435
|
+
* literals stay judged), a release version (`1.2.3`, `v1.2.3`, with an
|
|
20436
|
+
* optional prerelease or build tail), and the run's own id when the
|
|
20437
|
+
* runtime supplies `runId`. Host vocabulary is declared: `notValues`
|
|
20438
|
+
* lists spans this document writes as identity, verdict words like
|
|
20439
|
+
* `conditionally ready` among them.
|
|
20440
|
+
*
|
|
20441
|
+
* The run-id exclusion is what makes the bundle self consistent
|
|
20442
|
+
* (RV2501, RV2202): the evidence grade instructs a failing model to
|
|
20443
|
+
* write this run's id inside the offending sentence, and before RV2502
|
|
20444
|
+
* doing so beside a citation traded an evidence-grade failure for a
|
|
20445
|
+
* cited-value one. The two repair instructions now compose.
|
|
20446
|
+
*
|
|
20311
20447
|
* `resolve` is host code and must be PURE over a snapshot the host
|
|
20312
20448
|
* froze before the run, exactly like every other finish validator: a
|
|
20313
20449
|
* resolver that reads the filesystem live would make a verdict depend
|
|
@@ -20318,6 +20454,9 @@ function containsToken(haystack, value) {
|
|
|
20318
20454
|
*/
|
|
20319
20455
|
function citedValueValidator(options) {
|
|
20320
20456
|
if (typeof options.resolve !== "function") throw new ConfigError("citedValueValidator resolve must be a function");
|
|
20457
|
+
if (options.notValues !== void 0) {
|
|
20458
|
+
if (!Array.isArray(options.notValues) || options.notValues.some((value) => typeof value !== "string" || value.length === 0)) throw new ConfigError("citedValueValidator notValues must be an array of non empty strings");
|
|
20459
|
+
}
|
|
20321
20460
|
const window = options.window ?? 0;
|
|
20322
20461
|
if (!Number.isInteger(window) || window < 0) throw new ConfigError(`citedValueValidator window must be a non negative integer; got ${String(window)}`);
|
|
20323
20462
|
const pattern = options.pattern ?? "[\\w./-]+\\.\\w+:\\d+";
|
|
@@ -20326,10 +20465,17 @@ function citedValueValidator(options) {
|
|
|
20326
20465
|
} catch (thrown) {
|
|
20327
20466
|
throw new ConfigError(`citedValueValidator pattern does not compile: ${thrown instanceof Error ? thrown.message : String(thrown)}`);
|
|
20328
20467
|
}
|
|
20468
|
+
const declaredNotValues = new Set(options.notValues ?? []);
|
|
20329
20469
|
return {
|
|
20330
20470
|
name: options.name ?? "cited-value",
|
|
20331
20471
|
validate: (input) => {
|
|
20332
20472
|
const reasons = [];
|
|
20473
|
+
const runId = typeof input.runId === "string" && input.runId.trim().length >= MIN_RUN_ID_ARTIFACT_CHARS ? input.runId.trim() : void 0;
|
|
20474
|
+
/**
|
|
20475
|
+
* True when the span NAMES the artefact under review instead of
|
|
20476
|
+
* asserting something about a cited line (RV2502).
|
|
20477
|
+
*/
|
|
20478
|
+
const isIdentity = (span) => declaredNotValues.has(span) || span === runId || COMMIT_SHA_SPAN.test(span) || VERSION_SPAN.test(span);
|
|
20333
20479
|
for (const sentence of sentencesOf(input.text)) {
|
|
20334
20480
|
const spans = [...sentence.matchAll(/`([^`]+)`/gu)].map((match) => match[1]);
|
|
20335
20481
|
const citations = [];
|
|
@@ -20341,7 +20487,7 @@ function citedValueValidator(options) {
|
|
|
20341
20487
|
path: parsed[1],
|
|
20342
20488
|
line
|
|
20343
20489
|
});
|
|
20344
|
-
else values.push(span);
|
|
20490
|
+
else if (!isIdentity(span)) values.push(span);
|
|
20345
20491
|
}
|
|
20346
20492
|
if (citations.length === 0 || values.length === 0) continue;
|
|
20347
20493
|
for (const citation of citations) {
|
|
@@ -20917,8 +21063,10 @@ function pairRunFactClaims(draftText, sheet, options) {
|
|
|
20917
21063
|
/** Derives the {@link ClaimCoverageGrade} of a claim-consistency meta. */
|
|
20918
21064
|
function claimCoverageOf(meta) {
|
|
20919
21065
|
if (meta.judgeFailed === true) return "judge-failed";
|
|
21066
|
+
if (meta.judgeDeclined === true) return "judge-declined";
|
|
20920
21067
|
if ((meta.criticalUncoveredTotal ?? 0) > 0) return "critical-uncovered";
|
|
20921
21068
|
if (meta.truncated || meta.runFactPairsTruncated === true || meta.coveredCitingSentences < meta.draftCitingSentences) return "partial";
|
|
21069
|
+
if (meta.draftCitingSentences === 0) return "vacuous";
|
|
20922
21070
|
return "full";
|
|
20923
21071
|
}
|
|
20924
21072
|
//#endregion
|
|
@@ -21545,6 +21693,11 @@ function validateOrchestrateOptions(opts) {
|
|
|
21545
21693
|
if (typeof conditional.carryDraftGaps !== "boolean") throw new ConfigError("orchestrate synthesis.carryDraftGaps must be a boolean; got " + typeof conditional.carryDraftGaps);
|
|
21546
21694
|
if (conditional.carryDraftGaps && conditional.skipWhenDraftValid !== true) throw new ConfigError("orchestrate synthesis.carryDraftGaps requires skipWhenDraftValid: the gaps ARE the failed pre-pass verdict, and without the pre-pass there is nothing to carry");
|
|
21547
21695
|
}
|
|
21696
|
+
const floor = synthesis.fallbackToValidDraft;
|
|
21697
|
+
if (floor !== void 0) {
|
|
21698
|
+
if (typeof floor !== "boolean") throw new ConfigError("orchestrate synthesis.fallbackToValidDraft must be a boolean; got " + typeof floor);
|
|
21699
|
+
if (floor && opts.finishValidation === void 0) throw new ConfigError("orchestrate synthesis.fallbackToValidDraft requires finishValidation: without a declared finish contract there is nothing to judge the draft valid by");
|
|
21700
|
+
}
|
|
21548
21701
|
if (symmetry.context !== void 0 && symmetry.context !== "digests" && symmetry.context !== "full") throw new ConfigError("orchestrate synthesis.context must be 'digests' or 'full'; got " + JSON.stringify(symmetry.context));
|
|
21549
21702
|
const index = synthesis.evidenceIndex;
|
|
21550
21703
|
if (index !== void 0) {
|
|
@@ -22900,7 +23053,8 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
22900
23053
|
const input = {
|
|
22901
23054
|
result,
|
|
22902
23055
|
text: typeof result === "string" ? result : JSON.stringify(result),
|
|
22903
|
-
children: validationChildren()
|
|
23056
|
+
children: validationChildren(),
|
|
23057
|
+
runId: internals.runId
|
|
22904
23058
|
};
|
|
22905
23059
|
const failed = [];
|
|
22906
23060
|
for (const validator of validationSpec.validators) {
|
|
@@ -23013,7 +23167,8 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
23013
23167
|
const input = {
|
|
23014
23168
|
result,
|
|
23015
23169
|
text,
|
|
23016
|
-
children: validationChildren()
|
|
23170
|
+
children: validationChildren(),
|
|
23171
|
+
runId: internals.runId
|
|
23017
23172
|
};
|
|
23018
23173
|
for (const validator of validationSpec?.validators ?? []) {
|
|
23019
23174
|
let verdict;
|
|
@@ -23848,7 +24003,8 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
23848
24003
|
const input = {
|
|
23849
24004
|
result: draftValue,
|
|
23850
24005
|
text: typeof draftValue === "string" ? draftValue : JSON.stringify(draftValue),
|
|
23851
|
-
children: validationChildren()
|
|
24006
|
+
children: validationChildren(),
|
|
24007
|
+
runId: internals.runId
|
|
23852
24008
|
};
|
|
23853
24009
|
const failed = [];
|
|
23854
24010
|
for (const validator of validationSpec.validators) {
|
|
@@ -24036,13 +24192,14 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
24036
24192
|
}
|
|
24037
24193
|
return `RUN FACTS: ${JSON.stringify({
|
|
24038
24194
|
scope: "settled-children-only",
|
|
24195
|
+
runId: internals.runId,
|
|
24039
24196
|
children: settledEntries.length,
|
|
24040
24197
|
byStatus: Object.fromEntries(Object.keys(byStatus).sort().map((status) => [status, byStatus[status]])),
|
|
24041
24198
|
wireRequests,
|
|
24042
24199
|
wireIdsMissing,
|
|
24043
24200
|
inputTokens,
|
|
24044
24201
|
outputTokens
|
|
24045
|
-
})} (live-observed by this run's own harness; production evidence it is not; the settled children ONLY, excluding this orchestrator, judges, and synthesis; the whole run's totals are the terminal envelope and invoice)`;
|
|
24202
|
+
})} (live-observed by run ${internals.runId}, this run's own harness; production evidence it is not; the settled children ONLY, excluding this orchestrator, judges, and synthesis; the whole run's totals are the terminal envelope and invoice)`;
|
|
24046
24203
|
})()] : [],
|
|
24047
24204
|
`GOAL: ${goal}`,
|
|
24048
24205
|
`DRAFT: ${draftJson}`,
|
|
@@ -24403,6 +24560,83 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
24403
24560
|
} : { ran: true },
|
|
24404
24561
|
synthesis
|
|
24405
24562
|
});
|
|
24563
|
+
/**
|
|
24564
|
+
* The no-regression fallback verdict (RV2505), set only when the
|
|
24565
|
+
* floor actually caught a failing synthesis: the truncated failure
|
|
24566
|
+
* message and the journal seq of the decision that recorded it.
|
|
24567
|
+
*/
|
|
24568
|
+
let synthesisRegressed;
|
|
24569
|
+
/**
|
|
24570
|
+
* The no-regression floor under the synthesis (RV2505, the 1.226.0
|
|
24571
|
+
* comparison run): a synthesis that fails terminally must not throw
|
|
24572
|
+
* away a coordination draft the SAME declared contract accepts.
|
|
24573
|
+
* Judges the draft with the validator bundle, journals what it
|
|
24574
|
+
* found either way, and answers whether the caller should settle on
|
|
24575
|
+
* the draft instead of rethrowing. Pure: the verdict is a function
|
|
24576
|
+
* of the draft and the validators, so a resume that re-fails the
|
|
24577
|
+
* synthesis re-derives the identical answer, and the journaled
|
|
24578
|
+
* decision is reused rather than duplicated.
|
|
24579
|
+
*/
|
|
24580
|
+
const draftFallbackOnRegression = async (draft, thrown) => {
|
|
24581
|
+
if (!(opts?.synthesis?.fallbackToValidDraft === true) || validationSpec === void 0) return { used: false };
|
|
24582
|
+
if (thrown instanceof ConfigError) return { used: false };
|
|
24583
|
+
const draftValue = draft ?? null;
|
|
24584
|
+
const draftHash = createHash("sha256").update(jcsSerialize(draftValue), "utf8").digest("hex");
|
|
24585
|
+
const validatorNames = validationSpec.validators.map((validator) => validator.name);
|
|
24586
|
+
const input = {
|
|
24587
|
+
result: draftValue,
|
|
24588
|
+
text: typeof draftValue === "string" ? draftValue : JSON.stringify(draftValue),
|
|
24589
|
+
children: validationChildren()
|
|
24590
|
+
};
|
|
24591
|
+
const failed = [];
|
|
24592
|
+
for (const validator of validationSpec.validators) {
|
|
24593
|
+
let verdict;
|
|
24594
|
+
try {
|
|
24595
|
+
verdict = validator.validate(input);
|
|
24596
|
+
} catch (validatorThrew) {
|
|
24597
|
+
throw new ConfigError(`finish validator '${validator.name}' threw instead of returning a verdict during the fallbackToValidDraft judgement: ` + (validatorThrew instanceof Error ? validatorThrew.message : String(validatorThrew)));
|
|
24598
|
+
}
|
|
24599
|
+
if (!verdict.ok) failed.push({
|
|
24600
|
+
name: validator.name,
|
|
24601
|
+
reasons: verdict.reasons
|
|
24602
|
+
});
|
|
24603
|
+
}
|
|
24604
|
+
const regressed = failed.length === 0;
|
|
24605
|
+
const reason = (thrown instanceof Error ? thrown.message : String(thrown)).slice(0, 300);
|
|
24606
|
+
const key = deriverV2.deriveKey({ kind: regressed ? "orchestrator-synthesis-regressed" : "orchestrator-synthesis-fallback-declined" });
|
|
24607
|
+
const entryRef = internals.replayer.snapshot().find((entry) => entry.kind === "decision" && entry.key === key)?.seq ?? (await internals.replayer.appendSinglePhase({
|
|
24608
|
+
scope: callingState.scope,
|
|
24609
|
+
key,
|
|
24610
|
+
kind: "decision",
|
|
24611
|
+
status: "ok",
|
|
24612
|
+
spanId: internals.spans.mint(callingState.spanId),
|
|
24613
|
+
site: "orchestrator-synthesis-fallback",
|
|
24614
|
+
value: {
|
|
24615
|
+
decisionType: regressed ? "orchestrator_synthesis_regressed" : "orchestrator_synthesis_fallback_declined",
|
|
24616
|
+
reason,
|
|
24617
|
+
validators: validatorNames,
|
|
24618
|
+
...regressed ? {} : { failed },
|
|
24619
|
+
...validationSpec.contract === void 0 ? {} : { contractHash: validationSpec.contract.hash },
|
|
24620
|
+
draftHash
|
|
24621
|
+
}
|
|
24622
|
+
})).seq;
|
|
24623
|
+
internals.events.emit({
|
|
24624
|
+
type: "log",
|
|
24625
|
+
level: "warn",
|
|
24626
|
+
msg: regressed ? "orchestrator synthesis regressed" : "orchestrator synthesis fallback declined",
|
|
24627
|
+
data: {
|
|
24628
|
+
reason,
|
|
24629
|
+
decisionRef: entryRef,
|
|
24630
|
+
...regressed ? {} : { draftFailed: failed.map((row) => row.name) }
|
|
24631
|
+
}
|
|
24632
|
+
}, callingState.spanId);
|
|
24633
|
+
if (!regressed) return { used: false };
|
|
24634
|
+
synthesisRegressed = {
|
|
24635
|
+
reason,
|
|
24636
|
+
decisionRef: entryRef
|
|
24637
|
+
};
|
|
24638
|
+
return { used: true };
|
|
24639
|
+
};
|
|
24406
24640
|
const enrichSynthesisFailure = (thrown, snapshot) => {
|
|
24407
24641
|
const passTruth = {
|
|
24408
24642
|
...claimConsistencyMeta === void 0 ? {} : { claimConsistencyMeta },
|
|
@@ -24445,6 +24679,7 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
24445
24679
|
return await runSynthesis(result.output);
|
|
24446
24680
|
} catch (thrown) {
|
|
24447
24681
|
await journalSynthesisAdmissionDecline(thrown);
|
|
24682
|
+
if ((await draftFallbackOnRegression(result.output, thrown)).used) return result.output;
|
|
24448
24683
|
return enrichSynthesisFailure(thrown);
|
|
24449
24684
|
}
|
|
24450
24685
|
}
|
|
@@ -24652,7 +24887,8 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
24652
24887
|
synthesizedFinal = await runSynthesis(result.output);
|
|
24653
24888
|
} catch (thrown) {
|
|
24654
24889
|
await journalSynthesisAdmissionDecline(thrown);
|
|
24655
|
-
|
|
24890
|
+
if ((await draftFallbackOnRegression(result.output, thrown)).used) synthesizedFinal = result.output;
|
|
24891
|
+
else enrichSynthesisFailure(thrown, {
|
|
24656
24892
|
completion: decision.completion,
|
|
24657
24893
|
childStatusCounts: decision.childStatusCounts,
|
|
24658
24894
|
degradedReasons: decision.degradedReasons,
|
|
@@ -24676,6 +24912,7 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
24676
24912
|
...envelopeSchemaRecovered === 0 ? {} : { schemaRecoveredFinishExchanges: envelopeSchemaRecovered },
|
|
24677
24913
|
...synthesisReserveLifecycle === void 0 ? {} : { synthesisReserve: synthesisReserveLifecycle },
|
|
24678
24914
|
...synthesisSkippedByValidDraft ? { synthesisSkipped: "synthesis_skipped_by_valid_draft" } : {},
|
|
24915
|
+
...synthesisRegressed === void 0 ? {} : { synthesisRegressed },
|
|
24679
24916
|
...contradictionsFound === void 0 ? {} : {
|
|
24680
24917
|
contradictions: contradictionsFound,
|
|
24681
24918
|
contradictionsMeta
|
|
@@ -25337,12 +25574,18 @@ function preflightEstimate(input) {
|
|
|
25337
25574
|
cacheReadTokens: 0,
|
|
25338
25575
|
cacheWriteTokens: 0
|
|
25339
25576
|
});
|
|
25340
|
-
const
|
|
25341
|
-
const
|
|
25342
|
-
|
|
25343
|
-
|
|
25577
|
+
const grantedRepairs = input.finishValidation === void 0 ? 0 : input.finishValidation.maxRepairs ?? 1;
|
|
25578
|
+
const tailTurns = 1 + grantedRepairs;
|
|
25579
|
+
const requiredUsd = compositionUsd * tailTurns;
|
|
25580
|
+
const exposureCapUsd = input.run?.maxInFlightExposureUsd;
|
|
25581
|
+
const reserveLineUsd = ceilingUsd === void 0 ? void 0 : ceilingUsd - declared;
|
|
25582
|
+
const exposureRoomUsd = exposureCapUsd === void 0 || reserveLineUsd === void 0 ? void 0 : exposureCapUsd - reserveLineUsd;
|
|
25583
|
+
const reserveShort = declared < requiredUsd;
|
|
25584
|
+
const exposureShort = exposureRoomUsd !== void 0 && exposureRoomUsd < requiredUsd;
|
|
25585
|
+
if (reserveShort || exposureShort) say({
|
|
25586
|
+
severity: reserveShort && exposureShort ? "error" : "warning",
|
|
25344
25587
|
code: "synthesis-reserve-below-cap-composition",
|
|
25345
|
-
message: `
|
|
25588
|
+
message: `the mandatory synthesis tail is ${String(tailTurns)} turn(s) (one composition ` + (grantedRepairs === 0 ? "and no granted repair" : `plus the ${String(grantedRepairs)} granted repair(s)`) + `), each writing to its ${String(outputBound)} token output allowance over the declared ${String(synthesis.estInputTokens ?? 0)} input floor: ${String(tailTurns)} x ${compositionUsd.toFixed(4)} = ${requiredUsd.toFixed(4)} USD at the rates of '${servedBy}'` + (reserveShort ? `; the committed synthesis reserve holds only ${declared.toFixed(4)} USD` : `; the committed ${declared.toFixed(4)} USD reserve covers it`) + (exposureRoomUsd === void 0 ? "" : exposureShort ? `, and maxInFlightExposureUsd ${(exposureCapUsd ?? 0).toFixed(4)} leaves only ${exposureRoomUsd.toFixed(4)} USD above the reserve line ${(reserveLineUsd ?? 0).toFixed(4)} USD` : `, and the exposure ceiling leaves ${exposureRoomUsd.toFixed(4)} USD above the reserve line`) + ": a composition cut at the allowance can fail its validators with no room left for the repairs the runtime will grant; hold the reserve at the tail arithmetic, raise maxInFlightExposureUsd toward the ceiling, lower maxOutputTokensPerTurn, or grant fewer repairs",
|
|
25346
25589
|
spawn: "synthesis"
|
|
25347
25590
|
});
|
|
25348
25591
|
}
|
|
@@ -26513,6 +26756,7 @@ function createEngine(options) {
|
|
|
26513
26756
|
if (wf.kind !== "workflow" && wf.kind !== "compiled-workflow") throw new ConfigError("engine.run accepts in-process Workflow values or compileScript CompiledWorkflow values");
|
|
26514
26757
|
if (opts?.budgetUsd !== void 0) requireNonNegativeNumber(opts.budgetUsd, "RunOptions.budgetUsd");
|
|
26515
26758
|
if (opts?.maxInFlightExposureUsd !== void 0) requireNonNegativeNumber(opts.maxInFlightExposureUsd, "RunOptions.maxInFlightExposureUsd");
|
|
26759
|
+
if (opts?.clampTurnToExposure !== void 0 && typeof opts.clampTurnToExposure !== "boolean") throw new ConfigError("RunOptions.clampTurnToExposure must be a boolean; got " + JSON.stringify(opts.clampTurnToExposure));
|
|
26516
26760
|
if (opts?.strictPricing !== void 0 && typeof opts.strictPricing !== "boolean" && (typeof opts.strictPricing !== "object" || opts.strictPricing === null || Array.isArray(opts.strictPricing))) throw new ConfigError("RunOptions.strictPricing must be a boolean or an options object; got " + JSON.stringify(opts.strictPricing));
|
|
26517
26761
|
if (opts?.limits !== void 0) validateUsageLimits(opts.limits, "RunOptions.limits");
|
|
26518
26762
|
const deadlineAtMs = opts?.deadlineAt === void 0 ? void 0 : parseDeadlineAt(opts.deadlineAt);
|
|
@@ -26550,6 +26794,7 @@ function createEngine(options) {
|
|
|
26550
26794
|
const makeBudget = () => new RunBudget({
|
|
26551
26795
|
...ceilingUsd === void 0 ? {} : { ceilingUsd },
|
|
26552
26796
|
...exposureCapUsd === void 0 ? {} : { maxInFlightExposureUsd: exposureCapUsd },
|
|
26797
|
+
...opts?.clampTurnToExposure === true ? { clampTurnToExposure: true } : {},
|
|
26553
26798
|
...strictPricing === void 0 ? {} : {
|
|
26554
26799
|
strictPricing,
|
|
26555
26800
|
now: realNow
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulvar/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.227.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",
|