@martinloop/mcp 0.5.5 → 0.5.7
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 +14 -52
- package/dist/arcade/capabilities.d.ts +36 -0
- package/dist/arcade/capabilities.js +37 -0
- package/dist/arcade/resource.d.ts +31 -0
- package/dist/arcade/resource.js +52 -0
- package/dist/arcade/status.d.ts +23 -0
- package/dist/arcade/status.js +25 -0
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +3 -3
- package/dist/resources.js +1 -13
- package/dist/server.js +241 -75
- package/dist/tools/execution-policy.js +1 -1
- package/dist/tools/get-status.d.ts +2 -4
- package/dist/tools/get-status.js +6 -3
- package/dist/tools/preflight.d.ts +1 -1
- package/dist/tools/run-loop.d.ts +2 -3
- package/dist/tools/run-loop.js +4 -3
- package/dist/tools/run-store.js +1 -1
- package/dist/tools/tool-support.d.ts +3 -2
- package/dist/tools/tool-support.js +9 -2
- package/dist/tools/workflow-governance.d.ts +1 -1
- package/dist/tools/workflow-governance.js +1 -1
- package/dist/vendor/adapters/claude-cli.js +2 -1
- package/dist/vendor/adapters/cli-bridge.js +7 -1
- package/dist/vendor/adapters/index.d.ts +1 -0
- package/dist/vendor/adapters/index.js +1 -0
- package/dist/vendor/adapters/openai-compatible.js +6 -3
- package/dist/vendor/adapters/stub-agent-cli.d.ts +9 -0
- package/dist/vendor/adapters/stub-agent-cli.js +43 -0
- package/dist/vendor/adapters/stub-direct-provider.d.ts +8 -0
- package/dist/vendor/adapters/stub-direct-provider.js +16 -0
- package/dist/vendor/adapters/verifier-only.js +1 -1
- package/dist/vendor/contracts/execution-policy.js +1 -1
- package/dist/vendor/contracts/exits.d.ts +1 -1
- package/dist/vendor/contracts/governance.d.ts +1 -1
- package/dist/vendor/contracts/index.d.ts +1 -1
- package/dist/vendor/contracts/index.js +1 -2
- package/dist/vendor/contracts/operator.d.ts +1 -1
- package/dist/vendor/contracts/operator.js +1 -2
- package/dist/vendor/core/compiler.d.ts +2 -2
- package/dist/vendor/core/compiler.js +3 -1
- package/dist/vendor/core/exits.js +9 -4
- package/dist/vendor/core/grounding.d.ts +1 -0
- package/dist/vendor/core/grounding.js +28 -1
- package/dist/vendor/core/index.d.ts +2 -2
- package/dist/vendor/core/index.js +11 -64
- package/dist/vendor/core/policy-compiler.js +17 -1
- package/dist/vendor/core/policy.d.ts +2 -1
- package/dist/vendor/core/policy.js +15 -5
- package/dist/vendor/core/rollback.d.ts +3 -0
- package/dist/vendor/core/rollback.js +33 -20
- package/dist/vendor/core/verified-handoff.js +25 -6
- package/dist/vendor/presentation/motion.js +0 -4
- package/dist/vendor/presentation/verified-handoff.js +10 -13
- package/dist/workflow-state.js +1 -1
- package/package.json +20 -11
- package/server.json +3 -3
|
@@ -5,7 +5,7 @@ export function compileExecutionPolicy(input) {
|
|
|
5
5
|
maxUsd: resolveBudgetField("maxUsd", input, provenance),
|
|
6
6
|
softLimitUsd: resolveBudgetField("softLimitUsd", input, provenance),
|
|
7
7
|
maxIterations: resolveBudgetField("maxIterations", input, provenance),
|
|
8
|
-
|
|
8
|
+
...resolveOptionalTokenBudget(input, provenance)
|
|
9
9
|
};
|
|
10
10
|
if (budget.softLimitUsd >= budget.maxUsd) {
|
|
11
11
|
budget.softLimitUsd = Math.round(budget.maxUsd * 0.75 * 100) / 100;
|
|
@@ -103,6 +103,22 @@ export function compileExecutionPolicy(input) {
|
|
|
103
103
|
provenance
|
|
104
104
|
});
|
|
105
105
|
}
|
|
106
|
+
function resolveOptionalTokenBudget(input, provenance) {
|
|
107
|
+
if (input.request.budgetOverrides?.maxTokens && input.request.budget.maxTokens !== undefined) {
|
|
108
|
+
provenance.push({ field: "budget.maxTokens", source: "request", value: String(input.request.budget.maxTokens) });
|
|
109
|
+
return { maxTokens: input.request.budget.maxTokens };
|
|
110
|
+
}
|
|
111
|
+
const configValue = input.config?.budget?.maxTokens;
|
|
112
|
+
if (typeof configValue === "number" && Number.isFinite(configValue)) {
|
|
113
|
+
provenance.push({ field: "budget.maxTokens", source: "config", value: String(configValue) });
|
|
114
|
+
return { maxTokens: configValue };
|
|
115
|
+
}
|
|
116
|
+
if (input.defaults.budget.maxTokens !== undefined) {
|
|
117
|
+
provenance.push({ field: "budget.maxTokens", source: "default", value: String(input.defaults.budget.maxTokens) });
|
|
118
|
+
return { maxTokens: input.defaults.budget.maxTokens };
|
|
119
|
+
}
|
|
120
|
+
return {};
|
|
121
|
+
}
|
|
106
122
|
function resolveBudgetField(field, input, provenance) {
|
|
107
123
|
if (input.request.budgetOverrides?.[field]) {
|
|
108
124
|
const value = input.request.budget[field];
|
|
@@ -12,7 +12,7 @@ export interface CostGovernorState {
|
|
|
12
12
|
shouldStop: boolean;
|
|
13
13
|
remainingBudgetUsd: number;
|
|
14
14
|
remainingIterations: number;
|
|
15
|
-
remainingTokens
|
|
15
|
+
remainingTokens?: number;
|
|
16
16
|
recommendedIntervention?: InterventionType;
|
|
17
17
|
}
|
|
18
18
|
export interface ExitDecision {
|
|
@@ -97,6 +97,7 @@ export declare function inferExit(input: {
|
|
|
97
97
|
lastFailure?: FailureAssessment;
|
|
98
98
|
costState: CostGovernorState;
|
|
99
99
|
canSwitchAdapter?: boolean;
|
|
100
|
+
verificationRequired?: boolean;
|
|
100
101
|
}): ExitDecision;
|
|
101
102
|
export interface BudgetPreflightInput {
|
|
102
103
|
promptCharCount: number;
|
|
@@ -165,16 +165,18 @@ export function classifyFailure(input) {
|
|
|
165
165
|
export function evaluateCostGovernor(input) {
|
|
166
166
|
const remainingBudgetUsd = roundUsd(input.budget.maxUsd - input.cost.actualUsd);
|
|
167
167
|
const remainingIterations = Math.max(input.budget.maxIterations - input.attemptsUsed, 0);
|
|
168
|
-
const remainingTokens =
|
|
168
|
+
const remainingTokens = input.budget.maxTokens === undefined
|
|
169
|
+
? undefined
|
|
170
|
+
: Math.max(input.budget.maxTokens - input.cost.tokensIn - input.cost.tokensOut, 0);
|
|
169
171
|
if (input.cost.actualUsd >= input.budget.maxUsd ||
|
|
170
172
|
input.attemptsUsed >= input.budget.maxIterations ||
|
|
171
|
-
remainingTokens <= 0) {
|
|
173
|
+
(remainingTokens !== undefined && remainingTokens <= 0)) {
|
|
172
174
|
return {
|
|
173
175
|
pressure: "hard_limit",
|
|
174
176
|
shouldStop: true,
|
|
175
177
|
remainingBudgetUsd,
|
|
176
178
|
remainingIterations,
|
|
177
|
-
remainingTokens,
|
|
179
|
+
...(remainingTokens !== undefined ? { remainingTokens } : {}),
|
|
178
180
|
recommendedIntervention: "stop_loop"
|
|
179
181
|
};
|
|
180
182
|
}
|
|
@@ -184,7 +186,7 @@ export function evaluateCostGovernor(input) {
|
|
|
184
186
|
shouldStop: false,
|
|
185
187
|
remainingBudgetUsd,
|
|
186
188
|
remainingIterations,
|
|
187
|
-
remainingTokens,
|
|
189
|
+
...(remainingTokens !== undefined ? { remainingTokens } : {}),
|
|
188
190
|
recommendedIntervention: "compress_context"
|
|
189
191
|
};
|
|
190
192
|
}
|
|
@@ -193,7 +195,7 @@ export function evaluateCostGovernor(input) {
|
|
|
193
195
|
shouldStop: false,
|
|
194
196
|
remainingBudgetUsd,
|
|
195
197
|
remainingIterations,
|
|
196
|
-
remainingTokens
|
|
198
|
+
...(remainingTokens !== undefined ? { remainingTokens } : {})
|
|
197
199
|
};
|
|
198
200
|
}
|
|
199
201
|
export function calculateCostPerOutcome(input) {
|
|
@@ -210,6 +212,14 @@ export function calculateCostPerOutcome(input) {
|
|
|
210
212
|
};
|
|
211
213
|
}
|
|
212
214
|
export function inferExit(input) {
|
|
215
|
+
if (input.lastResult.status === "completed" && input.verificationRequired === false) {
|
|
216
|
+
return {
|
|
217
|
+
shouldExit: true,
|
|
218
|
+
lifecycleState: "completed",
|
|
219
|
+
status: "completed",
|
|
220
|
+
reason: "Execution-only work completed without verifier evidence; outcome is not VERIFIED."
|
|
221
|
+
};
|
|
222
|
+
}
|
|
213
223
|
if (input.lastResult.status === "completed" && input.lastResult.verification.passed) {
|
|
214
224
|
return {
|
|
215
225
|
shouldExit: true,
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { PatchDecision, RollbackBoundaryArtifact, RollbackOutcomeArtifact } from "../contracts/index.js";
|
|
2
|
+
export declare class RollbackStateUnavailableError extends Error {
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
2
5
|
export declare function listAttemptChangedFilesSinceBoundary(input: {
|
|
3
6
|
repoRoot?: string;
|
|
4
7
|
boundary?: RollbackBoundaryArtifact;
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
2
|
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
4
3
|
import { dirname, relative, resolve } from "node:path";
|
|
4
|
+
export class RollbackStateUnavailableError extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "RollbackStateUnavailableError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
5
10
|
export function listAttemptChangedFilesSinceBoundary(input) {
|
|
6
11
|
if (!input.repoRoot) {
|
|
7
12
|
return [];
|
|
@@ -12,24 +17,11 @@ export function listAttemptChangedFilesSinceBoundary(input) {
|
|
|
12
17
|
}
|
|
13
18
|
const baselineTracked = new Set(input.boundary.trackedDirtyFiles);
|
|
14
19
|
const baselineUntracked = new Set(input.boundary.untrackedFiles);
|
|
15
|
-
const baselineContentChanges = input.boundary.snapshots
|
|
16
|
-
.filter((snapshot) => snapshot.existed && repoFileDiffersFromSnapshot(input.repoRoot, snapshot))
|
|
17
|
-
.map((snapshot) => snapshot.path);
|
|
18
20
|
return uniqueSorted([
|
|
19
21
|
...repoState.trackedDirtyFiles.filter((filePath) => !baselineTracked.has(filePath)),
|
|
20
|
-
...repoState.untrackedFiles.filter((filePath) => !baselineUntracked.has(filePath))
|
|
21
|
-
...baselineContentChanges
|
|
22
|
+
...repoState.untrackedFiles.filter((filePath) => !baselineUntracked.has(filePath))
|
|
22
23
|
]);
|
|
23
24
|
}
|
|
24
|
-
function repoFileDiffersFromSnapshot(repoRoot, snapshot) {
|
|
25
|
-
try {
|
|
26
|
-
const current = readFileSync(resolveRepoPath(repoRoot, snapshot.path));
|
|
27
|
-
return current.toString("base64") !== snapshot.contentBase64;
|
|
28
|
-
}
|
|
29
|
-
catch {
|
|
30
|
-
return true;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
25
|
export async function captureRollbackBoundary(input) {
|
|
34
26
|
if (!input.repoRoot) {
|
|
35
27
|
return undefined;
|
|
@@ -147,6 +139,7 @@ function readRepoState(repoRoot) {
|
|
|
147
139
|
};
|
|
148
140
|
}
|
|
149
141
|
function readGitLines(repoRoot, args) {
|
|
142
|
+
let lastFailure = "unknown Git failure";
|
|
150
143
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
151
144
|
const result = spawnSync("git", args, {
|
|
152
145
|
cwd: repoRoot,
|
|
@@ -158,13 +151,14 @@ function readGitLines(repoRoot, args) {
|
|
|
158
151
|
.map((line) => normalizeRepoPath(line))
|
|
159
152
|
.filter(Boolean));
|
|
160
153
|
}
|
|
154
|
+
lastFailure = gitFailureMessage(result);
|
|
161
155
|
// Retry once after a short delay — handles git lock-file contention
|
|
162
156
|
// that occurs when another process holds .git/index.lock.
|
|
163
157
|
if (attempt === 0 && result.status !== 0) {
|
|
164
|
-
|
|
158
|
+
delaySynchronously(500);
|
|
165
159
|
}
|
|
166
160
|
}
|
|
167
|
-
|
|
161
|
+
throw new RollbackStateUnavailableError(`Git rollback state unavailable for ${args.join(" ")}: ${lastFailure}`);
|
|
168
162
|
}
|
|
169
163
|
function readGitScalar(repoRoot, args) {
|
|
170
164
|
const result = spawnSync("git", args, {
|
|
@@ -172,7 +166,7 @@ function readGitScalar(repoRoot, args) {
|
|
|
172
166
|
encoding: "utf8"
|
|
173
167
|
});
|
|
174
168
|
if (result.status !== 0 || typeof result.stdout !== "string") {
|
|
175
|
-
|
|
169
|
+
throw new RollbackStateUnavailableError(`Git rollback state unavailable for ${args.join(" ")}: ${gitFailureMessage(result)}`);
|
|
176
170
|
}
|
|
177
171
|
const value = result.stdout.trim();
|
|
178
172
|
return value.length > 0 ? value : undefined;
|
|
@@ -188,7 +182,10 @@ async function readRollbackSnapshot(repoRoot, filePath) {
|
|
|
188
182
|
contentBase64: contents.toString("base64")
|
|
189
183
|
};
|
|
190
184
|
}
|
|
191
|
-
catch {
|
|
185
|
+
catch (error) {
|
|
186
|
+
if (!isFileNotFoundError(error)) {
|
|
187
|
+
throw new RollbackStateUnavailableError(`Rollback snapshot unavailable for ${normalizeRepoPath(filePath)}: ${toErrorMessage(error)}`);
|
|
188
|
+
}
|
|
192
189
|
return {
|
|
193
190
|
path: normalizeRepoPath(filePath),
|
|
194
191
|
existed: false,
|
|
@@ -214,7 +211,7 @@ function restoreTrackedFileFromHead(repoRoot, filePath) {
|
|
|
214
211
|
return;
|
|
215
212
|
}
|
|
216
213
|
// Retry once after a short delay (git lock-file contention)
|
|
217
|
-
|
|
214
|
+
delaySynchronously(500);
|
|
218
215
|
const retry = spawnSync("git", ["restore", "--staged", "--worktree", "--source=HEAD", "--", filePath], { cwd: repoRoot, encoding: "utf8" });
|
|
219
216
|
if (retry.status === 0) {
|
|
220
217
|
return;
|
|
@@ -264,3 +261,19 @@ function emptyRepoState() {
|
|
|
264
261
|
function toErrorMessage(error) {
|
|
265
262
|
return error instanceof Error ? error.message : String(error);
|
|
266
263
|
}
|
|
264
|
+
function gitFailureMessage(result) {
|
|
265
|
+
if (result.error) {
|
|
266
|
+
return result.error.message;
|
|
267
|
+
}
|
|
268
|
+
if (typeof result.stderr === "string" && result.stderr.trim().length > 0) {
|
|
269
|
+
return result.stderr.trim();
|
|
270
|
+
}
|
|
271
|
+
return `git exited with status ${String(result.status)}`;
|
|
272
|
+
}
|
|
273
|
+
function isFileNotFoundError(error) {
|
|
274
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
275
|
+
}
|
|
276
|
+
function delaySynchronously(milliseconds) {
|
|
277
|
+
const signal = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
|
|
278
|
+
Atomics.wait(signal, 0, 0, milliseconds);
|
|
279
|
+
}
|
|
@@ -33,6 +33,9 @@ export function verifierActuallyPassed(evidence, expected) {
|
|
|
33
33
|
if (!evidence?.passed || !evidence.binding || !evidence.steps) {
|
|
34
34
|
return false;
|
|
35
35
|
}
|
|
36
|
+
if (expected.commands.length === 0) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
36
39
|
if (evidence.binding.runId !== expected.runId ||
|
|
37
40
|
evidence.binding.workspaceId !== expected.workspaceId ||
|
|
38
41
|
evidence.binding.cwd !== expected.cwd ||
|
|
@@ -93,7 +96,6 @@ export function deriveVerifiedHandoffExecutionBoundary(loop) {
|
|
|
93
96
|
const persistedMode = isExecutionMode(loop.metadata["executionMode"])
|
|
94
97
|
? loop.metadata["executionMode"]
|
|
95
98
|
: undefined;
|
|
96
|
-
const groundingEvidenceAvailable = loop.metadata["groundingEvidenceStatus"] !== "unavailable";
|
|
97
99
|
const executionMode = hasSimulatedAdapter
|
|
98
100
|
? "simulated"
|
|
99
101
|
: hasVerifierOnlyAdapter
|
|
@@ -101,7 +103,7 @@ export function deriveVerifiedHandoffExecutionBoundary(loop) {
|
|
|
101
103
|
: persistedMode ?? (adapterIds.length > 0 ? "governed" : "simulated");
|
|
102
104
|
return {
|
|
103
105
|
executionMode,
|
|
104
|
-
governanceClaimEligible: executionMode === "governed" && adapterIds.length > 0
|
|
106
|
+
governanceClaimEligible: executionMode === "governed" && adapterIds.length > 0,
|
|
105
107
|
};
|
|
106
108
|
}
|
|
107
109
|
function toEvidenceStatus(status) {
|
|
@@ -157,17 +159,34 @@ export function buildVerifiedHandoff(input) {
|
|
|
157
159
|
"Test integrity was not evaluated for this run.",
|
|
158
160
|
};
|
|
159
161
|
const executionBoundary = deriveVerifiedHandoffExecutionBoundary(input.loop);
|
|
162
|
+
const verifierStepsActuallyPassed = input.verification.steps.length > 0 &&
|
|
163
|
+
input.verification.steps.every((step) => step.launched === true &&
|
|
164
|
+
step.completed === true &&
|
|
165
|
+
step.crashed === false &&
|
|
166
|
+
step.timedOut !== true &&
|
|
167
|
+
step.exitCode === 0);
|
|
168
|
+
const effectiveVerificationStatus = input.verification.status === "passed" && !verifierStepsActuallyPassed
|
|
169
|
+
? input.verification.steps.length === 0
|
|
170
|
+
? "not_run"
|
|
171
|
+
: "contradicted"
|
|
172
|
+
: input.verification.status;
|
|
173
|
+
const verificationWarnings = [
|
|
174
|
+
...input.verification.warnings,
|
|
175
|
+
...(effectiveVerificationStatus !== input.verification.status
|
|
176
|
+
? ["A VERIFIED claim requires at least one launched, completed, exit-zero verifier step."]
|
|
177
|
+
: []),
|
|
178
|
+
];
|
|
160
179
|
const outcome = resolveVerifiedHandoffOutcome({
|
|
161
180
|
lifecycleState: input.loop.lifecycleState,
|
|
162
181
|
executionStatus: input.executionStatus,
|
|
163
|
-
verificationStatus:
|
|
182
|
+
verificationStatus: effectiveVerificationStatus,
|
|
164
183
|
receiptIntegrity: input.receiptIntegrity.state,
|
|
165
184
|
scopeStatus: scope.status,
|
|
166
185
|
testIntegrityStatus: testIntegrity.status,
|
|
167
186
|
mutationRequired: input.mutationRequired ?? input.loop.task.mutationMode === "edit",
|
|
168
187
|
changedFileCount: scope.changedFiles.length,
|
|
169
188
|
definitionOfDonePreSatisfied: input.definitionOfDonePreSatisfied,
|
|
170
|
-
evidenceContradicted: input.evidenceContradicted ??
|
|
189
|
+
evidenceContradicted: input.evidenceContradicted ?? effectiveVerificationStatus === "contradicted",
|
|
171
190
|
governanceClaimEligible: executionBoundary.governanceClaimEligible,
|
|
172
191
|
unresolvedWorkCount: unresolvedWork.length,
|
|
173
192
|
});
|
|
@@ -196,10 +215,10 @@ export function buildVerifiedHandoff(input) {
|
|
|
196
215
|
lifecycleState: input.loop.lifecycleState,
|
|
197
216
|
},
|
|
198
217
|
verification: {
|
|
199
|
-
status: toEvidenceStatus(
|
|
218
|
+
status: toEvidenceStatus(effectiveVerificationStatus),
|
|
200
219
|
summary: input.verification.summary,
|
|
201
220
|
checks: input.verification.steps.map(toCheck),
|
|
202
|
-
warnings:
|
|
221
|
+
warnings: verificationWarnings,
|
|
203
222
|
},
|
|
204
223
|
requirements: input.requirements ?? [],
|
|
205
224
|
scope,
|
|
@@ -10,10 +10,6 @@ export function canAnimate(env = {}) {
|
|
|
10
10
|
if (mode !== "human") {
|
|
11
11
|
return false;
|
|
12
12
|
}
|
|
13
|
-
const noColor = env.noColor ?? Object.prototype.hasOwnProperty.call(process.env, "NO_COLOR");
|
|
14
|
-
if (noColor) {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
13
|
const stdoutTty = env.stdoutIsTty ?? process.stdout.isTTY === true;
|
|
18
14
|
if (!stdoutTty) {
|
|
19
15
|
return false;
|
|
@@ -22,19 +22,6 @@ function evidenceTone(status) {
|
|
|
22
22
|
function humanize(value) {
|
|
23
23
|
return value.replaceAll("_", " ").toLowerCase();
|
|
24
24
|
}
|
|
25
|
-
function formatCost(usd, provenance) {
|
|
26
|
-
if (provenance === "unavailable") {
|
|
27
|
-
return "unavailable";
|
|
28
|
-
}
|
|
29
|
-
const amount = "$" + usd.toFixed(2);
|
|
30
|
-
if (provenance === "actual") {
|
|
31
|
-
return amount + " provider-settled actual";
|
|
32
|
-
}
|
|
33
|
-
if (provenance === "calculated") {
|
|
34
|
-
return amount + " calculated from observed usage";
|
|
35
|
-
}
|
|
36
|
-
return amount + " estimated";
|
|
37
|
-
}
|
|
38
25
|
function checkSymbol(status) {
|
|
39
26
|
if (status === "PASSED") {
|
|
40
27
|
return "✓";
|
|
@@ -44,6 +31,16 @@ function checkSymbol(status) {
|
|
|
44
31
|
}
|
|
45
32
|
return "•";
|
|
46
33
|
}
|
|
34
|
+
function formatCost(usd, provenance) {
|
|
35
|
+
if (provenance === "unavailable")
|
|
36
|
+
return "unavailable";
|
|
37
|
+
const amount = "$" + usd.toFixed(2);
|
|
38
|
+
if (provenance === "actual")
|
|
39
|
+
return amount + " provider-settled actual";
|
|
40
|
+
if (provenance === "calculated")
|
|
41
|
+
return amount + " calculated from observed usage";
|
|
42
|
+
return amount + " estimated";
|
|
43
|
+
}
|
|
47
44
|
/**
|
|
48
45
|
* Single source of truth for the trust-authority outcome calculation.
|
|
49
46
|
* Both terminal and Markdown renderers call this — never duplicate.
|
package/dist/workflow-state.js
CHANGED
|
@@ -145,7 +145,7 @@ function hashBudget(budget) {
|
|
|
145
145
|
maxUsd: Number(budget.maxUsd.toFixed(4)),
|
|
146
146
|
softLimitUsd: Number(budget.softLimitUsd.toFixed(4)),
|
|
147
147
|
maxIterations: budget.maxIterations,
|
|
148
|
-
maxTokens: budget.maxTokens
|
|
148
|
+
...(budget.maxTokens !== undefined ? { maxTokens: budget.maxTokens } : {})
|
|
149
149
|
};
|
|
150
150
|
return createHash("sha256").update(JSON.stringify(normalized)).digest("hex").slice(0, 12);
|
|
151
151
|
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@martinloop/mcp",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.7",
|
|
4
4
|
"mcpName": "io.github.Keesan12/martin-loop",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
7
|
-
"description": "MCP server for AI coding agents
|
|
7
|
+
"description": "MCP server for AI coding agents \u00e2\u20ac\u201d verifier gates, stop limits, and Verified Handoffs.",
|
|
8
8
|
"license": "Apache-2.0",
|
|
9
9
|
"author": "MartinLoop contributors",
|
|
10
10
|
"homepage": "https://martinloop.com/",
|
|
@@ -18,16 +18,23 @@
|
|
|
18
18
|
},
|
|
19
19
|
"keywords": [
|
|
20
20
|
"mcp",
|
|
21
|
+
"mcp-server",
|
|
21
22
|
"model-context-protocol",
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
23
|
+
"coding-agent",
|
|
24
|
+
"coding-agents",
|
|
25
|
+
"ai-coding-agent",
|
|
26
|
+
"ai-coding-agents",
|
|
27
|
+
"autonomous-coding",
|
|
28
|
+
"claude-code",
|
|
25
29
|
"codex",
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
30
|
+
"gemini-cli",
|
|
31
|
+
"cursor",
|
|
32
|
+
"vscode",
|
|
33
|
+
"agent-runtime",
|
|
34
|
+
"agent-verification",
|
|
35
|
+
"developer-tools",
|
|
36
|
+
"software-engineering",
|
|
37
|
+
"martin-loop"
|
|
31
38
|
],
|
|
32
39
|
"bin": {
|
|
33
40
|
"mcp": "./dist/server.js",
|
|
@@ -50,6 +57,8 @@
|
|
|
50
57
|
},
|
|
51
58
|
"scripts": {
|
|
52
59
|
"build": "node ./scripts/build-package.mjs",
|
|
60
|
+
"version:sync": "node ./scripts/version-sync.mjs",
|
|
61
|
+
"version:check": "node ./scripts/verify-version-consistency.mjs",
|
|
53
62
|
"prepack": "pnpm build",
|
|
54
63
|
"smoke:pack": "node ./scripts/smoke-package.mjs",
|
|
55
64
|
"smoke:published": "node ./scripts/smoke-published-package.mjs",
|
|
@@ -78,4 +87,4 @@
|
|
|
78
87
|
"@martin/contracts": "workspace:*",
|
|
79
88
|
"@martin/presentation": "workspace:*"
|
|
80
89
|
}
|
|
81
|
-
}
|
|
90
|
+
}
|
package/server.json
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
3
|
"name": "io.github.Keesan12/martin-loop",
|
|
4
4
|
"title": "Martin Loop",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "One system around coding agents to run, verify, recover, and hand off software work.",
|
|
6
6
|
"repository": {
|
|
7
7
|
"url": "https://github.com/Keesan12/martin-loop",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "0.5.
|
|
10
|
+
"version": "0.5.7",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "@martinloop/mcp",
|
|
15
|
-
"version": "0.5.
|
|
15
|
+
"version": "0.5.7",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
}
|