@oxdeai/langgraph
v1.0.1
Published
Thin LangGraph adapter for the OxDeAI universal execution guard
Maintainers
Readme
@oxdeai/langgraph
Thin LangGraph binding for @oxdeai/guard.
This package connects LangGraph tool calls to the OxDeAI universal execution
guard. It contains no authorization logic - all policy evaluation and PEP
enforcement is delegated to @oxdeai/guard.
What this package does
LangGraph tool calls ({ name, args, id }) carry no agent identity and use
a different shape from OxDeAI's ProposedAction. This adapter:
- Injects
agentIdfrom config into every call (tool calls carry no agent identity) - Maps
toolCall.id→context.intent_id - Passes the resulting
ProposedActiontoOxDeAIGuard
Everything else - policy evaluation, authorization verification, state
persistence, fail-closed behavior - happens inside @oxdeai/guard.
Installation
pnpm add @oxdeai/langgraph @oxdeai/coreUsage
import { createLangGraphGuard } from "@oxdeai/langgraph";
const guard = createLangGraphGuard({
engine, // PolicyEngine from @oxdeai/core
getState, // () => State | Promise<State>
setState, // (state: State) => void | Promise<void>
agentId: "gpu-agent-1",
});
// In your LangGraph tool node:
const result = await guard(
{ name: "provision_gpu", args: { asset: "a100", region: "us-east-1" }, id: "call-xyz" },
() => provisionGpu("a100", "us-east-1")
);The execute callback is only invoked when the policy engine returns ALLOW
and the authorization artifact passes verification. On DENY, OxDeAIDenyError
is thrown and the callback is never called.
With estimated cost
Attach estimatedCost and resourceType directly on the tool call to give
the default normalizer richer context:
await guard(
{
name: "provision_gpu",
args: { asset: "a100", region: "us-east-1" },
id: "call-xyz",
estimatedCost: 500,
resourceType: "gpu",
},
() => provisionGpu("a100", "us-east-1")
);With a custom intent mapper
Use mapActionToIntent when you need full control over how a tool call maps
to an OxDeAI Intent (e.g. to set a precise amount, asset, or target):
import { createLangGraphGuard } from "@oxdeai/langgraph";
const guard = createLangGraphGuard({
engine,
getState,
setState,
agentId: "gpu-agent-1",
mapActionToIntent(action) {
// action.name, action.args, action.context.agent_id and intent_id are available
return buildProvisionIntent(action.args.asset as string, action.args.region as string);
},
});Error handling
import {
OxDeAIDenyError,
OxDeAIAuthorizationError,
OxDeAINormalizationError,
} from "@oxdeai/langgraph";
try {
await guard(toolCall, execute);
} catch (err) {
if (err instanceof OxDeAIDenyError) {
// Policy denied - err.reasons contains the violation codes
console.error("denied:", err.reasons);
} else if (err instanceof OxDeAIAuthorizationError) {
// Authorization artifact missing or invalid - hard security failure
throw err;
}
}Deterministic boundary semantics
This adapter preserves the same deterministic, offline-verifiable boundary semantics as every other OxDeAI protocol demo:
- No Authorization = no execution, even on ALLOW
- DENY blocks the execute callback before it is called
- State transitions happen only after successful execution
- Envelope verification remains offline and deterministic
All of this is guaranteed by @oxdeai/guard - this package adds nothing on top.
Architecture boundary
This package is a thin binding only. Do not add:
- Authorization logic
- Policy evaluation logic
verifyAuthorizationcalls- Runtime security semantics beyond the ToolCall → ProposedAction mapping
All of that lives in @oxdeai/guard.
