Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
134 commits
Select commit Hold shift + click to select a range
ce9e29d
refactor(plugin): remove unused Deep scheduler history
mldangelo-oai Sep 12, 2026
35deeaf
Preserve distinct source remediations in published reports
mldangelo-oai Sep 12, 2026
5113f40
Reject unsupported Deep Scan recovery before ownership changes
mldangelo-oai Sep 12, 2026
88a811f
Read Deep Scan state from one SQLite snapshot
mldangelo-oai Sep 12, 2026
16a456e
Distinguish missing scan usage from reported zero
mldangelo-oai Sep 12, 2026
fca3a93
Honor worker checkpoint heads during result recovery
mldangelo-oai Sep 12, 2026
af83456
Preserve Deep Scan inputs and add execution history schema
mldangelo-oai Sep 12, 2026
99a6074
test(plugin): define shared audit acceptance contract
mldangelo-oai Sep 12, 2026
7f6f385
fix(plugin): keep Deep publication failures replayable
mldangelo-oai Sep 12, 2026
697d689
test: replay accepted sources across reducer batches
mldangelo-oai Sep 12, 2026
844b86d
test: compare migration history across coordinator claims
mldangelo-oai Sep 12, 2026
4683175
Fence Deep publication to the current coordinator and aggregate
mldangelo-oai Sep 12, 2026
81c3af5
test: align publication fixtures with supported recovery
mldangelo-oai Sep 12, 2026
de96630
Read selected Deep Scan finalization before recovery
mldangelo-oai Sep 12, 2026
0663681
refactor: share Codex session construction and stream reduction
mldangelo-oai Sep 12, 2026
0bba867
fix: bind Deep worker model selections to each scan
mldangelo-oai Sep 12, 2026
ce2fcc8
test: exercise Deep lifecycle against installed plugins
mldangelo-oai Sep 12, 2026
6ab9c7b
test: cover managed modes and follow-up child sessions
mldangelo-oai Sep 12, 2026
5f7bbe2
Reserve frozen checkpoint selections and guard requested workflows
mldangelo-oai Sep 12, 2026
69df53e
test(sdk): exercise installed Deep scan lifecycle and recovery
mldangelo-oai Sep 12, 2026
a6a2dd4
test(sdk): allow the shared session module in package checks
mldangelo-oai Sep 12, 2026
a548cea
Reserve original Deep Scan usage ownership metadata
mldangelo-oai Sep 12, 2026
85bcdbe
Freeze checkpoint selections for stopped scan replay
mldangelo-oai Sep 12, 2026
f3fa2e9
Preserve accepted source coverage during stopped recovery
mldangelo-oai Sep 12, 2026
d62c174
Restore original Deep Scan worker settings after coordinator replacement
mldangelo-oai Sep 12, 2026
4606bfd
Preserve accepted source coverage through deep scan publication
mldangelo-oai Sep 12, 2026
a6be9e5
Keep coverage accounting outside reducer model inputs
mldangelo-oai Sep 12, 2026
07e1b72
Isolate real coverage publication fixtures from module mocks
mldangelo-oai Sep 12, 2026
b45c1be
Preserve accepted reducer bytes during coverage recovery
mldangelo-oai Sep 12, 2026
b1fab6b
Gate persisted source coverage by workflow compatibility
mldangelo-oai Sep 12, 2026
b8bd35f
test: cover persisted worker settings and packaged config helper
mldangelo-oai Sep 12, 2026
6cb32c6
refactor: remove the pass-through Codex session wrapper
mldangelo-oai Sep 12, 2026
7501517
Verify versioned reducer snapshots and concurrent bindings
mldangelo-oai Sep 12, 2026
d15f27b
test: verify refreshed worker auth and resumed permission checks
mldangelo-oai Sep 12, 2026
c8b3bcc
refactor: share accepted audit attempts across managed scans
mldangelo-oai Sep 12, 2026
2c249d7
test(sdk): require shared audit module in installed package
mldangelo-oai Sep 12, 2026
6d3d75d
Keep restored worker executable and home paths consistent
mldangelo-oai Sep 12, 2026
75aa3d4
test(sdk): load shared session code in worker shutdown fixtures
mldangelo-oai Sep 12, 2026
1e90097
Recover omitted worker selections from the original native parent
mldangelo-oai Sep 12, 2026
adda049
Preserve receipt ownership for immutable discovery inputs
mldangelo-oai Sep 12, 2026
92ccb9c
Verify material fixes alongside recovered source coverage
mldangelo-oai Sep 12, 2026
ad90b17
fix: replay Deep publication from its selected accepted input
mldangelo-oai Sep 12, 2026
fdebdb5
fix: complete selected Deep results in the enclosing scan
mldangelo-oai Sep 12, 2026
538ad5c
test: cover original native selections and packaged completion helper
mldangelo-oai Sep 12, 2026
578b0fd
fix: preserve explicit cancellation during selected publication
mldangelo-oai Sep 12, 2026
77c6cba
Reconcile scan usage by owned execution and charged response
mldangelo-oai Sep 12, 2026
d7fd7b8
Reconcile native usage with durable execution attribution
mldangelo-oai Sep 12, 2026
04661c9
fix: preserve accepted attempts and compact merge receipts
mldangelo-oai Sep 12, 2026
4b903ba
Count native charged responses and retain usage uncertainty
mldangelo-oai Sep 12, 2026
064fafc
refactor: keep execution projections with scan usage
mldangelo-oai Sep 12, 2026
be95713
Fence publication to committed finalization input
mldangelo-oai Sep 12, 2026
2d61d8e
Recover and publish registered accepted result references
mldangelo-oai Sep 12, 2026
6e91224
Recover Deep Scan workers from accepted checkpoint references
mldangelo-oai Sep 12, 2026
7aeb8ae
fix: stop selected publication on cost limits and client close
mldangelo-oai Sep 12, 2026
9610d25
Test selected publication and stop recovery across process loss
mldangelo-oai Sep 12, 2026
d932ab6
Test cost completion and cancellation ordering with saved selections
mldangelo-oai Sep 12, 2026
f5b72e9
Verify selected terminal causes survive stop and recovery
mldangelo-oai Sep 12, 2026
d0a5e9c
Use committed input attempts in reducer execution context
mldangelo-oai Sep 12, 2026
7b62268
Check persisted reducer content separately from coverage projection
mldangelo-oai Sep 12, 2026
98d419c
Return legacy scan records in budget recovery fixtures
mldangelo-oai Sep 12, 2026
2d3baaa
test: attribute selected publication cost fixtures to native events
mldangelo-oai Sep 12, 2026
7909f93
test: cover managed scan and follow-up service tiers
mldangelo-oai Sep 12, 2026
dcdcd7e
fix: restore completed artifacts after follow-up cancellation
mldangelo-oai Sep 12, 2026
3931039
Cover recovery from recorded immutable inputs
mldangelo-oai Sep 12, 2026
4883ec3
Test original native turn selections and missing recovery history
mldangelo-oai Sep 12, 2026
9b3f703
Verify selected workflow ownership and observer compatibility
mldangelo-oai Sep 12, 2026
4495367
Verify original discovery context and deadline through restart
mldangelo-oai Sep 12, 2026
d3956fb
Recover worker selections from the recorded original owner turn
mldangelo-oai Sep 12, 2026
858da4c
Verify original settings and usage survive continuation handoff
mldangelo-oai Sep 12, 2026
dfe353e
test(sdk): preserve installed Deep result conversations
mldangelo-oai Sep 12, 2026
fd0e3df
test(plugin): account for immutable accepted checkpoint fixtures
mldangelo-oai Sep 12, 2026
8d05203
Recover legacy accepted inputs from frozen merge references
mldangelo-oai Sep 12, 2026
ea56607
Verify selected finalization preserves coverage and material fixes
mldangelo-oai Sep 12, 2026
d93ca5a
Recover missing original selections in existing execution snapshots
mldangelo-oai Sep 12, 2026
3a3bb34
Test selection commit loss before and after stopped replay
mldangelo-oai Sep 12, 2026
1e7ecbd
Recover original selections from native applied settings records
mldangelo-oai Sep 12, 2026
1b41a96
fix(qa): inspect compressed package assets as decoded text
mldangelo-oai Sep 12, 2026
440af39
Enable persisted Deep finalization for new scans
mldangelo-oai Sep 12, 2026
a7a20c4
Align managed audit admission with persisted worker semantics
mldangelo-oai Sep 12, 2026
f2ebd74
Verify accepted bytes during stopped result recovery
mldangelo-oai Sep 12, 2026
4ef3522
Recover committed scan completion and finish selected cost stops
mldangelo-oai Sep 12, 2026
b8147b2
Cover canceled follow-up after recovering a completion receipt
mldangelo-oai Sep 12, 2026
c17bd15
Keep legacy coverage fixtures explicit after workflow activation
mldangelo-oai Sep 12, 2026
261a825
test(sdk): verify persisted Deep package execution state
mldangelo-oai Sep 12, 2026
0a8b31d
test: assert v2 coverage in attempt replay
mldangelo-oai Sep 12, 2026
412fed6
Test complete native selections against legacy turn fields
mldangelo-oai Sep 12, 2026
82248b2
Verify historical settings upgrades and default selected recovery
mldangelo-oai Sep 12, 2026
9ed7afc
Preserve recorded native absent tier with explicit provenance
mldangelo-oai Sep 12, 2026
2b02eab
fix(sdk): recover committed budget completion receipts
mldangelo-oai Sep 12, 2026
c02b33b
test: preserve Node library resolution in worker fixtures
mldangelo-oai Sep 12, 2026
47f4029
fix(sdk): preserve stop errors after completed budget results
mldangelo-oai Sep 12, 2026
56a9ff9
fix(ci): resolve native proof helper dependencies from SDK
mldangelo-oai Sep 12, 2026
6e01a59
Preserve native usage coverage across null token counters
mldangelo-oai Sep 12, 2026
db82e96
test(sdk): match native Windows child executable paths
mldangelo-oai Sep 12, 2026
01fab0e
fix(deep-scan): ignore native compatibility summaries
mldangelo-oai Sep 12, 2026
696cdd1
fix: cancel selected scans without a status read
mldangelo-oai Sep 12, 2026
38ba26d
test: resolve shared SDK imports from MCP dependencies
mldangelo-oai Sep 12, 2026
c846b91
test: detach proof dependency junction with Node
mldangelo-oai Sep 12, 2026
7d640df
fix: reject unsupported stopped scan publication
mldangelo-oai Sep 12, 2026
d33362c
Stage legacy Deep Scan execution with compatible recovery readers
mldangelo-oai Sep 12, 2026
69a2670
Read existing frozen checkpoints before enabling publication writers
mldangelo-oai Sep 12, 2026
b855bf7
Separate reader fixtures from deferred state-writer tests
mldangelo-oai Sep 12, 2026
ca72760
Read original settings before reader-stage coordinator adoption
mldangelo-oai Sep 12, 2026
35aabf4
Preserve initial reader settings without capturing recovery history
mldangelo-oai Sep 12, 2026
5875524
Clarify recorded settings reader behavior
mldangelo-oai Sep 12, 2026
e501431
fix(deep-scan): capture original summary before owner launch
mldangelo-oai Sep 12, 2026
91eb9ed
test: include reasoning summary in package contract
mldangelo-oai Sep 12, 2026
2bb4c7d
fix: cancel selected scans without a cleanup state read
mldangelo-oai Sep 12, 2026
34564b7
test: cover selected cancellation with a lost cleanup state response
mldangelo-oai Sep 12, 2026
90baed1
Read saved worker usage from its original Codex home
mldangelo-oai Sep 12, 2026
ea54329
Include recorded worker homes in scan usage
mldangelo-oai Sep 12, 2026
7959579
Cancel owned Deep scans across resume and selection boundaries
mldangelo-oai Sep 12, 2026
dea9ba4
Include inherited SQLite state in worker usage
mldangelo-oai Sep 12, 2026
47c43e4
Read aliased scan session directories once
mldangelo-oai Sep 12, 2026
35cf9ee
Keep model usage with the most complete session copy
mldangelo-oai Sep 12, 2026
0550570
Allow saved budget finalization to resume in the reader release
mldangelo-oai Sep 12, 2026
1275f4b
Preserve event occurrences and expose known cost internally
mldangelo-oai Sep 12, 2026
3d5357b
Cancel registered Deep scans before thread initialization
mldangelo-oai Sep 12, 2026
571c475
Keep complete usage from copied worker rollouts
mldangelo-oai Sep 12, 2026
ab13751
Replay sealed budget output in the reader release
mldangelo-oai Sep 12, 2026
8480a14
Enforce measured cost bounds while preserving unknown totals
mldangelo-oai Sep 12, 2026
fdadd93
Recover recorded worker usage and clear resolved lookup warnings
mldangelo-oai Sep 12, 2026
faa31af
Reject unsupported workflows before budget completion
mldangelo-oai Sep 12, 2026
25b0261
Validate protocol before parent completion
mldangelo-oai Sep 12, 2026
b45b980
Check completion replay and budget ownership before writes
mldangelo-oai Sep 12, 2026
dc8edae
docs: describe recovery from existing Deep aggregates
mldangelo-oai Sep 12, 2026
e05e19c
test: resolve reader settings dependencies from MCP install
mldangelo-oai Sep 12, 2026
8f60665
test: collect original worker processes after shutdown
mldangelo-oai Sep 12, 2026
001d3f7
Preserve helper CLI behavior through linked plugin paths
mldangelo-oai Sep 12, 2026
d3f8cd8
test: align linked helper fixture formatting
mldangelo-oai Sep 12, 2026
f65be3e
fix(deep-scan): preserve compatible saved-result recovery
mldangelo-oai Sep 15, 2026
199c410
chore: integrate main with compatible Deep Scan readers
mldangelo-oai Sep 15, 2026
8c5b632
test: remove unused publication crash fixture
mldangelo-oai Sep 15, 2026
aff95e7
test(sdk): bind budget recovery fixture to registered snapshot
mldangelo-oai Sep 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 40 additions & 26 deletions plugins/codex-security/mcp-app/helpers-main.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,48 @@
import { existsSync, realpathSync } from "node:fs";
import { fileURLToPath } from "node:url";
export { parseCanonicalScanDraft } from "./src/artifact-scan-draft.js";
export { resumeSelectedDeepScan } from "./src/deep-scan/finalization.js";
import { resolveSecurityMdCommand } from "./src/helpers/resolve-security-md";
import { decodePosixBytes } from "./src/helpers/posix-path";
import { windowsBinding } from "./src/native";

let commandLine = process.argv.slice(2);
if (process.platform === "win32") {
const original = windowsBinding().windowsArguments();
commandLine = original
.slice(original.length - commandLine.length)
.map((argument) => argument.toString("utf16le"));
}
let posixHome = process.env.HOME;
if (commandLine[0] === "--helper") {
// Importing the bundled helper from the SDK does not invoke its CLI adapter.
const entryPath = import.meta.url.startsWith("file:") ? fileURLToPath(import.meta.url) : import.meta.url;
const invokedPath = process.argv[1];
if (
invokedPath && existsSync(invokedPath)
&& realpathSync(invokedPath) === realpathSync(entryPath)
) runHelper();

function runHelper(): void {
let commandLine = process.argv.slice(2);
if (process.platform === "win32") {
commandLine = commandLine.slice(1);
const original = windowsBinding().windowsArguments();
commandLine = original
.slice(original.length - commandLine.length)
.map((argument) => argument.toString("utf16le"));
}
let posixHome = process.env.HOME;
if (commandLine[0] === "--helper") {
if (process.platform === "win32") {
commandLine = commandLine.slice(1);
} else {
const [homeSet, home, ...args] = decodePosixBytes(
Buffer.from(commandLine[1] ?? "", "hex"),
)
.split("\0")
.slice(0, -1);
posixHome = homeSet ? home : undefined;
commandLine = args;
}
}
const [command, ...args] = commandLine;
if (command === "resolve-security-md") {
process.exitCode = resolveSecurityMdCommand(args, posixHome);
} else {
const [homeSet, home, ...args] = decodePosixBytes(
Buffer.from(commandLine[1] ?? "", "hex"),
)
.split("\0")
.slice(0, -1);
posixHome = homeSet ? home : undefined;
commandLine = args;
console.error(
"Usage: launch_codex_security_mcp[.cmd] --helper resolve-security-md [options]",
);
process.exitCode = 2;
}
}
const [command, ...args] = commandLine;
if (command === "resolve-security-md") {
process.exitCode = resolveSecurityMdCommand(args, posixHome);
} else {
console.error(
"Usage: launch_codex_security_mcp[.cmd] --helper resolve-security-md [options]",
);
process.exitCode = 2;
}
9 changes: 6 additions & 3 deletions plugins/codex-security/mcp-app/scripts/build_mcp_app.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#!/usr/bin/env node
import { existsSync, realpathSync } from "node:fs";
import { copyFile, mkdir, readFile, rm, writeFile } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
import { pathToFileURL } from "node:url";
import { fileURLToPath } from "node:url";
import { brotliCompressSync, constants as zlibConstants } from "node:zlib";
import { execFileSync } from "node:child_process";
import { build } from "esbuild";
Expand Down Expand Up @@ -41,6 +42,7 @@ export async function buildMcpApp({ output }) {
loader: { ".md": "text" },
logLevel: "info",
logOverride: { "empty-import-meta": "silent" },
nodePaths: [join(root, "node_modules")],
outfile: bundle,
platform: "node",
target: "node20"
Expand Down Expand Up @@ -68,8 +70,8 @@ export async function buildMcpApp({ output }) {

const invokedPath = process.argv[1];
if (
invokedPath !== undefined
&& pathToFileURL(resolve(invokedPath)).href === import.meta.url
invokedPath !== undefined && existsSync(invokedPath)
&& realpathSync(invokedPath) === realpathSync(fileURLToPath(import.meta.url))
) {
const args = process.argv.slice(2);
if (args.length !== 2 || args[0] !== "--output") {
Expand Down Expand Up @@ -108,5 +110,6 @@ const runtimeModule = new Module(loaderPath);
runtimeModule.filename = loaderPath;
runtimeModule.paths = Module._nodeModulePaths(dirname(loaderPath));
runtimeModule._compile(runtimeSource, loaderPath);
export default runtimeModule.exports;
`;
}
79 changes: 63 additions & 16 deletions plugins/codex-security/mcp-app/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
DeepScanStartLock,
startOrJoinDeepScanCoordinator
} from "./src/deep-scan/registry.js";
import { captureDeepScanExecutionSettings, loadDeepScanExecutionSettings, restoredDeepScanWorkerSettings, type DeepScanLegacySettingsContext } from "./src/deep-scan/recovery-settings.js";
import { CodexSdkWorkerExecutor } from "./src/deep-scan/executor.js";
import {
CODEX_SANDBOX_STATE_META_CAPABILITY,
Expand Down Expand Up @@ -750,6 +751,27 @@ export function createCodexSecurityServer(): McpServer {
registry: deepScanCoordinators,
options: {
store: deepScanStore,
prepareExecutor: async (run) => new CodexSdkWorkerExecutor({
...restoredDeepScanWorkerSettings(
begun.shouldStart
? await captureDeepScanExecutionSettings(run, parentSandbox, process.env,
{ threadId, startedAt: run.createdAt })
: await loadDeepScanExecutionSettings(run.scanDir, run, async () => {
const context = await runWorkbench(["get-scan", "--scan-id", run.scanId]);
const recipe = context.recipe as Pick<DeepScanLegacySettingsContext, "config"> | undefined;
const scan = context.scan as { executionAttribution?: { owner: DeepScanRunState["usageOwner"] } };
return { config: recipe?.config, usageOwner: scan.executionAttribution?.owner };
}),
parentSandbox
),
artifactContext: {
pluginRoot: PLUGIN_ROOT,
scanRoot: run.scanDir,
repoRoot: run.targetPath,
scanId: run.scanId,
scope: run.scope
}
}),
executor: new CodexSdkWorkerExecutor({
...modelSettings,
parentSandbox,
Expand All @@ -765,7 +787,7 @@ export function createCodexSecurityServer(): McpServer {
log: logDeepScanEvent,
handoffClaimToken,
threadId,
onComplete: async (draft, signal) => {
onComplete: async (draft, signal, publication) => {
const context = await createScanArtifactContext(
begun.run.scanId,
runWorkbench,
Expand All @@ -779,7 +801,7 @@ export function createCodexSecurityServer(): McpServer {
await recordCodexSecurityScanDraftViaWorkbench(context, {
...draft,
...(handoffClaimToken === undefined ? {} : { handoffClaimToken })
}, runWorkbench, signal);
}, runWorkbench, signal, publication);
},
onStopped: async (run) => {
await runWorkbench([
Expand All @@ -796,12 +818,26 @@ export function createCodexSecurityServer(): McpServer {
invocationFailure: toolErrorResult(deepScanInvocationFailureMessage(error))
}));
if ("invocationFailure" in preparation) return preparation.invocationFailure;
if (preparation.immediate) return preparation.immediate;
const completeSelectedParent = async (run: DeepScanRunState) => {
if (run.finalizationInput && run.status === "succeeded") {
await runWorkbench([
"complete-scan", "--scan-id", run.scanId, "--thread-id", threadId,
...optionalArg("--claim-token", handoffClaimToken),
]);
}
};
if (preparation.immediate) {
try { await completeSelectedParent(preparation.begun.run); }
catch (error) { return toolErrorResult(deepScanInvocationFailureMessage(error)); }
return preparation.immediate;
}
const { begun, coordinator, joined } = preparation;
if (joined) {
logDeepScanEvent({ event: "coordinator_joined", scanId: begun.run.scanId });
}
const terminal = await coordinator.wait(abortSignalFromExtra(extra));
try { await completeSelectedParent(terminal); }
catch (error) { return toolErrorResult(deepScanInvocationFailureMessage(error)); }
const result = deepScanTerminalResult(terminal);
if (!result) {
return toolErrorResult(deepScanInvocationFailureMessage(
Expand Down Expand Up @@ -1632,12 +1668,14 @@ function logDeepScanEvent(event: {

async function runWorkbench(
args: string[],
input?: string | Buffer
input?: string | Buffer,
selectFinalization = false,
withExecutionSettings = false,
): Promise<JsonObject> {
let pythonCommand: string | undefined;
try {
pythonCommand = await resolvePythonCommand();
return await executeWorkbenchWithStateSelection(pythonCommand, args, input);
return await executeWorkbenchWithStateSelection(pythonCommand, args, input, selectFinalization, withExecutionSettings);
} catch (error) {
const launchError = pythonCommand
? missingPythonHelperMessage(error, pythonCommand)
Expand All @@ -1655,36 +1693,38 @@ async function runWorkbench(
async function executeWorkbenchWithStateSelection(
pythonCommand: string,
args: string[],
input?: string | Buffer
input?: string | Buffer,
selectFinalization = false,
withExecutionSettings = false,
): Promise<JsonObject> {
if (WORKBENCH_COMMANDS_WITHOUT_DATABASE.has(args[0] ?? "")) {
return await executeWorkbench(pythonCommand, args, undefined, input);
return await executeWorkbench(pythonCommand, args, undefined, input, selectFinalization, withExecutionSettings);
}
if (CONFIGURED_WORKBENCH_STATE_DIR) {
return await executeWorkbench(pythonCommand, args, undefined, input);
return await executeWorkbench(pythonCommand, args, undefined, input, selectFinalization, withExecutionSettings);
}
if (fallbackWorkbenchStateDir) {
return await executeWorkbench(pythonCommand, args, await fallbackWorkbenchStateDir, input);
return await executeWorkbench(pythonCommand, args, await fallbackWorkbenchStateDir, input, selectFinalization, withExecutionSettings);
}
if (persistentWorkbenchStateSucceeded) {
return await executeWorkbench(pythonCommand, args, undefined, input);
return await executeWorkbench(pythonCommand, args, undefined, input, selectFinalization, withExecutionSettings);
}
return await withWorkbenchStateSelectionLock(async () => {
if (fallbackWorkbenchStateDir) {
return await executeWorkbench(pythonCommand, args, await fallbackWorkbenchStateDir, input);
return await executeWorkbench(pythonCommand, args, await fallbackWorkbenchStateDir, input, selectFinalization, withExecutionSettings);
}
if (persistentWorkbenchStateSucceeded) {
return await executeWorkbench(pythonCommand, args, undefined, input);
return await executeWorkbench(pythonCommand, args, undefined, input, selectFinalization, withExecutionSettings);
}
try {
const result = await executeWorkbench(pythonCommand, args, undefined, input);
const result = await executeWorkbench(pythonCommand, args, undefined, input, selectFinalization, withExecutionSettings);
persistentWorkbenchStateSucceeded = true;
return result;
} catch (error) {
if (!isUnwritableSqliteOpenError(error)) throw error;
const fallbackStateDir = await pinFallbackWorkbenchStateDir();
logWorkbenchStateFallback();
return await executeWorkbench(pythonCommand, args, fallbackStateDir, input);
return await executeWorkbench(pythonCommand, args, fallbackStateDir, input, selectFinalization, withExecutionSettings);
}
});
}
Expand All @@ -1707,7 +1747,9 @@ async function executeWorkbench(
pythonCommand: string,
args: string[],
stateDir?: string,
input?: string | Buffer
input?: string | Buffer,
selectFinalization = false,
withExecutionSettings = false,
): Promise<JsonObject> {
const userContextIndex = args.indexOf("--user-context");
const userContext = userContextIndex === -1 ? undefined : args[userContextIndex + 1];
Expand All @@ -1716,7 +1758,12 @@ async function executeWorkbench(
workbenchArgs.splice(userContextIndex, 2, "--user-context-stdin");
}
const workbenchInput = input ?? userContext;
const execution = execFileAsync(pythonCommand, [workbenchScriptPath(), ...workbenchArgs], {
const internalInvocation = selectFinalization ? "select_finalization=True"
: withExecutionSettings ? "with_execution_settings=True" : undefined;
const pythonArgs = internalInvocation
? ["-c", `import runpy, sys; script = sys.argv.pop(1); runpy.run_path(script)['main'](${internalInvocation})`, workbenchScriptPath(), ...workbenchArgs]
: [workbenchScriptPath(), ...workbenchArgs];
const execution = execFileAsync(pythonCommand, pythonArgs, {
cwd: PLUGIN_ROOT,
env: stateDir
? { ...process.env, CODEX_SECURITY_STATE_DIR: stateDir }
Expand Down
32 changes: 26 additions & 6 deletions plugins/codex-security/mcp-app/src/artifact-deep-reducer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { join } from "node:path";
import { dirname, join, relative, sep } from "node:path";
import type { ZodType } from "zod/v4";
import commonSchema from "../../schemas/definitions/artifact-common.schema.json";
import reducerSchema from "../../schemas/tools/deep-reducer.schema.json";
Expand All @@ -20,7 +20,9 @@ import {
type DeepScanArtifacts
} from "./deep-scan/artifacts.js";
import {
deepReductionForPersistence,
parseDeepReduction,
projectDiscoveryCoverage,
reconcileDeepReduction,
type DeepReductionInput,
type DeepReductionSources,
Expand Down Expand Up @@ -54,6 +56,18 @@ interface BoundReducer {
/** Read the findings and scan context assigned to this reducer. */
export async function getCodexSecurityDeepReducerInputs(
context: ArtifactContext
): Promise<DeepReductionSources> {
const inputs = await readDeepReductionSources(context);
const { sourceCoverage: _coverage, ...previous } = inputs.previous ?? {};
return {
discoveries: inputs.discoveries.map(({ workerId, result }) => ({ workerId, result })),
previous: inputs.previous === null ? null : previous as DeepReductionInput,
};
}

/** Capture host coverage alongside the reducer's immutable finding inputs. */
export async function readDeepReductionSources(
context: ArtifactContext
): Promise<DeepReductionSources> {
return withLogicalReducerErrors(context, async () => {
const bound = bindDeepReducer(context);
Expand All @@ -73,8 +87,13 @@ export async function getCodexSecurityDeepReducerInputs(
sourceFindingIds: [`${worker.id}:${index}`],
},
}));
const { coverage: _coverage, ...reduction } = result;
return { workerId: worker.id, result: reduction };
const { coverage, ...reduction } = result;
return {
workerId: worker.id,
...(worker.attempt === undefined ? {} : { attempt: worker.attempt }),
coverage: projectDiscoveryCoverage(coverage, worker, relative(bound.artifacts.scanDir, worker.artifactDir ?? dirname(worker.resultPath)).split(sep).join("/")),
result: reduction,
};
}));
const previous = await readPreviousReduction(bound);
const scanId = bound.scanId ?? previous?.scanId ?? discoveries[0]?.result.scanId;
Expand Down Expand Up @@ -107,7 +126,7 @@ export async function recordCodexSecurityDeepReduction(
const submitted = deepReductionInputSchema.parse(input);
let reduction = parseDeepReduction(submitted);
if (reduction.complete === false) throw new Error("Deep reduction is only a checkpoint, not a complete result.");
const inputs = await getCodexSecurityDeepReducerInputs(context);
const inputs = await readDeepReductionSources(context);
const expectedScanId = bound.scanId
?? inputs.previous?.scanId
?? inputs.discoveries[0]?.result.scanId;
Expand All @@ -116,8 +135,9 @@ export async function recordCodexSecurityDeepReduction(
}
reduction = reconcileDeepReduction(reduction, inputs.discoveries, inputs.previous);

await saveScanDraftCheckpoint(context, reduction);
await writeJsonAtomic(bound.resultPath, reduction);
const persisted = deepReductionForPersistence(reduction, bound.state.persistSourceCoverage);
await saveScanDraftCheckpoint(context, persisted);
await writeJsonAtomic(bound.resultPath, persisted);
return {
findingCount: reduction.findings.length,
consumedWorkerIds: bound.state.claimedWorkers.map((worker) => worker.id)
Expand Down
4 changes: 4 additions & 0 deletions plugins/codex-security/mcp-app/src/artifact-io.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ import { dirname, isAbsolute, join, resolve, sep } from "node:path";
export interface DeepReducerWorkerContext {
id: string;
resultPath: string;
/** Original output owner for relative evidence, including accepted checkpoints. */
artifactDir?: string;
attempt?: number;
}

export interface DeepReducerContext {
scanRoot: string;
claimedWorkers: DeepReducerWorkerContext[];
previousReducerResultPath?: string;
persistSourceCoverage?: boolean;
}

/**
Expand Down
Loading
Loading