@stacksona/sdk
Gate one exact action without rebuilding the request, review, revision, proof, and execution loop yourself.
Fast path
- 1Install.
npm install @stacksona/sdk - 2Create a client.
Use your Gate URL and
sg_agent API key. - 3Wrap the real side effect.
runGatedActioncalls it only after Gate allows or approves the exact request.
Gate an action
import { StacksonaGateClient } from '@stacksona/sdk';
const gate = new StacksonaGateClient({
baseUrl: process.env.STACKSONA_GATE_URL!,
apiKey: process.env.STACKSONA_API_KEY!,
});
const { decision, executed, result } = await gate.runGatedAction(
'refund-order-8821',
{
tool_name: 'issue_refund',
subject: 'Refund order #8821',
risk_level: 'high',
payload: { order_id: '8821', amount: 500, currency: 'usd' },
},
async (_decision, request) => issueRefund(request.payload),
);
if (!executed) console.log(`Action stopped: ${decision.status}`);It sends the Gate request, follows the exact review thread, returns requested changes as actionable state, fails closed on unexpected states, and only invokes your callback after allow or approved. The request payload is snapshotted so the callback receives the JSON Gate evaluated.
Reviewer requests changes
Add reviseAction when your runtime can revise a proposal. The revision is submitted on the same thread_id and the SDK continues following that review.
await gate.runGatedAction(taskId, request, executeTool, {
reviseAction: async ({ decision, request }) => ({
tool_name: request.tool_name,
subject: request.subject ?? 'Revised action',
request_payload: await reviseWithAgent(
request.payload,
decision.modification?.requested_changes,
),
}),
});Signed approval proof
await gate.runGatedAction(
taskId,
{ tool_name: 'deploy_production', payload: deployArgs },
async (_decision, request) => deploy(request.payload),
{ validateSignedApprovalToken: true },
);An approved review without required proof, or token validation with valid: false, fails closed.
Methods
| Method | Purpose |
|---|---|
resolveTools(tools) | Resolve registered names, descriptions, and input schemas. |
logEvent(taskId, event) | Append timeline evidence. |
requestDecision(taskId, input) | Evaluate one exact proposed tool call. |
getDecision(query) | Read a review by thread or task. |
listDecisions(query) | List recent pending or completed reviews. |
pollDecision(query, options) | Wait for approved, rejected, or changes requested. |
requestDecisionAndPoll(taskId, input, options) | Request and automatically wait on human review. |
submitRevision(taskId, threadId, revision) | Submit requested changes on the same review thread. |
validateApprovalToken(input) | Validate and consume one-time approval proof. |
runGatedAction(taskId, input, action, options) | Own the normal Gate lifecycle for one action. |
Dynamic tools
const { tools, unregistered } = await gate.resolveTools([
'issue_refund',
'send_customer_email',
]);Use the returned exact tool contracts when the runtime builds its model tool list dynamically.
Manual durable flow
When your app already has a queue or durable resume mechanism, use requestDecision and persist the returned thread_id, then resume with getDecision or pollDecision.
@stacksona/sdk 0.3.0 · Node.js 18+