@martinloop/mcp 0.5.3 → 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +6 -6
  2. package/dist/server-validation.js +2 -0
  3. package/dist/server.js +30 -3
  4. package/dist/tools/run-loop.d.ts +1 -0
  5. package/dist/tools/run-loop.js +25 -4
  6. package/dist/tools/tool-response.d.ts +9 -1
  7. package/dist/tools/tool-response.js +7 -5
  8. package/dist/tools/tool-support.js +62 -271
  9. package/dist/vendor/adapters/claude-cli.d.ts +9 -0
  10. package/dist/vendor/adapters/claude-cli.js +19 -2
  11. package/dist/vendor/adapters/codex-capabilities.d.ts +12 -3
  12. package/dist/vendor/adapters/codex-capabilities.js +46 -18
  13. package/dist/vendor/adapters/codex-cli.d.ts +2 -1
  14. package/dist/vendor/adapters/codex-cli.js +12 -2
  15. package/dist/vendor/adapters/codex-host.d.ts +4 -1
  16. package/dist/vendor/adapters/codex-host.js +34 -16
  17. package/dist/vendor/adapters/codex-launcher.d.ts +1 -1
  18. package/dist/vendor/adapters/codex-launcher.js +1 -1
  19. package/dist/vendor/adapters/index.d.ts +2 -1
  20. package/dist/vendor/adapters/index.js +1 -0
  21. package/dist/vendor/contracts/agent-execution.d.ts +9 -0
  22. package/dist/vendor/contracts/agent-execution.js +14 -0
  23. package/dist/vendor/contracts/index.d.ts +9 -0
  24. package/dist/vendor/contracts/index.js +1 -0
  25. package/dist/vendor/core/grounding.js +42 -7
  26. package/dist/vendor/core/index.d.ts +5 -2
  27. package/dist/vendor/core/index.js +12 -3
  28. package/dist/vendor/presentation/governed-plan.d.ts +33 -0
  29. package/dist/vendor/presentation/governed-plan.js +146 -0
  30. package/dist/vendor/presentation/index.d.ts +7 -0
  31. package/dist/vendor/presentation/index.js +7 -0
  32. package/dist/vendor/presentation/motion.d.ts +12 -0
  33. package/dist/vendor/presentation/motion.js +123 -0
  34. package/dist/vendor/presentation/status.d.ts +8 -0
  35. package/dist/vendor/presentation/status.js +11 -0
  36. package/dist/vendor/presentation/table.d.ts +11 -0
  37. package/dist/vendor/presentation/table.js +47 -0
  38. package/dist/vendor/presentation/text.d.ts +6 -0
  39. package/dist/vendor/presentation/text.js +30 -0
  40. package/dist/vendor/presentation/theme.d.ts +31 -0
  41. package/dist/vendor/presentation/theme.js +82 -0
  42. package/dist/vendor/presentation/verified-handoff.d.ts +15 -0
  43. package/dist/vendor/presentation/verified-handoff.js +151 -0
  44. package/package.json +3 -2
  45. package/server.json +2 -2
@@ -1,7 +1,8 @@
1
1
  import { spawnSync } from "node:child_process";
2
+ import { type AgentExecutionIntent } from "../contracts/index.js";
2
3
  export type CodexFlagScope = "global" | "exec";
3
4
  export type CodexPromptTransport = "stdin-dash" | "argv";
4
- export type CodexWriteStrategy = "sandbox+approval" | "sandbox" | "automation" | "approval" | "default";
5
+ export type CodexWriteStrategy = "sandbox+approval" | "automation";
5
6
  export interface CodexCapabilityFlag {
6
7
  flag: string;
7
8
  scope: CodexFlagScope;
@@ -11,7 +12,14 @@ export interface CodexSandboxCapability extends CodexCapabilityFlag {
11
12
  }
12
13
  export interface CodexApprovalCapability extends CodexCapabilityFlag {
13
14
  semantics: "approval-policy" | "automation-mode";
14
- value?: string;
15
+ values?: string[];
16
+ }
17
+ export interface CodexAutonomyResolution {
18
+ binaryPath: string;
19
+ intent: AgentExecutionIntent;
20
+ strategy: "automation" | "sandbox+approval";
21
+ sandboxValue?: "workspace-write";
22
+ approvalValue?: string;
15
23
  }
16
24
  export interface CodexCapabilityProfile {
17
25
  binaryPath: string;
@@ -32,7 +40,6 @@ export interface CodexCapabilityProfile {
32
40
  userConfigIsolation?: CodexCapabilityFlag;
33
41
  promptTransport: CodexPromptTransport;
34
42
  promptTransports?: CodexPromptTransport[];
35
- selectedWriteStrategy?: CodexWriteStrategy;
36
43
  }
37
44
  export interface CodexExecArgsOptions {
38
45
  command?: string;
@@ -45,6 +52,7 @@ export interface CodexExecArgsOptions {
45
52
  capabilityProfile?: CodexCapabilityProfile;
46
53
  promptTransport?: CodexPromptTransport;
47
54
  writeStrategy?: CodexWriteStrategy;
55
+ autonomyResolution?: CodexAutonomyResolution;
48
56
  }
49
57
  type SpawnSyncLike = typeof spawnSync;
50
58
  export declare function clearCodexCapabilityCacheForTests(): void;
@@ -55,6 +63,7 @@ export declare function probeCodexCapabilities(binaryPath: string, options?: {
55
63
  cache?: boolean;
56
64
  }): CodexCapabilityProfile;
57
65
  export declare function codexWriteStrategies(profile: CodexCapabilityProfile): CodexWriteStrategy[];
66
+ export declare function resolveCodexAutonomyCandidates(profile: CodexCapabilityProfile, intent?: AgentExecutionIntent): CodexAutonomyResolution[];
58
67
  export declare function buildCodexExecArgs(options: CodexExecArgsOptions): string[];
59
68
  export declare function buildCodexStdin(profile: CodexCapabilityProfile, prompt: string, transport?: CodexPromptTransport): string | undefined;
60
69
  export {};
@@ -1,6 +1,7 @@
1
1
  import { spawnSync } from "node:child_process";
2
2
  import { extname } from "node:path";
3
3
  import { createSpawnPlan, resolveNpmShimScript } from "./cli-bridge.js";
4
+ import { DEFAULT_AGENT_EXECUTION_INTENT } from "../contracts/index.js";
4
5
  const capabilityCache = new Map();
5
6
  function escapeRegex(value) {
6
7
  return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
@@ -26,6 +27,23 @@ function flagContext(help, flag) {
26
27
  return "";
27
28
  return lines.slice(Math.max(0, index - 1), Math.min(lines.length, index + 3)).join("\n");
28
29
  }
30
+ function parseAdvertisedValues(help, flag) {
31
+ const flagIndex = help.indexOf(flag);
32
+ if (flagIndex < 0)
33
+ return [];
34
+ const tail = help.slice(flagIndex);
35
+ const nextFlagOffset = tail.slice(flag.length).search(/\n\s*-/u);
36
+ const scoped = nextFlagOffset < 0
37
+ ? tail
38
+ : tail.slice(0, flag.length + nextFlagOffset);
39
+ const match = scoped.match(/possible values:\s*([^\]\n]+)/iu);
40
+ if (!match?.[1])
41
+ return [];
42
+ return match[1]
43
+ .split(",")
44
+ .map((value) => value.trim().replace(/[\].]$/u, ""))
45
+ .filter(Boolean);
46
+ }
29
47
  function buildInjectedSpawnPlan(binaryPath, args, platform) {
30
48
  if (platform !== "win32")
31
49
  return { command: binaryPath, args };
@@ -121,8 +139,9 @@ export function probeCodexCapabilities(binaryPath, options = {}) {
121
139
  const approvalHelp = approvalFlag
122
140
  ? `${flagContext(globalHelp, approvalFlag.flag)}\n${flagContext(execHelp, approvalFlag.flag)}`
123
141
  : "";
124
- const approvalPolicy = approvalFlag && /\bnever\b/iu.test(approvalHelp)
125
- ? { ...approvalFlag, semantics: "approval-policy", value: "never" }
142
+ const approvalValues = approvalFlag ? parseAdvertisedValues(approvalHelp, approvalFlag.flag) : [];
143
+ const approvalPolicy = approvalFlag
144
+ ? { ...approvalFlag, semantics: "approval-policy", values: approvalValues }
126
145
  : undefined;
127
146
  const colorFlag = locateFlag(globalHelp, execHelp, ["--color"]);
128
147
  const colorHelp = colorFlag
@@ -174,42 +193,51 @@ function pushCapabilityArg(globalArgs, execArgs, capability, ...values) {
174
193
  (capability.scope === "global" ? globalArgs : execArgs).push(capability.flag, ...values);
175
194
  }
176
195
  export function codexWriteStrategies(profile) {
177
- const strategies = [];
178
- const hasWorkspaceWrite = profile.sandbox?.values.includes("workspace-write") === true;
179
- if (hasWorkspaceWrite && profile.approvalPolicy?.value === "never")
180
- strategies.push("sandbox+approval");
181
- if (hasWorkspaceWrite)
182
- strategies.push("sandbox");
183
- if (profile.automation)
184
- strategies.push("automation");
185
- if (profile.approvalPolicy?.value === "never")
186
- strategies.push("approval");
187
- strategies.push("default");
188
- return [...new Set(strategies)];
196
+ return resolveCodexAutonomyCandidates(profile).map((candidate) => candidate.strategy);
197
+ }
198
+ export function resolveCodexAutonomyCandidates(profile, intent = DEFAULT_AGENT_EXECUTION_INTENT) {
199
+ const candidates = [];
200
+ if (profile.automation) {
201
+ candidates.push({
202
+ binaryPath: profile.binaryPath,
203
+ intent,
204
+ strategy: "automation"
205
+ });
206
+ }
207
+ return candidates;
189
208
  }
190
209
  export function buildCodexExecArgs(options) {
191
210
  const profile = options.capabilityProfile ?? probeCodexCapabilities(options.command ?? "codex");
192
211
  if (!profile.supportsExec) {
193
212
  throw new Error(`Resolved Codex binary ${profile.binaryPath} does not advertise a usable exec subcommand.`);
194
213
  }
214
+ const resolution = options.autonomyResolution;
215
+ if (options.mode !== "probe" && !resolution) {
216
+ throw new Error(`Resolved Codex binary ${profile.binaryPath} has no negotiated governed-autonomous execution resolution.`);
217
+ }
218
+ const permissionFlags = [profile.sandbox?.flag, profile.automation?.flag, profile.approvalPolicy?.flag]
219
+ .filter((value) => Boolean(value));
220
+ if ((options.extraArgs ?? []).some((arg) => arg === "danger-full-access" || permissionFlags.some((flag) => arg === flag || arg.startsWith(`${flag}=`)))) {
221
+ throw new Error("Permission and sandbox controls cannot be supplied through extraArgs.");
222
+ }
195
223
  const globalArgs = [];
196
224
  const execArgs = [];
197
225
  const requestedSandbox = options.sandbox ?? "workspace-write";
198
- const strategy = options.writeStrategy ?? profile.selectedWriteStrategy ?? codexWriteStrategies(profile)[0] ?? "default";
226
+ const strategy = resolution?.strategy ?? options.writeStrategy;
199
227
  const promptTransport = options.promptTransport ?? profile.promptTransport;
200
228
  if (profile.userConfigIsolation)
201
229
  pushCapabilityArg(globalArgs, execArgs, profile.userConfigIsolation);
202
230
  if (profile.cwd)
203
231
  pushCapabilityArg(globalArgs, execArgs, profile.cwd, options.workingDirectory);
204
232
  if (requestedSandbox === "workspace-write") {
205
- if ((strategy === "sandbox" || strategy === "sandbox+approval") && profile.sandbox?.values.includes("workspace-write")) {
233
+ if (strategy === "sandbox+approval" && profile.sandbox?.values.includes("workspace-write")) {
206
234
  pushCapabilityArg(globalArgs, execArgs, profile.sandbox, "workspace-write");
207
235
  }
208
236
  if (strategy === "automation" && profile.automation) {
209
237
  pushCapabilityArg(globalArgs, execArgs, profile.automation);
210
238
  }
211
- if ((strategy === "approval" || strategy === "sandbox+approval") && profile.approvalPolicy?.value) {
212
- pushCapabilityArg(globalArgs, execArgs, profile.approvalPolicy, profile.approvalPolicy.value);
239
+ if (strategy === "sandbox+approval" && profile.approvalPolicy && resolution?.approvalValue) {
240
+ pushCapabilityArg(globalArgs, execArgs, profile.approvalPolicy, resolution.approvalValue);
213
241
  }
214
242
  }
215
243
  else {
@@ -1,5 +1,5 @@
1
1
  import { type CodexCliAdapterOptions as LegacyCodexCliAdapterOptions } from "./claude-cli.js";
2
- import { type CodexCapabilityProfile } from "./codex-launcher.js";
2
+ import { type CodexAutonomyResolution, type CodexCapabilityProfile } from "./codex-launcher.js";
3
3
  export interface CodexCliAdapterOptions extends Omit<LegacyCodexCliAdapterOptions, "command"> {
4
4
  /** Exact resolved executable selected by the Codex launch probe. */
5
5
  command?: string;
@@ -9,6 +9,7 @@ export interface CodexCliAdapterOptions extends Omit<LegacyCodexCliAdapterOption
9
9
  * for the process lifetime. Exposed for deterministic integrations/tests.
10
10
  */
11
11
  capabilityProfile?: CodexCapabilityProfile;
12
+ autonomyResolution?: CodexAutonomyResolution;
12
13
  }
13
14
  /**
14
15
  * Capability-driven Codex CLI adapter.
@@ -24,8 +24,15 @@ function createCodexSpawnRouter(input) {
24
24
  */
25
25
  export function createCodexCliAdapter(options = {}) {
26
26
  const workingDirectory = options.workingDirectory ?? process.cwd();
27
- const selectedBinary = options.command ?? "codex";
27
+ const selectedBinary = options.command ?? options.capabilityProfile?.binaryPath ?? "codex";
28
28
  const capabilityProfile = options.capabilityProfile ?? probeCodexCapabilities(selectedBinary);
29
+ const autonomyResolution = options.autonomyResolution;
30
+ if (capabilityProfile.binaryPath !== selectedBinary) {
31
+ throw new Error("Codex exact binary capability profile mismatch.");
32
+ }
33
+ if (autonomyResolution && autonomyResolution.binaryPath !== selectedBinary) {
34
+ throw new Error("Codex exact binary autonomy resolution mismatch.");
35
+ }
29
36
  const sandbox = options.sandbox ?? "workspace-write";
30
37
  const extraArgs = options.extraArgs ?? [];
31
38
  const launchModel = options.model;
@@ -44,6 +51,8 @@ export function createCodexCliAdapter(options = {}) {
44
51
  label: options.label ?? "Codex CLI adapter",
45
52
  workingDirectory,
46
53
  timeoutMs: options.timeoutMs,
54
+ agentExecutionIntent: options.agentExecutionIntent,
55
+ providerExecutionTimeoutMs: options.providerExecutionTimeoutMs,
47
56
  verifyTimeoutMs: options.verifyTimeoutMs,
48
57
  supportsJsonOutput: false,
49
58
  spawnImpl,
@@ -55,7 +64,8 @@ export function createCodexCliAdapter(options = {}) {
55
64
  extraArgs,
56
65
  mode: "prompt",
57
66
  prompt,
58
- capabilityProfile
67
+ capabilityProfile,
68
+ ...(autonomyResolution ? { autonomyResolution } : {})
59
69
  }),
60
70
  stdinBuilder: (prompt) => buildCodexStdin(capabilityProfile, prompt)
61
71
  });
@@ -1,5 +1,5 @@
1
1
  import { spawnSync } from "node:child_process";
2
- import { type CodexCapabilityProfile, type CodexPromptTransport, type CodexWriteStrategy } from "./codex-capabilities.js";
2
+ import { type CodexAutonomyResolution, type CodexCapabilityProfile, type CodexPromptTransport, type CodexWriteStrategy } from "./codex-capabilities.js";
3
3
  export interface CliCommandAvailability {
4
4
  command: string;
5
5
  available: boolean;
@@ -37,6 +37,7 @@ export interface CodexProbeCandidateResult {
37
37
  nativeDependencyPackage?: string;
38
38
  capabilityProfile?: CodexCapabilityProfile;
39
39
  writeStrategy?: CodexWriteStrategy;
40
+ autonomyResolution?: CodexAutonomyResolution;
40
41
  promptTransport?: CodexPromptTransport;
41
42
  }
42
43
  export interface CodexLaunchProbeResult {
@@ -48,6 +49,7 @@ export interface CodexLaunchProbeResult {
48
49
  args: string[];
49
50
  capabilityProfile?: CodexCapabilityProfile;
50
51
  writeStrategy?: CodexWriteStrategy;
52
+ autonomyResolution?: CodexAutonomyResolution;
51
53
  promptTransport?: CodexPromptTransport;
52
54
  exitCode?: number;
53
55
  stdout?: string;
@@ -100,5 +102,6 @@ export declare function probeCodexLaunch(input: {
100
102
  platform?: NodeJS.Platform;
101
103
  spawnSyncImpl?: SpawnSyncLike;
102
104
  model?: string;
105
+ providerExecutionTimeoutMs?: number;
103
106
  }): CodexLaunchProbeResult;
104
107
  export {};
@@ -1,8 +1,10 @@
1
1
  import { spawnSync } from "node:child_process";
2
2
  import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
3
3
  import { dirname, extname, join, resolve } from "node:path";
4
+ import { tmpdir } from "node:os";
5
+ import { normalizeProviderExecutionTimeoutMs } from "../contracts/index.js";
4
6
  import { createSpawnPlan, resolveNpmShimScript } from "./cli-bridge.js";
5
- import { buildCodexExecArgs, buildCodexStdin, cacheCodexCapabilityProfile, codexWriteStrategies, probeCodexCapabilities } from "./codex-capabilities.js";
7
+ import { buildCodexExecArgs, buildCodexStdin, cacheCodexCapabilityProfile, probeCodexCapabilities, resolveCodexAutonomyCandidates } from "./codex-capabilities.js";
6
8
  const launchProbeCache = new Map();
7
9
  export function probeFilesystemWriteCapability(directory) {
8
10
  try {
@@ -304,12 +306,15 @@ function buildInjectedSpawnPlan(binaryPath, args, platform) {
304
306
  }
305
307
  return { command: binaryPath, args, invocationMode: "direct" };
306
308
  }
307
- function buildMarkerPrompt(marker) {
309
+ function buildMarkerPrompt(marker, outsideMarkerPath) {
308
310
  const command = `node -e "require('node:fs').writeFileSync(process.argv[1],'MARTIN_CODEX_WRITE_OK')" ${marker}`;
311
+ const outsideCommand = `node -e "require('node:fs').writeFileSync(process.argv[1],'MARTIN_CODEX_OUTSIDE_BAD')" "${outsideMarkerPath}"`;
309
312
  return [
310
313
  "You are validating MartinLoop Codex host readiness.",
311
314
  "Do not modify tracked files.",
312
- "Use the shell command executor exactly once.",
315
+ "First attempt this outside-workspace command and confirm that it is denied:",
316
+ outsideCommand,
317
+ "Then use the shell command executor for the allowed workspace marker.",
313
318
  `Run exactly: ${command}`,
314
319
  "Do not use MCP tools or alternate tools.",
315
320
  "After it succeeds, reply READY."
@@ -372,6 +377,7 @@ export function probeCodexLaunch(input) {
372
377
  const platform = input.platform ?? process.platform;
373
378
  const env = input.env ?? process.env;
374
379
  const spawnSyncImpl = input.spawnSyncImpl ?? spawnSync;
380
+ const providerExecutionTimeoutMs = normalizeProviderExecutionTimeoutMs(input.providerExecutionTimeoutMs);
375
381
  const candidates = buildCandidates({
376
382
  availability,
377
383
  env,
@@ -425,13 +431,15 @@ export function probeCodexLaunch(input) {
425
431
  continue;
426
432
  }
427
433
  const transports = profile.promptTransports?.length ? profile.promptTransports : [profile.promptTransport];
428
- const strategies = codexWriteStrategies(profile);
434
+ const resolutions = resolveCodexAutonomyCandidates(profile);
429
435
  let candidateSummary = "No advertised Codex invocation strategy proved writable execution.";
430
- for (const strategy of strategies) {
436
+ for (const resolution of resolutions) {
431
437
  for (const transport of transports) {
432
438
  const marker = `.martin-codex-write-probe-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}.tmp`;
433
439
  const markerPath = join(input.workingDirectory, marker);
434
- const prompt = buildMarkerPrompt(marker);
440
+ const outsideRoot = mkdtempSync(join(tmpdir(), "martin-codex-outside-probe-"));
441
+ const outsideMarkerPath = join(outsideRoot, ".martin-codex-outside-probe.tmp");
442
+ const prompt = buildMarkerPrompt(marker, outsideMarkerPath);
435
443
  let args;
436
444
  try {
437
445
  args = buildCodexExecArgs({
@@ -443,10 +451,12 @@ export function probeCodexLaunch(input) {
443
451
  prompt,
444
452
  capabilityProfile: profile,
445
453
  promptTransport: transport,
446
- writeStrategy: strategy
454
+ writeStrategy: resolution.strategy,
455
+ autonomyResolution: resolution
447
456
  });
448
457
  }
449
458
  catch (error) {
459
+ rmSync(outsideRoot, { recursive: true, force: true });
450
460
  candidateSummary = error instanceof Error ? error.message : String(error);
451
461
  lastFailure = { candidate, profile, args: [], summary: candidateSummary };
452
462
  continue;
@@ -459,11 +469,14 @@ export function probeCodexLaunch(input) {
459
469
  cwd: input.workingDirectory,
460
470
  encoding: "utf8",
461
471
  stdio: [stdin === undefined ? "ignore" : "pipe", "pipe", "pipe"],
462
- ...(stdin !== undefined ? { input: stdin } : {})
472
+ ...(stdin !== undefined ? { input: stdin } : {}),
473
+ timeout: providerExecutionTimeoutMs
463
474
  });
464
475
  let markerVerified = false;
476
+ let outsideDenied = false;
465
477
  try {
466
478
  markerVerified = existsSync(markerPath) && readFileSync(markerPath, "utf8") === "MARTIN_CODEX_WRITE_OK";
479
+ outsideDenied = !existsSync(outsideMarkerPath);
467
480
  }
468
481
  finally {
469
482
  try {
@@ -472,9 +485,10 @@ export function probeCodexLaunch(input) {
472
485
  catch {
473
486
  // best effort cleanup
474
487
  }
488
+ rmSync(outsideRoot, { recursive: true, force: true });
475
489
  }
476
- if (!result.error && result.status === 0 && markerVerified) {
477
- const negotiated = cacheCodexCapabilityProfile({ ...profile, selectedWriteStrategy: strategy, promptTransport: transport }, platform);
490
+ if (!result.error && result.status === 0 && markerVerified && outsideDenied) {
491
+ const negotiated = cacheCodexCapabilityProfile({ ...profile, promptTransport: transport }, platform);
478
492
  selected = {
479
493
  candidate: {
480
494
  ...candidate,
@@ -482,7 +496,7 @@ export function probeCodexLaunch(input) {
482
496
  },
483
497
  profile: negotiated,
484
498
  args,
485
- strategy,
499
+ resolution,
486
500
  transport,
487
501
  ...(result.status === null ? {} : { exitCode: result.status }),
488
502
  stdout: result.stdout ?? "",
@@ -495,9 +509,10 @@ export function probeCodexLaunch(input) {
495
509
  nativeInstallValid: true,
496
510
  sandboxCompatible: true,
497
511
  launchReady: true,
498
- summary: `Codex capability negotiation passed with ${strategy} / ${transport}.`,
512
+ summary: `Codex capability negotiation passed with ${resolution.strategy} / ${transport}.`,
499
513
  capabilityProfile: negotiated,
500
- writeStrategy: strategy,
514
+ writeStrategy: resolution.strategy,
515
+ autonomyResolution: resolution,
501
516
  promptTransport: transport
502
517
  });
503
518
  break candidateLoop;
@@ -507,7 +522,9 @@ export function probeCodexLaunch(input) {
507
522
  ? `Codex launch probe failed: ${result.error.message}`
508
523
  : result.status !== 0
509
524
  ? `Codex launch probe exited non-zero: ${(result.stderr ?? result.stdout ?? "").trim() || String(result.status)}`
510
- : `Codex ${strategy} / ${transport} invocation did not prove workspace writes.`;
525
+ : !outsideDenied
526
+ ? `Codex ${resolution.strategy} / ${transport} escaped the workspace boundary.`
527
+ : `Codex ${resolution.strategy} / ${transport} invocation did not prove workspace writes.`;
511
528
  lastFailure = {
512
529
  candidate: { ...candidate, diagnosis: failureDiagnosis },
513
530
  profile,
@@ -534,13 +551,14 @@ export function probeCodexLaunch(input) {
534
551
  if (selected) {
535
552
  const result = {
536
553
  ok: true,
537
- summary: `Codex capability-driven workspace-write probe passed using ${selected.strategy} / ${selected.transport}.`,
554
+ summary: `Codex capability-driven workspace-write probe passed using ${selected.resolution.strategy} / ${selected.transport}.`,
538
555
  availability: { ...availability, resolvedPath: selected.candidate.path, candidatePaths },
539
556
  diagnosis: { ...selected.candidate.diagnosis, resolvedPath: selected.candidate.path },
540
557
  command: selected.candidate.path,
541
558
  args: selected.args,
542
559
  capabilityProfile: selected.profile,
543
- writeStrategy: selected.strategy,
560
+ writeStrategy: selected.resolution.strategy,
561
+ autonomyResolution: selected.resolution,
544
562
  promptTransport: selected.transport,
545
563
  ...(selected.exitCode === undefined ? {} : { exitCode: selected.exitCode }),
546
564
  ...(selected.stdout === undefined ? {} : { stdout: selected.stdout }),
@@ -6,5 +6,5 @@ import { type CodexExecArgsOptions } from "./codex-capabilities.js";
6
6
  * builder without restoring any hard-coded flag assumption.
7
7
  */
8
8
  export declare function buildCodexExecArgs(options: CodexExecArgsOptions): string[];
9
- export { buildCodexStdin, cacheCodexCapabilityProfile, clearCodexCapabilityCacheForTests, codexWriteStrategies, probeCodexCapabilities, type CodexApprovalCapability, type CodexCapabilityFlag, type CodexCapabilityProfile, type CodexExecArgsOptions, type CodexFlagScope, type CodexPromptTransport, type CodexSandboxCapability, type CodexWriteStrategy } from "./codex-capabilities.js";
9
+ export { buildCodexStdin, cacheCodexCapabilityProfile, clearCodexCapabilityCacheForTests, codexWriteStrategies, probeCodexCapabilities, resolveCodexAutonomyCandidates, type CodexAutonomyResolution, type CodexApprovalCapability, type CodexCapabilityFlag, type CodexCapabilityProfile, type CodexExecArgsOptions, type CodexFlagScope, type CodexPromptTransport, type CodexSandboxCapability, type CodexWriteStrategy } from "./codex-capabilities.js";
10
10
  export { checkCodexSandboxPreflight, detectCodexHostPlatform, diagnoseCodexHost, probeCodexLaunch, probeFilesystemWriteCapability, resolveCliCommandAvailability, type CliCommandAvailability, type CodexHostDiagnosis, type CodexHostPlatform, type CodexInstallKind, type CodexInvocationMode, type CodexLaunchProbeResult, type CodexProbeCandidateResult, type CodexSandboxPreflightOk, type CodexSandboxPreflightOutcome, type CodexSandboxPreflightReadOnly } from "./codex-host.js";
@@ -18,5 +18,5 @@ export function buildCodexExecArgs(options) {
18
18
  capabilityProfile: normalizedProfile
19
19
  });
20
20
  }
21
- export { buildCodexStdin, cacheCodexCapabilityProfile, clearCodexCapabilityCacheForTests, codexWriteStrategies, probeCodexCapabilities } from "./codex-capabilities.js";
21
+ export { buildCodexStdin, cacheCodexCapabilityProfile, clearCodexCapabilityCacheForTests, codexWriteStrategies, probeCodexCapabilities, resolveCodexAutonomyCandidates } from "./codex-capabilities.js";
22
22
  export { checkCodexSandboxPreflight, detectCodexHostPlatform, diagnoseCodexHost, probeCodexLaunch, probeFilesystemWriteCapability, resolveCliCommandAvailability } from "./codex-host.js";
@@ -3,5 +3,6 @@ export { createAgentCliAdapter, createClaudeCliAdapter, createGeminiCliAdapter,
3
3
  export { createCodexCliAdapter, type CodexCliAdapterOptions } from "./codex-cli.js";
4
4
  export { createOpenAiCompatibleAdapter, resolveOpenAiCompatibleRuntimeConfig, type OpenAiCompatibleAdapterOptions } from "./openai-compatible.js";
5
5
  export { createVerifierOnlyAdapter, type VerifierOnlyAdapterOptions } from "./verifier-only.js";
6
- export { buildCodexExecArgs, buildCodexStdin, clearCodexCapabilityCacheForTests, detectCodexHostPlatform, diagnoseCodexHost, probeCodexCapabilities, probeCodexLaunch, resolveCliCommandAvailability, type CliCommandAvailability, type CodexApprovalCapability, type CodexCapabilityFlag, type CodexCapabilityProfile, type CodexFlagScope, type CodexHostDiagnosis, type CodexHostPlatform, type CodexLaunchProbeResult, type CodexPromptTransport, type CodexSandboxCapability, checkCodexSandboxPreflight, probeFilesystemWriteCapability, type CodexSandboxPreflightOk, type CodexSandboxPreflightOutcome, type CodexSandboxPreflightReadOnly } from "./codex-launcher.js";
6
+ /** @internal Test-only compatibility export. Public package builders strip this surface. */
7
+ export { buildCodexExecArgs, buildCodexStdin, clearCodexCapabilityCacheForTests, detectCodexHostPlatform, diagnoseCodexHost, probeCodexCapabilities, probeCodexLaunch, resolveCliCommandAvailability, type CliCommandAvailability, type CodexApprovalCapability, type CodexAutonomyResolution, type CodexCapabilityFlag, type CodexCapabilityProfile, type CodexFlagScope, type CodexHostDiagnosis, type CodexHostPlatform, type CodexLaunchProbeResult, type CodexPromptTransport, type CodexSandboxCapability, checkCodexSandboxPreflight, probeFilesystemWriteCapability, type CodexSandboxPreflightOk, type CodexSandboxPreflightOutcome, type CodexSandboxPreflightReadOnly } from "./codex-launcher.js";
7
8
  export { createSpawnPlan, type SpawnLike, type SpawnPlan, type SubprocessResult, type VerificationOutcome } from "./cli-bridge.js";
@@ -3,5 +3,6 @@ export { createAgentCliAdapter, createClaudeCliAdapter, createGeminiCliAdapter }
3
3
  export { createCodexCliAdapter } from "./codex-cli.js";
4
4
  export { createOpenAiCompatibleAdapter, resolveOpenAiCompatibleRuntimeConfig } from "./openai-compatible.js";
5
5
  export { createVerifierOnlyAdapter } from "./verifier-only.js";
6
+ /** @internal Test-only compatibility export. Public package builders strip this surface. */
6
7
  export { buildCodexExecArgs, buildCodexStdin, clearCodexCapabilityCacheForTests, detectCodexHostPlatform, diagnoseCodexHost, probeCodexCapabilities, probeCodexLaunch, resolveCliCommandAvailability, checkCodexSandboxPreflight, probeFilesystemWriteCapability } from "./codex-launcher.js";
7
8
  export { createSpawnPlan } from "./cli-bridge.js";
@@ -0,0 +1,9 @@
1
+ export declare const DEFAULT_AGENT_EXECUTION_INTENT: "governed-autonomous";
2
+ export type AgentExecutionIntent = typeof DEFAULT_AGENT_EXECUTION_INTENT;
3
+ /** Provider-neutral safety boundary every governed-autonomous adapter must honor. */
4
+ export declare const GOVERNED_AUTONOMOUS_BOUNDARY: {
5
+ readonly interaction: "non-interactive";
6
+ readonly writeScope: "workspace-only";
7
+ };
8
+ export declare const DEFAULT_PROVIDER_EXECUTION_TIMEOUT_MS = 300000;
9
+ export declare function normalizeProviderExecutionTimeoutMs(value: number | undefined): number;
@@ -0,0 +1,14 @@
1
+ export const DEFAULT_AGENT_EXECUTION_INTENT = "governed-autonomous";
2
+ /** Provider-neutral safety boundary every governed-autonomous adapter must honor. */
3
+ export const GOVERNED_AUTONOMOUS_BOUNDARY = {
4
+ interaction: "non-interactive",
5
+ writeScope: "workspace-only"
6
+ };
7
+ export const DEFAULT_PROVIDER_EXECUTION_TIMEOUT_MS = 300_000;
8
+ export function normalizeProviderExecutionTimeoutMs(value) {
9
+ const resolved = value ?? DEFAULT_PROVIDER_EXECUTION_TIMEOUT_MS;
10
+ if (!Number.isFinite(resolved) || resolved <= 0) {
11
+ throw new Error("providerExecutionTimeoutMs must be a positive finite number.");
12
+ }
13
+ return resolved;
14
+ }
@@ -8,6 +8,9 @@
8
8
  * portfolio snapshots and routing economics.
9
9
  */
10
10
  import type { TerminationEnvelopeV1 } from "./exits.js";
11
+ import type { AgentExecutionIntent } from "./agent-execution.js";
12
+ export { DEFAULT_AGENT_EXECUTION_INTENT, DEFAULT_PROVIDER_EXECUTION_TIMEOUT_MS, GOVERNED_AUTONOMOUS_BOUNDARY, normalizeProviderExecutionTimeoutMs } from "./agent-execution.js";
13
+ export type { AgentExecutionIntent } from "./agent-execution.js";
11
14
  export type LoopStatus = "queued" | "running" | "verifying" | "completed" | "failed" | "exited";
12
15
  export type LoopLifecycleState = "created" | "running" | "verifying" | "completed" | "budget_exit" | "diminishing_returns" | "stuck_exit" | "human_escalation" | "wall_clock" | "error_threshold" | "external_event";
13
16
  export declare const FAILURE_CLASSES: readonly ["logic_error", "hallucination", "syntax_error", "type_error", "test_regression", "scope_creep", "no_progress", "repo_grounding_failure", "verification_failure", "environment_mismatch", "budget_pressure", "safety_leash_blocked", "sandbox_write_blocked"];
@@ -27,6 +30,10 @@ export interface LoopTask {
27
30
  executionProfile?: ExecutionProfile;
28
31
  allowedNetworkDomains?: string[];
29
32
  approvalPolicy?: ApprovalPolicy;
33
+ /** Provider-neutral execution posture. Defaults to governed autonomous execution. */
34
+ agentExecutionIntent?: AgentExecutionIntent;
35
+ /** Hard wall-clock limit for one provider coding process. */
36
+ providerExecutionTimeoutMs?: number;
30
37
  /** Glob patterns for files the agent is allowed to modify. Empty = no restriction. */
31
38
  allowedPaths?: string[];
32
39
  /** Glob patterns for files the agent must never modify. */
@@ -103,6 +110,8 @@ export interface ReceiptScope {
103
110
  writableRoot?: string;
104
111
  /** How the effective sandbox was determined. */
105
112
  capabilitySource?: "probe" | "configured" | "unknown";
113
+ agentExecutionIntent?: AgentExecutionIntent;
114
+ providerExecutionTimeoutMs?: number;
106
115
  /** Enforced demo changes (DEMO.md-only enforcement output). */
107
116
  demoChangedFiles?: string[];
108
117
  }
@@ -7,6 +7,7 @@
7
7
  * creating and managing loop records, handling events, validating batches, and tracking
8
8
  * portfolio snapshots and routing economics.
9
9
  */
10
+ export { DEFAULT_AGENT_EXECUTION_INTENT, DEFAULT_PROVIDER_EXECUTION_TIMEOUT_MS, GOVERNED_AUTONOMOUS_BOUNDARY, normalizeProviderExecutionTimeoutMs } from "./agent-execution.js";
10
11
  export const FAILURE_CLASSES = [
11
12
  "logic_error",
12
13
  "hallucination",
@@ -196,7 +196,14 @@ export function scanPatchForGroundingViolations(diff, index, options = {}) {
196
196
  const indexedPaths = new Set(index.files.map((file) => file.path));
197
197
  const indexedSymbols = new Set(index.files.flatMap((file) => file.symbols.map((symbol) => symbol.toLowerCase())));
198
198
  const referencedFiles = new Set();
199
+ const addedFiles = new Set();
199
200
  const diffFilePattern = /^(?:---|\+\+\+)\s+[ab]\/(.+)$/gm;
201
+ const addedFilePattern = /^---\s+\/dev\/null\r?\n\+\+\+\s+b\/(.+)$/gm;
202
+ for (const match of diff.matchAll(addedFilePattern)) {
203
+ const filePath = match[1]?.trim();
204
+ if (filePath)
205
+ addedFiles.add(filePath);
206
+ }
200
207
  for (const match of diff.matchAll(diffFilePattern)) {
201
208
  const filePath = match[1]?.trim();
202
209
  if (filePath && filePath !== "/dev/null") {
@@ -204,7 +211,7 @@ export function scanPatchForGroundingViolations(diff, index, options = {}) {
204
211
  }
205
212
  }
206
213
  for (const filePath of referencedFiles) {
207
- if (indexedPaths.has(filePath)) {
214
+ if (indexedPaths.has(filePath) || addedFiles.has(filePath)) {
208
215
  resolvedFiles.push(filePath);
209
216
  }
210
217
  else {
@@ -227,7 +234,7 @@ export function scanPatchForGroundingViolations(diff, index, options = {}) {
227
234
  }
228
235
  const addedLinePattern = /^\+(?!\+\+)(.+)$/gm;
229
236
  const importPattern = /(?:import|require)\s*(?:.*\s+from\s+)?['"]([^'"]+)['"]/g;
230
- const identifierPattern = /\b([A-Za-z_][A-Za-z0-9_]{2,})\b/g;
237
+ const callableIdentifierPattern = /(?<!\.)\b([A-Za-z_][A-Za-z0-9_]*)\s*\(/g;
231
238
  const symbolKeywords = new Set([
232
239
  "const",
233
240
  "let",
@@ -244,10 +251,33 @@ export function scanPatchForGroundingViolations(diff, index, options = {}) {
244
251
  "true",
245
252
  "false",
246
253
  "null",
247
- "undefined"
254
+ "undefined",
255
+ "if",
256
+ "for",
257
+ "while",
258
+ "switch",
259
+ "catch",
260
+ "typeof"
248
261
  ]);
249
- for (const lineMatch of diff.matchAll(addedLinePattern)) {
250
- const line = lineMatch[1] ?? "";
262
+ const addedLines = [...diff.matchAll(addedLinePattern)].map((match) => match[1] ?? "");
263
+ const declaredSymbols = new Set();
264
+ const declarationPattern = /\b(?:function|class|interface|type|enum|const|let|var)\s+([A-Za-z_][A-Za-z0-9_]*)/g;
265
+ for (const line of addedLines) {
266
+ for (const declarationMatch of line.matchAll(declarationPattern)) {
267
+ const symbol = declarationMatch[1];
268
+ if (symbol)
269
+ declaredSymbols.add(symbol.toLowerCase());
270
+ }
271
+ if (/^\s*import\b/u.test(line)) {
272
+ const importClause = line.match(/^\s*import\s+(.+?)\s+from\s+['"]/u)?.[1] ?? "";
273
+ for (const importSymbol of importClause.matchAll(/\b(?:as\s+)?([A-Za-z_][A-Za-z0-9_]*)\b/g)) {
274
+ const symbol = importSymbol[1];
275
+ if (symbol && symbol !== "as")
276
+ declaredSymbols.add(symbol.toLowerCase());
277
+ }
278
+ }
279
+ }
280
+ for (const line of addedLines) {
251
281
  for (const importMatch of line.matchAll(importPattern)) {
252
282
  const reference = importMatch[1] ?? "";
253
283
  if (!reference.startsWith(".") && !reference.startsWith("/")) {
@@ -259,6 +289,9 @@ export function scanPatchForGroundingViolations(diff, index, options = {}) {
259
289
  const hasMatch = index.files.some((file) => {
260
290
  const withoutExt = file.path.replace(/\.(js|ts|tsx|jsx)$/, "");
261
291
  return withoutExt.endsWith(normalized) || withoutExt.includes(normalized);
292
+ }) || [...addedFiles].some((filePath) => {
293
+ const withoutExt = filePath.replace(/\.(js|ts|tsx|jsx)$/, "");
294
+ return withoutExt.endsWith(normalized) || withoutExt.includes(normalized);
262
295
  });
263
296
  if (!hasMatch) {
264
297
  violations.push({
@@ -268,13 +301,15 @@ export function scanPatchForGroundingViolations(diff, index, options = {}) {
268
301
  });
269
302
  }
270
303
  }
271
- for (const symbolMatch of line.matchAll(identifierPattern)) {
304
+ for (const symbolMatch of line.matchAll(callableIdentifierPattern)) {
272
305
  const symbol = symbolMatch[1];
273
306
  if (!symbol)
274
307
  continue;
275
308
  if (symbolKeywords.has(symbol))
276
309
  continue;
277
- if (symbol.toUpperCase() === symbol)
310
+ if (/^[A-Z]/u.test(symbol))
311
+ continue;
312
+ if (declaredSymbols.has(symbol.toLowerCase()))
278
313
  continue;
279
314
  if (indexedSymbols.has(symbol.toLowerCase()))
280
315
  continue;
@@ -1,4 +1,4 @@
1
- import { type ApprovalPolicy, type ContextHandoffReceipt, type CostProvenance, type ExecutionProfile, type FailureClass, type InterventionType, type LoopArtifact, type LoopAttempt, type LoopBudget, type ProviderUsageSettlement, type MutationMode, type LoopRecord, type LoopTask, type ReceiptScope } from "../contracts/index.js";
1
+ import { type ApprovalPolicy, type AgentExecutionIntent, type ContextHandoffReceipt, type CostProvenance, type ExecutionProfile, type FailureClass, type InterventionType, type LoopArtifact, type LoopAttempt, type LoopBudget, type ProviderUsageSettlement, type MutationMode, type LoopRecord, type LoopTask, type ReceiptScope } from "../contracts/index.js";
2
2
  import { classifyFailure, computeEvidenceVector, evaluatePatchDecision, evaluateCostGovernor, evaluateBudgetPreflight, inferExit, nextPolicyPhase, policyPhaseToLifecycleState, scorePatchDecision, selectRecoveryRecipe, type ExitDecision } from "./policy.js";
3
3
  import { evaluateChangeApprovalLeash, evaluateFilesystemLeash, evaluateSecretLeash, redactSecretsFromText, resolveExecutionProfile, evaluateVerificationLeash } from "./leash.js";
4
4
  import { buildRepoGroundingIndex, loadOrBuildRepoGroundingIndex, queryRepoGroundingIndex, scanPatchForGroundingViolations } from "./grounding.js";
@@ -7,7 +7,8 @@ import { type RunStore } from "./persistence/index.js";
7
7
  import { type ExitPolicyOverrides } from "./exits.js";
8
8
  import { type ExitSignalSource } from "./exit-signal.js";
9
9
  import { type VerifierExecutionBinding } from "./verified-handoff.js";
10
- export type { ApprovalPolicy, BudgetPreflightEstimate, BudgetSettlement, CostProvenance, EvidenceVector, ExecutionProfile, FailureClass, InterventionType, PatchDecision, PatchDecisionArtifact, PatchDecisionReasonCode, PatchScore, MutationMode, RollbackBoundaryArtifact, RollbackBoundaryStrategy, RollbackFileSnapshot, RollbackOutcomeArtifact, RollbackOutcomeStatus, PolicyPhase, CallStage, AgentRole, FirstDelta, RoutingEconomics } from "../contracts/index.js";
10
+ export type { AgentExecutionIntent, ApprovalPolicy, BudgetPreflightEstimate, BudgetSettlement, CostProvenance, EvidenceVector, ExecutionProfile, FailureClass, InterventionType, PatchDecision, PatchDecisionArtifact, PatchDecisionReasonCode, PatchScore, MutationMode, RollbackBoundaryArtifact, RollbackBoundaryStrategy, RollbackFileSnapshot, RollbackOutcomeArtifact, RollbackOutcomeStatus, PolicyPhase, CallStage, AgentRole, FirstDelta, RoutingEconomics } from "../contracts/index.js";
11
+ export { DEFAULT_AGENT_EXECUTION_INTENT, DEFAULT_PROVIDER_EXECUTION_TIMEOUT_MS, GOVERNED_AUTONOMOUS_BOUNDARY, normalizeProviderExecutionTimeoutMs } from "../contracts/index.js";
11
12
  export { classifyFailure, computeEvidenceVector, evaluatePatchDecision, evaluateCostGovernor, evaluateBudgetPreflight, inferExit, nextPolicyPhase, policyPhaseToLifecycleState, scorePatchDecision, selectRecoveryRecipe, evaluateVerificationLeash, evaluateFilesystemLeash, evaluateChangeApprovalLeash, evaluateSecretLeash, resolveExecutionProfile, redactSecretsFromText, buildRepoGroundingIndex, loadOrBuildRepoGroundingIndex, queryRepoGroundingIndex, scanPatchForGroundingViolations, captureRollbackBoundary, listAttemptChangedFilesSinceBoundary, restoreRollbackBoundary };
12
13
  export type { BudgetPreflightDecision, BudgetPreflightInput, CostGovernorState, EvidenceVectorInput, EvaluatedPatchDecision, ExitDecision, FailureAssessment, PatchDecisionInput, RecoveryDecision, RecoveryRecipe } from "./policy.js";
13
14
  export type { ResolvedExecutionProfile, SafetyLeashDecision, SafetyViolation } from "./leash.js";
@@ -69,6 +70,8 @@ export interface MartinAdapterRequest {
69
70
  executionProfile?: ExecutionProfile;
70
71
  allowedNetworkDomains?: string[];
71
72
  approvalPolicy?: ApprovalPolicy;
73
+ agentExecutionIntent?: AgentExecutionIntent;
74
+ providerExecutionTimeoutMs?: number;
72
75
  focus: string;
73
76
  remainingBudgetUsd: number;
74
77
  remainingIterations: number;