@claudexor/orchestrator 3.1.0 → 3.1.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/dist/attemptFinalize.d.ts +38 -0
- package/dist/attemptFinalize.d.ts.map +1 -1
- package/dist/attemptFinalize.js +44 -0
- package/dist/attemptFinalize.js.map +1 -1
- package/dist/deepScanReducer.d.ts.map +1 -1
- package/dist/deepScanReducer.js +1 -5
- package/dist/deepScanReducer.js.map +1 -1
- package/dist/effortGovernance.d.ts +49 -0
- package/dist/effortGovernance.d.ts.map +1 -0
- package/dist/effortGovernance.js +35 -0
- package/dist/effortGovernance.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/orchestrator.d.ts +6 -6
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +49 -49
- package/dist/orchestrator.js.map +1 -1
- package/dist/reviewerPanel.d.ts +4 -0
- package/dist/reviewerPanel.d.ts.map +1 -1
- package/dist/reviewerPanel.js +54 -9
- package/dist/reviewerPanel.js.map +1 -1
- package/dist/revisePrompt.d.ts +17 -0
- package/dist/revisePrompt.d.ts.map +1 -0
- package/dist/revisePrompt.js +15 -0
- package/dist/revisePrompt.js.map +1 -0
- package/dist/runSupport.d.ts +17 -0
- package/dist/runSupport.d.ts.map +1 -1
- package/dist/runSupport.js +26 -0
- package/dist/runSupport.js.map +1 -1
- package/dist/write-isolation.d.ts +11 -0
- package/dist/write-isolation.d.ts.map +1 -0
- package/dist/write-isolation.js +13 -0
- package/dist/write-isolation.js.map +1 -0
- package/package.json +17 -17
package/dist/orchestrator.js
CHANGED
|
@@ -12,11 +12,13 @@ import { HarnessRunSpec, ModeKind as ModeKindSchema, SCHEMA_VERSION, TRUST_FULL_
|
|
|
12
12
|
import { globalConfigDir, loadConfig, trustConfigPath } from "@claudexor/config";
|
|
13
13
|
import { AnswerAssembly, CLAUDEXOR_ARTIFACT_DIR, CLAUDEXOR_BROWSER_ARTIFACT_SUBDIR, HarnessUnavailableError, summarizeDiffPaths as diffStats, withInactivityWatchdog, } from "@claudexor/core";
|
|
14
14
|
import { assertRouteModelsAllowed } from "./modelGovernance.js";
|
|
15
|
+
import { governRouteEffort } from "./effortGovernance.js";
|
|
15
16
|
import { RequestRequirementsResolver } from "./requestRequirements.js";
|
|
17
|
+
import { buildRevisePrompt } from "./revisePrompt.js";
|
|
16
18
|
import { cancelledResult, failTerminally, guardAnnouncedRun, writeFailure, } from "./runTerminals.js";
|
|
17
19
|
import { budgetFailureRecord, classifyBudgetFailure } from "./budgetFailure.js";
|
|
18
20
|
import { assertOutputSchemaCompiles, finalizeStructuredOutput } from "./structuredOutput.js";
|
|
19
|
-
import { transientRetryDelayMs, promptWithEngineConstraints, sleep, redactHarnessEvent, harnessEventPayload,
|
|
21
|
+
import { transientRetryDelayMs, promptWithEngineConstraints, sleep, redactHarnessEvent, harnessEventPayload, safeErrorMessage, renderSummary, observeBudgetSignals, rotateOnStall, recordCleanAttemptMetrics, envInheritance, transientRetryPolicy, reviewerTimeoutMs, harnessInactivityTimeoutMs, observeAuthSwitch, emitPrimaryDivergence, emitPoolDegraded, deliveryRefusalFailure, writeRaceDeliveryDecision, } from "./runSupport.js";
|
|
20
22
|
import { candidateStatusInRouteContext, resolveReadOnlyRouteContext, } from "./routeContext.js";
|
|
21
23
|
import { resolveAutoReviewerPanel, resolveExplicitReviewerPanel } from "./reviewerPanel.js";
|
|
22
24
|
import { buildContinuation, } from "./continuity.js";
|
|
@@ -25,7 +27,7 @@ import { runDiffReview } from "./diffReview.js";
|
|
|
25
27
|
import { rawScoutBundle, resolveDeepScanSynthesis, } from "./deepScanReducer.js";
|
|
26
28
|
import { classifyAdapterThrow, createAttemptTelemetry, observeAttemptTelemetry, setAttemptOutcome, telemetrySummary, toolWarnings, unrecoveredToolErrors, webUnsatisfied, } from "./attemptTelemetry.js";
|
|
27
29
|
import { dominantHarnessFailureCategory, harnessFailureNextActions } from "./harnessFailure.js";
|
|
28
|
-
import { finalizeAttempt, readOnlyNoSuccessTerminal, resolveWorkReportEnvelope, unwrapWorkReportEnvelope, } from "./attemptFinalize.js";
|
|
30
|
+
import { finalizeAttempt, readOnlyNoSuccessTerminal, resolveWorkReportEnvelope, unrecoveredToolErrorFailure, unwrapWorkReportEnvelope, webEvidenceFailure, } from "./attemptFinalize.js";
|
|
29
31
|
import { buildContinuationPacket, decideContinuation, synthesizeContinuationRequest, } from "./continuation.js";
|
|
30
32
|
import { interactionChannelFor } from "./interaction.js";
|
|
31
33
|
import { gateSpecsFromContract, renderTestsEvidence, resolveContractGates, } from "./contract-gates.js";
|
|
@@ -40,6 +42,7 @@ import { arbitrate } from "@claudexor/arbitration";
|
|
|
40
42
|
import { buildSynthesisPlan, decideSynthesis } from "@claudexor/synthesis";
|
|
41
43
|
import { attemptCostEvidence, attemptUsageCostSettlement, BudgetLedger, isBudgetTerminal, explainRanking, loadHarnessMetrics, promptFingerprint, unknownCostSettlement, rankHarnesses, reviewUsageCostSettlement, } from "@claudexor/budget";
|
|
42
44
|
import { readTextSafe, appendLine, assertNoInlineSecretValues, containsSecretLikeToken, DELEGATION_ENV, hashJson, newId, noProjectRepoRoot, nowIso, redactSecrets, safeInvoke, sha256, userConfigDir, writeText, } from "@claudexor/util";
|
|
45
|
+
import { assertWriteIsolation } from "./write-isolation.js";
|
|
43
46
|
/** A routed candidate adapter plus its manifest capabilities and user settings. */
|
|
44
47
|
/** The two access profiles that map to codex `danger-full-access` / an
|
|
45
48
|
* unsandboxed lane — the only ones under which a full-access-requiring MCP
|
|
@@ -134,13 +137,15 @@ export class Orchestrator {
|
|
|
134
137
|
throw new Error(`unknown mode: ${String(resolved.mode)}`);
|
|
135
138
|
}
|
|
136
139
|
const mode = parsedMode.data;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
140
|
+
const projectProtectedPaths = mode === "agent" ? this.projectConfig(resolved.repoRoot).constraints.protected_paths : [];
|
|
141
|
+
assertWriteIsolation({
|
|
142
|
+
mode,
|
|
143
|
+
protectedPaths: projectProtectedPaths,
|
|
144
|
+
denyPaths: resolved.denyPaths,
|
|
145
|
+
inPlace: resolved.inPlace,
|
|
146
|
+
repoRoot: resolved.repoRoot,
|
|
147
|
+
executionRoot: this.execRootOf(resolved),
|
|
148
|
+
});
|
|
144
149
|
// outputSchema constrains the run's final ANSWER. It is honored exactly
|
|
145
150
|
// where a final answer is delivered (agent race incl. synthesis, and ask);
|
|
146
151
|
// every other strategy refuses loudly rather than carrying a contract the
|
|
@@ -204,7 +209,7 @@ export class Orchestrator {
|
|
|
204
209
|
// writer ran, which is the leak this closes).
|
|
205
210
|
(runId) => this.routingRationaleByRun.delete(runId));
|
|
206
211
|
}
|
|
207
|
-
async resolveReviewers(cwd, runAuthPreference) {
|
|
212
|
+
async resolveReviewers(cwd, runAuthPreference, onIgnoredSetting) {
|
|
208
213
|
if (this.deps.reviewers)
|
|
209
214
|
return this.deps.reviewers;
|
|
210
215
|
if (this.deps.reviewerPanel && this.deps.reviewerPanel.length > 0) {
|
|
@@ -215,10 +220,8 @@ export class Orchestrator {
|
|
|
215
220
|
registry: this.deps.registry,
|
|
216
221
|
harnessSettings: this.config(cwd)?.global.harnesses ?? {},
|
|
217
222
|
authPreferenceFor: (id) => this.authPreferenceForHarness(cwd, id, runAuthPreference),
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
reviewerEfforts: this.deps.reviewerEfforts,
|
|
221
|
-
});
|
|
223
|
+
onIgnoredSetting,
|
|
224
|
+
}, { reviewerModels: this.deps.reviewerModels, reviewerEfforts: this.deps.reviewerEfforts });
|
|
222
225
|
}
|
|
223
226
|
/**
|
|
224
227
|
* Resolve reviewers INSIDE a strategy, after run-dir creation: an explicit
|
|
@@ -229,7 +232,9 @@ export class Orchestrator {
|
|
|
229
232
|
*/
|
|
230
233
|
async resolveReviewersWithArtifacts(input, log, store, paths, runId, taskId, mode) {
|
|
231
234
|
try {
|
|
232
|
-
|
|
235
|
+
// Auto-panel dropped knobs (reviewerEfforts) → ignored-settings channel (QA-070):
|
|
236
|
+
const warn = (d) => void log.emit("review.preflight", { ignored_settings: [d] });
|
|
237
|
+
return { reviewers: await this.resolveReviewers(input.repoRoot, input.authPreference, warn) };
|
|
233
238
|
}
|
|
234
239
|
catch (err) {
|
|
235
240
|
const message = safeErrorMessage(err);
|
|
@@ -917,13 +922,13 @@ export class Orchestrator {
|
|
|
917
922
|
intent,
|
|
918
923
|
qualityTiers: config.routing.quality_tiers,
|
|
919
924
|
ledger: routeLedger,
|
|
925
|
+
now: Date.now(), // ONE instant for the sort AND the rationale below
|
|
920
926
|
};
|
|
921
927
|
const ranked = rankHarnesses(remaining, routeCtx)
|
|
922
928
|
.map((candidate) => byId.get(candidate.harnessId))
|
|
923
929
|
.filter((candidate) => Boolean(candidate));
|
|
924
|
-
// QA-034:
|
|
925
|
-
//
|
|
926
|
-
// reason can never disagree with the order actually taken.
|
|
930
|
+
// QA-034: the rationale is run evidence recorded ONCE at pool ordering,
|
|
931
|
+
// pinned to routeCtx.now so it cannot disagree with the order just taken.
|
|
927
932
|
if (runId)
|
|
928
933
|
this.routingRationaleByRun.set(runId, explainRanking(remaining, routeCtx));
|
|
929
934
|
ordered = ranked;
|
|
@@ -1067,7 +1072,7 @@ export class Orchestrator {
|
|
|
1067
1072
|
projectCommands: cfg.tests?.commands ?? [],
|
|
1068
1073
|
});
|
|
1069
1074
|
const commands = resolvedGates.commands;
|
|
1070
|
-
const protectedPaths = [];
|
|
1075
|
+
const protectedPaths = [...new Set(cfg.constraints.protected_paths)];
|
|
1071
1076
|
const autoProtectedPaths = resolvedGates.autoProtectedPaths;
|
|
1072
1077
|
const protectedPathApprovals = [
|
|
1073
1078
|
...new Map([...(input.protectedPathApprovals ?? [])].map((approval) => [approval.path, approval])).values(),
|
|
@@ -1278,17 +1283,16 @@ export class Orchestrator {
|
|
|
1278
1283
|
// per-harness map, which beats the per-harness settings default. There is
|
|
1279
1284
|
// no run-global model.
|
|
1280
1285
|
const model = overrideModel ?? contract.routing_models[routed.adapter.id] ?? s?.defaultModel ?? null;
|
|
1281
|
-
// Effort disclosure (INV-105)
|
|
1282
|
-
//
|
|
1283
|
-
//
|
|
1284
|
-
//
|
|
1285
|
-
//
|
|
1286
|
-
//
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
}
|
|
1286
|
+
// Effort disclosure (INV-105) against the harness's advertised ladder. This
|
|
1287
|
+
// gate only DISCLOSES an unplaceable level; the clamp belongs to the adapter,
|
|
1288
|
+
// which resolves against the catalog for the profile env the child runs in
|
|
1289
|
+
// (the manifest here is the DEFAULT account's — see effortGovernance.ts). The
|
|
1290
|
+
// contract's FROZEN per-lane effort (QA-035) wins so Exact Retry replays it
|
|
1291
|
+
// without re-reading settings; `effortHint`/settings apply only to an unfrozen lane.
|
|
1292
|
+
const governed = governRouteEffort(contract.routing_efforts[routed.adapter.id] ?? effortHint ?? s?.effort ?? null, { id: routed.adapter.id, ...routed });
|
|
1293
|
+
const effort = governed.effort;
|
|
1294
|
+
if (governed.ignored)
|
|
1295
|
+
ignored.push(governed.ignored);
|
|
1292
1296
|
return {
|
|
1293
1297
|
model,
|
|
1294
1298
|
effort,
|
|
@@ -1721,7 +1725,7 @@ export class Orchestrator {
|
|
|
1721
1725
|
}
|
|
1722
1726
|
const attemptStreamEndedMs = Date.now();
|
|
1723
1727
|
if (webUnsatisfied(telemetry)) {
|
|
1724
|
-
errors.push(
|
|
1728
|
+
errors.push(webEvidenceFailure(telemetry.web));
|
|
1725
1729
|
}
|
|
1726
1730
|
const diff = await wsm.diff(envelope);
|
|
1727
1731
|
// D-16: un-nest {work_report, output} so answer.md persists the OUTPUT, not the envelope.
|
|
@@ -3436,7 +3440,7 @@ export class Orchestrator {
|
|
|
3436
3440
|
: "";
|
|
3437
3441
|
const prompt = attempt === 1
|
|
3438
3442
|
? input.prompt
|
|
3439
|
-
:
|
|
3443
|
+
: buildRevisePrompt(input.prompt, lastFindings, runtimeErrors);
|
|
3440
3444
|
// Loop detection (budget router): the 3rd identical repair prompt means
|
|
3441
3445
|
// findings/errors are not changing — stop burning paid attempts.
|
|
3442
3446
|
const fingerprint = promptFingerprint(prompt);
|
|
@@ -4208,20 +4212,22 @@ export class Orchestrator {
|
|
|
4208
4212
|
input.signal?.removeEventListener("abort", onAbort);
|
|
4209
4213
|
ledger.settle(lease.lease?.lease_id ?? "", attemptUsageCostSettlement(cost, costEstimated, attemptId, adapter.id, telemetry.authMode, telemetry.usageCost));
|
|
4210
4214
|
}
|
|
4211
|
-
const unrecovered = unrecoveredToolErrors(telemetry);
|
|
4212
|
-
const webBlocked = webUnsatisfied(telemetry);
|
|
4213
|
-
if (!harnessError && webBlocked) {
|
|
4214
|
-
harnessError = `web evidence unsatisfied: ${telemetry.web.errorSummary ?? (telemetry.web.attempted ? "web tool failed without verified recovery" : "web evidence required but never attempted")}`;
|
|
4215
|
-
}
|
|
4216
|
-
if (!harnessError && unrecovered.length > 0) {
|
|
4217
|
-
const first = unrecovered[0];
|
|
4218
|
-
harnessError = `${first.tool} failed without recovery: ${first.summary}`;
|
|
4219
|
-
}
|
|
4220
4215
|
// D-16: unwrap and require PLAN TEXT — a plan with no text is not delivered.
|
|
4216
|
+
// The unwrap runs BEFORE the error axes: the deliverable it yields is what
|
|
4217
|
+
// decides whether an unrecovered tool error is fatal (explorer parity).
|
|
4221
4218
|
const planUnwrapped = unwrapWorkReportEnvelope(answer.machineText() ?? "", planWorkMode, {
|
|
4222
4219
|
sideToolReport: telemetry.sideToolWorkReport ?? undefined,
|
|
4223
4220
|
});
|
|
4224
4221
|
const planText = redactSecrets(planUnwrapped.deliverable).trim();
|
|
4222
|
+
const unrecovered = unrecoveredToolErrors(telemetry);
|
|
4223
|
+
const webBlocked = webUnsatisfied(telemetry);
|
|
4224
|
+
if (!harnessError && webBlocked) {
|
|
4225
|
+
harnessError = webEvidenceFailure(telemetry.web);
|
|
4226
|
+
}
|
|
4227
|
+
// INV-043/INV-044, explorer parity: a DELIVERED plan keeps an unrecovered
|
|
4228
|
+
// non-web tool error as warning evidence instead of discarding the plan (see
|
|
4229
|
+
// the helper). Web keeps its hard gate above; the finalizer outranks both.
|
|
4230
|
+
harnessError ??= unrecoveredToolErrorFailure(unrecovered, planText.length > 0);
|
|
4225
4231
|
const planFinalized = finalizeAttempt({
|
|
4226
4232
|
deliverableEvidence: planText.length > 0,
|
|
4227
4233
|
harnessErrored: harnessError !== null && !webBlocked,
|
|
@@ -5112,12 +5118,9 @@ export class Orchestrator {
|
|
|
5112
5118
|
const webBlocked = webUnsatisfied(telemetry);
|
|
5113
5119
|
const reportPresent = report.length > 0;
|
|
5114
5120
|
if (!harnessError && webBlocked) {
|
|
5115
|
-
harnessError =
|
|
5116
|
-
}
|
|
5117
|
-
if (!harnessError && unrecovered.length > 0 && !reportPresent) {
|
|
5118
|
-
const first = unrecovered[0];
|
|
5119
|
-
harnessError = `${first.tool} failed without recovery: ${first.summary}`;
|
|
5121
|
+
harnessError = webEvidenceFailure(telemetry.web);
|
|
5120
5122
|
}
|
|
5123
|
+
harnessError ??= unrecoveredToolErrorFailure(unrecovered, reportPresent);
|
|
5121
5124
|
const roFinalized = finalizeAttempt({
|
|
5122
5125
|
deliverableEvidence: reportPresent,
|
|
5123
5126
|
harnessErrored: harnessError !== null && !webBlocked,
|
|
@@ -5696,7 +5699,4 @@ function assertNoSecretLikeTokens(label, text) {
|
|
|
5696
5699
|
throw new Error(`${label} contains secret-like token; refusing to persist artifact`);
|
|
5697
5700
|
}
|
|
5698
5701
|
}
|
|
5699
|
-
function safeErrorMessage(err) {
|
|
5700
|
-
return redactSecrets(err instanceof Error ? err.message : String(err));
|
|
5701
|
-
}
|
|
5702
5702
|
//# sourceMappingURL=orchestrator.js.map
|