@claudexor/orchestrator 3.2.0 → 3.2.1
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 +3 -1
- package/dist/attemptUsageCost.d.ts.map +1 -1
- package/dist/attemptUsageCost.js +15 -1
- package/dist/attemptUsageCost.js.map +1 -1
- package/dist/credential-profile-rotation.d.ts +5 -2
- package/dist/credential-profile-rotation.d.ts.map +1 -1
- package/dist/credential-profile-rotation.js +18 -11
- package/dist/credential-profile-rotation.js.map +1 -1
- package/dist/credential-profiles.d.ts +3 -0
- package/dist/credential-profiles.d.ts.map +1 -1
- package/dist/credential-profiles.js +3 -3
- package/dist/credential-profiles.js.map +1 -1
- package/dist/orchestrator.d.ts +13 -1
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +86 -36
- package/dist/orchestrator.js.map +1 -1
- package/dist/planQuestions.js +4 -1
- package/dist/planQuestions.js.map +1 -1
- package/package.json +17 -17
package/dist/orchestrator.js
CHANGED
|
@@ -326,9 +326,11 @@ export class Orchestrator {
|
|
|
326
326
|
execRootOf(input) {
|
|
327
327
|
return input.executionRoot ?? input.repoRoot;
|
|
328
328
|
}
|
|
329
|
-
async sessionSpecFields(input, harnessId, log, defaultRoute = null) {
|
|
329
|
+
async sessionSpecFields(input, harnessId, model, log, defaultRoute = null, quotaAdmission) {
|
|
330
330
|
const cfg = this.config(input.repoRoot)?.global;
|
|
331
|
-
const profile =
|
|
331
|
+
const profile = quotaAdmission?.model === model
|
|
332
|
+
? quotaAdmission.profile
|
|
333
|
+
: await this.preflightProfile(input, harnessId, model, log, defaultRoute);
|
|
332
334
|
return {
|
|
333
335
|
// "auto" at ANY level falls through (thread turns send the thread default
|
|
334
336
|
// "auto" as a per-run value; it must not shadow a configured preference).
|
|
@@ -426,13 +428,13 @@ export class Orchestrator {
|
|
|
426
428
|
.filter(({ profile, status }) => profileStatusAdmits(profile, status))
|
|
427
429
|
.map(({ profile }) => profile.profile_id));
|
|
428
430
|
}
|
|
429
|
-
async preflightProfile(input, harnessId, log, defaultRoute) {
|
|
431
|
+
async preflightProfile(input, harnessId, model, log, defaultRoute) {
|
|
430
432
|
const profile = this.resolveCredentialProfile(input, harnessId);
|
|
431
433
|
const policy = this.profilePolicy(input.repoRoot, harnessId);
|
|
432
434
|
const registry = this.config(input.repoRoot)?.global.credential_profiles ?? [];
|
|
433
435
|
const snapshots = this.deps.quotaSnapshots?.() ?? [];
|
|
434
436
|
const emit = (type, payload) => log?.emit(type, payload);
|
|
435
|
-
const breach = profileHeadroomBreach(snapshots, harnessId, profile?.profile_id ?? null, policy.headroom_threshold);
|
|
437
|
+
const breach = profileHeadroomBreach(snapshots, harnessId, profile?.profile_id ?? null, policy.headroom_threshold, model);
|
|
436
438
|
const readyProfileIds = policy.limit_action === "rotate" &&
|
|
437
439
|
breach !== null &&
|
|
438
440
|
(profile !== null || defaultRoute === "local_session")
|
|
@@ -449,6 +451,7 @@ export class Orchestrator {
|
|
|
449
451
|
snapshots,
|
|
450
452
|
readyProfileIds,
|
|
451
453
|
defaultRoute,
|
|
454
|
+
model,
|
|
452
455
|
emit,
|
|
453
456
|
});
|
|
454
457
|
}
|
|
@@ -459,6 +462,7 @@ export class Orchestrator {
|
|
|
459
462
|
registry,
|
|
460
463
|
snapshots,
|
|
461
464
|
readyProfileIds,
|
|
465
|
+
model,
|
|
462
466
|
emit,
|
|
463
467
|
});
|
|
464
468
|
}
|
|
@@ -728,6 +732,7 @@ export class Orchestrator {
|
|
|
728
732
|
// the default store's sources apply only to profile-less runs.
|
|
729
733
|
authRouteEstimate: this.profileAuthRoute(input, id) ??
|
|
730
734
|
estimateEffectiveAuthRoute(this.authPreferenceForHarness(input.repoRoot, id, input.authPreference), status.authSources),
|
|
735
|
+
quotaAdmission: { model: null, profile: null, route: null },
|
|
731
736
|
supportsSynthesize: manifest.capabilities.synthesize,
|
|
732
737
|
supportsInteractive: manifest.capabilities.interactive,
|
|
733
738
|
supportsJsonSchemaOutput: manifest.capabilities.json_schema_output,
|
|
@@ -762,11 +767,29 @@ export class Orchestrator {
|
|
|
762
767
|
if (pool.length === 0) {
|
|
763
768
|
throw new HarnessUnavailableError(`no harness can perform '${intent}' for this mode${dropped.length ? ` (skipped: ${dropped.join(", ")})` : ""}`);
|
|
764
769
|
}
|
|
765
|
-
|
|
770
|
+
// Quota admission must use the account that will actually spawn. In
|
|
771
|
+
// particular, an opt-in default-subject rotation has to select its ready
|
|
772
|
+
// profile before the budget router filters the exhausted default away.
|
|
773
|
+
// The same resolved profile is reused by the first spec build below.
|
|
774
|
+
const quotaPreparedPool = await Promise.all(pool.map(async (routed) => {
|
|
775
|
+
const model = input.models?.[routed.adapter.id] ?? routed.settings?.defaultModel ?? null;
|
|
776
|
+
const profile = await this.preflightProfile(input, routed.adapter.id, model, log, routed.authRouteEstimate);
|
|
777
|
+
const route = profile
|
|
778
|
+
? profile.credential_kind === "api_key"
|
|
779
|
+
? "managed_api_key"
|
|
780
|
+
: "vendor_native"
|
|
781
|
+
: routed.authRouteEstimate === "api_key"
|
|
782
|
+
? "managed_api_key"
|
|
783
|
+
: routed.authRouteEstimate === "local_session"
|
|
784
|
+
? "vendor_native"
|
|
785
|
+
: null;
|
|
786
|
+
return { ...routed, quotaAdmission: { model, profile, route } };
|
|
787
|
+
}));
|
|
788
|
+
const ordered = this.orderPool(quotaPreparedPool, input, intent, statusById, ledger, runId);
|
|
766
789
|
if (ordered.length === 0) {
|
|
767
790
|
throw new HarnessUnavailableError(`no harness remains eligible for '${intent}' after budget and quota routing`);
|
|
768
791
|
}
|
|
769
|
-
emitPrimaryDivergence(log, input.primaryHarness, ordered,
|
|
792
|
+
emitPrimaryDivergence(log, input.primaryHarness, ordered, quotaPreparedPool, dropped);
|
|
770
793
|
const n = input.n ?? ordered.length;
|
|
771
794
|
const selectionOrder = ordered;
|
|
772
795
|
const out = [];
|
|
@@ -873,28 +896,25 @@ export class Orchestrator {
|
|
|
873
896
|
: authModes.includes("api_key")
|
|
874
897
|
? "api_key"
|
|
875
898
|
: "unknown";
|
|
876
|
-
//
|
|
877
|
-
// (round-18 #2)
|
|
899
|
+
// The quota-preflight profile's credential_kind decides the route
|
|
900
|
+
// outright (round-18 #2). This includes an unpinned default subject
|
|
901
|
+
// that rotated before ranking; an api_key profile must never inherit a
|
|
878
902
|
// subscription classification from the default store's metric.
|
|
879
|
-
const
|
|
903
|
+
const admittedProfileRoute = r.quotaAdmission.profile
|
|
904
|
+
? r.quotaAdmission.profile.credential_kind === "api_key"
|
|
905
|
+
? "api_key"
|
|
906
|
+
: "local_session"
|
|
907
|
+
: null;
|
|
908
|
+
const authMode = admittedProfileRoute ??
|
|
880
909
|
(input.authPreference === "api_key"
|
|
881
910
|
? "api_key"
|
|
882
911
|
: input.authPreference === "subscription"
|
|
883
912
|
? "local_session"
|
|
884
913
|
: (metric?.last_auth_mode ?? guessedAuthMode));
|
|
885
|
-
// The quota subject
|
|
886
|
-
//
|
|
887
|
-
// default
|
|
888
|
-
|
|
889
|
-
// as unknown (undefined) and stays conservatively any-subject.
|
|
890
|
-
let credentialSubjectId;
|
|
891
|
-
try {
|
|
892
|
-
credentialSubjectId =
|
|
893
|
-
this.resolveCredentialProfile(input, r.adapter.id)?.profile_id ?? null;
|
|
894
|
-
}
|
|
895
|
-
catch {
|
|
896
|
-
credentialSubjectId = undefined;
|
|
897
|
-
}
|
|
914
|
+
// The exact quota subject selected before ranking: profile id or the
|
|
915
|
+
// engine default. Profile A's cooldown never excludes profile B or the
|
|
916
|
+
// default on the same harness and route.
|
|
917
|
+
const credentialSubjectId = r.quotaAdmission.profile?.profile_id ?? null;
|
|
898
918
|
// QA-034: the typed auth-route evidence (doctor source verification x the
|
|
899
919
|
// resolved route) is AUTHORITATIVE for billing knowledge in the router —
|
|
900
920
|
// a VERIFIED native route proves subscription_entitlement, so it survives
|
|
@@ -905,20 +925,19 @@ export class Orchestrator {
|
|
|
905
925
|
return {
|
|
906
926
|
harnessId: r.adapter.id,
|
|
907
927
|
available: true,
|
|
908
|
-
model:
|
|
909
|
-
config.harnesses[r.adapter.id]?.default_model ??
|
|
910
|
-
undefined,
|
|
928
|
+
model: r.quotaAdmission.model,
|
|
911
929
|
effort: input.efforts?.[r.adapter.id] ??
|
|
912
930
|
input.effort ??
|
|
913
931
|
config.harnesses[r.adapter.id]?.effort ??
|
|
914
932
|
undefined,
|
|
915
933
|
billingKnowledge: authMode === "api_key" ? "metered" : "unknown",
|
|
916
934
|
incrementalCostUsd: authMode === "api_key" ? (metric?.avg_cost_usd ?? null) : null,
|
|
917
|
-
credentialRoute:
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
935
|
+
credentialRoute: r.quotaAdmission.route ??
|
|
936
|
+
(authMode === "api_key"
|
|
937
|
+
? "managed_api_key"
|
|
938
|
+
: authMode === "local_session"
|
|
939
|
+
? "vendor_native"
|
|
940
|
+
: undefined),
|
|
922
941
|
...(authRoute ? { authRoute } : {}),
|
|
923
942
|
credentialSubjectId,
|
|
924
943
|
};
|
|
@@ -966,6 +985,36 @@ export class Orchestrator {
|
|
|
966
985
|
}
|
|
967
986
|
return ordered;
|
|
968
987
|
}
|
|
988
|
+
/** Harness-only convergence helpers still need the exact quota identity
|
|
989
|
+
* selected before ranking. This facade keeps their small interface while
|
|
990
|
+
* preventing a scoped limit on one model/account from cooling another. */
|
|
991
|
+
quotaLedgerView(ledger, routes) {
|
|
992
|
+
const byId = new Map(routes.map((route) => [route.adapter.id, route]));
|
|
993
|
+
const identity = (id) => {
|
|
994
|
+
const route = byId.get(id);
|
|
995
|
+
return route
|
|
996
|
+
? {
|
|
997
|
+
credentialRoute: route.quotaAdmission.route ?? undefined,
|
|
998
|
+
credentialSubjectId: route.quotaAdmission.profile?.profile_id ?? null,
|
|
999
|
+
model: route.quotaAdmission.model,
|
|
1000
|
+
}
|
|
1001
|
+
: null;
|
|
1002
|
+
};
|
|
1003
|
+
return {
|
|
1004
|
+
bindingPaceSlack: (id) => {
|
|
1005
|
+
const selected = identity(id);
|
|
1006
|
+
return selected
|
|
1007
|
+
? ledger.bindingPaceSlack(id, selected.credentialRoute, selected.credentialSubjectId, Date.now(), selected.model)
|
|
1008
|
+
: ledger.bindingPaceSlack(id);
|
|
1009
|
+
},
|
|
1010
|
+
cooldownActive: (id) => {
|
|
1011
|
+
const selected = identity(id);
|
|
1012
|
+
return selected
|
|
1013
|
+
? ledger.cooldownActive(id, selected.credentialRoute, selected.credentialSubjectId, Date.now(), selected.model)
|
|
1014
|
+
: ledger.cooldownActive(id);
|
|
1015
|
+
},
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
969
1018
|
/**
|
|
970
1019
|
* Typed auth-route evidence for one candidate (QA-034): the concrete
|
|
971
1020
|
* credential route the resolved auth mode maps to, plus the doctor's
|
|
@@ -1343,7 +1392,7 @@ export class Orchestrator {
|
|
|
1343
1392
|
const inPlaceEnvelope = envelope.worktree_path === envelope.repo_root;
|
|
1344
1393
|
const rawContextPacket = await rawContextForEnvelope(routed.implementationTransport, envelope);
|
|
1345
1394
|
const sessionFields = runInput
|
|
1346
|
-
? await this.sessionSpecFields(runInput, adapter.id, log, routed.authRouteEstimate)
|
|
1395
|
+
? await this.sessionSpecFields(runInput, adapter.id, knobs.model, log, routed.authRouteEstimate, routed.quotaAdmission)
|
|
1347
1396
|
: undefined;
|
|
1348
1397
|
// Continuity (INV-137): once the lane (harness + resolved profile) is known,
|
|
1349
1398
|
// build the continuation packet, materialize context/THREAD.md, and point
|
|
@@ -3263,7 +3312,8 @@ export class Orchestrator {
|
|
|
3263
3312
|
// observed quota cooldown across all harnesses, or genuine no-progress (a stall on the same
|
|
3264
3313
|
// failure signature after every available harness has tried it).
|
|
3265
3314
|
const stallThreshold = input.untilClean === true ? 4 : 2;
|
|
3266
|
-
const
|
|
3315
|
+
const convergenceQuotaLedger = this.quotaLedgerView(ledger, adapterPool);
|
|
3316
|
+
const allCooledDown = () => adapterPool.every((a) => convergenceQuotaLedger.cooldownActive(a.adapter.id));
|
|
3267
3317
|
const attemptTelemetries = [];
|
|
3268
3318
|
let lastDiffStable = true;
|
|
3269
3319
|
try {
|
|
@@ -3614,7 +3664,7 @@ export class Orchestrator {
|
|
|
3614
3664
|
if (adapterPool.length > 1 && triedSinceProgress.size < adapterPool.length) {
|
|
3615
3665
|
// Quota-headroom consumer (mid-run, where quota observations EXIST);
|
|
3616
3666
|
// pick + honest route event owned by runSupport.rotateOnStall.
|
|
3617
|
-
adapterIdx = rotateOnStall(adapterPool.map((a) => a.adapter.id), adapterIdx,
|
|
3667
|
+
adapterIdx = rotateOnStall(adapterPool.map((a) => a.adapter.id), adapterIdx, convergenceQuotaLedger, triedSinceProgress, log, lastRun?.harnessId ?? null);
|
|
3618
3668
|
routed = adapterPool[adapterIdx];
|
|
3619
3669
|
adapter = routed.adapter;
|
|
3620
3670
|
}
|
|
@@ -3903,7 +3953,7 @@ export class Orchestrator {
|
|
|
3903
3953
|
const adapter = routed.adapter;
|
|
3904
3954
|
const knobs = this.routeSpecKnobs(routed, contract, undefined, input.effort);
|
|
3905
3955
|
const effectiveWeb = this.discloseWebUpgrade(log, routed, knobs.webPolicy, attemptId);
|
|
3906
|
-
const sessionFields = await this.sessionSpecFields(input, adapter.id, log, routed.authRouteEstimate);
|
|
3956
|
+
const sessionFields = await this.sessionSpecFields(input, adapter.id, knobs.model, log, routed.authRouteEstimate, routed.quotaAdmission);
|
|
3907
3957
|
const laneContinuity = args.laneRun
|
|
3908
3958
|
? await this.resolveContinuity(input, adapter.id, sessionFields.credential_profile?.profile_id ?? input.credentialProfileId ?? null, sessionFields.resume_session_id !== null, sessionFields, store, paths, this.execRootOf(input), log)
|
|
3909
3959
|
: null;
|
|
@@ -4281,7 +4331,7 @@ export class Orchestrator {
|
|
|
4281
4331
|
buildSpec: async (routed, homeEnv, prompt, attemptId) => {
|
|
4282
4332
|
const knobs = this.routeSpecKnobs(routed, contract, undefined, input.effort);
|
|
4283
4333
|
const effectiveWeb = this.discloseWebUpgrade(log, routed, knobs.webPolicy, attemptId);
|
|
4284
|
-
const sessionFields = await this.sessionSpecFields(input, routed.adapter.id, log, routed.authRouteEstimate);
|
|
4334
|
+
const sessionFields = await this.sessionSpecFields(input, routed.adapter.id, knobs.model, log, routed.authRouteEstimate, routed.quotaAdmission);
|
|
4285
4335
|
const spec = HarnessRunSpec.parse({
|
|
4286
4336
|
session_id: newId("ses"),
|
|
4287
4337
|
intent: "synthesize",
|
|
@@ -4519,7 +4569,7 @@ export class Orchestrator {
|
|
|
4519
4569
|
const explorerPrompt = (opts.deepScan
|
|
4520
4570
|
? `${prompt}\n\nExplorer ${idx + 1}/${adapters.length}: focus on a distinct slice. Emit evidence-cited findings, explicit unknowns/omissions, and follow-up questions. Do not edit files.`
|
|
4521
4571
|
: prompt) + contextSection;
|
|
4522
|
-
const sessionFields = await this.sessionSpecFields(input, adapter.id, log, routed.authRouteEstimate);
|
|
4572
|
+
const sessionFields = await this.sessionSpecFields(input, adapter.id, knobs.model, log, routed.authRouteEstimate, routed.quotaAdmission);
|
|
4523
4573
|
const grantResume = sessionFields.resume_session_id !== null && !resumeGranted.has(adapter.id);
|
|
4524
4574
|
if (grantResume)
|
|
4525
4575
|
resumeGranted.add(adapter.id);
|