@martinloop/mcp 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/vendor/adapters/claude-cli.d.ts +5 -0
- package/dist/vendor/adapters/claude-cli.js +1 -0
- package/dist/vendor/adapters/codex-capabilities.d.ts +4 -1
- package/dist/vendor/adapters/codex-capabilities.js +36 -4
- package/dist/vendor/adapters/codex-cli.d.ts +7 -0
- package/dist/vendor/adapters/codex-cli.js +11 -0
- package/dist/vendor/adapters/codex-host.js +24 -1
- package/dist/vendor/adapters/index.d.ts +1 -1
- package/dist/vendor/adapters/index.js +1 -1
- package/dist/vendor/contracts/index.d.ts +4 -0
- package/dist/vendor/core/index.d.ts +5 -0
- package/dist/vendor/core/index.js +2 -0
- package/dist/vendor/core/policy.d.ts +3 -0
- package/dist/vendor/core/policy.js +13 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const MARTIN_MCP_PACKAGE_VERSION = "0.8.
|
|
1
|
+
export declare const MARTIN_MCP_PACKAGE_VERSION = "0.8.1";
|
package/dist/package-version.js
CHANGED
|
@@ -63,6 +63,11 @@ export interface AgentCliAdapterOptions {
|
|
|
63
63
|
streamingTokenCap?: boolean;
|
|
64
64
|
/** Provider detail-token fields are already included in input/output token totals. */
|
|
65
65
|
streamingUsageDetailsIncludedInTotals?: boolean;
|
|
66
|
+
/** Estimated floor used by the host to reject impossible token caps before spawning. */
|
|
67
|
+
budgetPreflight?: {
|
|
68
|
+
minimumViableTokens: number;
|
|
69
|
+
basis: string;
|
|
70
|
+
};
|
|
66
71
|
/** Test-only override for subprocess spawning. */
|
|
67
72
|
spawnImpl?: SpawnLike;
|
|
68
73
|
}
|
|
@@ -675,6 +675,7 @@ export function createAgentCliAdapter(options) {
|
|
|
675
675
|
transport: "cli",
|
|
676
676
|
agentExecutionIntent,
|
|
677
677
|
providerExecutionTimeoutMs: timeoutMs,
|
|
678
|
+
...(options.budgetPreflight ? { budgetPreflight: options.budgetPreflight } : {}),
|
|
678
679
|
capabilities: createAdapterCapabilities({
|
|
679
680
|
preflight: true,
|
|
680
681
|
usageSettlement: supportsUsageSettlement,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { type AgentExecutionIntent } from "../contracts/index.js";
|
|
3
|
-
export type CodexFlagScope = "global" | "exec";
|
|
3
|
+
export type CodexFlagScope = "global" | "exec" | "sandbox";
|
|
4
4
|
export type CodexPromptTransport = "stdin-dash" | "argv";
|
|
5
5
|
export type CodexWriteStrategy = "sandbox+approval" | "automation";
|
|
6
6
|
export interface CodexCapabilityFlag {
|
|
@@ -26,6 +26,8 @@ export interface CodexCapabilityProfile {
|
|
|
26
26
|
supportsExec: boolean;
|
|
27
27
|
probeSucceeded: boolean;
|
|
28
28
|
probeError?: string;
|
|
29
|
+
config?: CodexCapabilityFlag;
|
|
30
|
+
sandboxConfig?: CodexCapabilityFlag;
|
|
29
31
|
model?: CodexCapabilityFlag;
|
|
30
32
|
cwd?: CodexCapabilityFlag;
|
|
31
33
|
sandbox?: CodexSandboxCapability;
|
|
@@ -44,6 +46,7 @@ export interface CodexCapabilityProfile {
|
|
|
44
46
|
export interface CodexExecArgsOptions {
|
|
45
47
|
command?: string;
|
|
46
48
|
workingDirectory: string;
|
|
49
|
+
platform?: NodeJS.Platform;
|
|
47
50
|
sandbox?: "read-only" | "workspace-write" | "danger-full-access";
|
|
48
51
|
model?: string;
|
|
49
52
|
extraArgs?: string[];
|
|
@@ -134,8 +134,18 @@ export function probeCodexCapabilities(binaryPath, options = {}) {
|
|
|
134
134
|
spawnSyncImpl,
|
|
135
135
|
injected
|
|
136
136
|
});
|
|
137
|
+
const sandboxProbe = platform === "win32"
|
|
138
|
+
? runHelpProbe({
|
|
139
|
+
binaryPath,
|
|
140
|
+
args: ["sandbox", "--help"],
|
|
141
|
+
platform,
|
|
142
|
+
spawnSyncImpl,
|
|
143
|
+
injected
|
|
144
|
+
})
|
|
145
|
+
: { text: "", status: null };
|
|
137
146
|
const globalHelp = globalProbe.text;
|
|
138
147
|
const execHelp = execProbe.text;
|
|
148
|
+
const sandboxCommandHelp = sandboxProbe.text;
|
|
139
149
|
const supportsExec = execProbe.status === 0 &&
|
|
140
150
|
!/(unknown command|unrecognized subcommand|unexpected argument ['"]?exec)/iu.test(execHelp);
|
|
141
151
|
const sandboxFlag = locateFlag(globalHelp, execHelp, ["--sandbox", "-s"]);
|
|
@@ -160,7 +170,8 @@ export function probeCodexCapabilities(binaryPath, options = {}) {
|
|
|
160
170
|
? `${flagContext(globalHelp, colorFlag.flag)}\n${flagContext(execHelp, colorFlag.flag)}`
|
|
161
171
|
: "";
|
|
162
172
|
const promptTransports = parsePromptTransports(execHelp);
|
|
163
|
-
const probeError = globalProbe.error ?? execProbe.error;
|
|
173
|
+
const probeError = globalProbe.error ?? execProbe.error ?? sandboxProbe.error;
|
|
174
|
+
const sandboxConfigFlag = ["--config", "-c"].find((flag) => flagPattern(flag).test(sandboxCommandHelp));
|
|
164
175
|
const profile = {
|
|
165
176
|
binaryPath,
|
|
166
177
|
supportsExec,
|
|
@@ -169,6 +180,12 @@ export function probeCodexCapabilities(binaryPath, options = {}) {
|
|
|
169
180
|
...(locateFlag(globalHelp, execHelp, ["--model", "-m"])
|
|
170
181
|
? { model: locateFlag(globalHelp, execHelp, ["--model", "-m"]) }
|
|
171
182
|
: {}),
|
|
183
|
+
...(locateFlag(globalHelp, execHelp, ["--config", "-c"])
|
|
184
|
+
? { config: locateFlag(globalHelp, execHelp, ["--config", "-c"]) }
|
|
185
|
+
: {}),
|
|
186
|
+
...(sandboxConfigFlag
|
|
187
|
+
? { sandboxConfig: { flag: sandboxConfigFlag, scope: "sandbox" } }
|
|
188
|
+
: {}),
|
|
172
189
|
...(locateFlag(globalHelp, execHelp, ["--cd", "--cwd", "--working-dir", "-C"])
|
|
173
190
|
? { cwd: locateFlag(globalHelp, execHelp, ["--cd", "--cwd", "--working-dir", "-C"]) }
|
|
174
191
|
: {}),
|
|
@@ -242,18 +259,33 @@ export function buildCodexExecArgs(options) {
|
|
|
242
259
|
if (options.mode !== "probe" && !resolution) {
|
|
243
260
|
throw new Error(`Resolved Codex binary ${profile.binaryPath} has no negotiated governed-autonomous execution resolution.`);
|
|
244
261
|
}
|
|
245
|
-
const
|
|
262
|
+
const controlledFlags = [
|
|
263
|
+
profile.sandbox?.flag,
|
|
264
|
+
profile.automation?.flag,
|
|
265
|
+
profile.approvalPolicy?.flag,
|
|
266
|
+
profile.config?.flag
|
|
267
|
+
]
|
|
246
268
|
.filter((value) => Boolean(value));
|
|
247
|
-
if ((options.extraArgs ?? []).some((arg) =>
|
|
248
|
-
|
|
269
|
+
if ((options.extraArgs ?? []).some((arg) => {
|
|
270
|
+
const isReservedConfig = arg === "--config" ||
|
|
271
|
+
arg.startsWith("--config=") ||
|
|
272
|
+
arg === "-c" ||
|
|
273
|
+
(arg.startsWith("-c") && arg.length > 2);
|
|
274
|
+
return arg === "danger-full-access" || isReservedConfig || controlledFlags.some((flag) => arg === flag || arg.startsWith(`${flag}=`));
|
|
275
|
+
})) {
|
|
276
|
+
throw new Error("Permission, sandbox, and configuration controls cannot be supplied through extraArgs.");
|
|
249
277
|
}
|
|
250
278
|
const globalArgs = [];
|
|
251
279
|
const execArgs = [];
|
|
280
|
+
const platform = options.platform ?? process.platform;
|
|
252
281
|
const requestedSandbox = options.sandbox ?? "workspace-write";
|
|
253
282
|
const strategy = resolution?.strategy ?? options.writeStrategy;
|
|
254
283
|
const promptTransport = options.promptTransport ?? profile.promptTransport;
|
|
255
284
|
if (profile.userConfigIsolation)
|
|
256
285
|
pushCapabilityArg(globalArgs, execArgs, profile.userConfigIsolation);
|
|
286
|
+
if (platform === "win32" && profile.userConfigIsolation && profile.config) {
|
|
287
|
+
pushCapabilityArg(globalArgs, execArgs, profile.config, 'windows.sandbox="elevated"');
|
|
288
|
+
}
|
|
257
289
|
if (profile.cwd)
|
|
258
290
|
pushCapabilityArg(globalArgs, execArgs, profile.cwd, options.workingDirectory);
|
|
259
291
|
if (requestedSandbox === "workspace-write") {
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { type CodexCliAdapterOptions as LegacyCodexCliAdapterOptions } from "./claude-cli.js";
|
|
2
2
|
import { type CodexAutonomyResolution, type CodexCapabilityProfile } from "./codex-launcher.js";
|
|
3
|
+
/**
|
|
4
|
+
* Conservative admission floor for one Codex CLI turn. Codex reports token
|
|
5
|
+
* usage in turn.completed, after the first turn has already consumed context,
|
|
6
|
+
* so smaller explicit caps cannot be enforced before spend.
|
|
7
|
+
*/
|
|
8
|
+
export declare const CODEX_MINIMUM_VIABLE_TOKEN_BUDGET = 128000;
|
|
9
|
+
export declare const CODEX_TOKEN_BUDGET_PREFLIGHT_BASIS = "codex_first_turn_usage_reports_after_completion";
|
|
3
10
|
export interface CodexCliAdapterOptions extends Omit<LegacyCodexCliAdapterOptions, "command"> {
|
|
4
11
|
/** Exact resolved executable selected by the Codex launch probe. */
|
|
5
12
|
command?: string;
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { createAgentCliAdapter } from "./claude-cli.js";
|
|
2
2
|
import { buildCodexExecArgs, buildCodexStdin, isCodexAutonomyResolutionVerifiedByLaunchProbe, probeCodexCapabilities } from "./codex-launcher.js";
|
|
3
|
+
/**
|
|
4
|
+
* Conservative admission floor for one Codex CLI turn. Codex reports token
|
|
5
|
+
* usage in turn.completed, after the first turn has already consumed context,
|
|
6
|
+
* so smaller explicit caps cannot be enforced before spend.
|
|
7
|
+
*/
|
|
8
|
+
export const CODEX_MINIMUM_VIABLE_TOKEN_BUDGET = 128_000;
|
|
9
|
+
export const CODEX_TOKEN_BUDGET_PREFLIGHT_BASIS = "codex_first_turn_usage_reports_after_completion";
|
|
3
10
|
/**
|
|
4
11
|
* Capability-driven Codex CLI adapter.
|
|
5
12
|
*
|
|
@@ -44,6 +51,10 @@ export function createCodexCliAdapter(options = {}) {
|
|
|
44
51
|
streamingUsageCap: true,
|
|
45
52
|
streamingTokenCap: true,
|
|
46
53
|
streamingUsageDetailsIncludedInTotals: true,
|
|
54
|
+
budgetPreflight: {
|
|
55
|
+
minimumViableTokens: CODEX_MINIMUM_VIABLE_TOKEN_BUDGET,
|
|
56
|
+
basis: CODEX_TOKEN_BUDGET_PREFLIGHT_BASIS
|
|
57
|
+
},
|
|
47
58
|
spawnImpl: options.spawnImpl,
|
|
48
59
|
argsBuilder: (prompt) => buildCodexExecArgs({
|
|
49
60
|
command: selectedBinary,
|
|
@@ -319,8 +319,14 @@ function buildLocalSandboxProbeArgs(input) {
|
|
|
319
319
|
" if (!error || !['EACCES', 'EPERM', 'EROFS'].includes(error.code)) throw error;",
|
|
320
320
|
"}"
|
|
321
321
|
].join("\n");
|
|
322
|
+
const windowsSandboxConfig = input.platform === "win32" &&
|
|
323
|
+
input.capabilityProfile.userConfigIsolation &&
|
|
324
|
+
input.capabilityProfile.sandboxConfig
|
|
325
|
+
? [input.capabilityProfile.sandboxConfig.flag, 'windows.sandbox="elevated"']
|
|
326
|
+
: [];
|
|
322
327
|
return [
|
|
323
328
|
"sandbox",
|
|
329
|
+
...windowsSandboxConfig,
|
|
324
330
|
"--permission-profile",
|
|
325
331
|
":workspace",
|
|
326
332
|
"-C",
|
|
@@ -442,6 +448,21 @@ export function probeCodexLaunch(input) {
|
|
|
442
448
|
});
|
|
443
449
|
continue;
|
|
444
450
|
}
|
|
451
|
+
if (platform === "win32" && profile.userConfigIsolation && (!profile.config || !profile.sandboxConfig)) {
|
|
452
|
+
const summary = "Resolved Codex binary does not advertise configuration override support for both exec and sandbox; cannot prove isolated native Windows workspace writes.";
|
|
453
|
+
lastFailure = { candidate, profile, args: [], summary };
|
|
454
|
+
candidateResults.push({
|
|
455
|
+
path: candidate.path,
|
|
456
|
+
installKind: candidate.diagnosis.installKind,
|
|
457
|
+
invocationMode: candidate.diagnosis.invocationMode,
|
|
458
|
+
nativeInstallValid: true,
|
|
459
|
+
sandboxCompatible: false,
|
|
460
|
+
launchReady: false,
|
|
461
|
+
summary,
|
|
462
|
+
capabilityProfile: profile
|
|
463
|
+
});
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
445
466
|
const transport = profile.promptTransports?.[0] ?? profile.promptTransport;
|
|
446
467
|
const resolution = resolveCodexAutonomyCandidates(profile)[0];
|
|
447
468
|
let candidateSummary = "No advertised Codex invocation strategy supports governed workspace writes.";
|
|
@@ -470,7 +491,9 @@ export function probeCodexLaunch(input) {
|
|
|
470
491
|
const args = buildLocalSandboxProbeArgs({
|
|
471
492
|
workingDirectory: input.workingDirectory,
|
|
472
493
|
markerPath,
|
|
473
|
-
outsideMarkerPath
|
|
494
|
+
outsideMarkerPath,
|
|
495
|
+
platform,
|
|
496
|
+
capabilityProfile: profile
|
|
474
497
|
});
|
|
475
498
|
const plan = input.spawnSyncImpl
|
|
476
499
|
? buildInjectedSpawnPlan(candidate.path, args, platform)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { createDirectProviderAdapter, type DirectProviderAdapterOptions } from "./direct-provider.js";
|
|
2
2
|
export { createAgentCliAdapter, createClaudeCliAdapter, createGeminiCliAdapter, type AgentCliAdapterOptions, type ClaudeCliAdapterOptions, type GeminiCliAdapterOptions, type CliArgsBuilder } from "./claude-cli.js";
|
|
3
|
-
export { createCodexCliAdapter, type CodexCliAdapterOptions } from "./codex-cli.js";
|
|
3
|
+
export { createCodexCliAdapter, CODEX_MINIMUM_VIABLE_TOKEN_BUDGET, CODEX_TOKEN_BUDGET_PREFLIGHT_BASIS, type CodexCliAdapterOptions } from "./codex-cli.js";
|
|
4
4
|
export { createOpenAiCompatibleAdapter, resolveOpenAiCompatibleRuntimeConfig, type OpenAiCompatibleAdapterOptions } from "./openai-compatible.js";
|
|
5
5
|
export { createSwarmVerifierExecutor, createVerifierOnlyAdapter, type SwarmVerifierExecutorOptions, type VerifierOnlyAdapterOptions } from "./verifier-only.js";
|
|
6
6
|
/** @internal Test-only compatibility export. Public package builders strip this surface. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { createDirectProviderAdapter } from "./direct-provider.js";
|
|
2
2
|
export { createAgentCliAdapter, createClaudeCliAdapter, createGeminiCliAdapter } from "./claude-cli.js";
|
|
3
|
-
export { createCodexCliAdapter } from "./codex-cli.js";
|
|
3
|
+
export { createCodexCliAdapter, CODEX_MINIMUM_VIABLE_TOKEN_BUDGET, CODEX_TOKEN_BUDGET_PREFLIGHT_BASIS } from "./codex-cli.js";
|
|
4
4
|
export { createOpenAiCompatibleAdapter, resolveOpenAiCompatibleRuntimeConfig } from "./openai-compatible.js";
|
|
5
5
|
export { createSwarmVerifierExecutor, createVerifierOnlyAdapter } from "./verifier-only.js";
|
|
6
6
|
/** @internal Test-only compatibility export. Public package builders strip this surface. */
|
|
@@ -368,6 +368,10 @@ export interface BudgetPreflightEstimate {
|
|
|
368
368
|
estimatedPromptTokens: number;
|
|
369
369
|
estimatedToolOverheadTokens: number;
|
|
370
370
|
estimatedOutputTokensMax: number;
|
|
371
|
+
/** Provider-specific minimum viable first-turn envelope, never actual usage. */
|
|
372
|
+
estimatedMinimumViableTokens: number;
|
|
373
|
+
/** Largest of the generic attempt estimate and provider minimum. */
|
|
374
|
+
estimatedTotalTokens: number;
|
|
371
375
|
estimatedVerifierCostUsd: number;
|
|
372
376
|
estimatedAttemptCostUsd: number;
|
|
373
377
|
provenance: CostProvenance;
|
|
@@ -177,6 +177,11 @@ export interface MartinAdapter {
|
|
|
177
177
|
providerId: string;
|
|
178
178
|
model?: string;
|
|
179
179
|
transport?: "cli" | "http" | "routed_http";
|
|
180
|
+
budgetPreflight?: {
|
|
181
|
+
/** Estimated token floor required before the adapter can report first-turn usage. */
|
|
182
|
+
minimumViableTokens?: number;
|
|
183
|
+
basis?: string;
|
|
184
|
+
};
|
|
180
185
|
capabilities?: {
|
|
181
186
|
preflight?: boolean;
|
|
182
187
|
usageSettlement?: boolean;
|
|
@@ -600,6 +600,8 @@ export async function runMartin(input) {
|
|
|
600
600
|
promptCharCount: distilled.focus.length + loop.task.objective.length * 3,
|
|
601
601
|
attemptCount: loop.attempts.length,
|
|
602
602
|
remainingBudgetUsd: distilled.constraints.remainingBudgetUsd,
|
|
603
|
+
remainingTokens: distilled.constraints.remainingTokens,
|
|
604
|
+
minimumViableTokens: executingAdapter.metadata.budgetPreflight?.minimumViableTokens,
|
|
603
605
|
perAttemptCapUsd: loop.budget.maxUsd * 0.25
|
|
604
606
|
});
|
|
605
607
|
if (!budgetPreflight.allowed) {
|
|
@@ -103,6 +103,9 @@ export interface BudgetPreflightInput {
|
|
|
103
103
|
promptCharCount: number;
|
|
104
104
|
attemptCount: number;
|
|
105
105
|
remainingBudgetUsd: number;
|
|
106
|
+
remainingTokens?: number;
|
|
107
|
+
/** Adapter-declared estimated floor required to admit a credible first turn. */
|
|
108
|
+
minimumViableTokens?: number;
|
|
106
109
|
perAttemptCapUsd?: number;
|
|
107
110
|
pricePerMTokenUsd?: number;
|
|
108
111
|
}
|
|
@@ -365,6 +365,8 @@ export function evaluateBudgetPreflight(input) {
|
|
|
365
365
|
const estimatedPromptTokens = Math.ceil(rawPromptTokens * 1.2);
|
|
366
366
|
const estimatedToolOverheadTokens = 800 + input.attemptCount * 200;
|
|
367
367
|
const estimatedOutputTokensMax = 4_000;
|
|
368
|
+
const estimatedMinimumViableTokens = Math.max(0, input.minimumViableTokens ?? 0);
|
|
369
|
+
const estimatedTotalTokens = Math.max(estimatedPromptTokens + estimatedToolOverheadTokens + estimatedOutputTokensMax, estimatedMinimumViableTokens);
|
|
368
370
|
const estimatedVerifierCostUsd = 0.01;
|
|
369
371
|
const estimatedAttemptCostUsd = roundUsd(((estimatedPromptTokens + estimatedToolOverheadTokens + estimatedOutputTokensMax) /
|
|
370
372
|
1_000_000) *
|
|
@@ -374,10 +376,21 @@ export function evaluateBudgetPreflight(input) {
|
|
|
374
376
|
estimatedPromptTokens,
|
|
375
377
|
estimatedToolOverheadTokens,
|
|
376
378
|
estimatedOutputTokensMax,
|
|
379
|
+
estimatedMinimumViableTokens,
|
|
380
|
+
estimatedTotalTokens,
|
|
377
381
|
estimatedVerifierCostUsd,
|
|
378
382
|
estimatedAttemptCostUsd,
|
|
379
383
|
provenance
|
|
380
384
|
};
|
|
385
|
+
if (input.remainingTokens !== undefined
|
|
386
|
+
&& estimatedMinimumViableTokens > 0
|
|
387
|
+
&& estimatedTotalTokens > input.remainingTokens) {
|
|
388
|
+
return {
|
|
389
|
+
allowed: false,
|
|
390
|
+
reason: `Preflight: ${String(estimatedTotalTokens)} estimated tokens exceed ${String(input.remainingTokens)} remaining tokens.`,
|
|
391
|
+
estimate
|
|
392
|
+
};
|
|
393
|
+
}
|
|
381
394
|
if (estimatedAttemptCostUsd > input.remainingBudgetUsd) {
|
|
382
395
|
return {
|
|
383
396
|
allowed: false,
|
package/package.json
CHANGED
package/server.json
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"url": "https://github.com/Keesan12/martin-loop",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "0.8.
|
|
10
|
+
"version": "0.8.1",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "@martinloop/mcp",
|
|
15
|
-
"version": "0.8.
|
|
15
|
+
"version": "0.8.1",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
}
|