Skip to main content
Best for: when you own the harness and want the smallest truthful TypeScript integration.
If your app already uses OpenAI direct, OpenAI Agents SDK, Anthropic direct, Claude Agent SDK, LangGraph, LangChain, Vercel AI SDK, or MCP, prefer that native adapter page instead of the raw runtime.

Install

npm install @verifiedx-core/sdk
The raw TypeScript SDK is ESM and targets Node.js 18+.

Net-new VerifiedX code

This is the real VerifiedX delta in a custom TypeScript harness.
import { initVerifiedX } from "@verifiedx-core/sdk";

const harness = {
  async callModel(messages) {
    return { messages };
  },

  async lookupInternalWorkflow({ workflowId }) {
    return { workflowId, status: "legal_complete" };
  },

  async setWorkflowStatus({ workflowId, status }) {
    return { ok: true, workflowId, status };
  },
};

const verifiedx = await initVerifiedX();

verifiedx.bindHarness(harness, {
  llm: {
    callModel: "gpt-5.4-mini",
  },
  retrievals: {
    lookupInternalWorkflow: "internal workflow evidence",
  },
  actions: {
    setWorkflowStatus: "setWorkflowStatus",
  },
});
That is the important part. You keep your existing harness and declare which business methods should be treated as LLM calls, retrievals, memories, tools, or actions.

What the runtime already captures by default

When you call initVerifiedX(), VerifiedX already installs lower-seam fallbacks for common Node.js side effects, including:
  • globalThis.fetch and undici.fetch
  • node:fs/promises.writeFile and appendFile
  • fs.writeFileSync and appendFileSync
  • pg.Client.query and pg.Pool.query mutation calls
  • amqplib.Channel.publish and sendToQueue
So even without bindHarness(), the raw runtime can still preflight real fetch, file, database, and queue side effects. bindHarness() is how you get cleaner business-level boundaries, better names, and richer receipts.

When to bind methods explicitly

Explicit binding is how you promote your own harness methods into first-class VerifiedX boundaries. That means you can choose exactly which methods you want to name and preflight under:
  • llm
  • retrievals
  • actions
  • memories
  • tools
This is useful when you want:
  • A business-level boundary like setWorkflowStatus instead of only the lower-seam fetch or pg.query
  • A memory boundary like rememberCustomerPreference instead of only the underlying storage write
  • Better receipts with your own toolName
  • Optional schema and docstring metadata on the boundary
  • Protection for custom in-process methods that might not otherwise hit an auto-patched lower seam

Binding categories

Use the smallest surface that matches what the method really does.

llm

Declare model-call methods here. You can use a dict or the string shorthand.
llm: {
  callModel: { modelName: "gpt-5.4-mini" },
}
llm: {
  callModel: "gpt-5.4-mini",
}

retrievals

Declare reads and lookups that provide context to the model here. Internal reads can use the string shorthand:
retrievals: {
  lookupInternalWorkflow: "internal workflow evidence",
}
For public-web or weaker outside context, set objectType to external_retrieval:
retrievals: {
  searchPublicWeb: { query: "search public web", objectType: "external_retrieval" },
}
You can also use the tuple-style shorthand:
retrievals: {
  searchPublicWeb: ["search public web", "external_retrieval"],
}

actions

Declare high-impact business methods here, such as record mutations, system changes, internal or external messages, webhooks, and other writes.
actions: {
  setWorkflowStatus: { toolName: "setWorkflowStatus" },
  sendExternalEmail: { toolName: "sendExternalEmail" },
}
String shorthand also works:
actions: {
  setWorkflowStatus: "setWorkflowStatus",
  sendExternalEmail: "sendExternalEmail",
}
VerifiedX then infers whether the protected boundary should be treated as:
  • memory_write
  • record_mutation
  • system_change
  • external_message_send
If the method does not resolve to one of those high-impact classes, it still gets wrapped as a normal tool boundary with tool history.

memories

Declare durable memory writes here.
memories: {
  rememberCustomerPreference: { toolName: "rememberCustomerPreference" },
}
String shorthand also works:
memories: {
  rememberCustomerPreference: "rememberCustomerPreference",
}
Each declared memory method gets a memory_write preflight before it runs.

tools

tools is still supported for general helper wrapping and tool history.
tools: {
  syncCustomerCrm: { toolName: "syncCustomerCrm" },
}
String shorthand also works:
tools: {
  syncCustomerCrm: "syncCustomerCrm",
}
If a method is definitely a durable memory write or a high-impact side effect, prefer memories or actions. Use tools for general helper wrapping when the real protected boundary may still be caught lower in the runtime.

Optional metadata

You can attach schema and docstring metadata to actions, memories, and tools bindings for better boundary context and better receipts.
actions: {
  setWorkflowStatus: {
    toolName: "setWorkflowStatus",
    docstring: "Update an internal workflow record.",
    schema: {
      type: "object",
      properties: {
        workflow_id: { type: "string" },
        status: { type: "string" },
      },
      required: ["workflow_id", "status"],
    },
  },
}

Composed systems

If this node is part of a larger multi-agent or agent+human workflow, pass upstream context into VerifiedX so the current node has better system and situational awareness before it takes a high-impact action. This is useful when a supervisor agent, parent workflow, or human reviewer already has context that the current node should use before taking action. VerifiedX does not require a fixed schema for this. Pass the upstream context you already have in any JSON-serializable shape.
const upstream = {
  source: "workflow_supervisor",
  workflow_id: "WF-2203",
  approval_status: "approved_with_follow_up",
  human_review: {
    reviewer: "ops_lead",
    result: "approved",
  },
  prior_agent_output: {
    summary: "Billing verification is complete.",
  },
};

await verifiedx.withUpstreamContext(upstream, async () => {
  await harness.setWorkflowStatus({ workflowId: "WF-2203", status: "awaiting_human" });
});
Upstream context is supporting workflow context from outside the current node. It is not proof that this node already executed any local action.

What to expect at runtime

Protected boundaries in the raw TypeScript runtime can return:
  • allow
  • allow_with_warning
  • replan_required
  • goal_fail_terminal
Every outcome includes a structured decision receipt. In the production-style raw TypeScript scenarios, VerifiedX exercises:
  • allow for grounded memory writes and internal handoff writes
  • allow_with_warning when public-web context is supplementary but a safe internal next step is still grounded
  • replan_required when an unsafe external email is blocked and the same goal continues through a safer internal Slack update
If a boundary is replanned, the side effect does not execute. Wrapped methods return a structured blocked result with ok: false, blocked: true, the boundary outcome, and a decision receipt your system can keep local or route upstream.

Pricing note

One protected action check equals one real boundary preflight. Taint, event ingest, execution reports, and decision reads are all included at that price. The Free Sandbox includes the raw runtime and every native adapter. VerifiedX does not replace your orchestrator or human workflow. It returns receipts your system can keep local, route downstream, or pass upstream.