@martinloop/mcp 0.2.7 → 0.3.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 +49 -104
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/prompts.d.ts +1 -1
- package/dist/resources.d.ts +1 -1
- package/dist/resources.js +2 -2
- package/dist/server-validation.d.ts +1 -0
- package/dist/server-validation.js +8 -0
- package/dist/server.js +87 -9
- package/dist/tools/doctor.d.ts +39 -1
- package/dist/tools/doctor.js +68 -9
- package/dist/tools/eval.js +3 -2
- package/dist/tools/get-run.d.ts +3 -0
- package/dist/tools/get-run.js +3 -1
- package/dist/tools/get-verification-results.d.ts +3 -0
- package/dist/tools/get-verification-results.js +3 -1
- package/dist/tools/plan.js +4 -2
- package/dist/tools/pr-tools.js +2 -1
- package/dist/tools/preflight.d.ts +41 -1
- package/dist/tools/preflight.js +74 -19
- package/dist/tools/run-dossier.d.ts +3 -0
- package/dist/tools/run-dossier.js +5 -2
- package/dist/tools/run-loop.d.ts +7 -2
- package/dist/tools/run-loop.js +67 -35
- package/dist/tools/run-store.js +67 -15
- package/dist/tools/tool-errors.js +1 -1
- package/dist/tools/tool-support.d.ts +8 -3
- package/dist/tools/tool-support.js +61 -18
- package/dist/tools/workflow-governance.d.ts +19 -3
- package/dist/tools/workflow-governance.js +107 -55
- package/dist/vendor/adapters/claude-cli.d.ts +45 -3
- package/dist/vendor/adapters/claude-cli.js +465 -45
- package/dist/vendor/adapters/cli-bridge.d.ts +46 -0
- package/dist/vendor/adapters/cli-bridge.js +147 -38
- package/dist/vendor/adapters/codex-launcher.d.ts +76 -0
- package/dist/vendor/adapters/codex-launcher.js +538 -0
- package/dist/vendor/adapters/index.d.ts +3 -2
- package/dist/vendor/adapters/index.js +3 -2
- package/dist/vendor/adapters/openai-compatible.d.ts +19 -4
- package/dist/vendor/adapters/openai-compatible.js +50 -19
- package/dist/vendor/adapters/runtime-support.d.ts +3 -0
- package/dist/vendor/adapters/runtime-support.js +9 -1
- package/dist/vendor/adapters/stub-direct-provider.js +3 -0
- package/dist/vendor/adapters/verifier-only.d.ts +2 -0
- package/dist/vendor/adapters/verifier-only.js +11 -4
- package/dist/vendor/contracts/index.d.ts +39 -0
- package/dist/vendor/contracts/index.js +2 -0
- package/dist/vendor/core/context-integrity.js +28 -3
- package/dist/vendor/core/grounding.d.ts +1 -0
- package/dist/vendor/core/grounding.js +6 -2
- package/dist/vendor/core/index.d.ts +24 -3
- package/dist/vendor/core/index.js +113 -21
- package/dist/vendor/core/leash.js +85 -8
- package/dist/vendor/core/persistence/index.d.ts +2 -0
- package/dist/vendor/core/persistence/index.js +1 -0
- package/dist/vendor/core/persistence/integrity.d.ts +38 -0
- package/dist/vendor/core/persistence/integrity.js +248 -0
- package/dist/vendor/core/persistence/store.d.ts +7 -0
- package/dist/vendor/core/persistence/store.js +25 -1
- package/dist/vendor/core/policy.d.ts +9 -0
- package/dist/workflow-state.d.ts +9 -0
- package/dist/workflow-state.js +46 -3
- package/package.json +2 -2
- package/server.json +2 -2
package/dist/tools/get-run.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { buildArtifactSummary, buildBudgetSnapshot, buildCostSnapshot, buildLoopPreview, buildVerificationSummary } from "./tool-support.js";
|
|
2
|
+
import type { ReceiptIntegritySummary, ReceiptScope } from "../vendor/contracts/index.js";
|
|
2
3
|
export interface MartinGetRunInput {
|
|
3
4
|
file?: string;
|
|
4
5
|
loopId?: string;
|
|
@@ -12,6 +13,8 @@ export interface MartinGetRunOutput {
|
|
|
12
13
|
budget: ReturnType<typeof buildBudgetSnapshot>;
|
|
13
14
|
cost: ReturnType<typeof buildCostSnapshot>;
|
|
14
15
|
verification: ReturnType<typeof buildVerificationSummary>;
|
|
16
|
+
receiptIntegrity: ReceiptIntegritySummary;
|
|
17
|
+
receiptScope?: ReceiptScope;
|
|
15
18
|
artifacts: ReturnType<typeof buildArtifactSummary>;
|
|
16
19
|
inspection: {
|
|
17
20
|
runsRoot: string;
|
package/dist/tools/get-run.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildArtifactSummary, buildBudgetSnapshot, buildCostSnapshot, buildLoopPreview, buildVerificationSummary } from "./tool-support.js";
|
|
1
|
+
import { buildArtifactSummary, buildBudgetSnapshot, buildCostSnapshot, buildLoopPreview, resolveReceiptIntegrity, buildVerificationSummary } from "./tool-support.js";
|
|
2
2
|
import { loadDetailedLoopRecord, readLedgerEvents } from "./run-store.js";
|
|
3
3
|
export async function martinGetRunTool(input) {
|
|
4
4
|
const detail = await loadDetailedLoopRecord(input);
|
|
@@ -11,6 +11,8 @@ export async function martinGetRunTool(input) {
|
|
|
11
11
|
budget: buildBudgetSnapshot(detail.loop.budget),
|
|
12
12
|
cost: buildCostSnapshot(detail.loop.cost),
|
|
13
13
|
verification,
|
|
14
|
+
receiptIntegrity: resolveReceiptIntegrity(detail.loop),
|
|
15
|
+
...(detail.loop.receiptScope ? { receiptScope: detail.loop.receiptScope } : {}),
|
|
14
16
|
artifacts: buildArtifactSummary(detail.loop),
|
|
15
17
|
inspection: {
|
|
16
18
|
runsRoot: detail.runsRoot,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { buildLoopPreview, buildVerificationSummary } from "./tool-support.js";
|
|
2
|
+
import type { ReceiptIntegritySummary, ReceiptScope } from "../vendor/contracts/index.js";
|
|
2
3
|
export interface MartinGetVerificationResultsInput {
|
|
3
4
|
file?: string;
|
|
4
5
|
loopId?: string;
|
|
@@ -9,6 +10,8 @@ export interface MartinGetVerificationResultsOutput {
|
|
|
9
10
|
sourceKind: "file" | "loop_id" | "latest" | "runs_root";
|
|
10
11
|
loop: ReturnType<typeof buildLoopPreview>;
|
|
11
12
|
verification: ReturnType<typeof buildVerificationSummary>;
|
|
13
|
+
receiptIntegrity: ReceiptIntegritySummary;
|
|
14
|
+
receiptScope?: ReceiptScope;
|
|
12
15
|
warnings: string[];
|
|
13
16
|
}
|
|
14
17
|
export declare function martinGetVerificationResultsTool(input: MartinGetVerificationResultsInput): Promise<MartinGetVerificationResultsOutput>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildLoopPreview, buildVerificationSummary } from "./tool-support.js";
|
|
1
|
+
import { buildLoopPreview, buildVerificationSummary, resolveReceiptIntegrity } from "./tool-support.js";
|
|
2
2
|
import { loadDetailedLoopRecord, readLedgerEvents } from "./run-store.js";
|
|
3
3
|
export async function martinGetVerificationResultsTool(input) {
|
|
4
4
|
const detail = await loadDetailedLoopRecord(input);
|
|
@@ -9,6 +9,8 @@ export async function martinGetVerificationResultsTool(input) {
|
|
|
9
9
|
sourceKind: detail.sourceKind,
|
|
10
10
|
loop: buildLoopPreview(detail.loop),
|
|
11
11
|
verification,
|
|
12
|
+
receiptIntegrity: resolveReceiptIntegrity(detail.loop),
|
|
13
|
+
...(detail.loop.receiptScope ? { receiptScope: detail.loop.receiptScope } : {}),
|
|
12
14
|
warnings: [...detail.warnings, ...verification.warnings]
|
|
13
15
|
};
|
|
14
16
|
}
|
package/dist/tools/plan.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { resolveSafeRepoRoot } from "../server-validation.js";
|
|
2
|
-
import { buildPlanProposal } from "./workflow-governance.js";
|
|
2
|
+
import { buildPlanProposal, inspectRepoSignals } from "./workflow-governance.js";
|
|
3
3
|
export async function martinPlanTool(input) {
|
|
4
4
|
const workingDirectory = resolveSafeRepoRoot(input.workingDirectory);
|
|
5
|
-
const proposal = buildPlanProposal(workingDirectory, input
|
|
5
|
+
const proposal = buildPlanProposal(workingDirectory, input, {
|
|
6
|
+
signals: inspectRepoSignals(workingDirectory, { includeHostAvailability: false })
|
|
7
|
+
});
|
|
6
8
|
return {
|
|
7
9
|
workingDirectory,
|
|
8
10
|
...proposal
|
package/dist/tools/pr-tools.js
CHANGED
|
@@ -2,6 +2,7 @@ import { spawnSync } from "node:child_process";
|
|
|
2
2
|
import { loadDetailedLoopRecord } from "./run-store.js";
|
|
3
3
|
import { martinRunDossierTool } from "./run-dossier.js";
|
|
4
4
|
import { martinEvalTool } from "./eval.js";
|
|
5
|
+
import { resolveTrustedLoopRepoRoot } from "../server-validation.js";
|
|
5
6
|
import { detectCliAvailability } from "./tool-support.js";
|
|
6
7
|
import { MartinToolError } from "./tool-errors.js";
|
|
7
8
|
export async function martinPrSummaryTool(input) {
|
|
@@ -20,7 +21,7 @@ export async function martinPrSummaryTool(input) {
|
|
|
20
21
|
export async function martinCreatePrTool(input) {
|
|
21
22
|
const summary = await martinPrSummaryTool(input);
|
|
22
23
|
const detail = await loadDetailedLoopRecord(input);
|
|
23
|
-
const repoRoot = detail.loop.task?.repoRoot
|
|
24
|
+
const repoRoot = resolveTrustedLoopRepoRoot(detail.loop.task?.repoRoot);
|
|
24
25
|
const branch = readGitValue(repoRoot, ["branch", "--show-current"]);
|
|
25
26
|
const title = input.title?.trim() || summary.title;
|
|
26
27
|
if (!input.execute) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CodexHostPlatform } from "../vendor/adapters/index.js";
|
|
1
2
|
import { type MartinEngine } from "./tool-support.js";
|
|
2
3
|
import { buildPolicyPackDefinition, type MartinPlanProposal, type MartinPolicyPack, type MartinRiskAssessment, type MartinRunContract } from "./workflow-governance.js";
|
|
3
4
|
export interface MartinPreflightInput {
|
|
@@ -23,8 +24,20 @@ export interface MartinPreflightOutput {
|
|
|
23
24
|
ok: boolean;
|
|
24
25
|
summary: string;
|
|
25
26
|
warnings: string[];
|
|
27
|
+
receiptScope: {
|
|
28
|
+
invocationRoot: string;
|
|
29
|
+
workingDirectory: string;
|
|
30
|
+
repoRoot: string;
|
|
31
|
+
runsRoot: string;
|
|
32
|
+
};
|
|
33
|
+
scope: {
|
|
34
|
+
invocationRoot: string;
|
|
35
|
+
workingDirectory: string;
|
|
36
|
+
repoRoot: string;
|
|
37
|
+
runsRoot: string;
|
|
38
|
+
};
|
|
26
39
|
readiness: {
|
|
27
|
-
mode: "live" | "
|
|
40
|
+
mode: "live" | "proof";
|
|
28
41
|
liveMode: boolean;
|
|
29
42
|
engineReady: boolean;
|
|
30
43
|
};
|
|
@@ -51,6 +64,33 @@ export interface MartinPreflightOutput {
|
|
|
51
64
|
available: boolean;
|
|
52
65
|
detail: string;
|
|
53
66
|
resolvedPath?: string;
|
|
67
|
+
candidatePaths?: string[];
|
|
68
|
+
};
|
|
69
|
+
codexDiagnostics?: {
|
|
70
|
+
selectedPath?: string;
|
|
71
|
+
hostPlatform: CodexHostPlatform;
|
|
72
|
+
installKind: string;
|
|
73
|
+
nativeInstallValid: boolean;
|
|
74
|
+
invocationMode: string;
|
|
75
|
+
sandboxMode: string;
|
|
76
|
+
sandboxCompatible: boolean;
|
|
77
|
+
nativeDependencyStatus?: string;
|
|
78
|
+
nativeDependencyPackage?: string;
|
|
79
|
+
launchReady: boolean;
|
|
80
|
+
summary: string;
|
|
81
|
+
remediation?: string;
|
|
82
|
+
candidateProbeResults?: Array<{
|
|
83
|
+
path: string;
|
|
84
|
+
installKind: string;
|
|
85
|
+
invocationMode: string;
|
|
86
|
+
nativeInstallValid: boolean;
|
|
87
|
+
sandboxCompatible: boolean;
|
|
88
|
+
launchReady: boolean;
|
|
89
|
+
summary: string;
|
|
90
|
+
remediation?: string;
|
|
91
|
+
nativeDependencyStatus?: string;
|
|
92
|
+
nativeDependencyPackage?: string;
|
|
93
|
+
}>;
|
|
54
94
|
};
|
|
55
95
|
runsRoot: string;
|
|
56
96
|
pathScope: {
|
package/dist/tools/preflight.js
CHANGED
|
@@ -1,30 +1,45 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { probeCodexLaunch, resolveCliCommandAvailability } from "../vendor/adapters/index.js";
|
|
2
2
|
import { resolveRunsRoot } from "../vendor/core/index.js";
|
|
3
3
|
import { resolveSafeRepoRoot } from "../server-validation.js";
|
|
4
|
-
import { formatUsd, getEngineAvailability, resolveExecutionMode } from "./tool-support.js";
|
|
5
|
-
import { buildPlanProposal, buildRunContract, buildPolicyPackDefinition, inspectRepoSignals } from "./workflow-governance.js";
|
|
4
|
+
import { createSkippedCliAvailability, formatUsd, getEngineAvailability, resolveExecutionMode } from "./tool-support.js";
|
|
5
|
+
import { buildPlanProposal, normalizeLoopBudget, buildRunContract, buildPolicyPackDefinition, inspectRepoSignals } from "./workflow-governance.js";
|
|
6
6
|
export async function martinPreflightTool(input) {
|
|
7
7
|
const executionMode = resolveExecutionMode();
|
|
8
|
+
const workspaceRoot = resolveSafeRepoRoot();
|
|
8
9
|
const workingDirectory = resolveSafeRepoRoot(input.workingDirectory);
|
|
9
|
-
const signals = inspectRepoSignals(workingDirectory
|
|
10
|
+
const signals = inspectRepoSignals(workingDirectory, {
|
|
11
|
+
includeHostAvailability: executionMode.liveMode
|
|
12
|
+
});
|
|
10
13
|
const engine = input.engine ?? "claude";
|
|
11
|
-
const engineAvailability =
|
|
14
|
+
const engineAvailability = executionMode.liveMode
|
|
15
|
+
? engine === "codex"
|
|
16
|
+
? resolveCliCommandAvailability("codex")
|
|
17
|
+
: getEngineAvailability(engine)
|
|
18
|
+
: createSkippedCliAvailability(engine);
|
|
19
|
+
const codexProbe = executionMode.liveMode && engine === "codex" && engineAvailability.available
|
|
20
|
+
? probeCodexLaunch({
|
|
21
|
+
workingDirectory,
|
|
22
|
+
availability: engineAvailability
|
|
23
|
+
})
|
|
24
|
+
: undefined;
|
|
12
25
|
const warnings = [];
|
|
13
26
|
const allowedPaths = input.allowedPaths ?? [];
|
|
14
27
|
const deniedPaths = input.deniedPaths ?? [];
|
|
15
28
|
const overlappingScopes = allowedPaths.filter((candidate) => deniedPaths.includes(candidate));
|
|
16
|
-
const budget = {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
};
|
|
29
|
+
const budget = normalizeLoopBudget({
|
|
30
|
+
maxUsd: input.maxUsd,
|
|
31
|
+
maxIterations: input.maxIterations,
|
|
32
|
+
maxTokens: input.maxTokens
|
|
33
|
+
});
|
|
22
34
|
if (!executionMode.liveMode) {
|
|
23
|
-
warnings.push("
|
|
35
|
+
warnings.push("Proof mode is active; preflight only proves configuration shape, not live CLI readiness.");
|
|
24
36
|
}
|
|
25
37
|
else if (!engineAvailability.available) {
|
|
26
38
|
warnings.push(`Requested engine '${engine}' is not available on PATH.`);
|
|
27
39
|
}
|
|
40
|
+
else if (engine === "codex" && codexProbe && !codexProbe.ok) {
|
|
41
|
+
warnings.push(codexProbe.summary);
|
|
42
|
+
}
|
|
28
43
|
if ((input.verificationPlan?.length ?? 0) === 0) {
|
|
29
44
|
warnings.push("No verificationPlan was provided; Martin can run, but completion confidence will be lower.");
|
|
30
45
|
}
|
|
@@ -34,20 +49,34 @@ export async function martinPreflightTool(input) {
|
|
|
34
49
|
if (overlappingScopes.length > 0) {
|
|
35
50
|
warnings.push(`Some path patterns appear in both allowedPaths and deniedPaths: ${overlappingScopes.join(", ")}.`);
|
|
36
51
|
}
|
|
37
|
-
const plan = buildPlanProposal(workingDirectory, input);
|
|
38
|
-
const runContract = buildRunContract(workingDirectory, input);
|
|
52
|
+
const plan = buildPlanProposal(workingDirectory, input, { signals });
|
|
53
|
+
const runContract = buildRunContract(workingDirectory, input, { signals, plan });
|
|
39
54
|
const policy = buildPolicyPackDefinition(input.policyPack, signals);
|
|
40
|
-
const
|
|
55
|
+
const engineReady = !executionMode.liveMode ||
|
|
56
|
+
(engineAvailability.available && (engine !== "codex" || codexProbe?.ok !== false));
|
|
57
|
+
const ok = engineReady;
|
|
58
|
+
const receiptScope = {
|
|
59
|
+
invocationRoot: workspaceRoot,
|
|
60
|
+
workingDirectory,
|
|
61
|
+
repoRoot: workingDirectory,
|
|
62
|
+
runsRoot: resolveRunsRoot(process.env)
|
|
63
|
+
};
|
|
41
64
|
return {
|
|
42
65
|
ok,
|
|
43
66
|
summary: ok
|
|
44
67
|
? `Preflight ready for ${engine} in ${workingDirectory} with a ${formatUsd(budget.maxUsd)} budget cap and ${runContract.risk.level} risk.`
|
|
45
|
-
: `Preflight blocked: ${engine
|
|
68
|
+
: `Preflight blocked: ${engine === "codex" && codexProbe && !codexProbe.ok
|
|
69
|
+
? codexProbe.summary
|
|
70
|
+
: `${engine} is not available for live execution.`}`,
|
|
46
71
|
warnings,
|
|
72
|
+
receiptScope,
|
|
73
|
+
scope: {
|
|
74
|
+
...receiptScope
|
|
75
|
+
},
|
|
47
76
|
readiness: {
|
|
48
77
|
mode: executionMode.mode,
|
|
49
78
|
liveMode: executionMode.liveMode,
|
|
50
|
-
engineReady
|
|
79
|
+
engineReady
|
|
51
80
|
},
|
|
52
81
|
normalized: {
|
|
53
82
|
objective: input.objective,
|
|
@@ -66,10 +95,36 @@ export async function martinPreflightTool(input) {
|
|
|
66
95
|
engineAvailability: {
|
|
67
96
|
available: engineAvailability.available,
|
|
68
97
|
detail: engineAvailability.detail,
|
|
69
|
-
...(engineAvailability.resolvedPath
|
|
70
|
-
|
|
98
|
+
...(engineAvailability.resolvedPath ? { resolvedPath: engineAvailability.resolvedPath } : {}),
|
|
99
|
+
...(engineAvailability.candidatePaths?.length
|
|
100
|
+
? { candidatePaths: engineAvailability.candidatePaths }
|
|
71
101
|
: {})
|
|
72
102
|
},
|
|
103
|
+
...(codexProbe
|
|
104
|
+
? {
|
|
105
|
+
codexDiagnostics: {
|
|
106
|
+
selectedPath: codexProbe.command,
|
|
107
|
+
hostPlatform: codexProbe.diagnosis.hostPlatform,
|
|
108
|
+
installKind: codexProbe.diagnosis.installKind,
|
|
109
|
+
nativeInstallValid: codexProbe.diagnosis.nativeInstallValid,
|
|
110
|
+
invocationMode: codexProbe.diagnosis.invocationMode,
|
|
111
|
+
sandboxMode: codexProbe.diagnosis.sandboxMode,
|
|
112
|
+
sandboxCompatible: codexProbe.diagnosis.sandboxCompatible,
|
|
113
|
+
...(codexProbe.diagnosis.nativeDependencyStatus
|
|
114
|
+
? { nativeDependencyStatus: codexProbe.diagnosis.nativeDependencyStatus }
|
|
115
|
+
: {}),
|
|
116
|
+
...(codexProbe.diagnosis.nativeDependencyPackage
|
|
117
|
+
? { nativeDependencyPackage: codexProbe.diagnosis.nativeDependencyPackage }
|
|
118
|
+
: {}),
|
|
119
|
+
launchReady: codexProbe.ok,
|
|
120
|
+
summary: codexProbe.summary,
|
|
121
|
+
...(codexProbe.diagnosis.remediation ? { remediation: codexProbe.diagnosis.remediation } : {}),
|
|
122
|
+
...(codexProbe.candidateProbeResults?.length
|
|
123
|
+
? { candidateProbeResults: codexProbe.candidateProbeResults }
|
|
124
|
+
: {})
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
: {}),
|
|
73
128
|
runsRoot: resolveRunsRoot(process.env),
|
|
74
129
|
pathScope: {
|
|
75
130
|
repoRoot: workingDirectory,
|
|
@@ -2,6 +2,7 @@ import { buildArtifactSummary, buildBudgetSnapshot, buildCostSnapshot, buildEven
|
|
|
2
2
|
import { readRunControlState } from "./run-controls.js";
|
|
3
3
|
import { martinEvalTool } from "./eval.js";
|
|
4
4
|
import { assessRunRisk } from "./workflow-governance.js";
|
|
5
|
+
import type { ReceiptIntegritySummary, ReceiptScope } from "../vendor/contracts/index.js";
|
|
5
6
|
export interface MartinRunDossierInput {
|
|
6
7
|
file?: string;
|
|
7
8
|
loopId?: string;
|
|
@@ -15,6 +16,8 @@ export interface MartinRunDossierOutput {
|
|
|
15
16
|
loop: ReturnType<typeof buildLoopPreview>;
|
|
16
17
|
budget: ReturnType<typeof buildBudgetSnapshot>;
|
|
17
18
|
cost: ReturnType<typeof buildCostSnapshot>;
|
|
19
|
+
receiptIntegrity: ReceiptIntegritySummary;
|
|
20
|
+
receiptScope?: ReceiptScope;
|
|
18
21
|
attempts: Array<{
|
|
19
22
|
index: number;
|
|
20
23
|
attemptId?: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { buildArtifactSummary, buildBudgetSnapshot, buildCostSnapshot, buildEventSummaries, buildLoopPreview, buildSuggestedPromptNames, buildSuggestedResourceUris, buildVerificationSummary } from "./tool-support.js";
|
|
1
|
+
import { buildArtifactSummary, buildBudgetSnapshot, buildCostSnapshot, buildEventSummaries, buildLoopPreview, resolveReceiptIntegrity, buildSuggestedPromptNames, buildSuggestedResourceUris, buildVerificationSummary } from "./tool-support.js";
|
|
2
|
+
import { resolveTrustedLoopRepoRoot } from "../server-validation.js";
|
|
2
3
|
import { loadDetailedLoopRecord, readAttemptArtifactFiles, readLedgerEvents } from "./run-store.js";
|
|
3
4
|
import { readRunControlState } from "./run-controls.js";
|
|
4
5
|
import { martinEvalTool } from "./eval.js";
|
|
@@ -9,7 +10,7 @@ export async function martinRunDossierTool(input) {
|
|
|
9
10
|
const verification = buildVerificationSummary(detail.loop, ledgerEvents);
|
|
10
11
|
const control = await readRunControlState(detail);
|
|
11
12
|
const evaluation = await martinEvalTool(input);
|
|
12
|
-
const repoRoot = detail.loop.task?.repoRoot
|
|
13
|
+
const repoRoot = resolveTrustedLoopRepoRoot(detail.loop.task?.repoRoot);
|
|
13
14
|
const risk = assessRunRisk({
|
|
14
15
|
objective: detail.loop.task?.objective ?? detail.loop.loopId,
|
|
15
16
|
allowedPaths: detail.loop.task?.allowedPaths ?? [],
|
|
@@ -51,6 +52,8 @@ export async function martinRunDossierTool(input) {
|
|
|
51
52
|
loop: buildLoopPreview(detail.loop),
|
|
52
53
|
budget: buildBudgetSnapshot(detail.loop.budget),
|
|
53
54
|
cost: buildCostSnapshot(detail.loop.cost),
|
|
55
|
+
receiptIntegrity: resolveReceiptIntegrity(detail.loop),
|
|
56
|
+
...(detail.loop.receiptScope ? { receiptScope: detail.loop.receiptScope } : {}),
|
|
54
57
|
attempts,
|
|
55
58
|
verification,
|
|
56
59
|
artifacts: buildArtifactSummary(detail.loop),
|
package/dist/tools/run-loop.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type SpawnLike } from "../vendor/adapters/index.js";
|
|
2
|
+
import { type RunStore } from "../vendor/core/index.js";
|
|
3
|
+
import type { LoopBudget, ReceiptScope } from "../vendor/contracts/index.js";
|
|
2
4
|
import { buildArtifactSummary, buildVerificationSummary, buildLoopPreview, type MartinEngine } from "./tool-support.js";
|
|
3
5
|
export interface RunLoopInput {
|
|
4
6
|
objective: string;
|
|
5
7
|
workingDirectory?: string;
|
|
6
|
-
engine?: "claude" | "codex";
|
|
8
|
+
engine?: "claude" | "codex" | "gemini";
|
|
7
9
|
model?: string;
|
|
8
10
|
maxUsd?: number;
|
|
9
11
|
maxIterations?: number;
|
|
@@ -35,9 +37,12 @@ export interface RunLoopOutput {
|
|
|
35
37
|
runDirectory: string;
|
|
36
38
|
loopRecordPath: string;
|
|
37
39
|
ledgerPath: string;
|
|
40
|
+
receiptScope: ReceiptScope;
|
|
38
41
|
loop: ReturnType<typeof buildLoopPreview>;
|
|
39
42
|
verification: ReturnType<typeof buildVerificationSummary>;
|
|
40
43
|
artifacts: ReturnType<typeof buildArtifactSummary>;
|
|
41
44
|
};
|
|
42
45
|
}
|
|
46
|
+
export declare function __setProofModeVerifierSpawnImplForTests(spawnImpl?: SpawnLike): void;
|
|
47
|
+
export declare function __setRunStoreOverrideForTests(store?: RunStore): void;
|
|
43
48
|
export declare function runLoopTool(input: RunLoopInput): Promise<RunLoopOutput>;
|
package/dist/tools/run-loop.js
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import { createClaudeCliAdapter, createCodexCliAdapter,
|
|
1
|
+
import { createClaudeCliAdapter, createCodexCliAdapter, createGeminiCliAdapter, probeCodexLaunch, resolveCliCommandAvailability, createVerifierOnlyAdapter } from "../vendor/adapters/index.js";
|
|
2
2
|
import { createFileRunStore, evaluateCostGovernor, resolveRunsRoot, runMartin } from "../vendor/core/index.js";
|
|
3
|
-
import { DEFAULT_BUDGET } from "../vendor/contracts/index.js";
|
|
4
3
|
import { normalizeSafePathPatterns, resolveSafeRepoRoot } from "../server-validation.js";
|
|
5
|
-
import { evaluateMcpRunGate } from "../workflow-state.js";
|
|
6
4
|
import { MartinToolError } from "./tool-errors.js";
|
|
7
5
|
import { buildArtifactSummary, buildVerificationSummary, buildLoopPreview, buildRunRecordPaths, getEngineAvailability, resolveExecutionMode } from "./tool-support.js";
|
|
6
|
+
import { normalizeLoopBudget } from "./workflow-governance.js";
|
|
7
|
+
let proofModeVerifierSpawnImpl;
|
|
8
|
+
let runStoreOverrideForTests;
|
|
9
|
+
export function __setProofModeVerifierSpawnImplForTests(spawnImpl) {
|
|
10
|
+
proofModeVerifierSpawnImpl = spawnImpl;
|
|
11
|
+
}
|
|
12
|
+
export function __setRunStoreOverrideForTests(store) {
|
|
13
|
+
runStoreOverrideForTests = store;
|
|
14
|
+
}
|
|
8
15
|
export async function runLoopTool(input) {
|
|
9
16
|
const workingDirectory = resolveSafeRepoRoot(input.workingDirectory);
|
|
10
17
|
const engine = input.engine ?? "claude";
|
|
@@ -12,38 +19,64 @@ export async function runLoopTool(input) {
|
|
|
12
19
|
const allowedPaths = normalizeSafePathPatterns(input.allowedPaths, "allowedPaths");
|
|
13
20
|
const deniedPaths = normalizeSafePathPatterns(input.deniedPaths, "deniedPaths");
|
|
14
21
|
const executionMode = resolveExecutionMode();
|
|
15
|
-
const
|
|
22
|
+
const workspaceRoot = resolveSafeRepoRoot();
|
|
16
23
|
const runsRoot = resolveRunsRoot(process.env);
|
|
17
|
-
const
|
|
18
|
-
|
|
24
|
+
const receiptScope = {
|
|
25
|
+
invocationRoot: workspaceRoot,
|
|
19
26
|
workingDirectory,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
repoRoot: workingDirectory,
|
|
28
|
+
runsRoot
|
|
29
|
+
};
|
|
30
|
+
let codexCommandOverride;
|
|
31
|
+
if (executionMode.liveMode) {
|
|
32
|
+
if (engine === "codex") {
|
|
33
|
+
const engineAvailability = resolveCliCommandAvailability("codex");
|
|
34
|
+
if (!engineAvailability.available) {
|
|
35
|
+
throw new MartinToolError("engine_unavailable", `Engine '${engine}' is not available on PATH.`, {
|
|
36
|
+
category: "environment",
|
|
37
|
+
suggestion: "Install the requested CLI or set MARTIN_LIVE=false for a no-spend proof run.",
|
|
38
|
+
retryable: false
|
|
39
|
+
});
|
|
32
40
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
const codexProbe = probeCodexLaunch({
|
|
42
|
+
workingDirectory,
|
|
43
|
+
availability: engineAvailability
|
|
44
|
+
});
|
|
45
|
+
if (!codexProbe.ok) {
|
|
46
|
+
throw new MartinToolError("engine_unavailable", codexProbe.summary, {
|
|
47
|
+
category: "environment",
|
|
48
|
+
suggestion: "Run martin_doctor or martin_preflight with engine='codex' before retrying live governed work.",
|
|
49
|
+
retryable: false
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
codexCommandOverride = codexProbe.command;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
const engineAvailability = getEngineAvailability(engine);
|
|
56
|
+
if (!engineAvailability.available) {
|
|
57
|
+
throw new MartinToolError("engine_unavailable", `Engine '${engine}' is not available on PATH.`, {
|
|
58
|
+
category: "environment",
|
|
59
|
+
suggestion: "Install the requested CLI or set MARTIN_LIVE=false for a no-spend proof run.",
|
|
60
|
+
retryable: false
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
41
64
|
}
|
|
42
|
-
const adapter =
|
|
43
|
-
?
|
|
65
|
+
const adapter = !executionMode.liveMode
|
|
66
|
+
? createVerifierOnlyAdapter({
|
|
67
|
+
workingDirectory,
|
|
68
|
+
label: "Proof mode adapter (MARTIN_LIVE=false)",
|
|
69
|
+
...(proofModeVerifierSpawnImpl ? { spawnImpl: proofModeVerifierSpawnImpl } : {})
|
|
70
|
+
})
|
|
44
71
|
: engine === "codex"
|
|
45
|
-
? createCodexCliAdapter({
|
|
46
|
-
|
|
72
|
+
? createCodexCliAdapter({
|
|
73
|
+
workingDirectory,
|
|
74
|
+
...(model ? { model } : {}),
|
|
75
|
+
...(codexCommandOverride ? { command: codexCommandOverride } : {})
|
|
76
|
+
})
|
|
77
|
+
: engine === "gemini"
|
|
78
|
+
? createGeminiCliAdapter({ workingDirectory, ...(model ? { model } : {}) })
|
|
79
|
+
: createClaudeCliAdapter({ workingDirectory, ...(model ? { model } : {}) });
|
|
47
80
|
const partialBudget = {};
|
|
48
81
|
if (input.maxUsd !== undefined) {
|
|
49
82
|
partialBudget.maxUsd = input.maxUsd;
|
|
@@ -54,14 +87,12 @@ export async function runLoopTool(input) {
|
|
|
54
87
|
if (input.maxTokens !== undefined) {
|
|
55
88
|
partialBudget.maxTokens = input.maxTokens;
|
|
56
89
|
}
|
|
57
|
-
const budget =
|
|
58
|
-
...DEFAULT_BUDGET,
|
|
59
|
-
...partialBudget
|
|
60
|
-
};
|
|
90
|
+
const budget = normalizeLoopBudget(partialBudget);
|
|
61
91
|
const result = await runMartin({
|
|
62
92
|
workspaceId: input.workspaceId ?? "ws_mcp",
|
|
63
93
|
projectId: input.projectId ?? "proj_mcp",
|
|
64
|
-
store: createFileRunStore({ runsRoot }),
|
|
94
|
+
store: runStoreOverrideForTests ?? createFileRunStore({ runsRoot }),
|
|
95
|
+
receiptScope,
|
|
65
96
|
task: {
|
|
66
97
|
title: input.objective.slice(0, 100),
|
|
67
98
|
objective: input.objective,
|
|
@@ -106,6 +137,7 @@ export async function runLoopTool(input) {
|
|
|
106
137
|
budget,
|
|
107
138
|
inspection: {
|
|
108
139
|
...recordPaths,
|
|
140
|
+
receiptScope: result.loop.receiptScope ?? receiptScope,
|
|
109
141
|
loop: buildLoopPreview(result.loop),
|
|
110
142
|
verification,
|
|
111
143
|
artifacts
|
package/dist/tools/run-store.js
CHANGED
|
@@ -1,8 +1,60 @@
|
|
|
1
1
|
import { readFile, readdir, stat } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { readLatestLoopRecordFromFile, readLoopRecordsFromFile, resolveRunsRoot } from "../vendor/core/index.js";
|
|
3
|
+
import { readLatestLoopRecordFromFile, readLoopRecordsFromFile, resolveRunsRoot, verifyReceiptIntegrityFromFiles } from "../vendor/core/index.js";
|
|
4
4
|
import { resolveSafeLoopRecordPath, resolveSafeRunsJsonPath, resolveSafeRunsPath, resolveSafeRunsRootPath } from "../server-validation.js";
|
|
5
5
|
import { attemptNotFoundError, invalidSelectorError, noLoopRecordsError, storeUnreadableError } from "./tool-errors.js";
|
|
6
|
+
async function attachReceiptIntegrity(detail) {
|
|
7
|
+
const ledgerPath = detail.canonicalRunDirectory
|
|
8
|
+
? await resolveReceiptEvidencePath(detail.canonicalRunDirectory)
|
|
9
|
+
: detail.ledgerPath;
|
|
10
|
+
const integrity = detail.canonicalLoopRecordPath && detail.canonicalRunDirectory && ledgerPath
|
|
11
|
+
? await verifyReceiptIntegrityFromFiles({
|
|
12
|
+
runId: detail.loop.loopId,
|
|
13
|
+
runsRoot: detail.runsRoot,
|
|
14
|
+
loopRecordPath: detail.canonicalLoopRecordPath,
|
|
15
|
+
ledgerPath
|
|
16
|
+
}).catch(() => ({
|
|
17
|
+
state: "unsigned",
|
|
18
|
+
reason: "Receipt integrity verification could not be completed."
|
|
19
|
+
}))
|
|
20
|
+
: ({
|
|
21
|
+
state: "unsigned",
|
|
22
|
+
reason: "Receipt integrity is only available for canonical run directories."
|
|
23
|
+
});
|
|
24
|
+
const receiptScope = resolveReceiptScope(detail.loop, detail.runsRoot);
|
|
25
|
+
return {
|
|
26
|
+
...detail,
|
|
27
|
+
...(ledgerPath ? { ledgerPath } : {}),
|
|
28
|
+
loop: {
|
|
29
|
+
...detail.loop,
|
|
30
|
+
receiptIntegrity: integrity,
|
|
31
|
+
...(receiptScope ? { receiptScope } : {})
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function resolveReceiptScope(loop, runsRoot) {
|
|
36
|
+
if (loop.receiptScope) {
|
|
37
|
+
return loop.receiptScope;
|
|
38
|
+
}
|
|
39
|
+
if (!loop.task?.repoRoot && !runsRoot) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
...(loop.task?.repoRoot ? { repoRoot: loop.task.repoRoot } : {}),
|
|
44
|
+
...(loop.task?.repoRoot ? { workingDirectory: loop.task.repoRoot } : {}),
|
|
45
|
+
...(runsRoot ? { runsRoot } : {})
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
async function resolveReceiptEvidencePath(runDirectory) {
|
|
49
|
+
for (const candidate of ["ledger.jsonl", "events.jsonl"]) {
|
|
50
|
+
const candidatePath = path.join(runDirectory, candidate);
|
|
51
|
+
const candidateStats = await safeStat(candidatePath);
|
|
52
|
+
if (candidateStats?.isFile()) {
|
|
53
|
+
return candidatePath;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
6
58
|
export async function loadLoopRecordsForInspect(input) {
|
|
7
59
|
const runsRoot = resolveSafeRunsRootPath(input.runsDir, resolveRunsRoot(process.env));
|
|
8
60
|
if (!input.file) {
|
|
@@ -118,14 +170,14 @@ export async function loadDetailedLoopRecord(input) {
|
|
|
118
170
|
const canonicalStats = await safeStat(canonicalLoopRecordPath);
|
|
119
171
|
if (canonicalStats?.isFile()) {
|
|
120
172
|
const loop = await readCanonicalLoopRecord(canonicalLoopRecordPath);
|
|
121
|
-
return buildDetailedLoopSource({
|
|
173
|
+
return await attachReceiptIntegrity(buildDetailedLoopSource({
|
|
122
174
|
source: canonicalLoopRecordPath,
|
|
123
175
|
sourceKind: "file",
|
|
124
176
|
runsRoot,
|
|
125
177
|
loop,
|
|
126
178
|
canonicalLoopRecordPath,
|
|
127
179
|
canonicalRunDirectory: path.dirname(canonicalLoopRecordPath)
|
|
128
|
-
});
|
|
180
|
+
}));
|
|
129
181
|
}
|
|
130
182
|
}
|
|
131
183
|
const inspected = await readAllLoopRecordsSafely(targetPath);
|
|
@@ -139,10 +191,10 @@ export async function loadDetailedLoopRecord(input) {
|
|
|
139
191
|
runsRoot,
|
|
140
192
|
loop
|
|
141
193
|
});
|
|
142
|
-
return {
|
|
194
|
+
return await attachReceiptIntegrity({
|
|
143
195
|
...detail,
|
|
144
196
|
warnings: [...detail.warnings, ...inspected.warnings]
|
|
145
|
-
};
|
|
197
|
+
});
|
|
146
198
|
}
|
|
147
199
|
const latest = await readLatestLoopRecordFromFile(targetPath);
|
|
148
200
|
if (!latest) {
|
|
@@ -150,35 +202,35 @@ export async function loadDetailedLoopRecord(input) {
|
|
|
150
202
|
}
|
|
151
203
|
if (path.basename(targetPath) === "loop-record.json") {
|
|
152
204
|
const loop = await readCanonicalLoopRecord(targetPath);
|
|
153
|
-
return buildDetailedLoopSource({
|
|
205
|
+
return await attachReceiptIntegrity(buildDetailedLoopSource({
|
|
154
206
|
source: targetPath,
|
|
155
207
|
sourceKind: "file",
|
|
156
208
|
runsRoot,
|
|
157
209
|
loop,
|
|
158
210
|
canonicalLoopRecordPath: targetPath,
|
|
159
211
|
canonicalRunDirectory: path.dirname(targetPath)
|
|
160
|
-
});
|
|
212
|
+
}));
|
|
161
213
|
}
|
|
162
|
-
return await buildDetailedLoopSourceFromDiscoveredLoop({
|
|
214
|
+
return await attachReceiptIntegrity(await buildDetailedLoopSourceFromDiscoveredLoop({
|
|
163
215
|
source: targetPath,
|
|
164
216
|
sourceKind: "file",
|
|
165
217
|
runsRoot,
|
|
166
218
|
loop: latest
|
|
167
|
-
});
|
|
219
|
+
}));
|
|
168
220
|
}
|
|
169
221
|
if (input.loopId) {
|
|
170
222
|
const canonicalLoopRecordPath = resolvePotentialLoopRecordPath(input.loopId, runsRoot);
|
|
171
223
|
const canonicalStats = await safeStat(canonicalLoopRecordPath);
|
|
172
224
|
if (canonicalStats?.isFile()) {
|
|
173
225
|
const loop = await readCanonicalLoopRecord(canonicalLoopRecordPath);
|
|
174
|
-
return buildDetailedLoopSource({
|
|
226
|
+
return await attachReceiptIntegrity(buildDetailedLoopSource({
|
|
175
227
|
source: canonicalLoopRecordPath,
|
|
176
228
|
sourceKind: "loop_id",
|
|
177
229
|
runsRoot,
|
|
178
230
|
loop,
|
|
179
231
|
canonicalLoopRecordPath,
|
|
180
232
|
canonicalRunDirectory: path.dirname(canonicalLoopRecordPath)
|
|
181
|
-
});
|
|
233
|
+
}));
|
|
182
234
|
}
|
|
183
235
|
const inspected = await readAllLoopRecordsSafely(runsRoot);
|
|
184
236
|
const loop = inspected.loops.find((candidate) => candidate.loopId === input.loopId);
|
|
@@ -191,10 +243,10 @@ export async function loadDetailedLoopRecord(input) {
|
|
|
191
243
|
runsRoot,
|
|
192
244
|
loop
|
|
193
245
|
});
|
|
194
|
-
return {
|
|
246
|
+
return await attachReceiptIntegrity({
|
|
195
247
|
...detail,
|
|
196
248
|
warnings: [...detail.warnings, ...inspected.warnings]
|
|
197
|
-
};
|
|
249
|
+
});
|
|
198
250
|
}
|
|
199
251
|
const inspected = await readAllLoopRecordsSafely(runsRoot);
|
|
200
252
|
const loop = inspected.loops[0];
|
|
@@ -207,10 +259,10 @@ export async function loadDetailedLoopRecord(input) {
|
|
|
207
259
|
runsRoot,
|
|
208
260
|
loop
|
|
209
261
|
});
|
|
210
|
-
return {
|
|
262
|
+
return await attachReceiptIntegrity({
|
|
211
263
|
...detail,
|
|
212
264
|
warnings: [...detail.warnings, ...inspected.warnings]
|
|
213
|
-
};
|
|
265
|
+
});
|
|
214
266
|
}
|
|
215
267
|
export async function loadAttemptFromLoop(input) {
|
|
216
268
|
const detail = await loadDetailedLoopRecord(input);
|