First running version
This commit is contained in:
225
reference_impl/agents/agent-base.ts
Normal file
225
reference_impl/agents/agent-base.ts
Normal file
@@ -0,0 +1,225 @@
|
||||
/**
|
||||
* Lightweight AgentBase for reference implementation.
|
||||
*
|
||||
* When instantiated by the platform Host, deps (AgentDeps from platform_impl)
|
||||
* is passed as the third constructor argument. AgentBase wires callTool to go
|
||||
* through the platform's MCPlet router with pool-access, rate-limit, and
|
||||
* Passkey enforcement — without importing platform types directly.
|
||||
*
|
||||
* In standalone/test mode, call bindCallTool() to inject a mock implementation.
|
||||
*/
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import type { A2ATaskRequest, A2ATaskResponse } from './platform-types.js';
|
||||
|
||||
// Structural shape of the platform deps we need (duck typing, no import required)
|
||||
interface PlatformDeps {
|
||||
mcpRouter?: {
|
||||
callTool?: (
|
||||
toolName: string,
|
||||
args: Record<string, unknown>,
|
||||
) => Promise<{ content: Array<{ type: string; text?: string }> }>;
|
||||
};
|
||||
poolRegistry?: {
|
||||
getAllTools?: () => Array<{
|
||||
name: string;
|
||||
meta: {
|
||||
pool?: string;
|
||||
mcpletType: string;
|
||||
auth?: { required?: string; enforcement?: string; promptMessage?: string };
|
||||
};
|
||||
}>;
|
||||
canAgentAccess?: (agentId: string, pool: string | undefined, pools: string[]) => boolean;
|
||||
checkRateLimit?: (pool: string) => boolean;
|
||||
};
|
||||
auditLog?: {
|
||||
record?: (entry: Record<string, unknown>) => void;
|
||||
};
|
||||
passkeyServer?: {
|
||||
startCeremony?: (message: string) => Promise<unknown>;
|
||||
};
|
||||
}
|
||||
|
||||
export abstract class AgentBase {
|
||||
abstract readonly agentId: string;
|
||||
abstract readonly accessiblePools: string[];
|
||||
|
||||
abstract handle(task: A2ATaskRequest): Promise<A2ATaskResponse>;
|
||||
|
||||
protected callTool: (
|
||||
toolName: string,
|
||||
args: Record<string, unknown>,
|
||||
) => Promise<unknown>;
|
||||
|
||||
/** Set by subclasses (or AgentBase.handle wrapper) before calling tools in a task. */
|
||||
protected _currentContextId: string | undefined = undefined;
|
||||
|
||||
/** Per-contextId assertion cache for 'workflow' enforcement tools. */
|
||||
protected readonly _assertionCache = new Map<string, unknown>();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
constructor(_agentId?: string, _accessiblePools?: string[], deps?: any) {
|
||||
if (deps) {
|
||||
this.callTool = buildPlatformCallTool(this as unknown as AgentBase & { agentId: string; accessiblePools: string[] }, deps as PlatformDeps);
|
||||
} else {
|
||||
this.callTool = async (toolName) => {
|
||||
throw new Error(
|
||||
`[agent-base] callTool("${toolName}") called without a bound implementation. ` +
|
||||
'Inject via bindCallTool() or use the platform Host.',
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/** Bind a tool caller (used in standalone/test mode). */
|
||||
bindCallTool(fn: (toolName: string, args: Record<string, unknown>) => Promise<unknown>): void {
|
||||
this.callTool = fn;
|
||||
}
|
||||
|
||||
protected success(task: A2ATaskRequest, result: unknown): A2ATaskResponse {
|
||||
return {
|
||||
messageId: randomUUID(),
|
||||
contextId: task.contextId,
|
||||
senderId: this.agentId,
|
||||
recipientId: task.senderId,
|
||||
timestamp: new Date().toISOString(),
|
||||
type: 'task_response',
|
||||
replyToMessageId: task.messageId,
|
||||
status: 'success',
|
||||
payload: { result },
|
||||
};
|
||||
}
|
||||
|
||||
protected error(task: A2ATaskRequest, message: string, code?: string): A2ATaskResponse {
|
||||
return {
|
||||
messageId: randomUUID(),
|
||||
contextId: task.contextId,
|
||||
senderId: this.agentId,
|
||||
recipientId: task.senderId,
|
||||
timestamp: new Date().toISOString(),
|
||||
type: 'task_response',
|
||||
replyToMessageId: task.messageId,
|
||||
status: 'error',
|
||||
payload: { error: { message, code } },
|
||||
};
|
||||
}
|
||||
|
||||
protected cancelled(task: A2ATaskRequest, reason: string): A2ATaskResponse {
|
||||
return {
|
||||
messageId: randomUUID(),
|
||||
contextId: task.contextId,
|
||||
senderId: this.agentId,
|
||||
recipientId: task.senderId,
|
||||
timestamp: new Date().toISOString(),
|
||||
type: 'task_response',
|
||||
replyToMessageId: task.messageId,
|
||||
status: 'cancelled',
|
||||
payload: { error: { message: reason } },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a callTool function that goes through the platform's MCPlet router
|
||||
* with pool-access, rate-limit, and Passkey enforcement.
|
||||
*/
|
||||
function buildPlatformCallTool(
|
||||
agent: AgentBase & { agentId: string; accessiblePools: string[] },
|
||||
deps: PlatformDeps,
|
||||
): (toolName: string, args: Record<string, unknown>) => Promise<unknown> {
|
||||
// Access protected cache fields via a typed cast (same class file, safe).
|
||||
const agentCache = agent as unknown as {
|
||||
_currentContextId: string | undefined;
|
||||
_assertionCache: Map<string, unknown>;
|
||||
};
|
||||
|
||||
return async (toolName: string, args: Record<string, unknown>) => {
|
||||
const tools = deps.poolRegistry?.getAllTools?.() ?? [];
|
||||
const tool = tools.find((t) => t.name === toolName);
|
||||
|
||||
// Pool access check
|
||||
if (tool?.meta.pool) {
|
||||
const canAccess =
|
||||
deps.poolRegistry?.canAgentAccess?.(agent.agentId, tool.meta.pool, agent.accessiblePools) ?? true;
|
||||
if (!canAccess) {
|
||||
throw new Error(`[${agent.agentId}] Pool access denied for tool "${toolName}" (pool: ${tool.meta.pool})`);
|
||||
}
|
||||
}
|
||||
|
||||
// Rate limit check
|
||||
if (tool?.meta.pool) {
|
||||
const allowed = deps.poolRegistry?.checkRateLimit?.(tool.meta.pool) ?? true;
|
||||
if (!allowed) {
|
||||
throw new Error(`[${agent.agentId}] Rate limit exceeded for pool "${tool.meta.pool}"`);
|
||||
}
|
||||
}
|
||||
|
||||
// Passkey interception for action tools
|
||||
let callArgs = args;
|
||||
if (tool?.meta.mcpletType === 'action' && tool.meta.auth?.required === 'passkey') {
|
||||
const enforcement = tool.meta.auth.enforcement ?? 'strict';
|
||||
|
||||
let assertion: unknown;
|
||||
if (enforcement === 'workflow') {
|
||||
// Cache key: contextId (scopes assertion to one Director cycle)
|
||||
const contextId = agentCache._currentContextId ?? 'default';
|
||||
if (agentCache._assertionCache.has(contextId)) {
|
||||
assertion = agentCache._assertionCache.get(contextId);
|
||||
console.log(`[${agent.agentId}] Reusing cached Passkey assertion for contextId="${contextId}" (tool: ${toolName})`);
|
||||
} else {
|
||||
assertion = await deps.passkeyServer?.startCeremony?.(
|
||||
tool.meta.auth.promptMessage ?? `Authorize workflow actions for this task`,
|
||||
);
|
||||
if (!assertion) {
|
||||
throw new Error(`[${agent.agentId}] Passkey authentication cancelled for "${toolName}"`);
|
||||
}
|
||||
agentCache._assertionCache.set(contextId, assertion);
|
||||
console.log(`[${agent.agentId}] Passkey assertion cached for contextId="${contextId}"`);
|
||||
}
|
||||
} else {
|
||||
// 'strict': new ceremony per invocation
|
||||
assertion = await deps.passkeyServer?.startCeremony?.(
|
||||
tool.meta.auth.promptMessage ?? `Authorize action: ${toolName}`,
|
||||
);
|
||||
if (!assertion) {
|
||||
throw new Error(`[${agent.agentId}] Passkey authentication cancelled for "${toolName}"`);
|
||||
}
|
||||
}
|
||||
|
||||
callArgs = { ...args, _mcplet_auth: assertion };
|
||||
}
|
||||
|
||||
// Audit log for action tools
|
||||
if (tool?.meta.mcpletType === 'action') {
|
||||
deps.auditLog?.record?.({
|
||||
type: 'action_invocation',
|
||||
agentId: agent.agentId,
|
||||
toolName,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
if (!deps.mcpRouter?.callTool) {
|
||||
throw new Error(`[${agent.agentId}] mcpRouter not available for tool "${toolName}"`);
|
||||
}
|
||||
|
||||
const result = await deps.mcpRouter.callTool(toolName, callArgs);
|
||||
const text = result.content.find((c) => c.type === 'text')?.text ?? '{}';
|
||||
|
||||
let parsed: unknown;
|
||||
try {
|
||||
parsed = JSON.parse(text);
|
||||
} catch {
|
||||
return text;
|
||||
}
|
||||
|
||||
// MCPlet server wraps results in { result, _meta } envelope — unwrap it.
|
||||
const envelope = parsed as { result?: unknown; error?: { message: string; code: string } };
|
||||
if (envelope && typeof envelope === 'object' && 'error' in envelope && envelope.error) {
|
||||
throw new Error(`Tool "${toolName}" returned error: ${envelope.error.message} (${envelope.error.code})`);
|
||||
}
|
||||
if (envelope && typeof envelope === 'object' && 'result' in envelope) {
|
||||
return envelope.result;
|
||||
}
|
||||
return parsed;
|
||||
};
|
||||
}
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
109
reference_impl/agents/info-gathering/index.ts
Normal file
109
reference_impl/agents/info-gathering/index.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* 情報収集・分析 Agent
|
||||
*
|
||||
* accessiblePools: [info-pool]
|
||||
* 利用可能ツール: fetch_web_content (info-pool), call_external_api (info-pool),
|
||||
* query_crm (pool-less), query_erp (pool-less), query_hr (pool-less)
|
||||
*
|
||||
* タスク: 天気予報・在庫・顧客キャンセル傾向を収集し、分析サマリを返す
|
||||
*/
|
||||
import type { A2ATaskRequest, A2ATaskResponse } from '../platform-types.js';
|
||||
import { AgentBase } from '../agent-base.js';
|
||||
|
||||
export class InfoGatheringAgent extends AgentBase {
|
||||
readonly agentId = 'info-gathering-agent';
|
||||
readonly accessiblePools = ['info-pool'];
|
||||
|
||||
async handle(task: A2ATaskRequest): Promise<A2ATaskResponse> {
|
||||
const instruction = (task.payload.parameters['instruction'] as string | undefined) ?? '';
|
||||
const targetDate = new Date().toISOString().slice(0, 10);
|
||||
|
||||
try {
|
||||
// 1. Collect weather forecast
|
||||
const weather = await this.callTool('fetch_web_content', {
|
||||
source: 'weather_forecast',
|
||||
date: targetDate,
|
||||
});
|
||||
|
||||
// 2. Collect dessert inventory via external API
|
||||
const inventory = await this.callTool('call_external_api', {
|
||||
api: 'inventory',
|
||||
params: { item: 'dessert' },
|
||||
});
|
||||
|
||||
// 3. Query CRM for high-cancellation-tendency customers
|
||||
const customers = await this.callTool('query_crm', {
|
||||
entity: 'customers',
|
||||
filter: 'rain_cancel_tendency',
|
||||
});
|
||||
|
||||
// 4. Query reservations for tomorrow
|
||||
const reservations = await this.callTool('query_crm', {
|
||||
entity: 'reservations',
|
||||
filter: `date=${targetDate}`,
|
||||
});
|
||||
|
||||
// 5. Synthesize analysis
|
||||
const analysis = this.synthesize({ weather, inventory, customers, reservations, targetDate, instruction });
|
||||
|
||||
console.log(`[info-gathering] Analysis complete: ${analysis.summary}`);
|
||||
|
||||
return this.success(task, {
|
||||
analysis,
|
||||
rawData: { weather, inventory, customers, reservations },
|
||||
});
|
||||
} catch (err) {
|
||||
return this.error(task, (err as Error).message);
|
||||
}
|
||||
}
|
||||
|
||||
private synthesize(data: {
|
||||
weather: unknown;
|
||||
inventory: unknown;
|
||||
customers: unknown;
|
||||
reservations: unknown;
|
||||
targetDate: string;
|
||||
instruction: string;
|
||||
}): Record<string, unknown> {
|
||||
const wx = data.weather as { forecasts?: Array<{ condition?: string; summary?: string; precipitationProbability?: number }> };
|
||||
const forecast = wx?.forecasts?.[0];
|
||||
const isRainy = forecast?.condition === 'rainy';
|
||||
const precipProb = forecast?.precipitationProbability ?? 0;
|
||||
|
||||
const inv = data.inventory as { items?: Array<{ name?: string; stock?: number; category?: string }> };
|
||||
const desserts = (inv?.items ?? []).filter((i) => i.category === 'dessert');
|
||||
const hasDessertStock = desserts.some((d) => (d.stock ?? 0) > 0);
|
||||
|
||||
const cust = data.customers as { customers?: Array<unknown>; total?: number };
|
||||
const highCancelCount = cust?.total ?? (cust?.customers?.length ?? 0);
|
||||
|
||||
const res = data.reservations as { reservations?: Array<unknown>; total?: number };
|
||||
const reservationCount = res?.total ?? (res?.reservations?.length ?? 0);
|
||||
|
||||
const actionRecommended = isRainy && hasDessertStock && highCancelCount > 0;
|
||||
|
||||
return {
|
||||
targetDate: data.targetDate,
|
||||
weather: {
|
||||
condition: forecast?.condition ?? 'unknown',
|
||||
isRainy,
|
||||
precipitationProbability: precipProb,
|
||||
summary: forecast?.summary ?? '',
|
||||
},
|
||||
inventory: {
|
||||
hasDessertStock,
|
||||
dessertsAvailable: desserts.map((d) => ({ name: d.name, stock: d.stock })),
|
||||
},
|
||||
customers: {
|
||||
highCancelTendencyCount: highCancelCount,
|
||||
},
|
||||
reservations: {
|
||||
tomorrowCount: reservationCount,
|
||||
},
|
||||
actionRecommended,
|
||||
summary: actionRecommended
|
||||
? `明日は雨予報(${precipProb}%)で、デザート在庫あり、高キャンセル傾向顧客${highCancelCount}名・予約${reservationCount}件あり。無料デザートキャンペーンを推奨。`
|
||||
: `現時点でキャンペーン実施条件が揃っていません。`,
|
||||
};
|
||||
}
|
||||
}
|
||||
108
reference_impl/agents/planning/index.ts
Normal file
108
reference_impl/agents/planning/index.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* 企画・Plan Agent
|
||||
*
|
||||
* accessiblePools: [] (pool-less のみ)
|
||||
* 利用可能ツール: query_crm, query_erp, query_hr (全て pool-less)
|
||||
*
|
||||
* タスク: 情報収集結果を受け取り、具体的な施策を立案する。
|
||||
* Passkey (host-only) で店長に承認を求める。
|
||||
*/
|
||||
import type { A2ATaskRequest, A2ATaskResponse } from '../platform-types.js';
|
||||
import { AgentBase } from '../agent-base.js';
|
||||
|
||||
interface AnalysisResult {
|
||||
targetDate: string;
|
||||
weather: { isRainy: boolean; precipitationProbability: number; summary: string };
|
||||
inventory: { hasDessertStock: boolean; dessertsAvailable: Array<{ name: string; stock: number }> };
|
||||
customers: { highCancelTendencyCount: number };
|
||||
reservations: { tomorrowCount: number };
|
||||
actionRecommended: boolean;
|
||||
summary: string;
|
||||
}
|
||||
|
||||
export class PlanningAgent extends AgentBase {
|
||||
readonly agentId = 'planning-agent';
|
||||
readonly accessiblePools: string[] = [];
|
||||
|
||||
async handle(task: A2ATaskRequest): Promise<A2ATaskResponse> {
|
||||
const { analysis, rawData } = task.payload.parameters as {
|
||||
analysis?: AnalysisResult;
|
||||
rawData?: unknown;
|
||||
};
|
||||
|
||||
if (!analysis) {
|
||||
return this.error(task, 'No analysis data provided in task parameters');
|
||||
}
|
||||
|
||||
if (!analysis.actionRecommended) {
|
||||
return this.cancelled(task, `施策実施条件未達: ${analysis.summary}`);
|
||||
}
|
||||
|
||||
try {
|
||||
// 1. Get today's reservations from CRM for targeting
|
||||
const reservationData = await this.callTool('query_crm', {
|
||||
entity: 'reservations',
|
||||
filter: `date=${analysis.targetDate}`,
|
||||
}) as { reservations?: Array<{ customerName: string; customerId: string }> };
|
||||
|
||||
// 2. Get dessert inventory details from ERP
|
||||
const inventoryData = await this.callTool('query_erp', {
|
||||
entity: 'inventory',
|
||||
item: 'dessert',
|
||||
}) as { items?: Array<{ name: string; stock: number; costPerUnit: number }> };
|
||||
|
||||
// 3. Build the plan
|
||||
const desserts = (inventoryData?.items ?? []).filter((i) => (i.stock ?? 0) > 0);
|
||||
const targetDessert = desserts[0];
|
||||
const targetCustomers = (reservationData?.reservations ?? []).slice(0, analysis.customers.highCancelTendencyCount);
|
||||
|
||||
const plan = {
|
||||
title: '無料デザートキャンペーン',
|
||||
targetDate: analysis.targetDate,
|
||||
rationale: analysis.summary,
|
||||
campaign: {
|
||||
dessertItem: targetDessert?.name ?? 'デザート',
|
||||
freeItemPerCustomer: 1,
|
||||
totalCost: (targetDessert?.costPerUnit ?? 0) * targetCustomers.length,
|
||||
},
|
||||
targetCustomers: targetCustomers.map((c) => ({
|
||||
customerId: c.customerId,
|
||||
name: c.customerName,
|
||||
})),
|
||||
emailTemplate: buildEmailTemplate(analysis.targetDate, targetDessert?.name ?? 'デザート'),
|
||||
createdAt: new Date().toISOString(),
|
||||
status: 'pending_approval',
|
||||
};
|
||||
|
||||
console.log(
|
||||
`[planning] Plan created: ${plan.title} for ${targetCustomers.length} customers`,
|
||||
);
|
||||
|
||||
// 4. Approval is handled by the Host (Passkey ceremony triggered by DispatchAgent)
|
||||
// PlanningAgent returns the plan with status: pending_approval.
|
||||
// The orchestration layer (Director → Dispatch) is responsible for auth flow.
|
||||
return this.success(task, {
|
||||
plan,
|
||||
rawData,
|
||||
nextAgent: 'dispatch-agent',
|
||||
});
|
||||
} catch (err) {
|
||||
return this.error(task, (err as Error).message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function buildEmailTemplate(date: string, dessertName: string): string {
|
||||
return `件名: 【特別ご招待】明日のご来店に無料${dessertName}をプレゼント
|
||||
|
||||
お客様へ
|
||||
|
||||
明日 ${date} のご予約、誠にありがとうございます。
|
||||
|
||||
明日は雨模様のお天気が予想されますが、
|
||||
特別に無料の${dessertName}をご用意しております。
|
||||
|
||||
ぜひお越しください。スタッフ一同、お待ちしております。
|
||||
|
||||
※ 本メールは予約システムより自動送信されています。`;
|
||||
}
|
||||
33
reference_impl/agents/platform-types.ts
Normal file
33
reference_impl/agents/platform-types.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Minimal A2A type definitions for reference implementation agents.
|
||||
* Mirrors platform_impl/src/types/a2a.ts — keep in sync.
|
||||
*/
|
||||
|
||||
export interface A2ATaskRequest {
|
||||
messageId: string;
|
||||
contextId?: string;
|
||||
senderId: string;
|
||||
recipientId: string;
|
||||
timestamp?: string;
|
||||
locale?: string;
|
||||
type: 'task_request';
|
||||
payload: {
|
||||
parameters: Record<string, unknown>;
|
||||
history?: Array<{ role: 'system' | 'user' | 'assistant'; content: string }>;
|
||||
};
|
||||
}
|
||||
|
||||
export interface A2ATaskResponse {
|
||||
messageId: string;
|
||||
contextId?: string;
|
||||
senderId: string;
|
||||
recipientId: string;
|
||||
timestamp?: string;
|
||||
type: 'task_response';
|
||||
replyToMessageId: string;
|
||||
status: 'success' | 'error' | 'timeout' | 'cancelled' | 'partial';
|
||||
payload?: {
|
||||
result?: unknown;
|
||||
error?: { message: string; code?: string };
|
||||
};
|
||||
}
|
||||
22
reference_impl/agents/register.ts
Normal file
22
reference_impl/agents/register.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Register reference implementation Agent classes with the platform Host.
|
||||
* Import this module before calling MCPletHost.start() to make the agent
|
||||
* classes available for instantiation from config.
|
||||
*
|
||||
* Usage in platform host entry point:
|
||||
* import '../../reference_impl/agents/register.js';
|
||||
* import { MCPletHost } from '../platform_impl/src/host/mcplet-host.js';
|
||||
*/
|
||||
import { InfoGatheringAgent } from './info-gathering/index.js';
|
||||
import { PlanningAgent } from './planning/index.js';
|
||||
import { DispatchAgent } from './dispatch/index.js';
|
||||
|
||||
// Lazy import of registerAgentClass to avoid circular dependency at module level
|
||||
// In production, this registration is done in the host entry point.
|
||||
export { InfoGatheringAgent, PlanningAgent, DispatchAgent };
|
||||
|
||||
export const AGENT_CLASSES: Record<string, new (...args: never[]) => unknown> = {
|
||||
InfoGatheringAgent,
|
||||
PlanningAgent,
|
||||
DispatchAgent,
|
||||
};
|
||||
Reference in New Issue
Block a user