@martinloop/mcp 0.3.4 → 0.3.5
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/discovery-metadata.d.ts +6 -6
- package/dist/discovery-metadata.js +6 -0
- package/dist/resources.d.ts +1 -0
- package/dist/resources.js +63 -1
- package/dist/server.js +86 -0
- package/dist/tools/run-store.js +4 -4
- package/dist/tools/tool-support.d.ts +2 -0
- package/dist/tools/tool-support.js +16 -1
- package/dist/vendor/adapters/claude-cli.js +31 -2
- package/dist/vendor/adapters/cli-bridge.d.ts +10 -0
- package/dist/vendor/adapters/cli-bridge.js +30 -0
- package/dist/vendor/adapters/codex-launcher.d.ts +2 -0
- package/dist/vendor/adapters/codex-launcher.js +59 -2
- package/dist/vendor/contracts/execution-policy.d.ts +16 -0
- package/dist/vendor/contracts/execution-policy.js +9 -0
- package/dist/vendor/contracts/index.d.ts +28 -1
- package/dist/vendor/contracts/index.js +1 -0
- package/dist/vendor/core/index.d.ts +5 -3
- package/dist/vendor/core/index.js +93 -28
- package/dist/vendor/core/persistence/store.js +13 -0
- package/dist/vendor/core/policy.d.ts +13 -0
- package/dist/vendor/core/policy.js +13 -0
- package/dist/vendor/core/rollback.d.ts +4 -0
- package/dist/vendor/core/rollback.js +15 -0
- package/dist/vendor/core/routing.d.ts +40 -0
- package/dist/vendor/core/routing.js +185 -0
- package/dist/workflow-state.d.ts +16 -0
- package/dist/workflow-state.js +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export declare const MARTIN_TOOL_NAMES: readonly ["martin_run", "martin_inspect", "martin_status", "martin_doctor", "martin_plan", "martin_preflight", "martin_logs", "martin_pause", "martin_cancel", "martin_continue", "martin_list_runs", "martin_triage_runs", "martin_get_run", "martin_get_attempt", "martin_get_verification_results", "martin_run_dossier", "martin_dossier", "martin_eval", "martin_pr_summary", "martin_create_pr", "martin_review_pr"];
|
|
2
|
-
export declare const MARTIN_STARTER_TOOL_NAMES: readonly ["martin_doctor", "martin_plan", "martin_preflight", "martin_run", "martin_triage_runs", "martin_dossier"];
|
|
3
|
-
export declare const MARTIN_MINIMAL_TOOL_NAMES: readonly ["martin_doctor", "martin_plan", "martin_preflight", "martin_list_runs", "martin_triage_runs", "martin_dossier"];
|
|
4
|
-
export declare const MARTIN_DIAGNOSTIC_TOOL_NAMES: readonly ["martin_doctor", "martin_plan", "martin_preflight", "martin_logs", "martin_list_runs", "martin_triage_runs", "martin_get_run", "martin_get_attempt", "martin_get_verification_results", "martin_dossier", "martin_eval"];
|
|
1
|
+
export declare const MARTIN_TOOL_NAMES: readonly ["martin_run", "martin_inspect", "martin_status", "martin_doctor", "martin_plan", "martin_preflight", "martin_estimate", "martin_logs", "martin_pause", "martin_cancel", "martin_continue", "martin_list_runs", "martin_triage_runs", "martin_get_run", "martin_get_attempt", "martin_get_verification_results", "martin_run_dossier", "martin_dossier", "martin_eval", "martin_pr_summary", "martin_create_pr", "martin_review_pr"];
|
|
2
|
+
export declare const MARTIN_STARTER_TOOL_NAMES: readonly ["martin_doctor", "martin_plan", "martin_preflight", "martin_estimate", "martin_run", "martin_triage_runs", "martin_dossier"];
|
|
3
|
+
export declare const MARTIN_MINIMAL_TOOL_NAMES: readonly ["martin_doctor", "martin_plan", "martin_preflight", "martin_estimate", "martin_list_runs", "martin_triage_runs", "martin_dossier"];
|
|
4
|
+
export declare const MARTIN_DIAGNOSTIC_TOOL_NAMES: readonly ["martin_doctor", "martin_plan", "martin_preflight", "martin_estimate", "martin_logs", "martin_list_runs", "martin_triage_runs", "martin_get_run", "martin_get_attempt", "martin_get_verification_results", "martin_dossier", "martin_eval"];
|
|
5
5
|
export declare const MARTIN_GITHUB_REVIEW_TOOL_NAMES: readonly ["martin_doctor", "martin_plan", "martin_preflight", "martin_dossier", "martin_eval", "martin_pr_summary", "martin_create_pr", "martin_review_pr"];
|
|
6
|
-
export declare const MARTIN_PAID_REMOTE_TOOL_NAMES: readonly ["martin_doctor", "martin_plan", "martin_preflight", "martin_run", "martin_list_runs", "martin_triage_runs", "martin_get_run", "martin_get_verification_results", "martin_dossier", "martin_eval"];
|
|
7
|
-
export declare const MARTIN_RESOURCE_URIS: readonly ["martin://server/health", "martin://runs/recent", "martin://runs/triage", "martin://runs/latest", "martin://runs/latest/summary", "martin://runs/latest/proof-card", "martin://runs/latest/budget-status", "martin://runs/latest/verifier-evidence", "martin://runs/latest/rollback-evidence", "martin://policies/current", "martin://repo/risk-map", "martin://verifiers/results", "martin://agent/next-step", "martin://guides/mcp-usage", "martin://guides/agent-start", "martin://guides/command-map", "martin://guides/ide-onboarding", "martin://guides/operating-rules", "martin://guides/publish-readiness"];
|
|
6
|
+
export declare const MARTIN_PAID_REMOTE_TOOL_NAMES: readonly ["martin_doctor", "martin_plan", "martin_preflight", "martin_estimate", "martin_run", "martin_list_runs", "martin_triage_runs", "martin_get_run", "martin_get_verification_results", "martin_dossier", "martin_eval"];
|
|
7
|
+
export declare const MARTIN_RESOURCE_URIS: readonly ["martin://server/health", "martin://runs/recent", "martin://runs/triage", "martin://runs/latest", "martin://runs/latest/summary", "martin://runs/latest/proof-card", "martin://runs/latest/budget-status", "martin://runs/latest/verifier-evidence", "martin://runs/latest/rollback-evidence", "martin://policies/current", "martin://repo/risk-map", "martin://verifiers/results", "martin://agent/next-step", "martin://agent/governance-status", "martin://guides/mcp-usage", "martin://guides/agent-start", "martin://guides/command-map", "martin://guides/ide-onboarding", "martin://guides/operating-rules", "martin://guides/publish-readiness"];
|
|
8
8
|
export declare const MARTIN_RESOURCE_TEMPLATE_URIS: readonly ["martin://runs/{loopId}", "martin://runs/{loopId}/dossier", "martin://runs/{loopId}/attempts/{attemptIndex}", "martin://runs/{loopId}/verification"];
|
|
9
9
|
export declare const MARTIN_PROMPT_NAMES: readonly ["martin_start", "martin_preflight", "martin_triage", "martin_resume", "martin_prove", "martin_release_check", "martin_governed_coding_kickoff", "martin_debug_failed_run", "martin_publish_readiness_review", "martin_triage_run_store", "safe_bug_fix", "write_tests_first", "small_refactor", "security_review", "pr_review", "release_check"];
|
|
10
10
|
export interface MartinDiscoveryMetadata {
|
|
@@ -6,6 +6,7 @@ export const MARTIN_TOOL_NAMES = [
|
|
|
6
6
|
"martin_doctor",
|
|
7
7
|
"martin_plan",
|
|
8
8
|
"martin_preflight",
|
|
9
|
+
"martin_estimate",
|
|
9
10
|
"martin_logs",
|
|
10
11
|
"martin_pause",
|
|
11
12
|
"martin_cancel",
|
|
@@ -26,6 +27,7 @@ export const MARTIN_STARTER_TOOL_NAMES = [
|
|
|
26
27
|
"martin_doctor",
|
|
27
28
|
"martin_plan",
|
|
28
29
|
"martin_preflight",
|
|
30
|
+
"martin_estimate",
|
|
29
31
|
"martin_run",
|
|
30
32
|
"martin_triage_runs",
|
|
31
33
|
"martin_dossier"
|
|
@@ -34,6 +36,7 @@ export const MARTIN_MINIMAL_TOOL_NAMES = [
|
|
|
34
36
|
"martin_doctor",
|
|
35
37
|
"martin_plan",
|
|
36
38
|
"martin_preflight",
|
|
39
|
+
"martin_estimate",
|
|
37
40
|
"martin_list_runs",
|
|
38
41
|
"martin_triage_runs",
|
|
39
42
|
"martin_dossier"
|
|
@@ -42,6 +45,7 @@ export const MARTIN_DIAGNOSTIC_TOOL_NAMES = [
|
|
|
42
45
|
"martin_doctor",
|
|
43
46
|
"martin_plan",
|
|
44
47
|
"martin_preflight",
|
|
48
|
+
"martin_estimate",
|
|
45
49
|
"martin_logs",
|
|
46
50
|
"martin_list_runs",
|
|
47
51
|
"martin_triage_runs",
|
|
@@ -65,6 +69,7 @@ export const MARTIN_PAID_REMOTE_TOOL_NAMES = [
|
|
|
65
69
|
"martin_doctor",
|
|
66
70
|
"martin_plan",
|
|
67
71
|
"martin_preflight",
|
|
72
|
+
"martin_estimate",
|
|
68
73
|
"martin_run",
|
|
69
74
|
"martin_list_runs",
|
|
70
75
|
"martin_triage_runs",
|
|
@@ -87,6 +92,7 @@ export const MARTIN_RESOURCE_URIS = [
|
|
|
87
92
|
"martin://repo/risk-map",
|
|
88
93
|
"martin://verifiers/results",
|
|
89
94
|
"martin://agent/next-step",
|
|
95
|
+
"martin://agent/governance-status",
|
|
90
96
|
"martin://guides/mcp-usage",
|
|
91
97
|
"martin://guides/agent-start",
|
|
92
98
|
"martin://guides/command-map",
|
package/dist/resources.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export declare const MARTIN_STATIC_RESOURCE_URIS: {
|
|
|
20
20
|
readonly ideOnboardingGuide: "martin://guides/ide-onboarding";
|
|
21
21
|
readonly operatingRulesGuide: "martin://guides/operating-rules";
|
|
22
22
|
readonly publishReadinessGuide: "martin://guides/publish-readiness";
|
|
23
|
+
readonly governanceStatus: "martin://agent/governance-status";
|
|
23
24
|
};
|
|
24
25
|
export declare const MARTIN_RESOURCE_TEMPLATES: ResourceTemplate[];
|
|
25
26
|
export declare const MARTIN_STATIC_RESOURCES: Resource[];
|
package/dist/resources.js
CHANGED
|
@@ -5,6 +5,7 @@ import { martinDoctorTool } from "./tools/doctor.js";
|
|
|
5
5
|
import { martinTriageRunsTool } from "./tools/triage-runs.js";
|
|
6
6
|
import { martinRunDossierTool } from "./tools/run-dossier.js";
|
|
7
7
|
import { inspectRepoSignals, buildPolicyPackDefinition, buildRepoRiskMap } from "./tools/workflow-governance.js";
|
|
8
|
+
import { readWorkflowState } from "./workflow-state.js";
|
|
8
9
|
import { buildAttemptSnapshot, buildPersistedLoopPreview, buildVerificationHistorySnapshot, parseAttemptIndex, resolveMartinDiscoveryContext, toPrettyJson } from "./discovery-support.js";
|
|
9
10
|
import { loadDetailedLoopRecord, readLedgerEvents } from "./tools/run-store.js";
|
|
10
11
|
import { invalidArgumentsError, MartinToolError } from "./tools/tool-errors.js";
|
|
@@ -27,7 +28,8 @@ export const MARTIN_STATIC_RESOURCE_URIS = {
|
|
|
27
28
|
commandMapGuide: "martin://guides/command-map",
|
|
28
29
|
ideOnboardingGuide: "martin://guides/ide-onboarding",
|
|
29
30
|
operatingRulesGuide: "martin://guides/operating-rules",
|
|
30
|
-
publishReadinessGuide: "martin://guides/publish-readiness"
|
|
31
|
+
publishReadinessGuide: "martin://guides/publish-readiness",
|
|
32
|
+
governanceStatus: "martin://agent/governance-status"
|
|
31
33
|
};
|
|
32
34
|
export const MARTIN_RESOURCE_TEMPLATES = [
|
|
33
35
|
{
|
|
@@ -188,6 +190,13 @@ export const MARTIN_STATIC_RESOURCES = [
|
|
|
188
190
|
title: "Martin Publish Readiness Guide",
|
|
189
191
|
description: "Checklist-oriented guide for MCP publish readiness reviews.",
|
|
190
192
|
mimeType: "text/markdown"
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
uri: MARTIN_STATIC_RESOURCE_URIS.governanceStatus,
|
|
196
|
+
name: "martin_governance_status",
|
|
197
|
+
title: "Martin Governance Status",
|
|
198
|
+
description: "Proactive governance status: whether work is governed, budget remaining, unreceipted runs, and recommended next action. Read this before starting agent work.",
|
|
199
|
+
mimeType: "application/json"
|
|
191
200
|
}
|
|
192
201
|
];
|
|
193
202
|
export function listMartinResources() {
|
|
@@ -237,6 +246,8 @@ export async function readMartinResource(input) {
|
|
|
237
246
|
return jsonResource(input.uri, withDiscoveryMetadata(buildRepoRiskMap(inspectRepoSignals(context.workingDirectory)), context.runsRoot));
|
|
238
247
|
case MARTIN_STATIC_RESOURCE_URIS.verifierResults:
|
|
239
248
|
return jsonResource(input.uri, withDiscoveryMetadata(await buildLatestVerifierEvidenceResource(context.runsRoot), context.runsRoot));
|
|
249
|
+
case MARTIN_STATIC_RESOURCE_URIS.governanceStatus:
|
|
250
|
+
return jsonResource(input.uri, withDiscoveryMetadata(await buildGovernanceStatusResource(context.runsRoot, context.workingDirectory), context.runsRoot));
|
|
240
251
|
case MARTIN_STATIC_RESOURCE_URIS.agentNextStep:
|
|
241
252
|
return jsonResource(input.uri, withDiscoveryMetadata(await buildAgentNextStepResource(context.runsRoot), context.runsRoot));
|
|
242
253
|
case MARTIN_STATIC_RESOURCE_URIS.mcpUsageGuide:
|
|
@@ -565,6 +576,57 @@ function compactEmptyState(kind, runsRoot, warnings) {
|
|
|
565
576
|
warnings
|
|
566
577
|
};
|
|
567
578
|
}
|
|
579
|
+
async function buildGovernanceStatusResource(runsRoot, workingDirectory) {
|
|
580
|
+
const state = await readWorkflowState(runsRoot);
|
|
581
|
+
const mcpState = state.mcp ?? {};
|
|
582
|
+
const hasDoctor = Boolean(mcpState.doctor);
|
|
583
|
+
const hasPlan = Boolean(mcpState.plan);
|
|
584
|
+
const hasPreflight = Boolean(mcpState.preflight);
|
|
585
|
+
const governed = hasDoctor && hasPlan && hasPreflight;
|
|
586
|
+
const latest = await loadLatestRunForCompactResource(runsRoot);
|
|
587
|
+
const budgetRemaining = (!latest.empty && latest.detail)
|
|
588
|
+
? buildPersistedLoopPreview(latest.detail.loop).remainingBudgetUsd
|
|
589
|
+
: null;
|
|
590
|
+
let unreceiptedRuns = 0;
|
|
591
|
+
try {
|
|
592
|
+
const inspection = await inspectLoopTool({ runsDir: runsRoot });
|
|
593
|
+
const total = inspection.loopCount ?? 0;
|
|
594
|
+
const completed = (inspection.statusBreakdown?.completed ?? 0) + (inspection.statusBreakdown?.failed ?? 0);
|
|
595
|
+
unreceiptedRuns = Math.max(0, total - completed);
|
|
596
|
+
}
|
|
597
|
+
catch {
|
|
598
|
+
// No runs yet
|
|
599
|
+
}
|
|
600
|
+
const missingSteps = [];
|
|
601
|
+
if (!hasDoctor)
|
|
602
|
+
missingSteps.push("martin_doctor");
|
|
603
|
+
if (!hasPlan)
|
|
604
|
+
missingSteps.push("martin_plan");
|
|
605
|
+
if (!hasPreflight)
|
|
606
|
+
missingSteps.push("martin_preflight");
|
|
607
|
+
const recommendedAction = !hasDoctor
|
|
608
|
+
? "Run martin_doctor to confirm environment before any work."
|
|
609
|
+
: !hasPlan
|
|
610
|
+
? "Run martin_plan with your objective to scope the task."
|
|
611
|
+
: !hasPreflight
|
|
612
|
+
? "Run martin_preflight to validate the run contract before execution."
|
|
613
|
+
: "Governance complete. You may proceed with martin_run.";
|
|
614
|
+
return {
|
|
615
|
+
kind: "governance-status",
|
|
616
|
+
governed,
|
|
617
|
+
workflowReceipts: {
|
|
618
|
+
doctor: hasDoctor ? { recordedAt: mcpState.doctor.recordedAt } : null,
|
|
619
|
+
plan: hasPlan ? { recordedAt: mcpState.plan.recordedAt } : null,
|
|
620
|
+
preflight: hasPreflight ? { recordedAt: mcpState.preflight.recordedAt } : null
|
|
621
|
+
},
|
|
622
|
+
missingSteps,
|
|
623
|
+
budgetRemaining,
|
|
624
|
+
unreceiptedRuns,
|
|
625
|
+
recommendedAction,
|
|
626
|
+
requiredSequence: ["martin_doctor", "martin_plan", "martin_preflight", "martin_run", "martin_dossier"],
|
|
627
|
+
warning: governed ? undefined : "This session is NOT governed. Complete the required sequence before making changes."
|
|
628
|
+
};
|
|
629
|
+
}
|
|
568
630
|
function buildCurrentPoliciesResource(workingDirectory) {
|
|
569
631
|
const signals = inspectRepoSignals(workingDirectory);
|
|
570
632
|
const recommended = buildPolicyPackDefinition(undefined, signals);
|
package/dist/server.js
CHANGED
|
@@ -43,6 +43,7 @@ import { createRunControlReceipt } from "./tools/run-controls.js";
|
|
|
43
43
|
import { martinTriageRunsTool } from "./tools/triage-runs.js";
|
|
44
44
|
import { runLoopTool } from "./tools/run-loop.js";
|
|
45
45
|
import { MARTIN_ENGINE_VALUES } from "./tools/tool-support.js";
|
|
46
|
+
import { classifyRoute } from "./vendor/core/index.js";
|
|
46
47
|
import { createToolErrorResult, createToolSuccessResult } from "./tools/tool-response.js";
|
|
47
48
|
import { MartinToolError, toToolFailure } from "./tools/tool-errors.js";
|
|
48
49
|
import { normalizeLoopBudget } from "./tools/workflow-governance.js";
|
|
@@ -1370,6 +1371,59 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1370
1371
|
oneOf: [{ required: ["file"] }, { required: ["loopId"] }, { required: ["latest"] }]
|
|
1371
1372
|
},
|
|
1372
1373
|
outputSchema: prReviewOutputSchema
|
|
1374
|
+
},
|
|
1375
|
+
{
|
|
1376
|
+
name: "martin_estimate",
|
|
1377
|
+
description: "Estimate the cost, recommended route, and Pre Work Burn for an objective without spending anything. Use before martin_run to understand what a task will cost.",
|
|
1378
|
+
annotations: {
|
|
1379
|
+
readOnlyHint: true,
|
|
1380
|
+
idempotentHint: true
|
|
1381
|
+
},
|
|
1382
|
+
inputSchema: {
|
|
1383
|
+
type: "object",
|
|
1384
|
+
additionalProperties: false,
|
|
1385
|
+
properties: {
|
|
1386
|
+
objective: {
|
|
1387
|
+
type: "string",
|
|
1388
|
+
description: "The coding task to estimate."
|
|
1389
|
+
},
|
|
1390
|
+
engine: {
|
|
1391
|
+
type: "string",
|
|
1392
|
+
enum: [...MARTIN_ENGINE_VALUES],
|
|
1393
|
+
description: "Which agent CLI would be used. Defaults to claude."
|
|
1394
|
+
},
|
|
1395
|
+
budgetUsd: {
|
|
1396
|
+
type: "number",
|
|
1397
|
+
exclusiveMinimum: 0,
|
|
1398
|
+
description: "Budget ceiling for estimation. Defaults to 5."
|
|
1399
|
+
},
|
|
1400
|
+
fileScope: {
|
|
1401
|
+
type: "array",
|
|
1402
|
+
items: { type: "string" },
|
|
1403
|
+
description: "Optional file paths to scope the estimate."
|
|
1404
|
+
}
|
|
1405
|
+
},
|
|
1406
|
+
required: ["objective"]
|
|
1407
|
+
},
|
|
1408
|
+
outputSchema: {
|
|
1409
|
+
type: "object",
|
|
1410
|
+
additionalProperties: true,
|
|
1411
|
+
properties: {
|
|
1412
|
+
objective: { type: "string" },
|
|
1413
|
+
engine: { type: "string" },
|
|
1414
|
+
budgetUsd: { type: "number" },
|
|
1415
|
+
selectedMode: { type: "string" },
|
|
1416
|
+
confidence: { type: "number" },
|
|
1417
|
+
expectedCostUsd: { type: "number" },
|
|
1418
|
+
expectedPreworkBurnPct: { type: "number" },
|
|
1419
|
+
reason: { type: "array", items: { type: "string" } },
|
|
1420
|
+
blockedSteps: { type: "array", items: { type: "string" } },
|
|
1421
|
+
compressed: { type: "boolean" },
|
|
1422
|
+
compressionSummary: { type: "string" },
|
|
1423
|
+
recommendedBudgetUsd: { type: "number" }
|
|
1424
|
+
},
|
|
1425
|
+
required: ["objective", "engine", "budgetUsd", "selectedMode", "confidence", "expectedCostUsd", "expectedPreworkBurnPct", "reason"]
|
|
1426
|
+
}
|
|
1373
1427
|
}
|
|
1374
1428
|
]
|
|
1375
1429
|
}));
|
|
@@ -1580,6 +1634,38 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1580
1634
|
const output = await martinReviewPrTool(input);
|
|
1581
1635
|
return createToolSuccessResult(output, `PR review verdict for ${output.loopId}: ${output.verdict}.`);
|
|
1582
1636
|
}
|
|
1637
|
+
if (name === "martin_estimate") {
|
|
1638
|
+
const input = args;
|
|
1639
|
+
const objective = input.objective;
|
|
1640
|
+
const engine = input.engine ?? "claude";
|
|
1641
|
+
const budgetUsd = input.budgetUsd ?? 5;
|
|
1642
|
+
const fileScope = input.fileScope ?? [];
|
|
1643
|
+
const route = classifyRoute({
|
|
1644
|
+
objective,
|
|
1645
|
+
verificationPlan: [],
|
|
1646
|
+
budgetUsd,
|
|
1647
|
+
allowedPaths: fileScope,
|
|
1648
|
+
scopedFileCount: fileScope.length > 0 ? fileScope.length : undefined
|
|
1649
|
+
});
|
|
1650
|
+
const recommendedBudgetUsd = route.selectedMode === "direct"
|
|
1651
|
+
? Math.max(2, Math.round(route.expectedCostUsd * 3 * 100) / 100)
|
|
1652
|
+
: Math.max(5, Math.round(route.expectedCostUsd * 2 * 100) / 100);
|
|
1653
|
+
const output = {
|
|
1654
|
+
objective,
|
|
1655
|
+
engine,
|
|
1656
|
+
budgetUsd,
|
|
1657
|
+
selectedMode: route.selectedMode,
|
|
1658
|
+
confidence: route.confidence,
|
|
1659
|
+
expectedCostUsd: route.expectedCostUsd,
|
|
1660
|
+
expectedPreworkBurnPct: route.expectedPreworkBurnPct,
|
|
1661
|
+
reason: route.reason,
|
|
1662
|
+
blockedSteps: route.blockedSteps,
|
|
1663
|
+
compressed: route.compressed,
|
|
1664
|
+
...(route.compressionSummary ? { compressionSummary: route.compressionSummary } : {}),
|
|
1665
|
+
recommendedBudgetUsd
|
|
1666
|
+
};
|
|
1667
|
+
return createToolSuccessResult(output, `Estimate: ${route.selectedMode} route, ~$${route.expectedCostUsd.toFixed(2)} expected cost, ${route.expectedPreworkBurnPct}% pre-work burn. Recommended budget: $${recommendedBudgetUsd.toFixed(2)}.`);
|
|
1668
|
+
}
|
|
1583
1669
|
return createToolErrorResult(toToolFailure(new Error(`Unknown tool: ${name}`)));
|
|
1584
1670
|
}
|
|
1585
1671
|
catch (error) {
|
package/dist/tools/run-store.js
CHANGED
|
@@ -16,17 +16,17 @@ async function attachReceiptIntegrity(detail) {
|
|
|
16
16
|
loopRecordPath: detail.canonicalLoopRecordPath,
|
|
17
17
|
ledgerPath
|
|
18
18
|
}).catch(() => ({
|
|
19
|
-
state: "
|
|
19
|
+
state: "unsigned",
|
|
20
20
|
reason: "Receipt integrity verification could not be completed."
|
|
21
21
|
}))
|
|
22
22
|
: detail.canonicalLoopRecordPath && detail.canonicalRunDirectory
|
|
23
23
|
? ({
|
|
24
|
-
state: "
|
|
24
|
+
state: "unsigned",
|
|
25
25
|
reason: "Receipt integrity material is incomplete for this canonical run."
|
|
26
26
|
})
|
|
27
27
|
: ({
|
|
28
|
-
state: "
|
|
29
|
-
reason: "Receipt integrity is only available for canonical run
|
|
28
|
+
state: "unsigned",
|
|
29
|
+
reason: "Receipt integrity is only available for canonical run directories."
|
|
30
30
|
});
|
|
31
31
|
const receiptScope = resolveReceiptScope(detail.loop, detail.runsRoot);
|
|
32
32
|
return {
|
|
@@ -14,6 +14,7 @@ export interface InspectableLoopRecord extends Omit<LoopRunRecord, "attempts" |
|
|
|
14
14
|
metadata?: Record<string, string>;
|
|
15
15
|
receiptIntegrity?: ReceiptIntegritySummary;
|
|
16
16
|
receiptScope?: ReceiptScope;
|
|
17
|
+
routingEconomics?: import("../vendor/contracts/index.js").RoutingEconomics;
|
|
17
18
|
}
|
|
18
19
|
export interface LoopPreview {
|
|
19
20
|
loopId: string;
|
|
@@ -32,6 +33,7 @@ export interface LoopPreview {
|
|
|
32
33
|
remainingIterations: number;
|
|
33
34
|
remainingTokens: number;
|
|
34
35
|
lastAttempt?: AttemptSummary;
|
|
36
|
+
routingEconomics?: Record<string, unknown>;
|
|
35
37
|
}
|
|
36
38
|
export interface AttemptArtifactFiles {
|
|
37
39
|
directory: string;
|
|
@@ -214,7 +214,22 @@ export function buildLoopPreview(loop) {
|
|
|
214
214
|
remainingBudgetUsd: costState.remainingBudgetUsd,
|
|
215
215
|
remainingIterations: costState.remainingIterations,
|
|
216
216
|
remainingTokens: costState.remainingTokens,
|
|
217
|
-
...(lastAttempt ? { lastAttempt: buildAttemptSummary(lastAttempt) } : {})
|
|
217
|
+
...(lastAttempt ? { lastAttempt: buildAttemptSummary(lastAttempt) } : {}),
|
|
218
|
+
...(loop.routingEconomics ? { routingEconomics: buildRoutingEconomicsSummary(loop.routingEconomics) } : {})
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function buildRoutingEconomicsSummary(economics) {
|
|
222
|
+
return {
|
|
223
|
+
preworkBurnPct: economics.preworkBurnPct,
|
|
224
|
+
preworkCostUsd: economics.preworkCostUsd,
|
|
225
|
+
executionCostUsd: economics.executionCostUsd,
|
|
226
|
+
totalCostUsd: economics.totalCostUsd,
|
|
227
|
+
...(economics.timeToFirstDeltaMs !== undefined ? { timeToFirstDeltaMs: economics.timeToFirstDeltaMs } : {}),
|
|
228
|
+
...(economics.costPerAcceptedChange !== undefined ? { costPerAcceptedChange: economics.costPerAcceptedChange } : {}),
|
|
229
|
+
...(economics.routeRecommendation ? {
|
|
230
|
+
routeRecommendation: economics.routeRecommendation,
|
|
231
|
+
routeRecommendationReason: economics.routeRecommendationReason
|
|
232
|
+
} : {})
|
|
218
233
|
};
|
|
219
234
|
}
|
|
220
235
|
export function buildAttemptSummary(attempt, artifacts) {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* MCP tools and integration tests use the same factories.
|
|
13
13
|
*/
|
|
14
14
|
import { readGitExecutionArtifacts, resolveGitRepositoryRoot, runSubprocess, runVerification } from "./cli-bridge.js";
|
|
15
|
-
import { buildCodexExecArgs } from "./codex-launcher.js";
|
|
15
|
+
import { buildCodexExecArgs, DEFAULT_CODEX_CHATGPT_MODEL } from "./codex-launcher.js";
|
|
16
16
|
import { createAdapterCapabilities, normalizeStructuredErrors, normalizeUsage } from "./runtime-support.js";
|
|
17
17
|
// ---------------------------------------------------------------------------
|
|
18
18
|
// Cost estimation
|
|
@@ -253,6 +253,10 @@ function createStreamingUsageInspector(capUsd, modelLabel) {
|
|
|
253
253
|
const bytesCeiling = blendedCostPerChar > 0
|
|
254
254
|
? Math.ceil((capUsd / blendedCostPerChar) * 2)
|
|
255
255
|
: 100_000_000;
|
|
256
|
+
// Time-based fallback: if we receive data but no usage events for this long,
|
|
257
|
+
// estimate spend from byte volume and enforce the cap. Prevents the inspector
|
|
258
|
+
// from going blind when Claude changes its stream-json event format.
|
|
259
|
+
const USAGE_BLIND_TIMEOUT_MS = 30_000;
|
|
256
260
|
let buffer = "";
|
|
257
261
|
let cumulativeUsd = 0;
|
|
258
262
|
let tokensIn = 0;
|
|
@@ -260,6 +264,7 @@ function createStreamingUsageInspector(capUsd, modelLabel) {
|
|
|
260
264
|
let turns = 0;
|
|
261
265
|
let totalBytes = 0;
|
|
262
266
|
let usageEventSeen = false;
|
|
267
|
+
let firstChunkAt;
|
|
263
268
|
let finalResult;
|
|
264
269
|
const checkBudgetExceeded = (terminate) => {
|
|
265
270
|
if (capUsd > 0 && cumulativeUsd > effectiveCapUsd) {
|
|
@@ -324,6 +329,7 @@ function createStreamingUsageInspector(capUsd, modelLabel) {
|
|
|
324
329
|
const chunkStr = chunk.toString("utf8");
|
|
325
330
|
totalBytes += chunk.byteLength;
|
|
326
331
|
buffer += chunkStr;
|
|
332
|
+
firstChunkAt ??= Date.now();
|
|
327
333
|
// Token-count ceiling fallback: if we've ingested a lot of bytes but
|
|
328
334
|
// never seen a single usage event, the event format may have changed
|
|
329
335
|
// and the inspector is silently blind. Terminate as a last resort.
|
|
@@ -333,6 +339,23 @@ function createStreamingUsageInspector(capUsd, modelLabel) {
|
|
|
333
339
|
`Subprocess terminated as a fallback budget guard for cap $${capUsd.toFixed(4)}.`);
|
|
334
340
|
return;
|
|
335
341
|
}
|
|
342
|
+
// Time-based fallback: if we've been receiving data for 30+ seconds
|
|
343
|
+
// without a single usage event, estimate spend from byte volume and
|
|
344
|
+
// enforce the cap. This catches cases where Claude's event format
|
|
345
|
+
// changed but bytes are still flowing.
|
|
346
|
+
if (!usageEventSeen &&
|
|
347
|
+
capUsd > 0 &&
|
|
348
|
+
firstChunkAt !== undefined &&
|
|
349
|
+
Date.now() - firstChunkAt > USAGE_BLIND_TIMEOUT_MS &&
|
|
350
|
+
totalBytes > 10_000) {
|
|
351
|
+
const estimatedUsd = totalBytes * blendedCostPerChar;
|
|
352
|
+
if (estimatedUsd > effectiveCapUsd) {
|
|
353
|
+
terminate(`No usage events received after ${String(Math.round((Date.now() - firstChunkAt) / 1000))}s ` +
|
|
354
|
+
`(${String(totalBytes)} bytes). Estimated cost ~$${estimatedUsd.toFixed(4)} exceeds cap ` +
|
|
355
|
+
`$${capUsd.toFixed(4)}. Subprocess terminated to prevent unmetered spend.`);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
336
359
|
let newlineIndex = buffer.indexOf("\n");
|
|
337
360
|
while (newlineIndex !== -1) {
|
|
338
361
|
const line = buffer.slice(0, newlineIndex);
|
|
@@ -773,6 +796,11 @@ export function createClaudeCliAdapter(options = {}) {
|
|
|
773
796
|
"--verbose",
|
|
774
797
|
"--print",
|
|
775
798
|
"--dangerously-skip-permissions",
|
|
799
|
+
// Prevent the child Claude process from loading the parent's MCP servers.
|
|
800
|
+
// Without this, a MartinLoop MCP server in the user's config gets spawned
|
|
801
|
+
// inside the governed subprocess, causing conflicts and "MCP server being
|
|
802
|
+
// overwritten" errors.
|
|
803
|
+
"--strict-mcp-config",
|
|
776
804
|
...modelArgs,
|
|
777
805
|
...extraArgs
|
|
778
806
|
],
|
|
@@ -797,6 +825,7 @@ export function createCodexCliAdapter(options = {}) {
|
|
|
797
825
|
const sandbox = options.sandbox ?? "workspace-write";
|
|
798
826
|
const workingDirectory = options.workingDirectory ?? process.cwd();
|
|
799
827
|
const command = options.command ?? "codex";
|
|
828
|
+
const launchModel = options.model ?? DEFAULT_CODEX_CHATGPT_MODEL;
|
|
800
829
|
return createAgentCliAdapter({
|
|
801
830
|
command,
|
|
802
831
|
adapterIdSuffix: "codex",
|
|
@@ -810,7 +839,7 @@ export function createCodexCliAdapter(options = {}) {
|
|
|
810
839
|
argsBuilder: () => buildCodexExecArgs({
|
|
811
840
|
workingDirectory,
|
|
812
841
|
sandbox,
|
|
813
|
-
|
|
842
|
+
model: launchModel,
|
|
814
843
|
extraArgs,
|
|
815
844
|
mode: "prompt"
|
|
816
845
|
}),
|
|
@@ -71,6 +71,16 @@ export declare function readGitExecutionArtifacts(repoRoot: string, timeoutMs: n
|
|
|
71
71
|
diffStats?: ReturnType<typeof diffStatsFromNumstat>;
|
|
72
72
|
}>;
|
|
73
73
|
export declare function readGitChangedFiles(repoRoot: string, timeoutMs: number, spawnImpl?: SpawnLike): Promise<string[]>;
|
|
74
|
+
/**
|
|
75
|
+
* Detects whether a meaningful workspace delta has occurred by checking
|
|
76
|
+
* git status for changed files that aren't MartinLoop metadata, lockfiles,
|
|
77
|
+
* or cache artifacts. Returns the first meaningful changed file if found.
|
|
78
|
+
*/
|
|
79
|
+
export declare function detectFirstDelta(repoRoot: string, timeoutMs: number, spawnImpl?: SpawnLike): Promise<{
|
|
80
|
+
detected: boolean;
|
|
81
|
+
filePath?: string;
|
|
82
|
+
changeType?: "create" | "modify" | "delete";
|
|
83
|
+
}>;
|
|
74
84
|
export declare function resolveGitRepositoryRoot(workingDirectory: string): string | undefined;
|
|
75
85
|
export interface SpawnPlan {
|
|
76
86
|
command: string;
|
|
@@ -236,6 +236,36 @@ export async function readGitChangedFiles(repoRoot, timeoutMs, spawnImpl) {
|
|
|
236
236
|
}
|
|
237
237
|
return parsePorcelainEntries(statusResult.stdout).filter((entry) => typeof entry === "string" && entry.length > 0);
|
|
238
238
|
}
|
|
239
|
+
// Ignore paths that are not meaningful code changes for first-delta detection
|
|
240
|
+
const FIRST_DELTA_IGNORE = [
|
|
241
|
+
/^\.martin\//u,
|
|
242
|
+
/^PROGRESS\.md$/u,
|
|
243
|
+
/\.lock$/u,
|
|
244
|
+
/^node_modules\//u,
|
|
245
|
+
/^\.git\//u,
|
|
246
|
+
/^\.cache\//u,
|
|
247
|
+
];
|
|
248
|
+
/**
|
|
249
|
+
* Detects whether a meaningful workspace delta has occurred by checking
|
|
250
|
+
* git status for changed files that aren't MartinLoop metadata, lockfiles,
|
|
251
|
+
* or cache artifacts. Returns the first meaningful changed file if found.
|
|
252
|
+
*/
|
|
253
|
+
export async function detectFirstDelta(repoRoot, timeoutMs, spawnImpl) {
|
|
254
|
+
const changedFiles = await readGitChangedFiles(repoRoot, timeoutMs, spawnImpl);
|
|
255
|
+
for (const file of changedFiles) {
|
|
256
|
+
if (FIRST_DELTA_IGNORE.some((pattern) => pattern.test(file))) {
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
// Classify: new file = create, deleted = delete, else modify
|
|
260
|
+
const diffResult = await runSubprocess("git", ["status", "--porcelain", "--", file], { cwd: repoRoot, timeoutMs: 3000, spawnImpl });
|
|
261
|
+
const status = diffResult.stdout.trim().slice(0, 2);
|
|
262
|
+
const changeType = status.includes("?") ? "create"
|
|
263
|
+
: status.includes("D") ? "delete"
|
|
264
|
+
: "modify";
|
|
265
|
+
return { detected: true, filePath: file, changeType };
|
|
266
|
+
}
|
|
267
|
+
return { detected: false };
|
|
268
|
+
}
|
|
239
269
|
export function resolveGitRepositoryRoot(workingDirectory) {
|
|
240
270
|
const resolvedWorkingDirectory = resolve(workingDirectory);
|
|
241
271
|
const cached = gitRepositoryRootCache.get(resolvedWorkingDirectory);
|
|
@@ -43,6 +43,7 @@ export interface CodexExecArgsOptions {
|
|
|
43
43
|
mode?: "prompt" | "probe";
|
|
44
44
|
}
|
|
45
45
|
type SpawnSyncLike = typeof spawnSync;
|
|
46
|
+
export declare const DEFAULT_CODEX_CHATGPT_MODEL = "gpt-5.4";
|
|
46
47
|
export interface CodexProbeCandidateResult {
|
|
47
48
|
path: string;
|
|
48
49
|
installKind: CodexInstallKind;
|
|
@@ -72,5 +73,6 @@ export declare function probeCodexLaunch(input: {
|
|
|
72
73
|
env?: NodeJS.ProcessEnv;
|
|
73
74
|
platform?: NodeJS.Platform;
|
|
74
75
|
spawnSyncImpl?: SpawnSyncLike;
|
|
76
|
+
model?: string;
|
|
75
77
|
}): CodexLaunchProbeResult;
|
|
76
78
|
export {};
|
|
@@ -2,6 +2,7 @@ import { spawnSync } from "node:child_process";
|
|
|
2
2
|
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
3
3
|
import { dirname, extname, join, resolve } from "node:path";
|
|
4
4
|
const codexLaunchProbeCache = new Map();
|
|
5
|
+
export const DEFAULT_CODEX_CHATGPT_MODEL = "gpt-5.4";
|
|
5
6
|
const CODEX_LAUNCH_PROBE_PROMPT = [
|
|
6
7
|
"You are validating MartinLoop Codex host readiness.",
|
|
7
8
|
"Do not edit files.",
|
|
@@ -14,7 +15,8 @@ function buildProbeCacheKey(input) {
|
|
|
14
15
|
return JSON.stringify({
|
|
15
16
|
workingDirectory: resolve(input.workingDirectory),
|
|
16
17
|
platform: input.platform,
|
|
17
|
-
candidatePaths: input.candidatePaths
|
|
18
|
+
candidatePaths: input.candidatePaths,
|
|
19
|
+
model: input.model ?? DEFAULT_CODEX_CHATGPT_MODEL
|
|
18
20
|
});
|
|
19
21
|
}
|
|
20
22
|
function isInsideGitRepository(workingDirectory) {
|
|
@@ -167,6 +169,39 @@ function detectInvocationMode(resolvedPath, platform) {
|
|
|
167
169
|
}
|
|
168
170
|
function classifyProbeFailure(stderr, stdout, diagnosis) {
|
|
169
171
|
const combined = `${stderr}\n${stdout}`;
|
|
172
|
+
if (/MARTIN_REMOTE_TOKEN/iu.test(combined) ||
|
|
173
|
+
/AuthRequired\(AuthRequiredError/iu.test(combined) ||
|
|
174
|
+
/www_authenticate_header/iu.test(combined)) {
|
|
175
|
+
const warnings = [...diagnosis.warnings];
|
|
176
|
+
const configCollisionWarning = "Codex inherited auth-sensitive MCP or plugin state from the operator's default user config.";
|
|
177
|
+
if (!warnings.includes(configCollisionWarning)) {
|
|
178
|
+
warnings.push(configCollisionWarning);
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
summary: "Codex inherited auth-sensitive MCP or plugin state from the operator's default user config. Governed Codex runs should launch with `exec --ignore-user-config` so missing remote tokens or third-party auth do not abort MartinLoop work.",
|
|
182
|
+
diagnosis: {
|
|
183
|
+
...diagnosis,
|
|
184
|
+
warnings,
|
|
185
|
+
remediation: "Retry the governed Codex launch with `exec --ignore-user-config`, or remove auth-sensitive MCP/plugin dependencies from the default Codex user config before running MartinLoop."
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
if (/not supported when using Codex with a ChatGPT account/iu.test(combined) ||
|
|
190
|
+
/gpt-5\.3-codex/iu.test(combined)) {
|
|
191
|
+
const warnings = [...diagnosis.warnings];
|
|
192
|
+
const unsupportedModelWarning = "Codex tried to use a model that is not supported for ChatGPT-account authentication.";
|
|
193
|
+
if (!warnings.includes(unsupportedModelWarning)) {
|
|
194
|
+
warnings.push(unsupportedModelWarning);
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
summary: `Codex launched with a model that is not supported for ChatGPT-account authentication. Use an explicit supported model such as \`${DEFAULT_CODEX_CHATGPT_MODEL}\` for governed Codex work.`,
|
|
198
|
+
diagnosis: {
|
|
199
|
+
...diagnosis,
|
|
200
|
+
warnings,
|
|
201
|
+
remediation: `Override the Codex launch model to a ChatGPT-account-supported option such as \`${DEFAULT_CODEX_CHATGPT_MODEL}\` before running governed Codex work.`
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
}
|
|
170
205
|
if (/@openai\/codex-linux-x64/iu.test(combined) ||
|
|
171
206
|
/cannot find module ['"]@openai\/codex-linux-x64['"]/iu.test(combined)) {
|
|
172
207
|
const warnings = [...diagnosis.warnings];
|
|
@@ -205,6 +240,24 @@ function classifyProbeFailure(stderr, stdout, diagnosis) {
|
|
|
205
240
|
}
|
|
206
241
|
};
|
|
207
242
|
}
|
|
243
|
+
if (/writing is blocked by read-only sandbox/iu.test(combined) ||
|
|
244
|
+
/read-only filesystem sandbox/iu.test(combined) ||
|
|
245
|
+
/approval is disabled/iu.test(combined)) {
|
|
246
|
+
const warnings = [...diagnosis.warnings];
|
|
247
|
+
const readOnlySandboxWarning = "Codex stayed in a read-only or approval-disabled sandbox even though MartinLoop requested workspace-write.";
|
|
248
|
+
if (!warnings.includes(readOnlySandboxWarning)) {
|
|
249
|
+
warnings.push(readOnlySandboxWarning);
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
summary: "Codex stayed in a read-only or approval-disabled sandbox even though MartinLoop requested workspace-write.",
|
|
253
|
+
diagnosis: {
|
|
254
|
+
...diagnosis,
|
|
255
|
+
sandboxCompatible: false,
|
|
256
|
+
warnings,
|
|
257
|
+
remediation: "Launch governed Codex runs with `codex exec --ignore-user-config --sandbox workspace-write`. If the session still reports a read-only sandbox, treat it as a host/runtime mismatch and repair or relocate the affected Codex workspace before resuming governed work."
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
}
|
|
208
261
|
return {};
|
|
209
262
|
}
|
|
210
263
|
export function buildCodexExecArgs(options) {
|
|
@@ -213,6 +266,8 @@ export function buildCodexExecArgs(options) {
|
|
|
213
266
|
const extraArgs = options.extraArgs ?? [];
|
|
214
267
|
return [
|
|
215
268
|
"exec",
|
|
269
|
+
// Governed MartinLoop runs should not inherit auth-sensitive default MCP/plugin state.
|
|
270
|
+
"--ignore-user-config",
|
|
216
271
|
"--cd",
|
|
217
272
|
options.workingDirectory,
|
|
218
273
|
"--sandbox",
|
|
@@ -396,6 +451,7 @@ export function probeCodexLaunch(input) {
|
|
|
396
451
|
});
|
|
397
452
|
const args = buildCodexExecArgs({
|
|
398
453
|
workingDirectory: input.workingDirectory,
|
|
454
|
+
model: input.model ?? DEFAULT_CODEX_CHATGPT_MODEL,
|
|
399
455
|
mode: "probe"
|
|
400
456
|
});
|
|
401
457
|
if (!availability.available) {
|
|
@@ -431,7 +487,8 @@ export function probeCodexLaunch(input) {
|
|
|
431
487
|
const cacheKey = buildProbeCacheKey({
|
|
432
488
|
workingDirectory: input.workingDirectory,
|
|
433
489
|
platform,
|
|
434
|
-
candidatePaths
|
|
490
|
+
candidatePaths,
|
|
491
|
+
model: input.model
|
|
435
492
|
});
|
|
436
493
|
if (input.spawnSyncImpl === undefined) {
|
|
437
494
|
const cached = codexLaunchProbeCache.get(cacheKey);
|
|
@@ -59,4 +59,20 @@ export interface ExecutionPolicyCompileInput {
|
|
|
59
59
|
config?: ExecutionPolicyConfigInput;
|
|
60
60
|
request: ExecutionPolicyRequestInput;
|
|
61
61
|
}
|
|
62
|
+
export type RoutingMode = "direct" | "manager" | "adaptive" | "consensus";
|
|
63
|
+
export interface RoutingPolicy {
|
|
64
|
+
enabled: boolean;
|
|
65
|
+
mode: RoutingMode;
|
|
66
|
+
/** Max percentage of total budget that can be spent before first code change. */
|
|
67
|
+
maxPreworkBudgetPct: number;
|
|
68
|
+
/** Hard dollar cap on pre-work spend. */
|
|
69
|
+
maxPreworkCostUsd: number;
|
|
70
|
+
/** Max model calls allowed for manager/router/planner before worker starts. */
|
|
71
|
+
maxManagerCalls: number;
|
|
72
|
+
/** Skip orchestration entirely if task confidence is above this threshold (0-1). */
|
|
73
|
+
skipOrchestrationIfConfidenceAbove: number;
|
|
74
|
+
/** Force direct execution for tasks classified as simple. */
|
|
75
|
+
requireDirectForSimpleTasks: boolean;
|
|
76
|
+
}
|
|
77
|
+
export declare const DEFAULT_ROUTING_POLICY: RoutingPolicy;
|
|
62
78
|
export declare function cloneExecutionPolicy(policy: ExecutionPolicy): ExecutionPolicy;
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
export const DEFAULT_ROUTING_POLICY = {
|
|
2
|
+
enabled: true,
|
|
3
|
+
mode: "adaptive",
|
|
4
|
+
maxPreworkBudgetPct: 25,
|
|
5
|
+
maxPreworkCostUsd: 2.0,
|
|
6
|
+
maxManagerCalls: 2,
|
|
7
|
+
skipOrchestrationIfConfidenceAbove: 0.85,
|
|
8
|
+
requireDirectForSimpleTasks: true
|
|
9
|
+
};
|
|
1
10
|
export function cloneExecutionPolicy(policy) {
|
|
2
11
|
return {
|
|
3
12
|
configPath: policy.configPath,
|