Use Resources with the SDK
Declare logical needs, build model-facing Tools, and invoke accepted Resource operations with truthful effect semantics.
A Resource is a governed external capability. Definitions use logical binding names; runtime code invokes only the accepted CRN.
Invoke directly
const issue = await ctx.invoke<{ id: string }>(
ctx.resources.github!,
"issue.create",
{ owner: "acme", repository: "support", title: "Follow up" },
{ dedupeKey: `ticket:${ticketId}`, timeoutMs: 30_000 },
);The operation must exist in the pinned catalog, its arguments must match the schema, and Policy must allow the exact CRN and action. Use ctx.invokeAsync() when the result should be a durable Handle.
What the invocation preserves
ctx.invoke() creates one canonical invocation identity from the accepted Resource revision, operation, arguments, Policy decision, owner, and journal position. The owning runtime keeps that identity through dispatch, recovery, result projection, and replay. If the argument value is too large to remain inline, moving its bytes to content-addressed storage does not create a different invocation.
A Resource integration that needs a downstream privileged service can request an invocation-bound capability from its execution context. The platform derives the capability from the admitted invocation and limits it by audience, scope, claims, and expiry. The integration cannot replace the caller, widen Policy, or choose a different Resource snapshot. The receiving service validates that common binding; operations that require live owner state also redeem it against the same owner and canonical invocation record before acting.
This is why Agent code passes ordinary operation arguments instead of credentials or platform tokens. The Resource boundary preserves the caller's accepted authority while the integration handles provider authentication and transport. Read Invocation-bound capabilities for the complete authority contract.
Offer the operation to a model
import { agent, opTool } from "@constal/sdk";
const createIssue = opTool("github", "issue.create", {
name: "create_issue",
description: "Create one support issue.",
});
export default agent({
id: "triage", version: "1.0.0", model: "model",
tools: { create_issue: createIssue },
async onMessage(message, ctx) {
return ctx.turn({ system: "Triage support requests.", objective: message, tools: ["create_issue"] });
},
});opTool() is resolved against the deployment-pinned operation. For a custom Tool, declare a truthful maxEffect: read-only work may repeat, idempotent work repeats under one identity, reconcilable work needs a probe, and non-idempotent ambiguity remains outcome-unknown.
read-only operations may repeat, idempotent operations repeat with the same key, reconcilable operations require a probe after ambiguity, and non-idempotent operations surface an unknown outcome. Agent code never receives the Credential injected into a protected integration. Continue with Build and use Tools, Use Resources from Agents, and SDK Resource reference.