Tinkuy
@carloscortezcloud/tinkuy-agent
Minimal AI agent framework. ~200 lines. Provider-agnostic. Edge-native. Zero dependencies.
Agent Class
The main class that orchestrates the tool-use loop.
import { Agent } from '@carloscortezcloud/tinkuy-agent';
const agent = new Agent({
// Required
router: Router, // StyrRouter or any Router-compatible object
systemPrompt: 'You are...', // System prompt for the LLM
// Optional
tools: Tool[], // Array of tools from defineTool()
guard: Guard, // SayayGuard or any Guard-compatible object
maxIterations: 10, // Max tool-loop iterations (default 10)
onIteration: (msg) => void, // Called after each iteration
onToolCall: (tool, args) => void, // Called when a tool is invoked
onComplete: (result) => void, // Called when agent finishes
});Methods
agent.run(input)Run the agent with a prompt. Returns AgentResult.
const result = await agent.run('Hello!');
// → { text, toolsUsed, iterations, latencyMs, costUsd, toolResults }agent.stream(input)Stream the agent's response token by token. Returns AsyncGenerator.
for await (const chunk of agent.stream('Hello!')) {
// chunk: { type: 'token' | 'tool_call' | 'tool_result' | 'done', data }
process.stdout.write(chunk.data);
}AgentResult
| Field | Type | Description |
|---|---|---|
text | string | Final response text |
toolsUsed | string[] | Names of tools called |
iterations | number | Loop iterations |
latencyMs | number | Total time in ms |
costUsd | number | Estimated cost in USD |
toolResults | object[] | Raw tool execution results |
defineTool
Type-safe factory for creating agent tools. Uses JSON Schema for parameter validation.
import { defineTool } from '@carloscortezcloud/tinkuy-agent';
const tool = defineTool({
name: 'get_weather', 'Get weather for a city', // LLM uses this to decide
parameters: { // JSON Schema
type: 'object',
properties: {
city: { type: 'string' },
units: { type: 'string', enum: ['celsius', 'fahrenheit'] },
},
required: ['city'],
},
execute: async (args) => {
return { temperature: 22, condition: 'sunny' };
},
});Router Interface
Any object with a call() method works as a router. You can implement your own.
interface Router {
call(messages: Message[], tools?: Tool[]): Promise<RouterResult>;
}
// Minimal example (wraps raw fetch):
const myRouter: Router = {
call: async (messages, tools) => {
const res = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.OPENAI_KEY}` },
body: JSON.stringify({ model: 'gpt-4o-mini', messages }),
});
return res.json();
},
};Guard Interface
Any object with check() and record() methods works as a guard.
interface Guard {
check(userId: string, costUsd: number): Promise<GuardDecision>;
record(userId: string, costUsd: number): Promise<void>;
}
type GuardAction = 'allow' | 'warn' | 'degrade' | 'block';Hooks
Observability callbacks — no coupling to specific monitoring tools.
onIteration(msg: string) => voidCalled after each LLM call with the assistant's response text or tool call info.
onToolCall(tool: string, args: object) => voidCalled when a tool is invoked — useful for logging and tracing.
onComplete(result: AgentResult) => voidCalled when the agent finishes with the final result (streaming compatible).
Edge Compatibility
Tinkuy has zero dependencies and uses only fetch(). Works everywhere: