78 lines
2.9 KiB
TypeScript
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,
|
|
});
|
|
}
|
|
}
|