You've already forked agentic-coding-workflow
修改
This commit is contained in:
+164
-27
@@ -48,6 +48,15 @@ type Repository interface {
|
||||
ListEventsSince(ctx context.Context, sessionID uuid.UUID, sinceID int64, limit int32) ([]*Event, error)
|
||||
PurgeEventsBefore(ctx context.Context, before time.Time) (int64, error)
|
||||
|
||||
// PermissionRequest
|
||||
InsertPermissionRequest(ctx context.Context, p *PermissionRequest) (*PermissionRequest, error)
|
||||
GetPermissionRequestByID(ctx context.Context, id uuid.UUID) (*PermissionRequest, error)
|
||||
ListPendingPermissionRequestsBySession(ctx context.Context, sessionID uuid.UUID) ([]*PermissionRequest, error)
|
||||
ListPendingPermissionRequestsByUser(ctx context.Context, userID uuid.UUID) ([]*PermissionRequest, error)
|
||||
DecidePermissionRequest(ctx context.Context, id uuid.UUID, status PermissionStatus, chosenOptionID *string, decidedBy *uuid.UUID) (*PermissionRequest, bool, error)
|
||||
ExpirePendingPermissionRequestsBySession(ctx context.Context, sessionID uuid.UUID) (int64, error)
|
||||
ExpireStalePermissionRequests(ctx context.Context, before time.Time) (int64, error)
|
||||
|
||||
InTx(ctx context.Context, fn func(ctx context.Context, tx pgx.Tx) error) error
|
||||
WithTx(tx pgx.Tx) Repository
|
||||
}
|
||||
@@ -129,15 +138,16 @@ func pgxNoRowsToErrNotFound(err error, code errs.Code, msg string) error {
|
||||
|
||||
func (r *pgRepo) CreateAgentKind(ctx context.Context, k *AgentKind) (*AgentKind, error) {
|
||||
row, err := r.q.CreateAgentKind(ctx, acpsqlc.CreateAgentKindParams{
|
||||
ID: toPgUUID(k.ID),
|
||||
Name: k.Name,
|
||||
DisplayName: k.DisplayName,
|
||||
Description: k.Description,
|
||||
BinaryPath: k.BinaryPath,
|
||||
Args: k.Args,
|
||||
EncryptedEnv: k.EncryptedEnv,
|
||||
Enabled: k.Enabled,
|
||||
CreatedBy: toPgUUID(k.CreatedBy),
|
||||
ID: toPgUUID(k.ID),
|
||||
Name: k.Name,
|
||||
DisplayName: k.DisplayName,
|
||||
Description: k.Description,
|
||||
BinaryPath: k.BinaryPath,
|
||||
Args: k.Args,
|
||||
EncryptedEnv: k.EncryptedEnv,
|
||||
Enabled: k.Enabled,
|
||||
CreatedBy: toPgUUID(k.CreatedBy),
|
||||
ToolAllowlist: normalizeStrSlice(k.ToolAllowlist),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err, errs.CodeInternal, "create agent kind")
|
||||
@@ -187,13 +197,14 @@ func (r *pgRepo) ListEnabledAgentKinds(ctx context.Context) ([]*AgentKind, error
|
||||
|
||||
func (r *pgRepo) UpdateAgentKind(ctx context.Context, k *AgentKind) (*AgentKind, error) {
|
||||
row, err := r.q.UpdateAgentKind(ctx, acpsqlc.UpdateAgentKindParams{
|
||||
ID: toPgUUID(k.ID),
|
||||
DisplayName: k.DisplayName,
|
||||
Description: k.Description,
|
||||
BinaryPath: k.BinaryPath,
|
||||
Args: k.Args,
|
||||
EncryptedEnv: k.EncryptedEnv,
|
||||
Enabled: k.Enabled,
|
||||
ID: toPgUUID(k.ID),
|
||||
DisplayName: k.DisplayName,
|
||||
Description: k.Description,
|
||||
BinaryPath: k.BinaryPath,
|
||||
Args: k.Args,
|
||||
EncryptedEnv: k.EncryptedEnv,
|
||||
Enabled: k.Enabled,
|
||||
ToolAllowlist: normalizeStrSlice(k.ToolAllowlist),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, pgxNoRowsToErrNotFound(err, errs.CodeAcpAgentKindNotFound, "agent kind not found")
|
||||
@@ -223,20 +234,30 @@ func (r *pgRepo) CountAgentKindUsage(ctx context.Context, id uuid.UUID) (int64,
|
||||
|
||||
func rowToAgentKind(row acpsqlc.AcpAgentKind) *AgentKind {
|
||||
return &AgentKind{
|
||||
ID: fromPgUUID(row.ID),
|
||||
Name: row.Name,
|
||||
DisplayName: row.DisplayName,
|
||||
Description: row.Description,
|
||||
BinaryPath: row.BinaryPath,
|
||||
Args: row.Args,
|
||||
EncryptedEnv: row.EncryptedEnv,
|
||||
Enabled: row.Enabled,
|
||||
CreatedBy: fromPgUUID(row.CreatedBy),
|
||||
CreatedAt: row.CreatedAt.Time,
|
||||
UpdatedAt: row.UpdatedAt.Time,
|
||||
ID: fromPgUUID(row.ID),
|
||||
Name: row.Name,
|
||||
DisplayName: row.DisplayName,
|
||||
Description: row.Description,
|
||||
BinaryPath: row.BinaryPath,
|
||||
Args: row.Args,
|
||||
EncryptedEnv: row.EncryptedEnv,
|
||||
Enabled: row.Enabled,
|
||||
ToolAllowlist: row.ToolAllowlist,
|
||||
CreatedBy: fromPgUUID(row.CreatedBy),
|
||||
CreatedAt: row.CreatedAt.Time,
|
||||
UpdatedAt: row.UpdatedAt.Time,
|
||||
}
|
||||
}
|
||||
|
||||
// normalizeStrSlice 把 nil 切片归一为非 nil 空切片,避免写入 TEXT[] NOT NULL 列时
|
||||
// 产生 NULL(pg 驱动对 nil []string 写 NULL,违反 NOT NULL 约束)。
|
||||
func normalizeStrSlice(s []string) []string {
|
||||
if s == nil {
|
||||
return []string{}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// ===== Session =====
|
||||
|
||||
func (r *pgRepo) InsertSession(ctx context.Context, s *Session) (*Session, error) {
|
||||
@@ -454,3 +475,119 @@ func rowToEvent(row acpsqlc.AcpEvent) *Event {
|
||||
CreatedAt: row.CreatedAt.Time,
|
||||
}
|
||||
}
|
||||
|
||||
// ===== PermissionRequest =====
|
||||
|
||||
func (r *pgRepo) InsertPermissionRequest(ctx context.Context, p *PermissionRequest) (*PermissionRequest, error) {
|
||||
row, err := r.q.InsertPermissionRequest(ctx, acpsqlc.InsertPermissionRequestParams{
|
||||
ID: toPgUUID(p.ID),
|
||||
SessionID: toPgUUID(p.SessionID),
|
||||
AgentRequestID: p.AgentRequestID,
|
||||
ToolName: p.ToolName,
|
||||
ToolCall: p.ToolCall,
|
||||
Options: p.Options,
|
||||
Status: string(p.Status),
|
||||
ChosenOptionID: p.ChosenOptionID,
|
||||
DecidedBy: toPgUUIDPtr(p.DecidedBy),
|
||||
DecidedAt: timePtrToPg(p.DecidedAt),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err, errs.CodeInternal, "insert permission request")
|
||||
}
|
||||
return rowToPermissionRequest(row), nil
|
||||
}
|
||||
|
||||
func (r *pgRepo) GetPermissionRequestByID(ctx context.Context, id uuid.UUID) (*PermissionRequest, error) {
|
||||
row, err := r.q.GetPermissionRequestByID(ctx, toPgUUID(id))
|
||||
if err != nil {
|
||||
return nil, pgxNoRowsToErrNotFound(err, errs.CodeAcpPermissionNotFound, "permission request not found")
|
||||
}
|
||||
return rowToPermissionRequest(row), nil
|
||||
}
|
||||
|
||||
func (r *pgRepo) ListPendingPermissionRequestsBySession(ctx context.Context, sessionID uuid.UUID) ([]*PermissionRequest, error) {
|
||||
rows, err := r.q.ListPendingPermissionRequestsBySession(ctx, toPgUUID(sessionID))
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err, errs.CodeInternal, "list pending permission requests by session")
|
||||
}
|
||||
out := make([]*PermissionRequest, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
out = append(out, rowToPermissionRequest(row))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (r *pgRepo) ListPendingPermissionRequestsByUser(ctx context.Context, userID uuid.UUID) ([]*PermissionRequest, error) {
|
||||
rows, err := r.q.ListPendingPermissionRequestsByUser(ctx, toPgUUID(userID))
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err, errs.CodeInternal, "list pending permission requests by user")
|
||||
}
|
||||
out := make([]*PermissionRequest, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
out = append(out, rowToPermissionRequest(row))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// DecidePermissionRequest 用 CAS(WHERE status='pending')落地决策。第二个返回值
|
||||
// ok=false 表示该请求已非 pending(重复决策/已超时),调用方据此判 409 或忽略。
|
||||
func (r *pgRepo) DecidePermissionRequest(ctx context.Context, id uuid.UUID, status PermissionStatus, chosenOptionID *string, decidedBy *uuid.UUID) (*PermissionRequest, bool, error) {
|
||||
row, err := r.q.DecidePermissionRequest(ctx, acpsqlc.DecidePermissionRequestParams{
|
||||
ID: toPgUUID(id),
|
||||
Status: string(status),
|
||||
ChosenOptionID: chosenOptionID,
|
||||
DecidedBy: toPgUUIDPtr(decidedBy),
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, false, nil
|
||||
}
|
||||
return nil, false, errs.Wrap(err, errs.CodeInternal, "decide permission request")
|
||||
}
|
||||
return rowToPermissionRequest(row), true, nil
|
||||
}
|
||||
|
||||
func (r *pgRepo) ExpirePendingPermissionRequestsBySession(ctx context.Context, sessionID uuid.UUID) (int64, error) {
|
||||
n, err := r.q.ExpirePendingPermissionRequestsBySession(ctx, toPgUUID(sessionID))
|
||||
if err != nil {
|
||||
return 0, errs.Wrap(err, errs.CodeInternal, "expire pending permission requests by session")
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (r *pgRepo) ExpireStalePermissionRequests(ctx context.Context, before time.Time) (int64, error) {
|
||||
n, err := r.q.ExpireStalePermissionRequests(ctx, pgtype.Timestamptz{Time: before, Valid: true})
|
||||
if err != nil {
|
||||
return 0, errs.Wrap(err, errs.CodeInternal, "expire stale permission requests")
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// timePtrToPg 把 *time.Time 转为 pgtype.Timestamptz(nil → Valid=false)。
|
||||
func timePtrToPg(t *time.Time) pgtype.Timestamptz {
|
||||
if t == nil {
|
||||
return pgtype.Timestamptz{}
|
||||
}
|
||||
return pgtype.Timestamptz{Time: *t, Valid: true}
|
||||
}
|
||||
|
||||
func rowToPermissionRequest(row acpsqlc.AcpPermissionRequest) *PermissionRequest {
|
||||
var decidedAt *time.Time
|
||||
if row.DecidedAt.Valid {
|
||||
t := row.DecidedAt.Time
|
||||
decidedAt = &t
|
||||
}
|
||||
return &PermissionRequest{
|
||||
ID: fromPgUUID(row.ID),
|
||||
SessionID: fromPgUUID(row.SessionID),
|
||||
AgentRequestID: row.AgentRequestID,
|
||||
ToolName: row.ToolName,
|
||||
ToolCall: row.ToolCall,
|
||||
Options: row.Options,
|
||||
Status: PermissionStatus(row.Status),
|
||||
ChosenOptionID: row.ChosenOptionID,
|
||||
DecidedBy: fromPgUUIDPtr(row.DecidedBy),
|
||||
DecidedAt: decidedAt,
|
||||
CreatedAt: row.CreatedAt.Time,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user