Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,22 @@ agent.addChannel(
telegram({ token: process.env.TELEGRAM_BOT_TOKEN! }),
);
await agent.start();

await agent.registerGroup('self-chat', {
name: 'Main',
folder: 'main',
trigger: 'always',
isMain: true,
});

const task = await agent.scheduleTask({
jid: 'self-chat',
prompt: 'Send the weekly status summary.',
scheduleType: 'cron',
scheduleValue: '0 9 * * 1',
});

console.log(task.id);
```

## Quick Start
Expand Down Expand Up @@ -75,6 +91,7 @@ Key files:

- `src/api/sdk.ts` - Public API: `createAgentLite()`, `AgentLite` interface
- `src/api/agent.ts` - Public API: `Agent` interface
- `src/api/task.ts` - Public task types for SDK scheduling and task management
- `src/api/channel-driver.ts` - Public API: `ChannelDriver` interface
- `src/agentlite-impl.ts` - AgentLite implementation (hidden from consumers)
- `src/agent-impl.ts` - Agent implementation: message loop, channels, groups
Expand Down
146 changes: 89 additions & 57 deletions docs/SPEC.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"prepare": "husky",
"setup": "tsx setup/index.ts",
"auth": "tsx src/whatsapp-auth.ts",
"test:e2e:tasks": "tsx scripts/test-task-sdk-e2e.ts",
"lint": "eslint src/",
"lint:fix": "eslint src/ --fix",
"test": "vitest run",
Expand Down
119 changes: 119 additions & 0 deletions scripts/test-task-sdk-e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#!/usr/bin/env npx tsx

import fs from 'fs';
import os from 'os';
import path from 'path';

import { createAgentLite } from '../src/api/sdk.js';

const workdir = fs.mkdtempSync(path.join(os.tmpdir(), 'agentlite-task-e2e-'));
const anthropicToken =
process.env.ANTHROPIC_AUTH_TOKEN ?? process.env.ANTHROPIC_API_KEY;
const anthropicBaseUrl = process.env.ANTHROPIC_BASE_URL;

async function main(): Promise<void> {
console.log('=== AgentLite Task SDK E2E ===');
console.log(`Workdir: ${workdir}`);
console.log(
`Image: ${process.env.BOX_IMAGE || 'ghcr.io/boxlite-ai/agentlite-agent:latest'}`,
);
if (!anthropicToken) {
throw new Error(
'Missing ANTHROPIC_AUTH_TOKEN or ANTHROPIC_API_KEY for container credentials',
);
}

let platform = await createAgentLite({ workdir, timezone: 'UTC' });
let agent = platform.getOrCreateAgent('main', {
name: 'Andy',
credentials: async () => ({
ANTHROPIC_AUTH_TOKEN: anthropicToken,
ANTHROPIC_API_KEY: anthropicToken,
...(anthropicBaseUrl ? { ANTHROPIC_BASE_URL: anthropicBaseUrl } : {}),
}),
});

try {
await agent.start();

await agent.registerGroup('main@test', {
name: 'Main',
folder: 'main',
trigger: 'always',
isMain: true,
});
await agent.registerGroup('team@test', {
name: 'Task E2E',
folder: 'task_e2e',
trigger: '@Andy',
});

const task = await agent.scheduleTask({
jid: 'team@test',
prompt: 'Reply with exactly: task sdk e2e ok',
scheduleType: 'once',
scheduleValue: '2024-01-01T00:00:00Z',
});

console.log(`Scheduled task ${task.id}`);
console.log(
'Restarting through a fresh AgentLite instance so the scheduler immediately rechecks due tasks...',
);

await platform.stop();
platform = await createAgentLite({ workdir, timezone: 'UTC' });
agent = platform.getOrCreateAgent('main', {
name: 'Andy',
credentials: async () => ({
ANTHROPIC_AUTH_TOKEN: anthropicToken,
ANTHROPIC_API_KEY: anthropicToken,
...(anthropicBaseUrl ? { ANTHROPIC_BASE_URL: anthropicBaseUrl } : {}),
}),
});
await agent.start();

const timeoutMs = 120_000;
const pollIntervalMs = 1_000;
const startedAt = Date.now();

while (Date.now() - startedAt < timeoutMs) {
const current = agent.getTask(task.id);
if (current?.lastRun && current.runs.length > 0) {
console.log('Task completed.');
console.log(`Status: ${current.status}`);
console.log(`Last run: ${current.lastRun}`);
console.log(`Last result: ${current.lastResult}`);
console.log(`Run count: ${current.runs.length}`);

if (current.status !== 'completed') {
throw new Error(`Expected completed status, got ${current.status}`);
}
if (current.runs.length < 1) {
throw new Error('Expected at least one run log entry');
}
if (!current.lastRun) {
throw new Error('Expected lastRun to be set');
}

console.log('PASS: Task SDK E2E completed successfully');
return;
}

await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
}

throw new Error(`Timed out waiting for task ${task.id} to complete`);
} finally {
await platform.stop();
try {
fs.rmSync(workdir, { recursive: true, force: true });
} catch {
/* ignore */
}
}
}

main().catch((err) => {
console.error('FAIL:', err instanceof Error ? err.message : String(err));
process.exit(1);
});
Loading
Loading