First running version
This commit is contained in:
77
reference_impl/agents/dispatch/index.ts
Normal file
77
reference_impl/agents/dispatch/index.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* 発信・発注・発令 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,
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user