From cb845f2374ab65946feae1dd84173d30ad9449a3 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Thu, 7 May 2026 10:34:31 +0800 Subject: [PATCH] feat(acp): domain types + service interfaces (skeleton) --- internal/acp/domain.go | 143 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 internal/acp/domain.go diff --git a/internal/acp/domain.go b/internal/acp/domain.go new file mode 100644 index 0000000..3976be5 --- /dev/null +++ b/internal/acp/domain.go @@ -0,0 +1,143 @@ +// Package acp 实现 ACP(Agent Client Protocol)Client 模块。 +// 模块结构(spec §13 决策 #12):单 package + 多文件,与现有 chat/jobs/workspace 一致。 +// +// 三个聚合根: +// - AgentKind:admin 注册的 agent 类型(claude_code/gemini/...),含加密 env +// - Session:用户启动的一个 ACP 会话,绑定 workspace + agent kind + (issue/req?) +// - Event:JSON-RPC 双向消息流,每条入库 +package acp + +import ( + "context" + "time" + + "github.com/google/uuid" +) + +// SessionStatus 是 acp_sessions.status 枚举。 +type SessionStatus string + +const ( + SessionStarting SessionStatus = "starting" + SessionRunning SessionStatus = "running" + SessionCrashed SessionStatus = "crashed" + SessionExited SessionStatus = "exited" +) + +// IsActive 判定 session 是否仍占资源(worktree、subprocess、限流计数)。 +func (s SessionStatus) IsActive() bool { + return s == SessionStarting || s == SessionRunning +} + +// EventDirection 标识事件流向。in = client→agent,out = agent→client。 +type EventDirection string + +const ( + DirectionIn EventDirection = "in" + DirectionOut EventDirection = "out" +) + +// RPCKind 标识 JSON-RPC 消息类型。 +type RPCKind string + +const ( + RPCRequest RPCKind = "request" + RPCResponse RPCKind = "response" + RPCNotification RPCKind = "notification" + RPCError RPCKind = "error" +) + +// AgentKind 是 admin 注册的 agent 类型记录。EncryptedEnv 是 AES-GCM 密文, +// 仅 supervisor spawn 时解密为 map 注入子进程。其他场景不解密、不出 API。 +type AgentKind struct { + ID uuid.UUID + Name string + DisplayName string + Description string + BinaryPath string + Args []string + EncryptedEnv []byte + Enabled bool + CreatedBy uuid.UUID + CreatedAt time.Time + UpdatedAt time.Time +} + +// Session 是一次 ACP 会话。AgentSessionID 是 agent 侧的 session 标识(来自 +// session/new response),与本表 ID 不同,仅用于 ACP 协议 method 内填充。 +type Session struct { + ID uuid.UUID + WorkspaceID uuid.UUID + ProjectID uuid.UUID + AgentKindID uuid.UUID + UserID uuid.UUID + IssueID *uuid.UUID + RequirementID *uuid.UUID + AgentSessionID *string + Branch string + CwdPath string + IsMainWorktree bool + Status SessionStatus + PID *int32 + ExitCode *int32 + LastError *string + StartedAt time.Time + EndedAt *time.Time +} + +// Event 是一条入库的 JSON-RPC 消息。Payload 是 JSON-RPC envelope 完整 JSON +// 字节(不解析为结构体,便于无损 replay);超过 64KB 的会按 §3.5 规则裁剪。 +type Event struct { + ID int64 + SessionID uuid.UUID + Direction EventDirection + RPCKind RPCKind + Method *string + Payload []byte + PayloadSize int32 + Truncated bool + CreatedAt time.Time +} + +// Caller 表示发起请求的用户上下文,与 PM 模块的 Caller 同语义。 +type Caller struct { + UserID uuid.UUID + IsAdmin bool +} + +// AgentKindService 暴露 AgentKind 的应用服务方法。env 走"完整 map 替换" +// 语义:每次 PATCH 传完整 env,nil 表示不修改;非 nil 整体加密重写。 +type AgentKindService interface { + Create(ctx context.Context, c Caller, in CreateAgentKindInput) (*AgentKind, error) + List(ctx context.Context, c Caller) ([]*AgentKind, error) + Get(ctx context.Context, c Caller, id uuid.UUID) (*AgentKind, error) + Update(ctx context.Context, c Caller, id uuid.UUID, in UpdateAgentKindInput) (*AgentKind, error) + Delete(ctx context.Context, c Caller, id uuid.UUID) error +} + +// SessionService 在 Part 2 实施。占位以便 handler.go 编译通过。 +type SessionService interface { + // methods filled in Part 2 +} + +// CreateAgentKindInput 携带创建必需字段。Args / Env 可为 nil。 +type CreateAgentKindInput struct { + Name string + DisplayName string + Description string + BinaryPath string + Args []string + Env map[string]string + Enabled bool +} + +// UpdateAgentKindInput 是 patch 输入。Name 不可改(创建后稳定,引用约束)。 +// Env 三态:nil = 不修改;空 map = 清空全部 env;非空 = 整体替换。 +type UpdateAgentKindInput struct { + DisplayName *string + Description *string + BinaryPath *string + Args []string // nil = 不改;非 nil = 替换 + Env map[string]string // nil = 不改;非 nil(含空)= 替换 + Enabled *bool +}