@martinloop/mcp 0.8.8 → 0.8.10
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/server-validation.js +18 -0
- package/dist/server.js +10 -1
- package/dist/tools/run-loop.d.ts +2 -0
- package/dist/tools/run-loop.js +169 -71
- package/dist/vendor/adapters/process-supervisor.d.ts +12 -0
- package/dist/vendor/adapters/process-supervisor.js +40 -2
- package/dist/vendor/adapters/verifier-only.js +4 -0
- package/dist/vendor/core/index.d.ts +14 -1
- package/dist/vendor/core/index.js +1679 -1595
- package/dist/vendor/core/policy.js +11 -1
- package/dist/vendor/core/resume.d.ts +39 -0
- package/dist/vendor/core/resume.js +196 -0
- package/dist/vendor/core/verified-handoff.js +62 -2
- package/package.json +3 -7
- 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.10";
|
package/dist/package-version.js
CHANGED
|
@@ -145,6 +145,7 @@ function validateRunInput(args) {
|
|
|
145
145
|
"workingDirectory",
|
|
146
146
|
"engine",
|
|
147
147
|
"model",
|
|
148
|
+
"fallbackEngines",
|
|
148
149
|
"context",
|
|
149
150
|
"policyPack",
|
|
150
151
|
"maxUsd",
|
|
@@ -169,6 +170,7 @@ function validateRunInput(args) {
|
|
|
169
170
|
: {}),
|
|
170
171
|
...(engine ? { engine } : {}),
|
|
171
172
|
...optionalString(record.model, "model"),
|
|
173
|
+
...optionalEnumArrayAsObject(record.fallbackEngines, "fallbackEngines", MARTIN_ENGINE_VALUES),
|
|
172
174
|
...optionalString(record.context, "context"),
|
|
173
175
|
...optionalEnumAsObject(record.policyPack, "policyPack", [
|
|
174
176
|
"solo-founder",
|
|
@@ -643,3 +645,19 @@ function optionalEnumAsObject(value, name, allowed) {
|
|
|
643
645
|
const enumValue = optionalEnum(value, name, allowed);
|
|
644
646
|
return enumValue ? { [name]: enumValue } : {};
|
|
645
647
|
}
|
|
648
|
+
function optionalEnumArrayAsObject(value, name, allowed) {
|
|
649
|
+
if (value === undefined)
|
|
650
|
+
return {};
|
|
651
|
+
if (!Array.isArray(value))
|
|
652
|
+
throw invalidArgumentsError(`Invalid ${name}.`);
|
|
653
|
+
const values = value.map((item) => {
|
|
654
|
+
if (typeof item !== "string" || !allowed.includes(item)) {
|
|
655
|
+
throw invalidArgumentsError(`Invalid ${name}.`);
|
|
656
|
+
}
|
|
657
|
+
return item;
|
|
658
|
+
});
|
|
659
|
+
if (new Set(values).size !== values.length) {
|
|
660
|
+
throw invalidArgumentsError(`Invalid ${name}: duplicate engines are not allowed.`);
|
|
661
|
+
}
|
|
662
|
+
return { [name]: values };
|
|
663
|
+
}
|
package/dist/server.js
CHANGED
|
@@ -935,7 +935,16 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
935
935
|
},
|
|
936
936
|
model: {
|
|
937
937
|
type: "string",
|
|
938
|
-
description: "Optional model override passed to the
|
|
938
|
+
description: "Optional model override passed to the primary provider."
|
|
939
|
+
},
|
|
940
|
+
fallbackEngines: {
|
|
941
|
+
type: "array",
|
|
942
|
+
uniqueItems: true,
|
|
943
|
+
items: {
|
|
944
|
+
type: "string",
|
|
945
|
+
enum: [...MARTIN_ENGINE_VALUES]
|
|
946
|
+
},
|
|
947
|
+
description: "Explicit ordered provider fallbacks authorized for this run. No provider is added implicitly."
|
|
939
948
|
},
|
|
940
949
|
maxUsd: {
|
|
941
950
|
type: "number",
|
package/dist/tools/run-loop.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export interface RunLoopInput {
|
|
|
7
7
|
workingDirectory?: string;
|
|
8
8
|
engine?: MartinEngine;
|
|
9
9
|
model?: string;
|
|
10
|
+
/** Explicit ordered provider fallbacks authorized by the caller. */
|
|
11
|
+
fallbackEngines?: MartinEngine[];
|
|
10
12
|
maxUsd?: number;
|
|
11
13
|
maxIterations?: number;
|
|
12
14
|
maxTokens?: number;
|
package/dist/tools/run-loop.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createClaudeCliAdapter, createCodexCliAdapter, createGeminiCliAdapter, probeCodexLaunch, resolveCliCommandAvailability, createVerifierOnlyAdapter } from "../vendor/adapters/index.js";
|
|
1
|
+
import { createClaudeCliAdapter, createCodexCliAdapter, createGeminiCliAdapter, createOpenAiCompatibleAdapter, resolveOpenAiCompatibleRuntimeConfig, probeCodexLaunch, resolveCliCommandAvailability, createVerifierOnlyAdapter } from "../vendor/adapters/index.js";
|
|
2
2
|
import { createFileRunStore, evaluateCostGovernor, resolveRunsRoot, runMartin } from "../vendor/core/index.js";
|
|
3
3
|
import { normalizeSafePathPatterns, resolveSafeRepoRoot } from "../server-validation.js";
|
|
4
4
|
import { MartinToolError } from "./tool-errors.js";
|
|
@@ -12,10 +12,131 @@ export function __setProofModeVerifierSpawnImplForTests(spawnImpl) {
|
|
|
12
12
|
export function __setRunStoreOverrideForTests(store) {
|
|
13
13
|
runStoreOverrideForTests = store;
|
|
14
14
|
}
|
|
15
|
+
function assertAuthorizedFallbackEngines(primary, fallbacks) {
|
|
16
|
+
const seen = new Set();
|
|
17
|
+
for (const fallback of fallbacks) {
|
|
18
|
+
if (fallback === primary) {
|
|
19
|
+
throw new MartinToolError("invalid_arguments", "Fallback chain cannot repeat the primary engine.", {
|
|
20
|
+
category: "invalid_input",
|
|
21
|
+
retryable: false
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
if (seen.has(fallback)) {
|
|
25
|
+
throw new MartinToolError("invalid_arguments", `Duplicate fallback engine '${fallback}'.`, {
|
|
26
|
+
category: "invalid_input",
|
|
27
|
+
retryable: false
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
seen.add(fallback);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function ensureLiveEngineReady(engine, workingDirectory, providerExecutionTimeoutMs) {
|
|
34
|
+
if (engine === "openai") {
|
|
35
|
+
const config = resolveOpenAiCompatibleRuntimeConfig();
|
|
36
|
+
const localEndpoint = /^(?:https?:\/\/)?(?:localhost|127\.0\.0\.1|\[::1\])(?::|\/|$)/iu.test(config.baseUrl);
|
|
37
|
+
if (!config.model) {
|
|
38
|
+
throw new MartinToolError("engine_unavailable", "Authorized OpenAI-compatible fallback has no configured model.", {
|
|
39
|
+
category: "environment",
|
|
40
|
+
suggestion: "Set MARTIN_OPENAI_MODEL before authorizing the OpenAI fallback.",
|
|
41
|
+
retryable: false
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
if (!config.apiKeyConfigured && !localEndpoint) {
|
|
45
|
+
throw new MartinToolError("engine_unavailable", "Authorized OpenAI-compatible fallback is not authenticated.", {
|
|
46
|
+
category: "environment",
|
|
47
|
+
suggestion: "Set MARTIN_OPENAI_API_KEY, or use an explicitly local OpenAI-compatible endpoint.",
|
|
48
|
+
retryable: false
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
if (engine === "codex") {
|
|
54
|
+
const availability = resolveCliCommandAvailability("codex");
|
|
55
|
+
if (!availability.available) {
|
|
56
|
+
throw new MartinToolError("engine_unavailable", "Engine 'codex' is not available on PATH.", {
|
|
57
|
+
category: "environment",
|
|
58
|
+
suggestion: "Install/authenticate Codex before authorizing it for governed execution.",
|
|
59
|
+
retryable: false
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
const probe = probeCodexLaunch({
|
|
63
|
+
workingDirectory,
|
|
64
|
+
availability,
|
|
65
|
+
...(providerExecutionTimeoutMs !== undefined ? { providerExecutionTimeoutMs } : {})
|
|
66
|
+
});
|
|
67
|
+
if (!probe.ok) {
|
|
68
|
+
throw new MartinToolError("engine_unavailable", probe.summary, {
|
|
69
|
+
category: "environment",
|
|
70
|
+
suggestion: "Run martin_doctor or martin_preflight with engine='codex' before retrying.",
|
|
71
|
+
retryable: false
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return probe;
|
|
75
|
+
}
|
|
76
|
+
const availability = getEngineAvailability(engine);
|
|
77
|
+
if (!availability.available) {
|
|
78
|
+
throw new MartinToolError("engine_unavailable", `Engine '${engine}' is not available on PATH.`, {
|
|
79
|
+
category: "environment",
|
|
80
|
+
suggestion: "Install/authenticate every provider explicitly listed in the fallback chain.",
|
|
81
|
+
retryable: false
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
function createLiveEngineAdapter(input) {
|
|
87
|
+
if (input.engine === "codex") {
|
|
88
|
+
return createCodexCliAdapter({
|
|
89
|
+
workingDirectory: input.workingDirectory,
|
|
90
|
+
...(input.verifyTimeoutMs !== undefined ? { verifyTimeoutMs: input.verifyTimeoutMs } : {}),
|
|
91
|
+
...(input.providerExecutionTimeoutMs !== undefined
|
|
92
|
+
? { providerExecutionTimeoutMs: input.providerExecutionTimeoutMs }
|
|
93
|
+
: {}),
|
|
94
|
+
...(input.model ? { model: input.model } : {}),
|
|
95
|
+
...(input.codexProbe
|
|
96
|
+
? {
|
|
97
|
+
command: input.codexProbe.command,
|
|
98
|
+
capabilityProfile: input.codexProbe.capabilityProfile,
|
|
99
|
+
autonomyResolution: input.codexProbe.autonomyResolution
|
|
100
|
+
}
|
|
101
|
+
: {})
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
if (input.engine === "gemini") {
|
|
105
|
+
return createGeminiCliAdapter({
|
|
106
|
+
workingDirectory: input.workingDirectory,
|
|
107
|
+
...(input.verifyTimeoutMs !== undefined ? { verifyTimeoutMs: input.verifyTimeoutMs } : {}),
|
|
108
|
+
...(input.providerExecutionTimeoutMs !== undefined
|
|
109
|
+
? { providerExecutionTimeoutMs: input.providerExecutionTimeoutMs }
|
|
110
|
+
: {}),
|
|
111
|
+
...(input.model ? { model: input.model } : {})
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
if (input.engine === "openai") {
|
|
115
|
+
const config = resolveOpenAiCompatibleRuntimeConfig();
|
|
116
|
+
const resolvedModel = input.model ?? config.model;
|
|
117
|
+
return createOpenAiCompatibleAdapter({
|
|
118
|
+
baseUrl: config.baseUrl,
|
|
119
|
+
apiKey: config.apiKey,
|
|
120
|
+
...(resolvedModel ? { model: resolvedModel } : {}),
|
|
121
|
+
workingDirectory: input.workingDirectory,
|
|
122
|
+
...(input.verifyTimeoutMs !== undefined ? { verifyTimeoutMs: input.verifyTimeoutMs } : {})
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
return createClaudeCliAdapter({
|
|
126
|
+
workingDirectory: input.workingDirectory,
|
|
127
|
+
...(input.verifyTimeoutMs !== undefined ? { verifyTimeoutMs: input.verifyTimeoutMs } : {}),
|
|
128
|
+
...(input.providerExecutionTimeoutMs !== undefined
|
|
129
|
+
? { providerExecutionTimeoutMs: input.providerExecutionTimeoutMs }
|
|
130
|
+
: {}),
|
|
131
|
+
...(input.model ? { model: input.model } : {})
|
|
132
|
+
});
|
|
133
|
+
}
|
|
15
134
|
export async function runLoopTool(input) {
|
|
16
135
|
const workingDirectory = resolveSafeRepoRoot(input.workingDirectory);
|
|
17
136
|
const engine = input.engine ?? "claude";
|
|
18
137
|
const model = input.model;
|
|
138
|
+
const fallbackEngines = input.fallbackEngines ?? [];
|
|
139
|
+
assertAuthorizedFallbackEngines(engine, fallbackEngines);
|
|
19
140
|
const allowedPaths = normalizeSafePathPatterns(input.allowedPaths, "allowedPaths");
|
|
20
141
|
const deniedPaths = normalizeSafePathPatterns(input.deniedPaths, "deniedPaths");
|
|
21
142
|
const executionMode = resolveExecutionMode();
|
|
@@ -31,41 +152,11 @@ export async function runLoopTool(input) {
|
|
|
31
152
|
runsRoot
|
|
32
153
|
};
|
|
33
154
|
let codexProbeOverride;
|
|
155
|
+
const fallbackProbeOverrides = new Map();
|
|
34
156
|
if (executionMode.liveMode) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
throw new MartinToolError("engine_unavailable", `Engine '${engine}' is not available on PATH.`, {
|
|
39
|
-
category: "environment",
|
|
40
|
-
suggestion: "Install the requested CLI. MARTIN_LIVE=false is verification-only and cannot emit governed VERIFIED.",
|
|
41
|
-
retryable: false
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
const codexProbe = probeCodexLaunch({
|
|
45
|
-
workingDirectory,
|
|
46
|
-
availability: engineAvailability,
|
|
47
|
-
...(input.providerExecutionTimeoutMs !== undefined
|
|
48
|
-
? { providerExecutionTimeoutMs: input.providerExecutionTimeoutMs }
|
|
49
|
-
: {})
|
|
50
|
-
});
|
|
51
|
-
if (!codexProbe.ok) {
|
|
52
|
-
throw new MartinToolError("engine_unavailable", codexProbe.summary, {
|
|
53
|
-
category: "environment",
|
|
54
|
-
suggestion: "Run martin_doctor or martin_preflight with engine='codex' before retrying live governed work.",
|
|
55
|
-
retryable: false
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
codexProbeOverride = codexProbe;
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
const engineAvailability = getEngineAvailability(engine);
|
|
62
|
-
if (!engineAvailability.available) {
|
|
63
|
-
throw new MartinToolError("engine_unavailable", `Engine '${engine}' is not available on PATH.`, {
|
|
64
|
-
category: "environment",
|
|
65
|
-
suggestion: "Install the requested CLI. MARTIN_LIVE=false is verification-only and cannot emit governed VERIFIED.",
|
|
66
|
-
retryable: false
|
|
67
|
-
});
|
|
68
|
-
}
|
|
157
|
+
codexProbeOverride = ensureLiveEngineReady(engine, workingDirectory, input.providerExecutionTimeoutMs);
|
|
158
|
+
for (const fallbackEngine of fallbackEngines) {
|
|
159
|
+
fallbackProbeOverrides.set(fallbackEngine, ensureLiveEngineReady(fallbackEngine, workingDirectory, input.providerExecutionTimeoutMs));
|
|
69
160
|
}
|
|
70
161
|
}
|
|
71
162
|
const adapter = !executionMode.liveMode
|
|
@@ -75,39 +166,35 @@ export async function runLoopTool(input) {
|
|
|
75
166
|
...(input.verifyTimeoutMs !== undefined ? { verifyTimeoutMs: input.verifyTimeoutMs } : {}),
|
|
76
167
|
...(proofModeVerifierSpawnImpl ? { spawnImpl: proofModeVerifierSpawnImpl } : {})
|
|
77
168
|
})
|
|
78
|
-
:
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
:
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
? { providerExecutionTimeoutMs: input.providerExecutionTimeoutMs }
|
|
108
|
-
: {}),
|
|
109
|
-
...(model ? { model } : {})
|
|
110
|
-
});
|
|
169
|
+
: createLiveEngineAdapter({
|
|
170
|
+
engine,
|
|
171
|
+
workingDirectory,
|
|
172
|
+
...(model ? { model } : {}),
|
|
173
|
+
...(input.verifyTimeoutMs !== undefined ? { verifyTimeoutMs: input.verifyTimeoutMs } : {}),
|
|
174
|
+
...(input.providerExecutionTimeoutMs !== undefined
|
|
175
|
+
? { providerExecutionTimeoutMs: input.providerExecutionTimeoutMs }
|
|
176
|
+
: {}),
|
|
177
|
+
...(codexProbeOverride ? { codexProbe: codexProbeOverride } : {})
|
|
178
|
+
});
|
|
179
|
+
if (!executionMode.liveMode && fallbackEngines.length > 0) {
|
|
180
|
+
throw new MartinToolError("invalid_arguments", "fallbackEngines are available only in live governed mode.", {
|
|
181
|
+
category: "invalid_input",
|
|
182
|
+
retryable: false
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
const fallbackAdapters = executionMode.liveMode
|
|
186
|
+
? fallbackEngines.map((fallbackEngine) => createLiveEngineAdapter({
|
|
187
|
+
engine: fallbackEngine,
|
|
188
|
+
workingDirectory,
|
|
189
|
+
...(input.verifyTimeoutMs !== undefined ? { verifyTimeoutMs: input.verifyTimeoutMs } : {}),
|
|
190
|
+
...(input.providerExecutionTimeoutMs !== undefined
|
|
191
|
+
? { providerExecutionTimeoutMs: input.providerExecutionTimeoutMs }
|
|
192
|
+
: {}),
|
|
193
|
+
...(fallbackProbeOverrides.get(fallbackEngine)
|
|
194
|
+
? { codexProbe: fallbackProbeOverrides.get(fallbackEngine) }
|
|
195
|
+
: {})
|
|
196
|
+
}))
|
|
197
|
+
: [];
|
|
111
198
|
const partialBudget = {};
|
|
112
199
|
if (input.maxUsd !== undefined) {
|
|
113
200
|
partialBudget.maxUsd = input.maxUsd;
|
|
@@ -141,9 +228,14 @@ export async function runLoopTool(input) {
|
|
|
141
228
|
budget,
|
|
142
229
|
metadata: {
|
|
143
230
|
executionMode: evidenceExecutionMode,
|
|
144
|
-
governanceClaimEligible: evidenceExecutionMode === "governed" ? "true" : "false"
|
|
231
|
+
governanceClaimEligible: evidenceExecutionMode === "governed" ? "true" : "false",
|
|
232
|
+
...(executionMode.liveMode ? { authorizedPrimaryEngine: engine } : {}),
|
|
233
|
+
...(executionMode.liveMode && fallbackEngines.length > 0
|
|
234
|
+
? { authorizedFallbackEngines: fallbackEngines.join(",") }
|
|
235
|
+
: {})
|
|
145
236
|
},
|
|
146
|
-
adapter
|
|
237
|
+
adapter,
|
|
238
|
+
...(fallbackAdapters.length > 0 ? { fallbackAdapters } : {})
|
|
147
239
|
});
|
|
148
240
|
const lastAttempt = result.loop.attempts.at(-1);
|
|
149
241
|
// D5: verificationPassed must reflect authoritative verifier execution/binding,
|
|
@@ -153,6 +245,12 @@ export async function runLoopTool(input) {
|
|
|
153
245
|
const verificationPassed = lastAttempt !== undefined &&
|
|
154
246
|
result.decision.lifecycleState === "completed" &&
|
|
155
247
|
(lastAttempt.verifierScore ?? 0) >= 1;
|
|
248
|
+
const fallbackAdapterIndex = lastAttempt
|
|
249
|
+
? fallbackAdapters.findIndex((candidate) => candidate.adapterId === lastAttempt.adapterId)
|
|
250
|
+
: -1;
|
|
251
|
+
const effectiveEngine = fallbackAdapterIndex >= 0
|
|
252
|
+
? (fallbackEngines[fallbackAdapterIndex] ?? engine)
|
|
253
|
+
: engine;
|
|
156
254
|
const costState = evaluateCostGovernor({
|
|
157
255
|
budget: result.loop.budget,
|
|
158
256
|
cost: {
|
|
@@ -183,7 +281,7 @@ export async function runLoopTool(input) {
|
|
|
183
281
|
...(costState.remainingTokens !== undefined
|
|
184
282
|
? { remainingTokens: costState.remainingTokens }
|
|
185
283
|
: {}),
|
|
186
|
-
engine,
|
|
284
|
+
engine: effectiveEngine,
|
|
187
285
|
workingDirectory,
|
|
188
286
|
budget,
|
|
189
287
|
inspection: {
|
|
@@ -64,4 +64,16 @@ export declare function spawnSupervisedProcessForTest(command: string, args: rea
|
|
|
64
64
|
* @internal - deterministic pure classification for process-supervisor tests
|
|
65
65
|
*/
|
|
66
66
|
export declare function classifyPosixProcessGroupSnapshot(output: string, ownedGroupPid: number): "closed" | "alive" | "absent" | "invalid";
|
|
67
|
+
type WindowsProcessTreeRow = {
|
|
68
|
+
ProcessId: number;
|
|
69
|
+
ParentProcessId: number;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Classify descendants that remain visible after a failed termination attempt.
|
|
73
|
+
* Two point-in-time snapshots cannot prove closure across the interval because
|
|
74
|
+
* a descendant may be created and reparented between them. Absence is therefore
|
|
75
|
+
* indeterminate and must retain the original termination failure.
|
|
76
|
+
* @internal - deterministic pure classification for process-supervisor tests
|
|
77
|
+
*/
|
|
78
|
+
export declare function classifyWindowsDescendantPostcondition(initialRows: WindowsProcessTreeRow[], postTerminationRows: WindowsProcessTreeRow[], rootPid: number): "closed" | "alive" | "invalid";
|
|
67
79
|
export {};
|
|
@@ -456,6 +456,39 @@ function isErrno(error, code) {
|
|
|
456
456
|
return typeof error === "object" && error !== null && "code" in error
|
|
457
457
|
&& String(error.code) === code;
|
|
458
458
|
}
|
|
459
|
+
/**
|
|
460
|
+
* Classify descendants that remain visible after a failed termination attempt.
|
|
461
|
+
* Two point-in-time snapshots cannot prove closure across the interval because
|
|
462
|
+
* a descendant may be created and reparented between them. Absence is therefore
|
|
463
|
+
* indeterminate and must retain the original termination failure.
|
|
464
|
+
* @internal - deterministic pure classification for process-supervisor tests
|
|
465
|
+
*/
|
|
466
|
+
export function classifyWindowsDescendantPostcondition(initialRows, postTerminationRows, rootPid) {
|
|
467
|
+
if (!isOwnedPid(rootPid))
|
|
468
|
+
return "invalid";
|
|
469
|
+
const allRows = [...initialRows, ...postTerminationRows];
|
|
470
|
+
if (allRows.some((row) => !isOwnedPid(row.ProcessId) || !Number.isSafeInteger(row.ParentProcessId))) {
|
|
471
|
+
return "invalid";
|
|
472
|
+
}
|
|
473
|
+
const ownedLineage = new Set([rootPid]);
|
|
474
|
+
const expandLineage = (rows) => {
|
|
475
|
+
let changed = true;
|
|
476
|
+
while (changed) {
|
|
477
|
+
changed = false;
|
|
478
|
+
for (const row of rows) {
|
|
479
|
+
if (ownedLineage.has(row.ParentProcessId) && !ownedLineage.has(row.ProcessId)) {
|
|
480
|
+
ownedLineage.add(row.ProcessId);
|
|
481
|
+
changed = true;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
expandLineage(initialRows);
|
|
487
|
+
expandLineage(postTerminationRows);
|
|
488
|
+
return postTerminationRows.some((row) => ownedLineage.has(row.ProcessId))
|
|
489
|
+
? "alive"
|
|
490
|
+
: "invalid";
|
|
491
|
+
}
|
|
459
492
|
async function terminateWindowsDescendantsAfterRootExit(rootPid, graceMs, waitImpl) {
|
|
460
493
|
let rows;
|
|
461
494
|
try {
|
|
@@ -471,11 +504,16 @@ async function terminateWindowsDescendantsAfterRootExit(rootPid, graceMs, waitIm
|
|
|
471
504
|
childrenByParent.set(row.ParentProcessId, children);
|
|
472
505
|
}
|
|
473
506
|
const directChildren = childrenByParent.get(rootPid) ?? [];
|
|
507
|
+
let taskkillFailure;
|
|
474
508
|
for (const childPid of directChildren) {
|
|
475
509
|
const failure = await runTaskkill(childPid, spawnProcess, graceMs, waitImpl);
|
|
476
|
-
|
|
477
|
-
return failure;
|
|
510
|
+
taskkillFailure ??= failure;
|
|
478
511
|
}
|
|
512
|
+
// Once taskkill has failed, a later process-table snapshot cannot prove the
|
|
513
|
+
// complete descendant set closed. Return the bounded failure immediately;
|
|
514
|
+
// starting another PowerShell query here could otherwise stall cleanup.
|
|
515
|
+
if (taskkillFailure)
|
|
516
|
+
return taskkillFailure;
|
|
479
517
|
return { state: "closed" };
|
|
480
518
|
}
|
|
481
519
|
function readWindowsProcessTree() {
|
|
@@ -49,6 +49,10 @@ export function createVerifierOnlyAdapter(options = {}) {
|
|
|
49
49
|
providerId: "verifier",
|
|
50
50
|
model: "verify-only",
|
|
51
51
|
transport: "cli",
|
|
52
|
+
budgetPreflight: {
|
|
53
|
+
pricePerMTokenUsd: 0,
|
|
54
|
+
basis: "Verifier-only execution launches no model provider."
|
|
55
|
+
},
|
|
52
56
|
capabilities: createAdapterCapabilities({
|
|
53
57
|
usageSettlement: true,
|
|
54
58
|
diffArtifacts: true,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ApprovalPolicy, type AgentExecutionIntent, type ContextHandoffReceipt, type CostProvenance, type ExecutionProfile, type ExternalOutcomeEvidenceReference, 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 ExternalOutcomeEvidenceReference, type FailureClass, type InterventionType, type LoopArtifact, type LoopAttempt, type LoopBudget, type ProviderUsageSettlement, type MutationMode, type LoopRecord, type LoopTask, type ReceiptScope, type RollbackBoundaryArtifact } 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,6 +7,7 @@ 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
|
+
import { acquireRunExecutionLease, evaluateRunResumability, loadResumeCandidateBoundary, loadResumeVerifierFailure } from "./resume.js";
|
|
10
11
|
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
12
|
export { DEFAULT_AGENT_EXECUTION_INTENT, DEFAULT_PROVIDER_EXECUTION_TIMEOUT_MS, GOVERNED_AUTONOMOUS_BOUNDARY, normalizeProviderExecutionTimeoutMs } from "../contracts/index.js";
|
|
12
13
|
export { classifyFailure, computeEvidenceVector, evaluatePatchDecision, evaluateCostGovernor, evaluateBudgetPreflight, inferExit, nextPolicyPhase, policyPhaseToLifecycleState, scorePatchDecision, selectRecoveryRecipe, evaluateVerificationLeash, evaluateFilesystemLeash, evaluateChangeApprovalLeash, evaluateSecretLeash, resolveExecutionProfile, redactSecretsFromText, buildRepoGroundingIndex, loadOrBuildRepoGroundingIndex, queryRepoGroundingIndex, scanPatchForGroundingViolations, captureRollbackBoundary, listAttemptChangedFilesSinceBoundary, listIgnoredFileChangesSinceBoundary, restoreRollbackBoundary };
|
|
@@ -22,6 +23,7 @@ export type { LocalIdentityAuthority } from "./identity.js";
|
|
|
22
23
|
export { compileExecutionPolicy } from "./policy-compiler.js";
|
|
23
24
|
export { assessTrajectory, decideCircuitBreak } from "./trajectory.js";
|
|
24
25
|
export { calculateAvoidedUsd, calculateLoopAvoidedUsd } from "./savings.js";
|
|
26
|
+
export { acquireRunExecutionLease, evaluateRunResumability, loadResumeCandidateBoundary, loadResumeVerifierFailure };
|
|
25
27
|
export type { AvoidedUsdInput } from "./savings.js";
|
|
26
28
|
export { classifyRoute, evaluatePreworkBurnPolicy } from "./routing.js";
|
|
27
29
|
export type { RouteDecision, RouteClassificationInput } from "./routing.js";
|
|
@@ -204,6 +206,8 @@ export interface MartinAdapter {
|
|
|
204
206
|
budgetPreflight?: {
|
|
205
207
|
/** Estimated token floor required before the adapter can report first-turn usage. */
|
|
206
208
|
minimumViableTokens?: number;
|
|
209
|
+
/** Adapter-specific provider price used by the admission estimate. */
|
|
210
|
+
pricePerMTokenUsd?: number;
|
|
207
211
|
basis?: string;
|
|
208
212
|
};
|
|
209
213
|
capabilities?: {
|
|
@@ -257,6 +261,15 @@ export interface RunMartinInput {
|
|
|
257
261
|
idFactory?: (prefix: string) => string;
|
|
258
262
|
maxRecentAttempts?: number;
|
|
259
263
|
fallbackAdapters?: MartinAdapter[];
|
|
264
|
+
/**
|
|
265
|
+
* Continue an unfinished persisted run under the same loop ID and authority.
|
|
266
|
+
* runMartin acquires the durable execution lease before any store persistence.
|
|
267
|
+
*/
|
|
268
|
+
resume?: {
|
|
269
|
+
loop: LoopRecord;
|
|
270
|
+
preCandidateBoundary?: RollbackBoundaryArtifact;
|
|
271
|
+
lastVerifierFailure?: MartinAdapterRequest["context"]["lastVerifierFailure"];
|
|
272
|
+
};
|
|
260
273
|
receiptScope?: ReceiptScope;
|
|
261
274
|
/** Optional persistence store. When provided, runMartin writes artifacts on each lifecycle event. */
|
|
262
275
|
store?: RunStore;
|