@martinloop/mcp 0.3.8 → 0.5.0
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 +90 -106
- package/dist/discovery-metadata.d.ts +1 -1
- package/dist/discovery-metadata.js +1 -1
- package/dist/resources.d.ts +1 -2
- package/dist/resources.js +190 -238
- package/dist/server-validation.d.ts +1 -1
- package/dist/server-validation.js +16 -11
- package/dist/server.d.ts +23 -0
- package/dist/server.js +152 -7
- package/dist/tools/run-dossier.d.ts +3 -1
- package/dist/tools/run-dossier.js +40 -2
- package/dist/tools/run-loop.d.ts +1 -4
- package/dist/tools/run-loop.js +6 -15
- package/dist/tools/tool-support.d.ts +2 -1
- package/dist/tools/tool-support.js +1 -12
- package/dist/vendor/adapters/claude-cli.js +38 -35
- package/dist/vendor/adapters/cli-bridge.d.ts +9 -1
- package/dist/vendor/adapters/cli-bridge.js +54 -13
- package/dist/vendor/adapters/codex-launcher.d.ts +61 -1
- package/dist/vendor/adapters/codex-launcher.js +118 -19
- package/dist/vendor/adapters/index.d.ts +1 -3
- package/dist/vendor/adapters/index.js +1 -3
- package/dist/vendor/adapters/openai-compatible.js +57 -18
- package/dist/vendor/contracts/adapter-capabilities.d.ts +1 -1
- package/dist/vendor/contracts/context-handoff.d.ts +100 -0
- package/dist/vendor/contracts/context-handoff.js +11 -0
- package/dist/vendor/contracts/context-manifest.d.ts +195 -0
- package/dist/vendor/contracts/context-manifest.js +17 -0
- package/dist/vendor/contracts/context-shadow.d.ts +75 -0
- package/dist/vendor/contracts/context-shadow.js +8 -0
- package/dist/vendor/contracts/exits.d.ts +126 -0
- package/dist/vendor/contracts/exits.js +32 -0
- package/dist/vendor/contracts/index.d.ts +39 -2
- package/dist/vendor/contracts/index.js +22 -0
- package/dist/vendor/contracts/mission.d.ts +102 -0
- package/dist/vendor/contracts/mission.js +64 -0
- package/dist/vendor/contracts/operator.d.ts +3 -1
- package/dist/vendor/contracts/operator.js +2 -1
- package/dist/vendor/contracts/verified-handoff.d.ts +100 -0
- package/dist/vendor/contracts/verified-handoff.js +35 -0
- package/dist/vendor/core/context-chain-gate.d.ts +89 -0
- package/dist/vendor/core/context-chain-gate.js +173 -0
- package/dist/vendor/core/context-compiler.d.ts +53 -0
- package/dist/vendor/core/context-compiler.js +261 -0
- package/dist/vendor/core/context-handoff.d.ts +42 -0
- package/dist/vendor/core/context-handoff.js +167 -0
- package/dist/vendor/core/context-shadow.d.ts +39 -0
- package/dist/vendor/core/context-shadow.js +200 -0
- package/dist/vendor/core/exit-signal.d.ts +88 -0
- package/dist/vendor/core/exit-signal.js +339 -0
- package/dist/vendor/core/exits.d.ts +41 -0
- package/dist/vendor/core/exits.js +258 -0
- package/dist/vendor/core/index.d.ts +42 -1
- package/dist/vendor/core/index.js +1086 -662
- package/dist/vendor/core/mission/aggregate.d.ts +27 -0
- package/dist/vendor/core/mission/aggregate.js +42 -0
- package/dist/vendor/core/mission/index.d.ts +2 -0
- package/dist/vendor/core/mission/index.js +1 -0
- package/dist/vendor/core/persistence/compiler.d.ts +8 -0
- package/dist/vendor/core/persistence/compiler.js +156 -0
- package/dist/vendor/core/persistence/context-handoff-store.d.ts +68 -0
- package/dist/vendor/core/persistence/context-handoff-store.js +112 -0
- package/dist/vendor/core/persistence/index.d.ts +4 -0
- package/dist/vendor/core/persistence/index.js +2 -0
- package/dist/vendor/core/persistence/ledger.d.ts +1 -1
- package/dist/vendor/core/persistence/mission-store.d.ts +56 -0
- package/dist/vendor/core/persistence/mission-store.js +281 -0
- package/dist/vendor/core/policy.d.ts +2 -0
- package/dist/vendor/core/policy.js +5 -2
- package/dist/vendor/core/rollback.js +15 -1
- package/dist/vendor/core/savings.d.ts +13 -0
- package/dist/vendor/core/savings.js +22 -0
- package/dist/vendor/core/termination-store.d.ts +15 -0
- package/dist/vendor/core/termination-store.js +44 -0
- package/dist/vendor/core/verified-handoff.d.ts +67 -0
- package/dist/vendor/core/verified-handoff.js +210 -0
- package/package.json +76 -83
- package/server.json +21 -21
- package/LICENSE +0 -216
- package/dist/vendor/adapters/adapters/src/claude-cli.d.ts +0 -146
- package/dist/vendor/adapters/adapters/src/claude-cli.js +0 -1100
- package/dist/vendor/adapters/adapters/src/cli-bridge.d.ts +0 -103
- package/dist/vendor/adapters/adapters/src/cli-bridge.js +0 -569
- package/dist/vendor/adapters/adapters/src/codex-launcher.d.ts +0 -78
- package/dist/vendor/adapters/adapters/src/codex-launcher.js +0 -663
- package/dist/vendor/adapters/adapters/src/direct-provider.d.ts +0 -10
- package/dist/vendor/adapters/adapters/src/direct-provider.js +0 -40
- package/dist/vendor/adapters/adapters/src/index.d.ts +0 -8
- package/dist/vendor/adapters/adapters/src/index.js +0 -8
- package/dist/vendor/adapters/adapters/src/openai-compatible.d.ts +0 -62
- package/dist/vendor/adapters/adapters/src/openai-compatible.js +0 -306
- package/dist/vendor/adapters/adapters/src/runtime-support.d.ts +0 -17
- package/dist/vendor/adapters/adapters/src/runtime-support.js +0 -59
- package/dist/vendor/adapters/adapters/src/stub-agent-cli.d.ts +0 -8
- package/dist/vendor/adapters/adapters/src/stub-agent-cli.js +0 -40
- package/dist/vendor/adapters/adapters/src/stub-direct-provider.d.ts +0 -8
- package/dist/vendor/adapters/adapters/src/stub-direct-provider.js +0 -12
- package/dist/vendor/adapters/adapters/src/verifier-only.d.ts +0 -9
- package/dist/vendor/adapters/adapters/src/verifier-only.js +0 -63
- package/dist/vendor/adapters/contracts/src/context-graph.d.ts +0 -48
- package/dist/vendor/adapters/contracts/src/context-graph.js +0 -26
- package/dist/vendor/adapters/contracts/src/execution-policy.d.ts +0 -78
- package/dist/vendor/adapters/contracts/src/execution-policy.js +0 -43
- package/dist/vendor/adapters/contracts/src/governance.d.ts +0 -24
- package/dist/vendor/adapters/contracts/src/governance.js +0 -11
- package/dist/vendor/adapters/contracts/src/identity.d.ts +0 -29
- package/dist/vendor/adapters/contracts/src/identity.js +0 -30
- package/dist/vendor/adapters/contracts/src/index.d.ts +0 -412
- package/dist/vendor/adapters/contracts/src/index.js +0 -225
- package/dist/vendor/adapters/contracts/src/operator.d.ts +0 -19
- package/dist/vendor/adapters/contracts/src/operator.js +0 -11
- package/dist/vendor/adapters/contracts/src/trajectory.d.ts +0 -26
- package/dist/vendor/adapters/contracts/src/trajectory.js +0 -27
- package/dist/vendor/adapters/core/src/compiler.d.ts +0 -52
- package/dist/vendor/adapters/core/src/compiler.js +0 -52
- package/dist/vendor/adapters/core/src/context-graph.d.ts +0 -3
- package/dist/vendor/adapters/core/src/context-graph.js +0 -371
- package/dist/vendor/adapters/core/src/context-integrity.d.ts +0 -26
- package/dist/vendor/adapters/core/src/context-integrity.js +0 -80
- package/dist/vendor/adapters/core/src/grounding.d.ts +0 -38
- package/dist/vendor/adapters/core/src/grounding.js +0 -273
- package/dist/vendor/adapters/core/src/identity.d.ts +0 -20
- package/dist/vendor/adapters/core/src/identity.js +0 -86
- package/dist/vendor/adapters/core/src/index.d.ts +0 -186
- package/dist/vendor/adapters/core/src/index.js +0 -1495
- package/dist/vendor/adapters/core/src/leash.d.ts +0 -48
- package/dist/vendor/adapters/core/src/leash.js +0 -497
- package/dist/vendor/adapters/core/src/persistence/compiler.d.ts +0 -18
- package/dist/vendor/adapters/core/src/persistence/compiler.js +0 -34
- package/dist/vendor/adapters/core/src/persistence/index.d.ts +0 -10
- package/dist/vendor/adapters/core/src/persistence/index.js +0 -5
- package/dist/vendor/adapters/core/src/persistence/integrity.d.ts +0 -39
- package/dist/vendor/adapters/core/src/persistence/integrity.js +0 -252
- package/dist/vendor/adapters/core/src/persistence/ledger.d.ts +0 -23
- package/dist/vendor/adapters/core/src/persistence/ledger.js +0 -9
- package/dist/vendor/adapters/core/src/persistence/memory-store.d.ts +0 -63
- package/dist/vendor/adapters/core/src/persistence/memory-store.js +0 -115
- package/dist/vendor/adapters/core/src/persistence/runs-reader.d.ts +0 -66
- package/dist/vendor/adapters/core/src/persistence/runs-reader.js +0 -140
- package/dist/vendor/adapters/core/src/persistence/store.d.ts +0 -89
- package/dist/vendor/adapters/core/src/persistence/store.js +0 -125
- package/dist/vendor/adapters/core/src/persistence/trace-store.d.ts +0 -85
- package/dist/vendor/adapters/core/src/persistence/trace-store.js +0 -125
- package/dist/vendor/adapters/core/src/policy-compiler.d.ts +0 -2
- package/dist/vendor/adapters/core/src/policy-compiler.js +0 -195
- package/dist/vendor/adapters/core/src/policy.d.ts +0 -156
- package/dist/vendor/adapters/core/src/policy.js +0 -704
- package/dist/vendor/adapters/core/src/repo-analyzer.d.ts +0 -53
- package/dist/vendor/adapters/core/src/repo-analyzer.js +0 -263
- package/dist/vendor/adapters/core/src/rollback.d.ts +0 -15
- package/dist/vendor/adapters/core/src/rollback.js +0 -253
- package/dist/vendor/adapters/core/src/routing.d.ts +0 -82
- package/dist/vendor/adapters/core/src/routing.js +0 -336
- package/dist/vendor/adapters/core/src/test-integrity.d.ts +0 -64
- package/dist/vendor/adapters/core/src/test-integrity.js +0 -197
- package/dist/vendor/adapters/core/src/trajectory.d.ts +0 -18
- package/dist/vendor/adapters/core/src/trajectory.js +0 -295
- package/dist/vendor/adapters/stub-agent-cli.d.ts +0 -8
- package/dist/vendor/adapters/stub-agent-cli.js +0 -40
- package/dist/vendor/adapters/verifier-only.d.ts +0 -9
- package/dist/vendor/adapters/verifier-only.js +0 -63
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import { delimiter, dirname, extname, isAbsolute, join, resolve } from "node:path";
|
|
2
|
+
import { basename, delimiter, dirname, extname, isAbsolute, join, resolve } from "node:path";
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import { diffStatsFromNumstat } from "./runtime-support.js";
|
|
5
5
|
const gitRepositoryRootCache = new Map();
|
|
@@ -32,7 +32,7 @@ export async function runSubprocess(command, args, options) {
|
|
|
32
32
|
}
|
|
33
33
|
catch (error) {
|
|
34
34
|
const message = error instanceof Error ? error.message : String(error);
|
|
35
|
-
resolveOnce({ exitCode: 1, stdout: "", stderr: message, launched: false });
|
|
35
|
+
resolveOnce({ exitCode: 1, stdout: "", stderr: message, launched: false, completed: false, crashed: true });
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
38
|
const trackOutput = (chunks, chunk) => {
|
|
@@ -53,6 +53,18 @@ export async function runSubprocess(command, args, options) {
|
|
|
53
53
|
terminationReason = reason;
|
|
54
54
|
proc.kill("SIGTERM");
|
|
55
55
|
};
|
|
56
|
+
// Honour the harness abort signal — kill the subprocess immediately
|
|
57
|
+
if (options.signal !== undefined) {
|
|
58
|
+
const sig = options.signal;
|
|
59
|
+
if (sig.aborted) {
|
|
60
|
+
proc.kill("SIGTERM");
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
const onAbort = () => { proc.kill("SIGTERM"); };
|
|
64
|
+
sig.addEventListener("abort", onAbort, { once: true });
|
|
65
|
+
proc.on("close", () => { sig.removeEventListener("abort", onAbort); });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
56
68
|
proc.stdout?.on("data", (chunk) => {
|
|
57
69
|
if (outputCapped || timedOut || terminationReason) {
|
|
58
70
|
return;
|
|
@@ -93,15 +105,18 @@ export async function runSubprocess(command, args, options) {
|
|
|
93
105
|
});
|
|
94
106
|
proc.on("error", (error) => {
|
|
95
107
|
clearTimeout(timer);
|
|
96
|
-
resolveOnce({ exitCode: 1, stdout: "", stderr: error.message, launched: false });
|
|
108
|
+
resolveOnce({ exitCode: 1, stdout: "", stderr: error.message, launched: false, completed: false, crashed: true });
|
|
97
109
|
});
|
|
98
110
|
proc.on("close", (code) => {
|
|
99
111
|
clearTimeout(timer);
|
|
112
|
+
const completed = code !== null && !timedOut && !outputCapped && !terminationReason;
|
|
100
113
|
resolveOnce({
|
|
101
114
|
exitCode: code ?? 1,
|
|
102
115
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
103
116
|
stderr: Buffer.concat(stderrChunks).toString("utf8"),
|
|
104
|
-
launched: true
|
|
117
|
+
launched: true,
|
|
118
|
+
completed,
|
|
119
|
+
crashed: !completed && !timedOut && !outputCapped && !terminationReason,
|
|
105
120
|
});
|
|
106
121
|
});
|
|
107
122
|
if (options.stdinData !== undefined && proc.stdin) {
|
|
@@ -116,22 +131,30 @@ export async function runSubprocess(command, args, options) {
|
|
|
116
131
|
exitCode: 1,
|
|
117
132
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
118
133
|
stderr: stdinError.message,
|
|
119
|
-
launched: false
|
|
134
|
+
launched: false,
|
|
135
|
+
completed: false,
|
|
136
|
+
crashed: true,
|
|
120
137
|
});
|
|
121
138
|
}
|
|
122
139
|
}
|
|
123
140
|
}
|
|
124
141
|
});
|
|
125
142
|
}
|
|
126
|
-
export async function runVerification(commands, cwd, timeoutMs, verificationStack, spawnImpl) {
|
|
143
|
+
export async function runVerification(commands, cwd, timeoutMs, verificationStack, spawnImpl, binding) {
|
|
127
144
|
const steps = verificationStack && verificationStack.length > 0
|
|
128
145
|
? verificationStack.map((step) => ({
|
|
129
146
|
command: step.command,
|
|
130
147
|
fastFail: step.fastFail !== false
|
|
131
148
|
}))
|
|
132
149
|
: commands.map((command) => ({ command, fastFail: true }));
|
|
150
|
+
const executionBinding = {
|
|
151
|
+
runId: binding?.runId ?? "unbound",
|
|
152
|
+
workspaceId: binding?.workspaceId ?? "unbound",
|
|
153
|
+
cwd: binding?.cwd ?? cwd,
|
|
154
|
+
commands: steps.map((step) => step.command),
|
|
155
|
+
};
|
|
133
156
|
if (steps.length === 0) {
|
|
134
|
-
return { passed: true, summary: "No verification commands specified.", steps: [] };
|
|
157
|
+
return { passed: true, summary: "No verification commands specified.", steps: [], binding: executionBinding };
|
|
135
158
|
}
|
|
136
159
|
const failedSteps = [];
|
|
137
160
|
const stepOutcomes = [];
|
|
@@ -169,6 +192,8 @@ export async function runVerification(commands, cwd, timeoutMs, verificationStac
|
|
|
169
192
|
stepOutcomes.push({
|
|
170
193
|
command: step.command,
|
|
171
194
|
launched: result.launched,
|
|
195
|
+
completed: result.completed,
|
|
196
|
+
crashed: result.crashed,
|
|
172
197
|
exitCode: result.exitCode,
|
|
173
198
|
timedOut: result.timedOut,
|
|
174
199
|
fastFail: step.fastFail,
|
|
@@ -179,6 +204,7 @@ export async function runVerification(commands, cwd, timeoutMs, verificationStac
|
|
|
179
204
|
passed: false,
|
|
180
205
|
summary: `Verification timed out: ${step.command}`,
|
|
181
206
|
steps: stepOutcomes,
|
|
207
|
+
binding: executionBinding,
|
|
182
208
|
...(warnings.length ? { warnings } : {})
|
|
183
209
|
};
|
|
184
210
|
}
|
|
@@ -188,7 +214,7 @@ export async function runVerification(commands, cwd, timeoutMs, verificationStac
|
|
|
188
214
|
warnings.push(`Verifier never launched: ${step.command}`);
|
|
189
215
|
}
|
|
190
216
|
if (step.fastFail) {
|
|
191
|
-
return { passed: false, summary, steps: stepOutcomes, ...(warnings.length ? { warnings } : {}) };
|
|
217
|
+
return { passed: false, summary, steps: stepOutcomes, binding: executionBinding, ...(warnings.length ? { warnings } : {}) };
|
|
192
218
|
}
|
|
193
219
|
failedSteps.push(step.command);
|
|
194
220
|
}
|
|
@@ -198,6 +224,7 @@ export async function runVerification(commands, cwd, timeoutMs, verificationStac
|
|
|
198
224
|
passed: false,
|
|
199
225
|
summary: `Failed steps: ${failedSteps.join(", ")}`,
|
|
200
226
|
steps: stepOutcomes,
|
|
227
|
+
binding: executionBinding,
|
|
201
228
|
...(warnings.length ? { warnings } : {})
|
|
202
229
|
};
|
|
203
230
|
}
|
|
@@ -205,6 +232,7 @@ export async function runVerification(commands, cwd, timeoutMs, verificationStac
|
|
|
205
232
|
passed: true,
|
|
206
233
|
summary: `All ${String(steps.length)} verification step(s) passed.`,
|
|
207
234
|
steps: stepOutcomes,
|
|
235
|
+
binding: executionBinding,
|
|
208
236
|
...(warnings.length ? { warnings } : {})
|
|
209
237
|
};
|
|
210
238
|
}
|
|
@@ -365,19 +393,32 @@ export function resolveNpmShimScript(shimPath) {
|
|
|
365
393
|
}
|
|
366
394
|
const shimDir = dirname(shimPath);
|
|
367
395
|
const scriptPathPattern = /["']?(?:%~?dp0%?|\$basedir)[\\/]([^"'\s]+\.[cm]?js)["']?/gi;
|
|
368
|
-
const matches = contents.matchAll(scriptPathPattern);
|
|
396
|
+
const matches = [...contents.matchAll(scriptPathPattern)];
|
|
397
|
+
// Collect all candidate scripts that exist on disk.
|
|
398
|
+
const candidates = [];
|
|
369
399
|
for (const match of matches) {
|
|
370
400
|
const relativeScript = match[1];
|
|
371
|
-
if (!relativeScript)
|
|
401
|
+
if (!relativeScript)
|
|
372
402
|
continue;
|
|
373
|
-
}
|
|
374
403
|
const segments = relativeScript.split(/[\\/]+/u).filter(Boolean);
|
|
375
404
|
const resolvedScript = resolve(shimDir, ...segments);
|
|
376
405
|
if (existsSync(resolvedScript)) {
|
|
377
|
-
|
|
406
|
+
candidates.push(resolvedScript);
|
|
378
407
|
}
|
|
379
408
|
}
|
|
380
|
-
|
|
409
|
+
if (candidates.length === 0)
|
|
410
|
+
return undefined;
|
|
411
|
+
// npm.cmd / npm.ps1 / npm.bat shims reference both npm-prefix.js and npm-cli.js.
|
|
412
|
+
// npm-cli.js is the semantic npm CLI entry point — never pick npm-prefix.js for
|
|
413
|
+
// these. Fail closed: if npm-cli.js is not among the resolved candidates, return
|
|
414
|
+
// undefined so the caller falls back to wrapper-shell behavior.
|
|
415
|
+
const shimBasename = basename(shimPath).toLowerCase();
|
|
416
|
+
if (/^npm(\.(cmd|ps1|bat))?$/.test(shimBasename)) {
|
|
417
|
+
return candidates.find((p) => basename(p).toLowerCase() === "npm-cli.js");
|
|
418
|
+
}
|
|
419
|
+
// For all other npm-installed executables (codex.cmd, claude.ps1, etc.), the
|
|
420
|
+
// shim wraps exactly one package bin target — use the first resolving candidate.
|
|
421
|
+
return candidates[0];
|
|
381
422
|
}
|
|
382
423
|
/**
|
|
383
424
|
* Resolve a reliable `node` executable for spawning CLI scripts on Windows.
|
|
@@ -43,7 +43,67 @@ export interface CodexExecArgsOptions {
|
|
|
43
43
|
mode?: "prompt" | "probe";
|
|
44
44
|
}
|
|
45
45
|
type SpawnSyncLike = typeof spawnSync;
|
|
46
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Outcome when the preflight probe confirms the working directory is writable.
|
|
48
|
+
* capabilitySource is always "probe" — result is measured, not assumed.
|
|
49
|
+
*/
|
|
50
|
+
export interface CodexSandboxPreflightOk {
|
|
51
|
+
ok: true;
|
|
52
|
+
effectiveSandbox: "read-only" | "workspace-write";
|
|
53
|
+
capabilitySource: "probe";
|
|
54
|
+
writableRoot: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Outcome when the working directory cannot be written but workspace-write
|
|
58
|
+
* was requested. This is a first-class typed failure — distinct from a
|
|
59
|
+
* provider-unavailable or environment-mismatch error. No model call has
|
|
60
|
+
* been attempted when this is returned.
|
|
61
|
+
*/
|
|
62
|
+
export interface CodexSandboxPreflightReadOnly {
|
|
63
|
+
ok: false;
|
|
64
|
+
code: "provider_sandbox_read_only";
|
|
65
|
+
requestedCapability: "workspace-write";
|
|
66
|
+
detectedCapability: "read-only";
|
|
67
|
+
effectiveSandbox: "read-only";
|
|
68
|
+
affectedPath: string;
|
|
69
|
+
writableRoot: string;
|
|
70
|
+
capabilitySource: "probe";
|
|
71
|
+
remediation: string;
|
|
72
|
+
}
|
|
73
|
+
export type CodexSandboxPreflightOutcome = CodexSandboxPreflightOk | CodexSandboxPreflightReadOnly;
|
|
74
|
+
/**
|
|
75
|
+
* Probes whether the given directory is writable by the current process.
|
|
76
|
+
*
|
|
77
|
+
* Strategy: create a uniquely named temp file inside the directory, write a
|
|
78
|
+
* sentinel byte, then remove it. This is a real filesystem action — not an
|
|
79
|
+
* inference from binary metadata or launch-probe output.
|
|
80
|
+
*
|
|
81
|
+
* The probe leaves no file behind on either success or failure.
|
|
82
|
+
*
|
|
83
|
+
* Exported for unit testing with a real tmp directory.
|
|
84
|
+
*/
|
|
85
|
+
export declare function probeFilesystemWriteCapability(directory: string): {
|
|
86
|
+
writable: true;
|
|
87
|
+
} | {
|
|
88
|
+
writable: false;
|
|
89
|
+
reason: string;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Checks whether the requested sandbox mode is achievable for the given
|
|
93
|
+
* working directory. The adapter receives `requestedSandbox` from CLI/core —
|
|
94
|
+
* it does not decide the mode itself.
|
|
95
|
+
*
|
|
96
|
+
* When `requestedSandbox` is "workspace-write" and the working directory is not
|
|
97
|
+
* writable, this function returns `provider_sandbox_read_only` before any model
|
|
98
|
+
* execution is attempted.
|
|
99
|
+
*
|
|
100
|
+
* When `requestedSandbox` is "read-only" no write probe is performed; the
|
|
101
|
+
* outcome is `ok: true, effectiveSandbox: "read-only"` immediately.
|
|
102
|
+
*/
|
|
103
|
+
export declare function checkCodexSandboxPreflight(input: {
|
|
104
|
+
requestedSandbox: "read-only" | "workspace-write";
|
|
105
|
+
workingDirectory: string;
|
|
106
|
+
}): CodexSandboxPreflightOutcome;
|
|
47
107
|
export interface CodexProbeCandidateResult {
|
|
48
108
|
path: string;
|
|
49
109
|
installKind: CodexInstallKind;
|
|
@@ -1,9 +1,100 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
-
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { existsSync, mkdirSync, mkdtempSync, readdirSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { dirname, extname, join, resolve } from "node:path";
|
|
4
4
|
import { resolveNpmShimScript } from "./cli-bridge.js";
|
|
5
5
|
const codexLaunchProbeCache = new Map();
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Probes whether the given directory is writable by the current process.
|
|
8
|
+
*
|
|
9
|
+
* Strategy: create a uniquely named temp file inside the directory, write a
|
|
10
|
+
* sentinel byte, then remove it. This is a real filesystem action — not an
|
|
11
|
+
* inference from binary metadata or launch-probe output.
|
|
12
|
+
*
|
|
13
|
+
* The probe leaves no file behind on either success or failure.
|
|
14
|
+
*
|
|
15
|
+
* Exported for unit testing with a real tmp directory.
|
|
16
|
+
*/
|
|
17
|
+
export function probeFilesystemWriteCapability(directory) {
|
|
18
|
+
// Ensure the directory exists before probing.
|
|
19
|
+
try {
|
|
20
|
+
mkdirSync(directory, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
return {
|
|
24
|
+
writable: false,
|
|
25
|
+
reason: `Could not create directory ${directory}: ${err instanceof Error ? err.message : String(err)}`
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
// Use mkdtempSync so the filename is guaranteed unique even under concurrent runs.
|
|
29
|
+
let tempDir;
|
|
30
|
+
try {
|
|
31
|
+
tempDir = mkdtempSync(join(directory, ".ml-write-probe-"));
|
|
32
|
+
const tempFile = join(tempDir, "capability.tmp");
|
|
33
|
+
writeFileSync(tempFile, "\x01", { encoding: "binary", flag: "wx" });
|
|
34
|
+
unlinkSync(tempFile);
|
|
35
|
+
return { writable: true };
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
return {
|
|
39
|
+
writable: false,
|
|
40
|
+
reason: err instanceof Error ? err.message : String(err)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
if (tempDir) {
|
|
45
|
+
try {
|
|
46
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
47
|
+
}
|
|
48
|
+
catch { /* ignore cleanup errors */ }
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Checks whether the requested sandbox mode is achievable for the given
|
|
54
|
+
* working directory. The adapter receives `requestedSandbox` from CLI/core —
|
|
55
|
+
* it does not decide the mode itself.
|
|
56
|
+
*
|
|
57
|
+
* When `requestedSandbox` is "workspace-write" and the working directory is not
|
|
58
|
+
* writable, this function returns `provider_sandbox_read_only` before any model
|
|
59
|
+
* execution is attempted.
|
|
60
|
+
*
|
|
61
|
+
* When `requestedSandbox` is "read-only" no write probe is performed; the
|
|
62
|
+
* outcome is `ok: true, effectiveSandbox: "read-only"` immediately.
|
|
63
|
+
*/
|
|
64
|
+
export function checkCodexSandboxPreflight(input) {
|
|
65
|
+
const dir = resolve(input.workingDirectory);
|
|
66
|
+
if (input.requestedSandbox === "read-only") {
|
|
67
|
+
return {
|
|
68
|
+
ok: true,
|
|
69
|
+
effectiveSandbox: "read-only",
|
|
70
|
+
capabilitySource: "probe",
|
|
71
|
+
writableRoot: dir
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// workspace-write: run the actual filesystem probe.
|
|
75
|
+
const probeResult = probeFilesystemWriteCapability(dir);
|
|
76
|
+
if (probeResult.writable) {
|
|
77
|
+
return {
|
|
78
|
+
ok: true,
|
|
79
|
+
effectiveSandbox: "workspace-write",
|
|
80
|
+
capabilitySource: "probe",
|
|
81
|
+
writableRoot: dir
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
ok: false,
|
|
86
|
+
code: "provider_sandbox_read_only",
|
|
87
|
+
requestedCapability: "workspace-write",
|
|
88
|
+
detectedCapability: "read-only",
|
|
89
|
+
effectiveSandbox: "read-only",
|
|
90
|
+
affectedPath: dir,
|
|
91
|
+
writableRoot: dir,
|
|
92
|
+
capabilitySource: "probe",
|
|
93
|
+
remediation: `The working directory ${dir} is not writable by the current process. ` +
|
|
94
|
+
"Launch MartinLoop in a session with write access to that directory, or use " +
|
|
95
|
+
"`--sandbox read-only` for inspection-only work."
|
|
96
|
+
};
|
|
97
|
+
}
|
|
7
98
|
const CODEX_LAUNCH_PROBE_PROMPT = [
|
|
8
99
|
"You are validating MartinLoop Codex host readiness.",
|
|
9
100
|
"Do not edit files.",
|
|
@@ -17,7 +108,7 @@ function buildProbeCacheKey(input) {
|
|
|
17
108
|
workingDirectory: resolve(input.workingDirectory),
|
|
18
109
|
platform: input.platform,
|
|
19
110
|
candidatePaths: input.candidatePaths,
|
|
20
|
-
model: input.model
|
|
111
|
+
model: input.model
|
|
21
112
|
});
|
|
22
113
|
}
|
|
23
114
|
function isInsideGitRepository(workingDirectory) {
|
|
@@ -89,6 +180,17 @@ function codexProbePreference(diagnosis) {
|
|
|
89
180
|
}
|
|
90
181
|
return 3;
|
|
91
182
|
}
|
|
183
|
+
function codexProbeCandidatePreference(path, diagnosis, platform) {
|
|
184
|
+
const hostPreference = codexProbePreference(diagnosis) * 10;
|
|
185
|
+
if (platform !== "win32" || diagnosis.installKind !== "windows_shim") {
|
|
186
|
+
return hostPreference;
|
|
187
|
+
}
|
|
188
|
+
// `where codex` commonly returns npm's extensionless POSIX shim before
|
|
189
|
+
// `codex.cmd`. Node cannot spawn that text shim directly on Windows, while
|
|
190
|
+
// the .cmd/.ps1 shim has a supported invocation path (or can be unwrapped).
|
|
191
|
+
const extension = extname(path).toLowerCase();
|
|
192
|
+
return hostPreference + (extension === ".cmd" || extension === ".bat" || extension === ".ps1" ? 0 : 1);
|
|
193
|
+
}
|
|
92
194
|
function buildProbeCandidates(input) {
|
|
93
195
|
const pathCandidates = normalizeCandidates(input.availability.candidatePaths ?? [input.availability.resolvedPath ?? input.availability.command]);
|
|
94
196
|
const desktopCandidates = input.platform === "win32" && input.includeDesktopCandidates
|
|
@@ -106,7 +208,9 @@ function buildProbeCandidates(input) {
|
|
|
106
208
|
return {
|
|
107
209
|
path,
|
|
108
210
|
diagnosis,
|
|
109
|
-
preference: input.platform === "win32"
|
|
211
|
+
preference: input.platform === "win32"
|
|
212
|
+
? codexProbeCandidatePreference(path, diagnosis, input.platform)
|
|
213
|
+
: 0,
|
|
110
214
|
discoveryIndex
|
|
111
215
|
};
|
|
112
216
|
})
|
|
@@ -205,11 +309,11 @@ function classifyProbeFailure(stderr, stdout, diagnosis) {
|
|
|
205
309
|
warnings.push(unsupportedModelWarning);
|
|
206
310
|
}
|
|
207
311
|
return {
|
|
208
|
-
summary:
|
|
312
|
+
summary: "Codex launched with a model that is not supported for ChatGPT-account authentication. Pass an explicit model that your ChatGPT account supports for governed Codex work.",
|
|
209
313
|
diagnosis: {
|
|
210
314
|
...diagnosis,
|
|
211
315
|
warnings,
|
|
212
|
-
remediation:
|
|
316
|
+
remediation: "Override the Codex launch model to a ChatGPT-account-supported option before running governed Codex work."
|
|
213
317
|
}
|
|
214
318
|
};
|
|
215
319
|
}
|
|
@@ -275,14 +379,18 @@ export function buildCodexExecArgs(options) {
|
|
|
275
379
|
const sandbox = options.sandbox ?? "workspace-write";
|
|
276
380
|
const modelArgs = options.model ? ["--model", options.model] : [];
|
|
277
381
|
const extraArgs = options.extraArgs ?? [];
|
|
382
|
+
const sandboxArgs = sandbox === "workspace-write"
|
|
383
|
+
// In Codex CLI, --approve-for-me is the write-enabled automatic-review
|
|
384
|
+
// mode and is mutually exclusive with --sandbox workspace-write.
|
|
385
|
+
? ["--approve-for-me"]
|
|
386
|
+
: ["--sandbox", sandbox];
|
|
278
387
|
return [
|
|
279
388
|
"exec",
|
|
280
389
|
// Governed MartinLoop runs should not inherit auth-sensitive default MCP/plugin state.
|
|
281
390
|
"--ignore-user-config",
|
|
282
391
|
"--cd",
|
|
283
392
|
options.workingDirectory,
|
|
284
|
-
|
|
285
|
-
sandbox,
|
|
393
|
+
...sandboxArgs,
|
|
286
394
|
"--json",
|
|
287
395
|
"--color",
|
|
288
396
|
"never",
|
|
@@ -357,10 +465,6 @@ function discoverCommandOffPath(command, platform, env) {
|
|
|
357
465
|
const localAppData = env.LOCALAPPDATA;
|
|
358
466
|
if (localAppData)
|
|
359
467
|
dirs.push(join(localAppData, "OpenAI", "Codex", "bin"));
|
|
360
|
-
// Claude Code native installer places binary at %USERPROFILE%\.local\bin
|
|
361
|
-
const userProfile = env.USERPROFILE ?? env.HOMEPATH;
|
|
362
|
-
if (userProfile)
|
|
363
|
-
dirs.push(join(userProfile, ".local", "bin"));
|
|
364
468
|
if (home)
|
|
365
469
|
dirs.push(join(home, "scoop", "shims"));
|
|
366
470
|
}
|
|
@@ -387,13 +491,8 @@ function discoverCommandOffPath(command, platform, env) {
|
|
|
387
491
|
return undefined;
|
|
388
492
|
}
|
|
389
493
|
function suggestInstall(command) {
|
|
390
|
-
if (command === "claude") {
|
|
391
|
-
const installCmd = process.platform === "win32"
|
|
392
|
-
? "irm https://claude.ai/install.ps1 | iex"
|
|
393
|
-
: "curl -fsSL https://claude.ai/install.sh | bash";
|
|
394
|
-
return `Install with: ${installCmd}`;
|
|
395
|
-
}
|
|
396
494
|
const installs = {
|
|
495
|
+
claude: "Install with: npm install -g @anthropic-ai/claude-code",
|
|
397
496
|
codex: "Install with: npm install -g @openai/codex",
|
|
398
497
|
gemini: "Install with: npm install -g @google/gemini-cli"
|
|
399
498
|
};
|
|
@@ -471,7 +570,7 @@ export function probeCodexLaunch(input) {
|
|
|
471
570
|
});
|
|
472
571
|
const args = buildCodexExecArgs({
|
|
473
572
|
workingDirectory: input.workingDirectory,
|
|
474
|
-
model: input.model
|
|
573
|
+
model: input.model,
|
|
475
574
|
mode: "probe"
|
|
476
575
|
});
|
|
477
576
|
if (!availability.available) {
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
export { createDirectProviderAdapter, type DirectProviderAdapterOptions } from "./direct-provider.js";
|
|
2
2
|
export { createStubDirectProviderAdapter, type StubDirectProviderAdapterOptions } from "./stub-direct-provider.js";
|
|
3
|
-
export { createStubAgentCliAdapter, type StubAgentCliAdapterOptions } from "./stub-agent-cli.js";
|
|
4
3
|
export { createAgentCliAdapter, createClaudeCliAdapter, createCodexCliAdapter, createGeminiCliAdapter, type AgentCliAdapterOptions, type ClaudeCliAdapterOptions, type CodexCliAdapterOptions, type GeminiCliAdapterOptions, type CliArgsBuilder } from "./claude-cli.js";
|
|
5
|
-
export { createVerifierOnlyAdapter, type VerifierOnlyAdapterOptions } from "./verifier-only.js";
|
|
6
4
|
export { createOpenAiCompatibleAdapter, resolveOpenAiCompatibleRuntimeConfig, type OpenAiCompatibleAdapterOptions } from "./openai-compatible.js";
|
|
7
|
-
export { detectCodexHostPlatform, diagnoseCodexHost, probeCodexLaunch, resolveCliCommandAvailability, type CliCommandAvailability, type CodexHostDiagnosis, type CodexHostPlatform, type CodexLaunchProbeResult } from "./codex-launcher.js";
|
|
5
|
+
export { detectCodexHostPlatform, diagnoseCodexHost, probeCodexLaunch, resolveCliCommandAvailability, type CliCommandAvailability, type CodexHostDiagnosis, type CodexHostPlatform, type CodexLaunchProbeResult, checkCodexSandboxPreflight, probeFilesystemWriteCapability, type CodexSandboxPreflightOk, type CodexSandboxPreflightOutcome, type CodexSandboxPreflightReadOnly } from "./codex-launcher.js";
|
|
8
6
|
export { createSpawnPlan, type SpawnLike, type SpawnPlan, type SubprocessResult, type VerificationOutcome } from "./cli-bridge.js";
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
export { createDirectProviderAdapter } from "./direct-provider.js";
|
|
2
2
|
export { createStubDirectProviderAdapter } from "./stub-direct-provider.js";
|
|
3
|
-
export { createStubAgentCliAdapter } from "./stub-agent-cli.js";
|
|
4
3
|
export { createAgentCliAdapter, createClaudeCliAdapter, createCodexCliAdapter, createGeminiCliAdapter } from "./claude-cli.js";
|
|
5
|
-
export { createVerifierOnlyAdapter } from "./verifier-only.js";
|
|
6
4
|
export { createOpenAiCompatibleAdapter, resolveOpenAiCompatibleRuntimeConfig } from "./openai-compatible.js";
|
|
7
|
-
export { detectCodexHostPlatform, diagnoseCodexHost, probeCodexLaunch, resolveCliCommandAvailability } from "./codex-launcher.js";
|
|
5
|
+
export { detectCodexHostPlatform, diagnoseCodexHost, probeCodexLaunch, resolveCliCommandAvailability, checkCodexSandboxPreflight, probeFilesystemWriteCapability } from "./codex-launcher.js";
|
|
8
6
|
export { createSpawnPlan } from "./cli-bridge.js";
|
|
@@ -69,15 +69,32 @@ function estimateCost(model, inputChars, outputChars) {
|
|
|
69
69
|
const actualUsd = (tokensIn / 1000) * pricing.inputPer1K + (tokensOut / 1000) * pricing.outputPer1K;
|
|
70
70
|
return { tokensIn, tokensOut, actualUsd };
|
|
71
71
|
}
|
|
72
|
+
function normalizeOpenAiCompatibleUsage(input) {
|
|
73
|
+
const hasKnownPricing = KNOWN_MODEL_PRICING[input.model] !== undefined;
|
|
74
|
+
const pricing = KNOWN_MODEL_PRICING[input.model] ?? {
|
|
75
|
+
inputPer1K: FALLBACK_INPUT_PER_1K,
|
|
76
|
+
outputPer1K: FALLBACK_OUTPUT_PER_1K
|
|
77
|
+
};
|
|
78
|
+
const actualUsd = (input.tokensIn / 1000) * pricing.inputPer1K +
|
|
79
|
+
(input.tokensOut / 1000) * pricing.outputPer1K;
|
|
80
|
+
const provenance = input.usageWasFullyProviderReported && hasKnownPricing ? "actual" : "estimated";
|
|
81
|
+
return normalizeUsage({
|
|
82
|
+
actualUsd,
|
|
83
|
+
...(provenance === "estimated" ? { estimatedUsd: actualUsd } : {}),
|
|
84
|
+
tokensIn: input.tokensIn,
|
|
85
|
+
tokensOut: input.tokensOut,
|
|
86
|
+
provenance
|
|
87
|
+
});
|
|
88
|
+
}
|
|
72
89
|
// ---------------------------------------------------------------------------
|
|
73
90
|
// Prompt builder
|
|
74
91
|
// ---------------------------------------------------------------------------
|
|
75
|
-
const DEFAULT_SYSTEM_PROMPT = `You are an expert software engineer executing a governed coding task.
|
|
76
|
-
Follow these rules exactly:
|
|
77
|
-
- Read the task description carefully and implement only what is asked.
|
|
78
|
-
- Do not add features, refactors, or improvements beyond the stated task.
|
|
79
|
-
- If the task asks you to write or modify code, output the complete file content with changes applied.
|
|
80
|
-
- Be precise, minimal, and test-backed in all changes.
|
|
92
|
+
const DEFAULT_SYSTEM_PROMPT = `You are an expert software engineer executing a governed coding task.
|
|
93
|
+
Follow these rules exactly:
|
|
94
|
+
- Read the task description carefully and implement only what is asked.
|
|
95
|
+
- Do not add features, refactors, or improvements beyond the stated task.
|
|
96
|
+
- If the task asks you to write or modify code, output the complete file content with changes applied.
|
|
97
|
+
- Be precise, minimal, and test-backed in all changes.
|
|
81
98
|
- State what you changed and why at the end of your response.`;
|
|
82
99
|
export const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com";
|
|
83
100
|
export const DEFAULT_OPENAI_MODEL = "gpt-4.1-mini";
|
|
@@ -172,10 +189,12 @@ export function createOpenAiCompatibleAdapter(options) {
|
|
|
172
189
|
// indicate a permanent configuration problem.
|
|
173
190
|
const MAX_RETRIES = 3;
|
|
174
191
|
const RETRYABLE_STATUS = new Set([429, 500, 502, 503, 504]);
|
|
192
|
+
const NON_RETRYABLE_STATUS = new Set([400, 401, 403]);
|
|
175
193
|
const endpoint = `${baseUrl}/v1/chat/completions`;
|
|
176
194
|
let responseText = "";
|
|
177
195
|
let tokensIn = estimated.tokensIn;
|
|
178
196
|
let tokensOut = 0;
|
|
197
|
+
let usageWasFullyProviderReported = false;
|
|
179
198
|
const headers = { "Content-Type": "application/json" };
|
|
180
199
|
if (apiKey)
|
|
181
200
|
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
@@ -207,6 +226,17 @@ export function createOpenAiCompatibleAdapter(options) {
|
|
|
207
226
|
const body = (await res.json());
|
|
208
227
|
if (!res.ok || body.error) {
|
|
209
228
|
const errMsg = body.error?.message ?? `HTTP ${res.status}`;
|
|
229
|
+
// Fail immediately on non-retryable errors (auth, bad request)
|
|
230
|
+
if (NON_RETRYABLE_STATUS.has(res.status)) {
|
|
231
|
+
return {
|
|
232
|
+
status: "failed",
|
|
233
|
+
summary: `${model} API error: ${errMsg}`,
|
|
234
|
+
usage: normalizeUsage({ actualUsd: 0, tokensIn: 0, tokensOut: 0, provenance: "unavailable" }),
|
|
235
|
+
verification: { passed: false, summary: "API call failed before verifier." },
|
|
236
|
+
failure: { message: errMsg, classHint: "infrastructure_error" }
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
// Retry on transient errors
|
|
210
240
|
if (RETRYABLE_STATUS.has(res.status) && attempt < MAX_RETRIES - 1) {
|
|
211
241
|
lastError = errMsg;
|
|
212
242
|
// Exponential backoff: 1s, 2s, 4s
|
|
@@ -223,8 +253,13 @@ export function createOpenAiCompatibleAdapter(options) {
|
|
|
223
253
|
}
|
|
224
254
|
responseText = body.choices?.[0]?.message?.content ?? "";
|
|
225
255
|
if (body.usage) {
|
|
226
|
-
|
|
227
|
-
|
|
256
|
+
const providerPromptTokens = body.usage.prompt_tokens;
|
|
257
|
+
const providerCompletionTokens = body.usage.completion_tokens;
|
|
258
|
+
usageWasFullyProviderReported =
|
|
259
|
+
typeof providerPromptTokens === "number" &&
|
|
260
|
+
typeof providerCompletionTokens === "number";
|
|
261
|
+
tokensIn = providerPromptTokens ?? tokensIn;
|
|
262
|
+
tokensOut = providerCompletionTokens ?? Math.ceil(responseText.length / CHARS_PER_TOKEN);
|
|
228
263
|
}
|
|
229
264
|
else {
|
|
230
265
|
tokensOut = Math.ceil(responseText.length / CHARS_PER_TOKEN);
|
|
@@ -267,33 +302,37 @@ export function createOpenAiCompatibleAdapter(options) {
|
|
|
267
302
|
return {
|
|
268
303
|
status: "failed",
|
|
269
304
|
summary: `${model} returned an empty response.`,
|
|
270
|
-
usage:
|
|
305
|
+
usage: normalizeOpenAiCompatibleUsage({
|
|
306
|
+
model,
|
|
307
|
+
tokensIn,
|
|
308
|
+
tokensOut: 0,
|
|
309
|
+
usageWasFullyProviderReported
|
|
310
|
+
}),
|
|
271
311
|
verification: { passed: false, summary: "Empty response — nothing to verify." },
|
|
272
312
|
failure: { message: "empty_response" }
|
|
273
313
|
};
|
|
274
314
|
}
|
|
275
315
|
// Run verification
|
|
276
|
-
const verification = await runVerification(request.context.verificationPlan, workingDirectory, verifyTimeoutMs, request.context.verificationStack
|
|
316
|
+
const verification = await runVerification(request.context.verificationPlan, workingDirectory, verifyTimeoutMs, request.context.verificationStack, undefined, {
|
|
317
|
+
runId: request.loopId,
|
|
318
|
+
workspaceId: request.workspaceId,
|
|
319
|
+
cwd: workingDirectory,
|
|
320
|
+
});
|
|
277
321
|
const execution = {
|
|
278
322
|
changedFiles: hasVerificationSteps
|
|
279
323
|
? (await readGitChangedFiles(workingDirectory, 5_000)).filter((file) => !baselineChangedFiles.has(file))
|
|
280
324
|
: []
|
|
281
325
|
};
|
|
282
|
-
const pricing = KNOWN_MODEL_PRICING[model] ?? {
|
|
283
|
-
inputPer1K: FALLBACK_INPUT_PER_1K,
|
|
284
|
-
outputPer1K: FALLBACK_OUTPUT_PER_1K
|
|
285
|
-
};
|
|
286
|
-
const actualUsd = (tokensIn / 1000) * pricing.inputPer1K + (tokensOut / 1000) * pricing.outputPer1K;
|
|
287
326
|
return {
|
|
288
327
|
status: verification.passed ? "completed" : "failed",
|
|
289
328
|
summary: verification.passed
|
|
290
329
|
? `${model} completed the task. Verifier passed.`
|
|
291
330
|
: `${model} completed but verifier failed: ${verification.summary}`,
|
|
292
|
-
usage:
|
|
293
|
-
|
|
331
|
+
usage: normalizeOpenAiCompatibleUsage({
|
|
332
|
+
model,
|
|
294
333
|
tokensIn,
|
|
295
334
|
tokensOut,
|
|
296
|
-
|
|
335
|
+
usageWasFullyProviderReported
|
|
297
336
|
}),
|
|
298
337
|
verification,
|
|
299
338
|
execution,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type DiffVisibilityLevel = "none" | "git" | "adapter_reported";
|
|
2
|
-
export type VerifierCompatibility = "full" | "
|
|
2
|
+
export type VerifierCompatibility = "full" | "proof" | "unsupported";
|
|
3
3
|
export type SandboxExpectation = "host_process" | "workspace_write" | "provider_managed" | "not_applicable";
|
|
4
4
|
export type LaunchReadiness = "path_lookup" | "configured_endpoint" | "built_in";
|
|
5
5
|
export type AdapterUsageSettlement = "actual" | "estimated" | "unavailable";
|