Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
61 changes: 61 additions & 0 deletions sdk/typescript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,67 @@ Failed, incomplete, or malformed responses reject the promise.
`validations/` under the state directory. Pass `auth` to select credentials
or `signal` to cancel.

### Generate and verify a patch

`patch()` runs the bundled remediation workflow non-interactively against a
workspace that the caller has intentionally made writable:

```ts
const security = new CodexSecurity();
try {
const result = await security.patch({
repositoryPath: "/path/to/disposable/workspace",
finding: {
title: "Possible SQL injection",
summary: "User input reaches a raw SQL query.",
locations: [{ path: "src/query.ts", startLine: 42 }],
},
signal,
onActivity(activity) {
console.log(activity.description);
},
onCost(cost) {
console.log(cost.estimatedUsd);
},
});

switch (result.status) {
case "verified":
case "no_change":
console.log(result.verificationReport);
break;
case "blocked":
case "failed":
console.error(result.reason);
break;
}
} finally {
await security.close();
}
```

Pass literal finding text or a JSON-serializable object; strings are never read
as file paths. Sandboxed patch commands may edit only `repositoryPath` and run
without network access or web search. Patch workspaces are always treated as
untrusted Codex projects, so repository-local configuration, hooks, rules, and
MCP servers are not loaded. The method does not create a commit, push, open a
pull request, publish findings, or add a scan to history.
Callers remain responsible for reviewing and deriving the authoritative diff,
approval, commit creation, and delivery.

The discriminated result reports `verified`, `no_change`, `blocked`, or
`failed`, plus repository-relative `changedFiles`, `threadId`, and estimated
`cost` when model pricing and usage are available. Verified and no-change
results include `verificationReport`; blocked and failed results include
`reason`. Transport, authentication, cancellation, incomplete turns, and
malformed results reject the promise. A rejected or interrupted operation can
leave partial workspace changes for the caller to inspect or discard.

Patch operations reuse constructor configuration and credentials. Pass `auth`,
`safetyIdentifier`, `model`, or `reasoningEffort` for per-call selection.
`onActivity`, `onSessionEvent`, `onCost`, `onReconnect`, `onAuthentication`,
`onWarning`, and `onObserverError` use the same observer contracts as scans.

### Import GitHub code scanning alerts

Import alerts, including third-party SARIF uploads, and validate them against
Expand Down
40 changes: 40 additions & 0 deletions sdk/typescript/scripts/fixtures/package-consumer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
type DeduplicateScanResult,
type CustomPublicationResult,
type Finding,
type PatchOptions,
type PatchResult,
type ScanCost,
type ScanOptions,
type ScanProgress,
Expand Down Expand Up @@ -99,6 +101,44 @@ export async function validate(
return await client.validate(options);
}

export async function patch(
repositoryPath: string,
finding: Finding | ImportedFinding,
): Promise<PatchResult> {
await using client = new CodexSecurity();
const options: PatchOptions = {
repositoryPath,
finding,
model: "gpt-5.6-sol",
reasoningEffort: "high",
};
const result = await client.patch(options);
result.changedFiles satisfies readonly string[];
// @ts-expect-error Patch results expose changed files as immutable metadata.
result.changedFiles.push("unexpected.ts");
switch (result.status) {
case "verified":
case "no_change":
result.verificationReport satisfies string;
break;
case "blocked":
case "failed":
result.reason satisfies string;
break;
default:
result satisfies never;
}
return result;
}

const invalidPatchOptions: PatchOptions = {
repositoryPath: "/synthetic/repository",
finding: "Synthetic finding",
// @ts-expect-error Patch reasoning effort is restricted to Codex SDK values.
reasoningEffort: "extreme",
};
void invalidPatchOptions;

// @ts-expect-error The dependency-injection constructor is internal.
new CodexSecurity({}, undefined as never, undefined as never);

Expand Down
2 changes: 1 addition & 1 deletion sdk/typescript/scripts/smoke-package.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ try {
[
"--input-type=module",
"--eval",
`const sdk = await import(${JSON.stringify(packageManifest.name)}); for (const name of ["CodexSecurity", "publishScan", "publishScanToCustom", "checkScanPublication", "deduplicateScan"]) if (typeof sdk[name] !== "function") throw new Error("The installed package does not export " + name + ".");`,
`const sdk = await import(${JSON.stringify(packageManifest.name)}); for (const name of ["CodexSecurity", "publishScan", "publishScanToCustom", "checkScanPublication", "deduplicateScan"]) if (typeof sdk[name] !== "function") throw new Error("The installed package does not export " + name + "."); if (typeof sdk.CodexSecurity.prototype.patch !== "function") throw new Error("The installed CodexSecurity client does not expose patch().");`,
],
{ cwd: consumer },
);
Expand Down
Loading