Files
2026-03-30 17:39:13 +09:00

78 lines
2.9 KiB
TypeScript

/**
* 発信・発注・発令 Agent
*
* accessiblePools: [media-pool]
* 利用可能ツール: read_site_stats (media-pool, read),
* send_email (media-pool, action, passkey-workflow),
* post_sns (media-pool, action, passkey-workflow)
*
* タスク: 企画・Plan Agent が作成した施策を実行する。
* send_email は action ツール (enforcement: workflow)。
* 最初の send_email 呼び出し時に一度だけ Passkey 認証を行い、
* 同じ contextId 内の後続呼び出しはキャッシュされた assertion を再利用する。
*/
import type { A2ATaskRequest, A2ATaskResponse } from '../platform-types.js';
import { AgentBase } from '../agent-base.js';
interface Plan {
title: string;
targetDate: string;
campaign: { dessertItem: string };
targetCustomers: Array<{ customerId: string; name: string }>;
emailTemplate: string;
}
export class DispatchAgent extends AgentBase {
readonly agentId = 'dispatch-agent';
readonly accessiblePools = ['media-pool'];
async handle(task: A2ATaskRequest): Promise<A2ATaskResponse> {
const { plan } = task.payload.parameters as { plan?: Plan };
if (!plan) {
return this.error(task, 'No plan provided in task parameters');
}
// Set contextId so buildPlatformCallTool can scope the workflow assertion cache
this._currentContextId = task.contextId;
const results: Array<{ customerId: string; name: string; status: string; error?: string }> = [];
for (const customer of plan.targetCustomers) {
try {
// send_email enforcement: 'workflow'. First call in this contextId triggers
// one Passkey ceremony; subsequent calls reuse the cached assertion.
await this.callTool('send_email', {
to: `${customer.customerId}@example.com`,
subject: `【特別ご招待】明日のご来店に無料${plan.campaign.dessertItem}をプレゼント`,
body: plan.emailTemplate,
customerId: customer.customerId,
});
results.push({ customerId: customer.customerId, name: customer.name, status: 'sent' });
console.log(`[dispatch] Email sent to ${customer.name} (${customer.customerId})`);
} catch (err) {
results.push({
customerId: customer.customerId,
name: customer.name,
status: 'failed',
error: (err as Error).message,
});
console.error(`[dispatch] Failed to send to ${customer.name}: ${(err as Error).message}`);
}
}
const sent = results.filter((r) => r.status === 'sent').length;
const failed = results.filter((r) => r.status === 'failed').length;
console.log(`[dispatch] Campaign dispatch complete: ${sent} sent, ${failed} failed`);
return this.success(task, {
campaign: plan.title,
targetDate: plan.targetDate,
dispatch: { sent, failed, total: plan.targetCustomers.length },
results,
});
}
}