package sdk import ( "bytes" "context" "crypto/hmac" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "io" "mime/multipart" "net/http" "sync" "time" "synapse/pkg/types" ) // ============================================================================= // 协议类型 // ============================================================================= // Protocol 协议类型 type Protocol string const ( ProtoP2A Protocol = "P2A" // 外部调用协议 ProtoA2A Protocol = "A2A" // 点对点通信协议 ProtoA2N Protocol = "A2N" // 群组协作协议 ) // ============================================================================= // 客户端配置 // ============================================================================= // Config 客户端配置 type Config struct { Protocol Protocol // 协议类型,决定客户端行为 // P2A 协议字段 BaseURL string // Agent 服务地址 APIKey string // API Key // A2A / A2N 协议字段 AgentEndpoint types.AgentEndpoint // 本 Agent 端点 AuthSecret string // HMAC 认证密钥 CallbackPort int // 回调监听端口 // 通用字段 Timeout time.Duration // HTTP 超时(默认 30s) MaxRetries int // 最大重试次数 } // ============================================================================= // 客户端 // ============================================================================= // Client 灵隙 SDK 客户端 // 通过 Protocol 类型区分三种协议行为 type Client struct { config Config // 通用 httpClient *http.Client // A2A 任务状态 tasks map[string]*P2PTaskState tasksMu sync.RWMutex // A2N 群组状态 groups map[string]*GroupState groupsMu sync.RWMutex // 事件回调 eventHandler A2NEventHandler // A2A 消息回调 messageHandler A2AMessageHandler } // P2PTaskState P2P 任务状态 type P2PTaskState struct { TaskID string Status types.P2PTaskStatus Progress int Message string Result any Error *types.SynapseError } // GroupState 群组状态 type GroupState struct { GroupID string Name string Topology types.GroupTopology Status types.GroupStatus Members []types.GroupMember Role string } // ============================================================================= // 构造函数 // ============================================================================= // NewClient 创建 SDK 客户端 // 通过 cfg.Protocol 区分协议行为: // - ProtoP2A:外部调用,需要 BaseURL // - ProtoA2A:点对点通信,需要 AgentEndpoint // - ProtoA2N:群组协作,需要 AgentEndpoint func NewClient(cfg Config) *Client { if cfg.Timeout == 0 { cfg.Timeout = 30 * time.Second } return &Client{ config: cfg, httpClient: &http.Client{Timeout: cfg.Timeout}, tasks: make(map[string]*P2PTaskState), groups: make(map[string]*GroupState), } } // Protocol 返回客户端协议类型 func (c *Client) Protocol() Protocol { return c.config.Protocol } // Config 返回客户端配置 func (c *Client) Config() Config { return c.config } // ============================================================================= // 事件处理器设置 // ============================================================================= // A2AMessageHandler A2A 消息回调接口 type A2AMessageHandler interface { HandleTaskRequest(ctx context.Context, msg *types.UnifiedSynapseMessage) (*types.UnifiedSynapseMessage, error) HandleTaskStatusUpdate(ctx context.Context, msg *types.UnifiedSynapseMessage) error HandleTaskComplete(ctx context.Context, msg *types.UnifiedSynapseMessage) error HandleTaskFailed(ctx context.Context, msg *types.UnifiedSynapseMessage) error HandleNeedHuman(ctx context.Context, msg *types.UnifiedSynapseMessage) error } // SetA2AMessageHandler 设置 A2A 消息回调处理器 func (c *Client) SetA2AMessageHandler(handler A2AMessageHandler) { c.messageHandler = handler } // A2NEventHandler A2N 群组事件接口 type A2NEventHandler interface { OnGroupCreated(ctx context.Context, groupID string) error OnGroupDisbanded(ctx context.Context, groupID string) error OnMemberJoined(ctx context.Context, groupID string, member types.GroupMember) error OnMemberLeft(ctx context.Context, groupID, agentID string) error OnTaskSplit(ctx context.Context, groupID, originalTaskID string, subtasks []types.Subtask) error OnSubtaskProgress(ctx context.Context, groupID, subtaskID string, progress int) error OnTaskMerged(ctx context.Context, groupID, originalTaskID string, result any) error OnVoteInitiated(ctx context.Context, groupID string, vote types.GroupVotePayload) error OnVoteResult(ctx context.Context, groupID, voteID, option string, passed bool) error OnEscalation(ctx context.Context, groupID, escalationID string, level int) error OnBroadcast(ctx context.Context, groupID string, sender types.AgentEndpoint, message []byte) error } // SetA2NEventHandler 设置 A2N 群组事件处理器 func (c *Client) SetA2NEventHandler(handler A2NEventHandler) { c.eventHandler = handler } // ============================================================================= // P2A 协议方法 — 外部调用 // ============================================================================= // ============================================================================= // P2A 数据结构 // ============================================================================= // TaskRequest 任务请求 type TaskRequest struct { TaskType string `json:"task_type"` Payload map[string]any `json:"payload"` ExternalTaskID string `json:"external_task_id,omitempty"` Priority string `json:"priority,omitempty"` CallbackURL string `json:"callback_url,omitempty"` CallbackToken string `json:"callback_token,omitempty"` } // TaskResponse 任务提交响应 type TaskResponse struct { Success bool `json:"success"` TaskID string `json:"task_id"` Status string `json:"status"` Message string `json:"message,omitempty"` Error string `json:"error,omitempty"` } // TaskInfo 任务详情 type TaskInfo struct { ID string `json:"id"` Type string `json:"type"` Status string `json:"status"` Progress int `json:"progress"` Message string `json:"message,omitempty"` CreatedAt string `json:"created_at"` UpdatedAt string `json:"updated_at"` CompletedAt string `json:"completed_at,omitempty"` ETA string `json:"eta,omitempty"` Result map[string]any `json:"result,omitempty"` Error *TaskError `json:"error,omitempty"` ExternalTaskID string `json:"external_task_id,omitempty"` } // TaskError 任务错误 type TaskError struct { Code string `json:"code"` Message string `json:"message"` } // TaskDetailResponse 任务详情响应 type TaskDetailResponse struct { Success bool `json:"success"` Task TaskInfo `json:"task"` Error string `json:"error,omitempty"` } // TaskStatusResponse 任务状态响应 type TaskStatusResponse struct { Success bool `json:"success"` TaskID string `json:"task_id"` Status string `json:"status"` Progress int `json:"progress"` Error string `json:"error,omitempty"` } // TaskListResponse 任务列表响应 type TaskListResponse struct { Success bool `json:"success"` Tasks []TaskInfo `json:"tasks"` Total int `json:"total"` Limit int `json:"limit"` Offset int `json:"offset"` HasMore bool `json:"has_more"` Error string `json:"error,omitempty"` } // CapabilitiesResponse 能力查询响应 type CapabilitiesResponse struct { Success bool `json:"success"` Capabilities CapabilitiesInfo `json:"capabilities"` Error string `json:"error,omitempty"` } // CapabilitiesInfo Agent 能力信息 type CapabilitiesInfo struct { AgentID string `json:"agent_id"` AgentName string `json:"agent_name"` SystemID string `json:"system_id"` ProtocolVersion string `json:"protocol_version"` SupportedTaskTypes []SupportedTaskType `json:"supported_task_types"` SupportedTransportModes []string `json:"supported_transport_modes"` MaxFileSize int64 `json:"max_file_size"` MaxConcurrentTasks int `json:"max_concurrent_tasks"` SupportProgressUpdates bool `json:"support_progress_updates"` SupportStreaming bool `json:"support_streaming"` SupportHiL bool `json:"support_hil"` SupportGroups bool `json:"support_groups"` } // SupportedTaskType 支持的任务类型 type SupportedTaskType struct { Type string `json:"type"` Name string `json:"name"` Description string `json:"description"` EstimatedTime string `json:"estimated_time"` InputSchema string `json:"input_schema,omitempty"` OutputSchema string `json:"output_schema,omitempty"` } // FileParam 上传文件参数 type FileParam struct { FieldName string Filename string Data []byte MimeType string } // CallbackRequest 回调请求结构 type CallbackRequest struct { TaskID string `json:"task_id"` Status string `json:"status"` Progress int `json:"progress"` Message string `json:"message,omitempty"` Result map[string]any `json:"result,omitempty"` Error *TaskError `json:"error,omitempty"` Timestamp string `json:"timestamp"` } // ============================================================================= // P2A 方法 // ============================================================================= // apiURL 构造 API URL func (c *Client) apiURL(path string) string { return c.config.BaseURL + path } // setAuthHeaders 设置认证头 func (c *Client) setAuthHeaders(req *http.Request) { if c.config.APIKey != "" { req.Header.Set("X-API-Key", c.config.APIKey) } req.Header.Set("Accept", "application/json") } // SubmitTask 提交任务(JSON 方式) func (c *Client) SubmitTask(ctx context.Context, req *TaskRequest) (*TaskResponse, error) { body, err := json.Marshal(req) if err != nil { return nil, fmt.Errorf("failed to marshal request: %w", err) } httpReq, err := http.NewRequestWithContext(ctx, "POST", c.apiURL("/api/v1/synapse/task"), bytes.NewReader(body)) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } httpReq.Header.Set("Content-Type", "application/json") c.setAuthHeaders(httpReq) resp, err := c.httpClient.Do(httpReq) if err != nil { return nil, fmt.Errorf("request failed: %w", err) } defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) var result TaskResponse if err := json.Unmarshal(respBody, &result); err != nil { return nil, fmt.Errorf("failed to decode response: %w", err) } if resp.StatusCode >= 400 { return &result, fmt.Errorf("API error: %s", result.Error) } return &result, nil } // SubmitTaskWithFiles 提交带文件的任务 func (c *Client) SubmitTaskWithFiles(ctx context.Context, req *TaskRequest, files []FileParam) (*TaskResponse, error) { var buf bytes.Buffer writer := multipart.NewWriter(&buf) _ = writer.WriteField("task_type", req.TaskType) payloadJSON, _ := json.Marshal(req.Payload) _ = writer.WriteField("payload", string(payloadJSON)) if req.ExternalTaskID != "" { _ = writer.WriteField("external_task_id", req.ExternalTaskID) } if req.Priority != "" { _ = writer.WriteField("priority", req.Priority) } if req.CallbackURL != "" { _ = writer.WriteField("callback_url", req.CallbackURL) } if req.CallbackToken != "" { _ = writer.WriteField("callback_token", req.CallbackToken) } for _, f := range files { var part io.Writer var err error if f.MimeType != "" { part, err = writer.CreatePart(map[string][]string{ "Content-Disposition": {fmt.Sprintf(`form-data; name="%s"; filename="%s"`, f.FieldName, f.Filename)}, "Content-Type": {f.MimeType}, }) } else { part, err = writer.CreateFormFile(f.FieldName, f.Filename) } if err != nil { return nil, fmt.Errorf("failed to create form file: %w", err) } _, _ = part.Write(f.Data) } _ = writer.Close() httpReq, err := http.NewRequestWithContext(ctx, "POST", c.apiURL("/api/v1/synapse/task/multipart"), &buf) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } httpReq.Header.Set("Content-Type", writer.FormDataContentType()) c.setAuthHeaders(httpReq) resp, err := c.httpClient.Do(httpReq) if err != nil { return nil, fmt.Errorf("request failed: %w", err) } defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) var result TaskResponse if err := json.Unmarshal(respBody, &result); err != nil { return nil, fmt.Errorf("failed to decode response: %w", err) } if resp.StatusCode >= 400 { return &result, fmt.Errorf("API error: %s", result.Error) } return &result, nil } // GetTask 获取任务详情 func (c *Client) GetTask(ctx context.Context, taskID string) (*TaskDetailResponse, error) { httpReq, err := http.NewRequestWithContext(ctx, "GET", c.apiURL("/api/v1/synapse/task/"+taskID), nil) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } c.setAuthHeaders(httpReq) resp, err := c.httpClient.Do(httpReq) if err != nil { return nil, fmt.Errorf("request failed: %w", err) } defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) var result TaskDetailResponse if err := json.Unmarshal(respBody, &result); err != nil { return nil, fmt.Errorf("failed to decode response: %w", err) } if resp.StatusCode >= 400 { return &result, fmt.Errorf("API error: %s", result.Error) } return &result, nil } // GetTaskStatus 获取任务状态 func (c *Client) GetTaskStatus(ctx context.Context, taskID string) (*TaskStatusResponse, error) { httpReq, err := http.NewRequestWithContext(ctx, "GET", c.apiURL("/api/v1/synapse/task/"+taskID+"/status"), nil) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } c.setAuthHeaders(httpReq) resp, err := c.httpClient.Do(httpReq) if err != nil { return nil, fmt.Errorf("request failed: %w", err) } defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) var result TaskStatusResponse if err := json.Unmarshal(respBody, &result); err != nil { return nil, fmt.Errorf("failed to decode response: %w", err) } if resp.StatusCode >= 400 { return &result, fmt.Errorf("API error: %s", result.Error) } return &result, nil } // ListTasks 获取任务列表 func (c *Client) ListTasks(ctx context.Context, status string, limit, offset int) (*TaskListResponse, error) { if limit <= 0 { limit = 20 } url := fmt.Sprintf("%s/api/v1/synapse/tasks?status=%s&limit=%d&offset=%d", c.config.BaseURL, status, limit, offset) httpReq, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } c.setAuthHeaders(httpReq) resp, err := c.httpClient.Do(httpReq) if err != nil { return nil, fmt.Errorf("request failed: %w", err) } defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) var result TaskListResponse if err := json.Unmarshal(respBody, &result); err != nil { return nil, fmt.Errorf("failed to decode response: %w", err) } if resp.StatusCode >= 400 { return &result, fmt.Errorf("API error: %s", result.Error) } return &result, nil } // CancelTask 取消任务 func (c *Client) CancelTask(ctx context.Context, taskID string) (*TaskResponse, error) { httpReq, err := http.NewRequestWithContext(ctx, "POST", c.apiURL("/api/v1/synapse/task/"+taskID+"/cancel"), nil) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } c.setAuthHeaders(httpReq) resp, err := c.httpClient.Do(httpReq) if err != nil { return nil, fmt.Errorf("request failed: %w", err) } defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) var result TaskResponse if err := json.Unmarshal(respBody, &result); err != nil { return nil, fmt.Errorf("failed to decode response: %w", err) } if resp.StatusCode >= 400 { return &result, fmt.Errorf("API error: %s", result.Error) } return &result, nil } // GetCapabilities 获取 Agent 能力清单 func (c *Client) GetCapabilities(ctx context.Context) (*CapabilitiesResponse, error) { httpReq, err := http.NewRequestWithContext(ctx, "GET", c.apiURL("/api/v1/synapse/capabilities"), nil) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } c.setAuthHeaders(httpReq) resp, err := c.httpClient.Do(httpReq) if err != nil { return nil, fmt.Errorf("request failed: %w", err) } defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) var result CapabilitiesResponse if err := json.Unmarshal(respBody, &result); err != nil { return nil, fmt.Errorf("failed to decode response: %w", err) } if resp.StatusCode >= 400 { return &result, fmt.Errorf("API error: %s", result.Error) } return &result, nil } // WaitForTaskP2A 等待任务完成(轮询方式) func (c *Client) WaitForTaskP2A(ctx context.Context, taskID string, interval time.Duration) (*TaskInfo, error) { if interval == 0 { interval = 5 * time.Second } ticker := time.NewTicker(interval) defer ticker.Stop() for { select { case <-ctx.Done(): return nil, ctx.Err() case <-ticker.C: resp, err := c.GetTask(ctx, taskID) if err != nil { continue } switch resp.Task.Status { case "completed", "failed", "cancelled", "rejected": return &resp.Task, nil } } } } // CreateAndWait 创建任务并等待完成 func (c *Client) CreateAndWait(ctx context.Context, taskType string, payload map[string]any, interval time.Duration) (*TaskInfo, error) { req := &TaskRequest{ TaskType: taskType, Payload: payload, Priority: "normal", } resp, err := c.SubmitTask(ctx, req) if err != nil { return nil, err } if !resp.Success { return nil, fmt.Errorf("task submission failed: %s", resp.Message) } return c.WaitForTaskP2A(ctx, resp.TaskID, interval) } // CreateWithCallback 创建任务并指定回调 func (c *Client) CreateWithCallback(ctx context.Context, taskType string, payload map[string]any, callbackURL, callbackToken string) (*TaskResponse, error) { req := &TaskRequest{ TaskType: taskType, Payload: payload, Priority: "normal", CallbackURL: callbackURL, CallbackToken: callbackToken, } return c.SubmitTask(ctx, req) } // VerifyCallback 验证回调请求 func (c *Client) VerifyCallback(r *http.Request, callbackToken string) bool { if callbackToken == "" { return true } return r.Header.Get("X-Callback-Token") == callbackToken } // ============================================================================= // A2A 协议方法 — 点对点通信 // ============================================================================= // signMessage HMAC 签消息 func (c *Client) signMessage(msg *types.UnifiedSynapseMessage) string { if c.config.AuthSecret == "" { return "" } h := hmac.New(sha256.New, []byte(c.config.AuthSecret)) h.Write([]byte(msg.ID + "." + string(msg.Payload))) return hex.EncodeToString(h.Sum(nil)) } // VerifySignature 验证消息签名 func (c *Client) VerifySignature(msg *types.UnifiedSynapseMessage, secret string) bool { if secret == "" { return true } if msg.Signature == "" { return false } h := hmac.New(sha256.New, []byte(secret)) h.Write([]byte(msg.ID + "." + string(msg.Payload))) expected := hex.EncodeToString(h.Sum(nil)) return hmac.Equal([]byte(msg.Signature), []byte(expected)) } // ackMessage 生成 ACK 响应 func (c *Client) ackMessage(msg *types.UnifiedSynapseMessage) *types.UnifiedSynapseMessage { return &types.UnifiedSynapseMessage{ ID: types.GenerateMessageID(), Version: msg.Version, Type: string(types.P2PAck), Sender: c.config.AgentEndpoint, Receiver: &msg.Sender, ReplyTo: msg.ID, Payload: []byte("{}"), Timestamp: types.NowTimestamp(), } } // getTaskState 获取任务状态 func (c *Client) getTaskState(taskID string) (*P2PTaskState, bool) { c.tasksMu.RLock() defer c.tasksMu.RUnlock() state, ok := c.tasks[taskID] return state, ok } // listTasks 列出所有任务 func (c *Client) listTasks() map[string]*P2PTaskState { c.tasksMu.RLock() defer c.tasksMu.RUnlock() result := make(map[string]*P2PTaskState, len(c.tasks)) for k, v := range c.tasks { result[k] = v } return result } // setTaskState 设置任务状态 func (c *Client) setTaskState(taskID string, status types.P2PTaskStatus, progress int, message string) { c.tasksMu.Lock() defer c.tasksMu.Unlock() if _, ok := c.tasks[taskID]; !ok { c.tasks[taskID] = &P2PTaskState{TaskID: taskID} } c.tasks[taskID].Status = status c.tasks[taskID].Progress = progress c.tasks[taskID].Message = message } func (c *Client) setTaskResult(taskID string, result any) { c.tasksMu.Lock() defer c.tasksMu.Unlock() if state, ok := c.tasks[taskID]; ok { state.Result = result } } func (c *Client) setTaskError(taskID string, err *types.SynapseError) { c.tasksMu.Lock() defer c.tasksMu.Unlock() if state, ok := c.tasks[taskID]; ok { state.Error = err } } // sendMessage 发送 P2P 消息 func (c *Client) sendMessage(ctx context.Context, endpoint string, msg *types.UnifiedSynapseMessage) error { msg.Signature = c.signMessage(msg) body, err := json.Marshal(msg) if err != nil { return fmt.Errorf("failed to marshal message: %w", err) } httpReq, err := http.NewRequestWithContext(ctx, "POST", endpoint, bytes.NewReader(body)) if err != nil { return fmt.Errorf("failed to create request: %w", err) } httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("X-Synapse-Signature", msg.Signature) resp, err := c.httpClient.Do(httpReq) if err != nil { return fmt.Errorf("request failed: %w", err) } defer resp.Body.Close() if resp.StatusCode >= 400 { respBody, _ := io.ReadAll(resp.Body) return fmt.Errorf("remote error: %s", string(respBody)) } return nil } // sendRequest 发送消息并等待响应 func (c *Client) sendRequest(ctx context.Context, endpoint string, msg *types.UnifiedSynapseMessage) (*types.UnifiedSynapseMessage, error) { msg.Signature = c.signMessage(msg) body, err := json.Marshal(msg) if err != nil { return nil, fmt.Errorf("failed to marshal message: %w", err) } httpReq, err := http.NewRequestWithContext(ctx, "POST", endpoint, bytes.NewReader(body)) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("X-Synapse-Signature", msg.Signature) resp, err := c.httpClient.Do(httpReq) if err != nil { return nil, fmt.Errorf("request failed: %w", err) } defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) if resp.StatusCode >= 400 { return nil, fmt.Errorf("remote error: %s", string(respBody)) } var respMsg types.UnifiedSynapseMessage if err := json.Unmarshal(respBody, &respMsg); err != nil { return nil, fmt.Errorf("failed to parse response: %w", err) } return &respMsg, nil } // SendTaskRequest 发送任务请求 func (c *Client) SendTaskRequest(ctx context.Context, target types.AgentEndpoint, taskType string, payload map[string]any) (string, error) { taskID := types.GenerateTaskID() taskReq := types.TaskRequestPayload{} taskReq.Task.ID = taskID taskReq.Task.Type = taskType taskReq.Task.Priority = types.PriorityNormal taskReq.Task.Payload = payload payloadBytes, _ := json.Marshal(taskReq) msg := &types.UnifiedSynapseMessage{ ID: types.GenerateMessageID(), Version: "1.0.0", Type: string(types.P2PTaskRequest), Sender: c.config.AgentEndpoint, Receiver: &target, Payload: payloadBytes, Priority: types.PriorityNormal, Timestamp: types.NowTimestamp(), } c.setTaskState(taskID, types.P2PStatusPending, 0, "任务已发送") err := c.sendMessage(ctx, target.Endpoint, msg) if err != nil { return "", fmt.Errorf("failed to send task request: %w", err) } return taskID, nil } // SendTaskCancel 发送取消 func (c *Client) SendTaskCancel(ctx context.Context, target types.AgentEndpoint, taskID, reason string) error { cancelPayload := types.TaskCancelPayload{ TaskID: taskID, Reason: reason, CancelledBy: c.config.AgentEndpoint.AgentID, } payloadBytes, _ := json.Marshal(cancelPayload) msg := &types.UnifiedSynapseMessage{ ID: types.GenerateMessageID(), Version: "1.0.0", Type: string(types.P2PTaskCancel), Sender: c.config.AgentEndpoint, Receiver: &target, Payload: payloadBytes, Timestamp: types.NowTimestamp(), } c.setTaskState(taskID, types.P2PStatusCancelled, 0, "任务已取消") return c.sendMessage(ctx, target.Endpoint, msg) } // SendHumanDecision 发送人类决策 func (c *Client) SendHumanDecision(ctx context.Context, target types.AgentEndpoint, taskID string, decision types.HumanDecisionPayload) error { payloadBytes, _ := json.Marshal(decision) msg := &types.UnifiedSynapseMessage{ ID: types.GenerateMessageID(), Version: "1.0.0", Type: string(types.P2PHumanDecision), Sender: c.config.AgentEndpoint, Receiver: &target, Payload: payloadBytes, Timestamp: types.NowTimestamp(), } return c.sendMessage(ctx, target.Endpoint, msg) } // SendCapabilityQueryA2A 查询对方能力 func (c *Client) SendCapabilityQueryA2A(ctx context.Context, target types.AgentEndpoint) ([]string, error) { msg := &types.UnifiedSynapseMessage{ ID: types.GenerateMessageID(), Version: "1.0.0", Type: string(types.P2PCapabilityQuery), Sender: c.config.AgentEndpoint, Receiver: &target, Payload: []byte("{}"), Timestamp: types.NowTimestamp(), } respMsg, err := c.sendRequest(ctx, target.Endpoint, msg) if err != nil { return nil, fmt.Errorf("failed to query capabilities: %w", err) } var resp struct { Capabilities []string `json:"capabilities"` } if err := json.Unmarshal(respMsg.Payload, &resp); err != nil { return nil, fmt.Errorf("failed to parse capabilities: %w", err) } return resp.Capabilities, nil } // WaitForTaskA2A 等待任务完成 func (c *Client) WaitForTaskA2A(ctx context.Context, taskID string, timeout time.Duration) (*P2PTaskState, error) { deadline := time.Now().Add(timeout) ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() for { select { case <-ctx.Done(): return nil, ctx.Err() case <-ticker.C: if time.Now().After(deadline) { return nil, fmt.Errorf("task timeout") } state, ok := c.getTaskState(taskID) if !ok { continue } switch state.Status { case types.P2PStatusCompleted, types.P2PStatusFailed, types.P2PStatusCancelled, types.P2PStatusRejected: return state, nil } } } } // HandleA2AMessage 处理收到的 A2A 消息 func (c *Client) HandleA2AMessage(ctx context.Context, msg *types.UnifiedSynapseMessage) (*types.UnifiedSynapseMessage, error) { if c.config.AuthSecret != "" && !c.VerifySignature(msg, c.config.AuthSecret) { return nil, fmt.Errorf("invalid signature") } switch types.P2PMessageType(msg.Type) { case types.P2PTaskRequest: if c.messageHandler != nil { return c.messageHandler.HandleTaskRequest(ctx, msg) } return c.ackMessage(msg), nil case types.P2PTaskConfirm: var payload types.TaskConfirmPayload _ = json.Unmarshal(msg.Payload, &payload) c.setTaskState(payload.TaskID, types.P2PStatusConfirmed, 0, "任务已确认") return c.ackMessage(msg), nil case types.P2PTaskReject: var payload types.TaskRejectPayload _ = json.Unmarshal(msg.Payload, &payload) c.setTaskState(payload.TaskID, types.P2PStatusRejected, 0, payload.Reason.Message) return c.ackMessage(msg), nil case types.P2PTaskStatusUpdate: var payload types.TaskStatusUpdatePayload _ = json.Unmarshal(msg.Payload, &payload) c.setTaskState(payload.TaskID, types.P2PTaskStatus(payload.Status), payload.Progress, payload.Message) if c.messageHandler != nil { _ = c.messageHandler.HandleTaskStatusUpdate(ctx, msg) } return c.ackMessage(msg), nil case types.P2PTaskComplete: var payload types.TaskCompletePayload _ = json.Unmarshal(msg.Payload, &payload) c.setTaskState(payload.TaskID, types.P2PStatusCompleted, 100, payload.Summary) c.setTaskResult(payload.TaskID, payload.Result.Output) if c.messageHandler != nil { _ = c.messageHandler.HandleTaskComplete(ctx, msg) } return c.ackMessage(msg), nil case types.P2PTaskFailed: var payload types.TaskFailedPayload _ = json.Unmarshal(msg.Payload, &payload) c.setTaskState(payload.TaskID, types.P2PStatusFailed, 0, payload.Error.Message) c.setTaskError(payload.TaskID, types.NewSynapseError( types.SynapseErrorCode(0), payload.Error.Message, nil, )) if c.messageHandler != nil { _ = c.messageHandler.HandleTaskFailed(ctx, msg) } return c.ackMessage(msg), nil case types.P2PTaskNeedHuman: var payload types.TaskNeedHumanPayload _ = json.Unmarshal(msg.Payload, &payload) c.setTaskState(payload.TaskID, types.P2PStatusNeedHuman, 0, payload.Reason.Message) if c.messageHandler != nil { _ = c.messageHandler.HandleNeedHuman(ctx, msg) } return c.ackMessage(msg), nil case types.P2PCapabilityQuery: return nil, nil default: return c.ackMessage(msg), nil } } // ============================================================================= // A2N 协议方法 — 群组协作 // ============================================================================= // CreateGroup 创建群组 func (c *Client) CreateGroup(ctx context.Context, name string, topology types.GroupTopology, config *types.GroupConfig, initialMembers []types.AgentEndpoint) (*GroupState, error) { groupID := fmt.Sprintf("group-%d", time.Now().UnixNano()) if config == nil { config = &types.GroupConfig{ AllowSelfLeave: true, RequiresApproval: false, ConsensusRequired: false, } } members := make([]types.GroupMember, 0, len(initialMembers)+1) for _, m := range initialMembers { members = append(members, types.GroupMember{ AgentID: m.AgentID, Role: "worker", Status: types.GroupMemberActive, }) } groupState := &GroupState{ GroupID: groupID, Name: name, Topology: topology, Status: types.GroupStatusActive, Members: members, Role: "coordinator", } c.groupsMu.Lock() c.groups[groupID] = groupState c.groupsMu.Unlock() if c.eventHandler != nil { _ = c.eventHandler.OnGroupCreated(ctx, groupID) } return groupState, nil } // JoinGroup 加入群组 func (c *Client) JoinGroup(ctx context.Context, groupID string) error { c.groupsMu.Lock() defer c.groupsMu.Unlock() if _, ok := c.groups[groupID]; !ok { c.groups[groupID] = &GroupState{ GroupID: groupID, Status: types.GroupStatusActive, Role: "worker", } } if c.eventHandler != nil { _ = c.eventHandler.OnMemberJoined(ctx, groupID, types.GroupMember{}) } return nil } // LeaveGroup 离开群组 func (c *Client) LeaveGroup(ctx context.Context, groupID string) error { c.groupsMu.Lock() delete(c.groups, groupID) c.groupsMu.Unlock() if c.eventHandler != nil { _ = c.eventHandler.OnMemberLeft(ctx, groupID, "") } return nil } // DisbandGroup 解散群组 func (c *Client) DisbandGroup(ctx context.Context, groupID string) error { c.groupsMu.Lock() if group, ok := c.groups[groupID]; ok { group.Status = types.GroupStatusDisbanded } c.groupsMu.Unlock() if c.eventHandler != nil { _ = c.eventHandler.OnGroupDisbanded(ctx, groupID) } return nil } // GetGroup 获取群组信息 func (c *Client) GetGroup(groupID string) (*GroupState, bool) { c.groupsMu.RLock() defer c.groupsMu.RUnlock() group, ok := c.groups[groupID] return group, ok } // ListGroups 列出所有群组 func (c *Client) ListGroups() []*GroupState { c.groupsMu.RLock() defer c.groupsMu.RUnlock() result := make([]*GroupState, 0, len(c.groups)) for _, g := range c.groups { result = append(result, g) } return result } // SubmitGroupTask 提交群组任务 func (c *Client) SubmitGroupTask(ctx context.Context, groupID, taskType string, requirements map[string]any, mergeStrategy types.MergeStrategy) ([]types.Subtask, error) { subtasks := []types.Subtask{ { ID: fmt.Sprintf("sub-%d-1", time.Now().UnixNano()), Description: "子任务 1", AssignedTo: "", Dependencies: []string{}, EstimatedDuration: 60, Priority: 1, }, } if c.eventHandler != nil { _ = c.eventHandler.OnTaskSplit(ctx, groupID, "", subtasks) } return subtasks, nil } // SendSubtaskResult 发送子任务结果 func (c *Client) SendSubtaskResult(ctx context.Context, groupID, subtaskID string, result any, err error) error { if c.eventHandler != nil { _ = c.eventHandler.OnTaskMerged(ctx, groupID, "", result) } return nil } // InitiateVote 发起投票 func (c *Client) InitiateVote(ctx context.Context, groupID, question string, options []string, voterIDs []string, threshold float64, deadline time.Duration) (string, error) { voteID := fmt.Sprintf("vote-%d", time.Now().UnixNano()) vote := types.GroupVotePayload{ VoteID: voteID, Question: question, Options: options, Voters: voterIDs, Deadline: types.Timestamp(time.Now().Add(deadline).UTC().Format(time.RFC3339)), Threshold: threshold, Status: "open", } if c.eventHandler != nil { _ = c.eventHandler.OnVoteInitiated(ctx, groupID, vote) } return voteID, nil } // SubmitVote 提交投票 func (c *Client) SubmitVote(ctx context.Context, groupID, voteID, option, reason string) error { if c.eventHandler != nil { _ = c.eventHandler.OnVoteResult(ctx, groupID, voteID, option, true) } return nil } // Broadcast 广播消息 func (c *Client) Broadcast(ctx context.Context, groupID string, message []byte) error { if c.eventHandler != nil { _ = c.eventHandler.OnBroadcast(ctx, groupID, types.AgentEndpoint{}, message) } return nil } // EscalateToHuman 升级到人类决策 func (c *Client) EscalateToHuman(ctx context.Context, groupID string, level int, reason string) (string, error) { escalationID := fmt.Sprintf("esc-%d", time.Now().UnixNano()) if c.eventHandler != nil { _ = c.eventHandler.OnEscalation(ctx, groupID, escalationID, level) } return escalationID, nil } // HandleA2NMessage 处理群组消息 func (c *Client) HandleA2NMessage(ctx context.Context, msg *types.UnifiedSynapseMessage) (*types.UnifiedSynapseMessage, error) { groupID := msg.GroupID switch types.GroupMessageType(msg.Type) { case types.GroupCreate: if c.eventHandler != nil { _ = c.eventHandler.OnGroupCreated(ctx, groupID) } case types.GroupDisband: if c.eventHandler != nil { _ = c.eventHandler.OnGroupDisbanded(ctx, groupID) } case types.GroupMemberUpdate: if c.eventHandler != nil { _ = c.eventHandler.OnMemberJoined(ctx, groupID, types.GroupMember{}) } case types.GroupTaskSplit: var payload types.GroupTaskSplitPayload if c.eventHandler != nil { _ = c.eventHandler.OnTaskSplit(ctx, groupID, payload.OriginalTask.ID, payload.Subtasks) } case types.GroupTaskProgress: if c.eventHandler != nil { _ = c.eventHandler.OnSubtaskProgress(ctx, groupID, "", 0) } case types.GroupTaskMerge: var payload types.GroupTaskMergePayload if c.eventHandler != nil { _ = c.eventHandler.OnTaskMerged(ctx, groupID, payload.OriginalTaskID, payload.MergedResult) } case types.GroupVote: var payload types.GroupVotePayload if c.eventHandler != nil { _ = c.eventHandler.OnVoteInitiated(ctx, groupID, payload) } case types.GroupConsensus: if c.eventHandler != nil { _ = c.eventHandler.OnVoteResult(ctx, groupID, "", "", true) } case types.GroupConsensusEscalate, types.GroupSubtaskEscalate: if c.eventHandler != nil { _ = c.eventHandler.OnEscalation(ctx, groupID, "", 1) } case types.GroupBroadcast: if c.eventHandler != nil { _ = c.eventHandler.OnBroadcast(ctx, groupID, msg.Sender, msg.Payload) } } return nil, nil }