Architecture
This document covers the internal architecture of Agent-TS, including the component structure, message flow, and extension patterns.
Component Overview
File Responsibilities
Core Files
| File | Responsibility |
|---|---|
index.ts | Express server, webhook routes, agent lifecycle |
smartpay-agent.ts | OpenAI client, assistant management, conversation state |
gateway-client.ts | Gateway API client with typed interfaces |
config.ts | Environment variable parsing and defaults |
logger.ts | Pino logging adapter |
Tools Directory
| File | Functions |
|---|---|
wallet.ts | check_balance, get_transactions, initiate_transfer, get_account_limits |
cards.ts | get_user_cards, link_card, block_card, unblock_card |
kyc.ts | upload_kyc_image, upgrade_kyc |
registration.ts | register_subscriber |
agent.ts | Agent-specific operations |
Service Files
| File | Responsibility |
|---|---|
ocr-service.ts | Tesseract.js OCR extraction and scoring |
id-card-validator.ts | OpenAI Vision + heuristics for ID validation |
image-compressor.ts | Image optimization before upload |
s3-uploader.ts | Private S3 uploads with pre-signed URLs |
Request Flow
Text Message Processing
Image Upload Processing
OpenAI Integration
Assistant Configuration
const assistant = await openai.beta.assistants.create({
name: "SmartPay Wallet Assistant",
model: "gpt-4o-mini",
instructions: `You are a helpful assistant for SmartPay mobile wallet users.
Help users check balances, send money, and manage their cards.
Always verify transaction details before executing.`,
tools: toolDefinitions
});
Tool Registration Pattern
// Define the tool schema
export const checkBalanceDefinition = {
type: "function" as const,
function: {
name: "check_balance",
description: "Get the user's current wallet balance",
parameters: {
type: "object",
properties: {
currency: {
type: "string",
enum: ["SLE", "USD"],
description: "Currency to check balance for"
}
},
required: []
}
}
};
// Implement the handler
export async function checkBalanceHandler(
args: { currency?: string },
context: ToolContext
): Promise<ToolResult> {
const balance = await gatewayClient.getBalance(
context.userId,
args.currency || "SLE"
);
return {
success: true,
data: balance,
message: `Balance: ${balance.balance} ${balance.currency}`
};
}
Security Layers
Input Sanitization
// chat-security.ts
export function sanitizeInput(input: string): string {
// Remove potential injection attacks
// Validate phone number format
// Strip control characters
return sanitized;
}
Rate Limiting
// rate-limiter.ts
const limiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 30, // 30 requests per minute per phone
keyGenerator: (req) => req.body.phoneE164
});
Service Authentication
For internal service calls, the agent uses HMAC-based authentication:
// service-auth.ts
export function generateServiceAuthHeader(
secret: string,
timestamp: string,
body: string
): string {
const signature = crypto
.createHmac('sha256', secret)
.update(`${timestamp}${body}`)
.digest('hex');
return `HMAC ${timestamp}:${signature}`;
}
Error Handling
Graceful Degradation
try {
const result = await gatewayClient.transfer(params);
return { success: true, data: result };
} catch (error) {
if (error.code === 'INSUFFICIENT_BALANCE') {
return {
success: false,
error: 'Insufficient balance for this transfer',
userMessage: 'You do not have enough balance. Please top up.'
};
}
// Log and return generic error
logger.error('Transfer failed', { error, params });
return {
success: false,
error: 'Transfer failed',
userMessage: 'Unable to complete transfer. Please try again.'
};
}
Extension Guide
Adding a New Tool
- Create the tool definition and handler:
// src/tools/my-tool.ts
export const myToolDefinition = {
type: "function" as const,
function: {
name: "my_tool",
description: "Description of what this tool does",
parameters: {
type: "object",
properties: {
param1: { type: "string" }
},
required: ["param1"]
}
}
};
export async function myToolHandler(
args: { param1: string },
context: ToolContext
): Promise<ToolResult> {
// Implementation
return { success: true, data: result };
}
- Register in
smartpay-agent.ts:
import { myToolDefinition, myToolHandler } from './tools/my-tool';
toolDefinitions.push(myToolDefinition);
toolHandlers['my_tool'] = myToolHandler;
- Restart the agent to apply changes.