Interactive architecture: all components
Task + Context
The runtime starts with an outcome, current context, and whatever memory your application provides. It receives a goal, not a fixed sequence.
- Goal + constraints
- Conversation + memory
- No hard-coded route
const task = {
id: 'refund-1042',
goal: 'Resolve eligible refund requests',
context: customerCase,
memory: await loadTaskMemory('refund-1042')
};
AI Runtime Node
Your runtime owns reasoning. It can use new tool results, memory, and reviewer feedback to decide what the next useful action should be.
- Plans dynamically
- Adapts from outcomes
- Runtime keeps model credentials
const next = await runtime.plan({
goal: task.goal,
context: task.context,
memory: task.memory,
tools: availableTools
});
// next = { tool_name, arguments }
Available Tools
Tools are capabilities the runtime may choose from. They are not a workflow diagram. Gate can optionally supply governed descriptions and input schemas.
- Capabilities, not steps
- Runtime chooses the order
- Optional Gate contracts
const toolRegistry = {
send_email: sendEmail,
issue_refund: issueRefund,
update_crm: updateCrm
};
const contracts = await gate('/api/agent/tools/resolve', {
tools: Object.keys(toolRegistry)
});
Stacksona Gate
Immediately before a governed side effect, send the exact selected tool and final arguments to Gate. This is the stable safety boundary no matter which path the AI created.
- Policy check
- Human review when needed
- Audit + approval proof
const decision = await gate(
`/api/agent/tasks/${encodeURIComponent(task.id)}/requests`,
{
tool_name: next.tool_name,
payload: next.arguments
}
);
Human Review When Needed
Human review is not a mandatory workflow step. Gate only pauses the exact action when policy requires a decision, and the runtime can resume the same review later.
- Exact thread persists
- Workflow can yield
- Feedback returns as context
if (decision.status === 'pending_review') {
await saveReviewState({
task_id: task.id,
thread_id: decision.thread_id,
proposal: next
});
return { state: 'waiting_for_human' };
}
Execute Tool
Only an executable decision reaches the real tool. Capture the result and return it to the runtime loop rather than treating one tool call as task completion.
- Validate proof when required
- Execute exact proposed arguments
- Capture the tool result
if (decision.status === 'approved' && decision.approval_token) {
const proof = await gate('/api/agent/approvals/validate', {
task_id: task.id,
signature: decision.approval_token
});
if (!proof?.valid) throw new Error('Invalid approval proof');
}
if (!['allow', 'approved'].includes(decision.status)) {
return runtime.handleDecision(decision);
}
const result = await toolRegistry[next.tool_name](next.arguments);
Observe, Update Context, Repeat
After each tool call, the result becomes new context. The runtime reasons again and either chooses another tool or decides the goal is complete.
- Result becomes context
- Runtime re-plans from the current state
- No predefined number or order of actions
task.memory.push({ tool: next.tool_name, result });
const nextDecision = await runtime.plan({
goal: task.goal,
context: task.context,
memory: task.memory,
tools: availableTools
});
// If the goal is not complete, Gate + execute the next chosen tool.
// Then observe its result and run this loop again.
Final Output
The runtime exits the action loop only when it determines the task goal is complete. The final response is separate from any intermediate tool result.
- Completion is decided by the runtime
- Intermediate actions stay inside the loop
- Return one final task result
if (nextDecision.done) {
return nextDecision.output;
}
// Otherwise continue the runtime loop.