@coderifts/openai-agents
v0.1.0
Published
CodeRifts contract governance for the OpenAI Agents SDK — a tool-input guardrail that gates contract mutations before a tool runs, plus REQUEST_APPROVAL mapped to the SDK's native approval surface.
Maintainers
Readme
@coderifts/openai-agents
CodeRifts contract governance for the OpenAI Agents SDK.
A tool-input guardrail decides, before a tool runs, whether the call is a governed contract mutation. It gates a tool even when CodeRifts never wrapped that tool's implementation — so you can govern third-party or hand-written tools you do not own.
Install
npm install @coderifts/openai-agents @openai/agents @coderifts/sdk@openai/agents is a peer dependency (>=0.17.0); @coderifts/sdk is a dependency.
Use
import { Agent, run, tool } from '@openai/agents';
import { CodeRifts } from '@coderifts/sdk';
import { withCodeRiftsGuardrail } from '@coderifts/openai-agents';
const client = new CodeRifts({ apiKey: process.env.CODERIFTS_API_KEY });
const { guardrail, needsApproval } = withCodeRiftsGuardrail({
client,
operation: 'merge',
readPrior: (p) => fs.readFileSync(p, 'utf8'), // the `before` side
});
const writeFile = tool({
name: 'write_file',
description: 'Write a file.',
parameters: z.object({ path: z.string(), content: z.string() }),
inputGuardrails: [guardrail],
needsApproval, // REQUEST_APPROVAL suspends for a human
execute: async ({ path, content }) => { /* ... */ },
});attachGuardrailToEach(tools, guardrail) attaches it across a list you already have.
Verdict mapping
| execution_action | Behaviour |
|---|---|
| CONTINUE, CONTINUE_WITH_MONITORING | allow() — the tool runs |
| REQUEST_APPROVAL | guardrail allows, needsApproval returns true → the SDK emits a RunToolApprovalItem and suspends for a human |
| STOP / BLOCK | rejectContent() with the WHY + remediation |
| anything unrecognised | rejectContent(), message names it as not permission (fail closed) |
| unreadable arguments / missing path on a mutator | rejectContent() fail-closed |
| non-contract path, read-only tool | allow() with no API call |
rejectContent, not throwException: a governance deny must teach. throwException aborts the
whole run with a tripwire and the model learns nothing.
One preflight per tool call. The guardrail and the needsApproval resolver share a memo keyed
by the SDK's callId, so enabling approvals does not double your API spend.
Coverage
With the CodeRifts guardrail attached to a tool, that tool is governed even if CodeRifts never
wrapped its implementation — so an agent can adopt governance for third-party or hand-written
tools it does not own, and a blocked contract change returns the reason and remediation to the model
rather than failing opaquely. The claim changes from per-table to per-tool-with-guardrail —
not to per-agent: tool input guardrails are attached per tool, there is no agent-wide form, and
we demonstrated that the same ungoverned tool still mutates an OpenAPI spec when the guardrail is not
attached to it. Handoff sub-agents, hosted tools, and direct client calls outside run() remain
ungated. Enforcement independent of the developer's diligence still lives downstream, at the
contract-gate on the pull request and the deploy-gate in CI.
What this does not cover
- Tools without the guardrail attached. There is no agent-wide form —
'toolInputGuardrails' in agentisfalse, and the Agent'sinputGuardrailsis a different type that validates the agent's input, not tool calls.attachGuardrailToEachcovers exactly the list you pass it. - Handoff sub-agents. A sub-agent's tools are separate tool objects and do not inherit it.
- Hosted / built-in tools (
codeInterpreterTool,applyPatchTool). Built by SDK factories; you would have to interpose your own wrapper to addinputGuardrails. - Direct client calls outside
run(). They never reach the runner, so no guardrail fires.
License
MIT
