Node SDK: Agent Integration
This guide covers patterns for integrating Keito with AI agents in TypeScript/JavaScript applications.
The Agent Session Pattern
The most common pattern: start a timer when the agent begins work, stop it when done, and log an LLM expense for token costs.
import { Keito } from '@keito/sdk';
const keito = new Keito();
// 1. Start a running timer
const entry = await keito.timeEntries.create({
project_id: 'prj_abc',
task_id: 'tsk_001',
spent_date: new Date().toISOString().split('T')[0],
hours: 0,
is_running: true,
source: 'agent',
metadata: {
agent_id: 'review-bot-01',
agent_type: 'claude-code',
session_id: crypto.randomUUID(),
model: 'claude-opus-4-6',
},
});
// 2. Agent does work...
const result = await myAgent.run('Review the authentication module');
// 3. Stop the timer
await keito.timeEntries.update(entry.id, {
hours: 1.5,
is_running: false,
notes: 'Reviewed and refactored auth module',
});
// 4. Log LLM cost
await keito.expenses.create({
project_id: 'prj_abc',
expense_category_id: 'cat_llm_usage',
spent_date: new Date().toISOString().split('T')[0],
units: 40,
notes: 'claude-opus-4-6: 25k input + 15k output',
source: 'agent',
metadata: {
agent_id: 'review-bot-01',
session_id: entry.metadata.session_id,
model: 'claude-opus-4-6',
input_tokens: 25000,
output_tokens: 15000,
},
});
The Wrapper Helper
For repeated agent calls, use a wrapper that handles tracking automatically:
async function trackAgentWork<T>(
keito: Keito,
config: {
projectId: string;
taskId: string;
agentId: string;
model: string;
},
work: () => Promise<{
result: T;
inputTokens: number;
outputTokens: number;
}>,
): Promise<T> {
const sessionId = crypto.randomUUID();
const today = new Date().toISOString().split('T')[0];
const entry = await keito.timeEntries.create({
project_id: config.projectId,
task_id: config.taskId,
spent_date: today,
hours: 0,
is_running: true,
source: 'agent',
metadata: {
agent_id: config.agentId,
session_id: sessionId,
model: config.model,
},
});
const startTime = Date.now();
try {
const { result, inputTokens, outputTokens } = await work();
const hours = (Date.now() - startTime) / 3_600_000;
await keito.timeEntries.update(entry.id, {
hours: Math.round(hours * 100) / 100,
is_running: false,
});
const totalTokensK = (inputTokens + outputTokens) / 1000;
await keito.expenses.create({
project_id: config.projectId,
expense_category_id: 'cat_llm_usage',
spent_date: today,
units: totalTokensK,
source: 'agent',
metadata: {
agent_id: config.agentId,
session_id: sessionId,
model: config.model,
input_tokens: inputTokens,
output_tokens: outputTokens,
},
});
return result;
} catch (err) {
await keito.timeEntries.delete(entry.id);
throw err;
}
}
Metadata Best Practices
- Always include
agent_idandsession_id. - Use
session_idto correlate time entries with their LLM expenses. - Keep metadata under 4KB.
- Include
modelfor cost analysis across different LLMs. - Don’t store sensitive data in metadata — it’s visible in reports.