feat(agentctl): protocol adapter abstraction with agent profile support (#15)

* feat(agentctl): implement protocol adapter abstraction

- Add AgentAdapter interface for protocol-agnostic agent communication
- Create ACPAdapter implementation for ACP protocol (JSON-RPC over stdin/stdout)
- Add Protocol field to agent registry (AgentTypeConfig)
- Update process.Manager to use adapters instead of hardcoded ACP
- Update API layer to use adapter interface
- Pass protocol from registry to agentctl via CLI flag and env var
- Support protocol configuration in both Docker and standalone modes

This refactoring allows agentctl to support multiple protocols (ACP, REST, MCP)
via a pluggable adapter pattern. The backend remains protocol-agnostic,
always communicating with agentctl's HTTP API.

* refactor(agentctl): consolidate types and clean up code

- Create shared types package (internal/agentctl/types) for permission types
- Create shared protocol package (pkg/agent) for Protocol type
- Remove duplicate permission types from acp/client.go and adapter.go
- Make adapter the single source of truth for session ID
- Remove unused Command field from adapter.Config
- Remove passthrough wrapper function handleAdapterPermissionRequest
- Clean up extra empty lines in process/manager.go and acp_adapter.go
- Re-export permission types from adapter package for convenience

* fix(agent-session): rename AgentType to AgentProfileID for consistency

- Rename AgentSession.AgentType to AgentProfileID in models
- Update agent_sessions table schema column from agent_type to agent_profile_id
- Update all SQL queries in sqlite.go to use agent_profile_id
- Fix ToAPI() method to return agent_profile_id key
- Remove misleading comments in executor.go

Also adds comprehensive tests for affected areas:
- adapter package unit tests (SessionUpdate, PlanEntry, AgentInfo, Config)
- profile resolver unit tests with mock repository
- AgentSession CRUD tests in sqlite_test.go
This commit is contained in:
João Salavisa
2026-01-14 18:16:26 +00:00
committed by GitHub
parent 3d01863d46
commit 85ecf7debb
22 changed files with 1759 additions and 272 deletions
+14 -14
View File
@@ -242,7 +242,7 @@ func (r *SQLiteRepository) initSchema() error {
task_id TEXT NOT NULL,
agent_instance_id TEXT NOT NULL DEFAULT '',
container_id TEXT NOT NULL DEFAULT '',
agent_type TEXT NOT NULL,
agent_profile_id TEXT NOT NULL,
acp_session_id TEXT DEFAULT '',
executor_id TEXT DEFAULT '',
environment_id TEXT DEFAULT '',
@@ -1570,10 +1570,10 @@ func (r *SQLiteRepository) CreateAgentSession(ctx context.Context, session *mode
_, err = r.db.ExecContext(ctx, `
INSERT INTO agent_sessions (
id, task_id, agent_instance_id, container_id, agent_type, acp_session_id, executor_id, environment_id,
id, task_id, agent_instance_id, container_id, agent_profile_id, acp_session_id, executor_id, environment_id,
status, progress, error_message, metadata, started_at, completed_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, session.ID, session.TaskID, session.AgentInstanceID, session.ContainerID, session.AgentType, session.ACPSessionID,
`, session.ID, session.TaskID, session.AgentInstanceID, session.ContainerID, session.AgentProfileID, session.ACPSessionID,
session.ExecutorID, session.EnvironmentID, string(session.Status), session.Progress, session.ErrorMessage, string(metadataJSON),
session.StartedAt, session.CompletedAt, session.UpdatedAt)
@@ -1588,11 +1588,11 @@ func (r *SQLiteRepository) GetAgentSession(ctx context.Context, id string) (*mod
var completedAt sql.NullTime
err := r.db.QueryRowContext(ctx, `
SELECT id, task_id, agent_instance_id, container_id, agent_type, acp_session_id, executor_id, environment_id,
SELECT id, task_id, agent_instance_id, container_id, agent_profile_id, acp_session_id, executor_id, environment_id,
status, progress, error_message, metadata, started_at, completed_at, updated_at
FROM agent_sessions WHERE id = ?
`, id).Scan(
&session.ID, &session.TaskID, &session.AgentInstanceID, &session.ContainerID, &session.AgentType,
&session.ID, &session.TaskID, &session.AgentInstanceID, &session.ContainerID, &session.AgentProfileID,
&session.ACPSessionID, &session.ExecutorID, &session.EnvironmentID, &status, &session.Progress, &session.ErrorMessage,
&metadataJSON, &session.StartedAt, &completedAt, &session.UpdatedAt,
)
@@ -1625,11 +1625,11 @@ func (r *SQLiteRepository) GetAgentSessionByTaskID(ctx context.Context, taskID s
var completedAt sql.NullTime
err := r.db.QueryRowContext(ctx, `
SELECT id, task_id, agent_instance_id, container_id, agent_type, acp_session_id, executor_id, environment_id,
SELECT id, task_id, agent_instance_id, container_id, agent_profile_id, acp_session_id, executor_id, environment_id,
status, progress, error_message, metadata, started_at, completed_at, updated_at
FROM agent_sessions WHERE task_id = ? ORDER BY started_at DESC LIMIT 1
`, taskID).Scan(
&session.ID, &session.TaskID, &session.AgentInstanceID, &session.ContainerID, &session.AgentType,
&session.ID, &session.TaskID, &session.AgentInstanceID, &session.ContainerID, &session.AgentProfileID,
&session.ACPSessionID, &session.ExecutorID, &session.EnvironmentID, &status, &session.Progress, &session.ErrorMessage,
&metadataJSON, &session.StartedAt, &completedAt, &session.UpdatedAt,
)
@@ -1662,13 +1662,13 @@ func (r *SQLiteRepository) GetActiveAgentSessionByTaskID(ctx context.Context, ta
var completedAt sql.NullTime
err := r.db.QueryRowContext(ctx, `
SELECT id, task_id, agent_instance_id, container_id, agent_type, acp_session_id, executor_id, environment_id,
SELECT id, task_id, agent_instance_id, container_id, agent_profile_id, acp_session_id, executor_id, environment_id,
status, progress, error_message, metadata, started_at, completed_at, updated_at
FROM agent_sessions
WHERE task_id = ? AND status IN ('pending', 'running', 'waiting')
ORDER BY started_at DESC LIMIT 1
`, taskID).Scan(
&session.ID, &session.TaskID, &session.AgentInstanceID, &session.ContainerID, &session.AgentType,
&session.ID, &session.TaskID, &session.AgentInstanceID, &session.ContainerID, &session.AgentProfileID,
&session.ACPSessionID, &session.ExecutorID, &session.EnvironmentID, &status, &session.Progress, &session.ErrorMessage,
&metadataJSON, &session.StartedAt, &completedAt, &session.UpdatedAt,
)
@@ -1704,10 +1704,10 @@ func (r *SQLiteRepository) UpdateAgentSession(ctx context.Context, session *mode
result, err := r.db.ExecContext(ctx, `
UPDATE agent_sessions SET
agent_instance_id = ?, container_id = ?, agent_type = ?, acp_session_id = ?, executor_id = ?, environment_id = ?,
agent_instance_id = ?, container_id = ?, agent_profile_id = ?, acp_session_id = ?, executor_id = ?, environment_id = ?,
status = ?, progress = ?, error_message = ?, metadata = ?, completed_at = ?, updated_at = ?
WHERE id = ?
`, session.AgentInstanceID, session.ContainerID, session.AgentType, session.ACPSessionID, session.ExecutorID, session.EnvironmentID,
`, session.AgentInstanceID, session.ContainerID, session.AgentProfileID, session.ACPSessionID, session.ExecutorID, session.EnvironmentID,
string(session.Status), session.Progress, session.ErrorMessage, string(metadataJSON), session.CompletedAt,
session.UpdatedAt, session.ID)
if err != nil {
@@ -1747,7 +1747,7 @@ func (r *SQLiteRepository) UpdateAgentSessionStatus(ctx context.Context, id stri
// ListAgentSessions returns all agent sessions for a task
func (r *SQLiteRepository) ListAgentSessions(ctx context.Context, taskID string) ([]*models.AgentSession, error) {
rows, err := r.db.QueryContext(ctx, `
SELECT id, task_id, agent_instance_id, container_id, agent_type, acp_session_id, executor_id, environment_id,
SELECT id, task_id, agent_instance_id, container_id, agent_profile_id, acp_session_id, executor_id, environment_id,
status, progress, error_message, metadata, started_at, completed_at, updated_at
FROM agent_sessions WHERE task_id = ? ORDER BY started_at DESC
`, taskID)
@@ -1762,7 +1762,7 @@ func (r *SQLiteRepository) ListAgentSessions(ctx context.Context, taskID string)
// ListActiveAgentSessions returns all active agent sessions across all tasks
func (r *SQLiteRepository) ListActiveAgentSessions(ctx context.Context) ([]*models.AgentSession, error) {
rows, err := r.db.QueryContext(ctx, `
SELECT id, task_id, agent_instance_id, container_id, agent_type, acp_session_id, executor_id, environment_id,
SELECT id, task_id, agent_instance_id, container_id, agent_profile_id, acp_session_id, executor_id, environment_id,
status, progress, error_message, metadata, started_at, completed_at, updated_at
FROM agent_sessions WHERE status IN ('pending', 'running', 'waiting') ORDER BY started_at DESC
`)
@@ -1784,7 +1784,7 @@ func (r *SQLiteRepository) scanAgentSessions(rows *sql.Rows) ([]*models.AgentSes
var completedAt sql.NullTime
err := rows.Scan(
&session.ID, &session.TaskID, &session.AgentInstanceID, &session.ContainerID, &session.AgentType,
&session.ID, &session.TaskID, &session.AgentInstanceID, &session.ContainerID, &session.AgentProfileID,
&session.ACPSessionID, &session.ExecutorID, &session.EnvironmentID, &status, &session.Progress, &session.ErrorMessage,
&metadataJSON, &session.StartedAt, &completedAt, &session.UpdatedAt,
)