feat: return worktree info in orchestrator.start response

- Add WorktreePath and WorktreeBranch to StartTaskResponse
- Add those fields to TaskExecution and LaunchAgentResponse
- Store worktree_path and worktree_branch in AgentInstance metadata
- Update frontend to use worktree info from start response directly
This commit is contained in:
jcfs
2026-01-12 16:28:56 +00:00
parent a7deb3300c
commit 74c020b371
6 changed files with 60 additions and 22 deletions
+13
View File
@@ -599,10 +599,23 @@ func (a *lifecycleAdapter) LaunchAgent(ctx context.Context, req *executor.Launch
// Streaming is now handled by the lifecycle manager
// Extract worktree info from metadata if available
var worktreePath, worktreeBranch string
if instance.Metadata != nil {
if path, ok := instance.Metadata["worktree_path"].(string); ok {
worktreePath = path
}
if branch, ok := instance.Metadata["worktree_branch"].(string); ok {
worktreeBranch = branch
}
}
return &executor.LaunchAgentResponse{
AgentInstanceID: instance.ID,
ContainerID: instance.ContainerID,
Status: instance.Status,
WorktreePath: worktreePath,
WorktreeBranch: worktreeBranch,
}, nil
}
@@ -304,6 +304,7 @@ func (m *Manager) Launch(ctx context.Context, req *LaunchRequest) (*AgentInstanc
workspacePath := req.WorkspacePath
var mainRepoGitDir string // Path to main repo's .git directory for container mount
var worktreeID string // Store worktree ID for session resumption
var worktreeBranch string // Store worktree branch for API responses
if req.UseWorktree && m.worktreeMgr != nil && req.RepositoryPath != "" {
wt, err := m.getOrCreateWorktree(ctx, req)
if err != nil {
@@ -314,6 +315,7 @@ func (m *Manager) Launch(ctx context.Context, req *LaunchRequest) (*AgentInstanc
} else {
workspacePath = wt.Path
worktreeID = wt.ID
worktreeBranch = wt.Branch
// Git worktrees reference the main repo's .git directory via a .git file
// The worktree metadata in .git/worktrees/{name} contains a 'commondir' file
// that points back to the main .git directory (usually '../..')
@@ -365,9 +367,11 @@ func (m *Manager) Launch(ctx context.Context, req *LaunchRequest) (*AgentInstanc
if instanceMetadata == nil {
instanceMetadata = make(map[string]interface{})
}
// Store worktree_id in metadata for session resumption
// Store worktree info in metadata for session resumption and API responses
if worktreeID != "" {
instanceMetadata["worktree_id"] = worktreeID
instanceMetadata["worktree_path"] = workspacePath
instanceMetadata["worktree_branch"] = worktreeBranch
}
instance := &AgentInstance{
ID: instanceID,
@@ -67,12 +67,22 @@ func (c *Controller) StartTask(ctx context.Context, req dto.StartTaskRequest) (d
return dto.StartTaskResponse{}, err
}
return dto.StartTaskResponse{
resp := dto.StartTaskResponse{
Success: true,
TaskID: execution.TaskID,
AgentInstanceID: execution.AgentInstanceID,
Status: string(execution.Status),
}, nil
}
// Include worktree info if available
if execution.WorktreePath != "" {
resp.WorktreePath = &execution.WorktreePath
}
if execution.WorktreeBranch != "" {
resp.WorktreeBranch = &execution.WorktreeBranch
}
return resp, nil
}
// StopTask stops task execution
@@ -51,10 +51,12 @@ type StartTaskRequest struct {
// StartTaskResponse is the response for orchestrator.start
type StartTaskResponse struct {
Success bool `json:"success"`
TaskID string `json:"task_id"`
AgentInstanceID string `json:"agent_instance_id"`
Status string `json:"status"`
Success bool `json:"success"`
TaskID string `json:"task_id"`
AgentInstanceID string `json:"agent_instance_id"`
Status string `json:"status"`
WorktreePath *string `json:"worktree_path,omitempty"`
WorktreeBranch *string `json:"worktree_branch,omitempty"`
}
// StopTaskRequest is the payload for orchestrator.stop
@@ -77,6 +77,8 @@ type LaunchAgentResponse struct {
AgentInstanceID string
ContainerID string
Status v1.AgentStatus
WorktreePath string
WorktreeBranch string
}
// TaskExecution tracks an active task execution (kept for API compatibility)
@@ -90,6 +92,9 @@ type TaskExecution struct {
LastUpdate time.Time
// SessionID is the database ID of the agent session
SessionID string
// Worktree info for the agent
WorktreePath string
WorktreeBranch string
}
// FromAgentSession converts a models.AgentSession to TaskExecution
@@ -341,6 +346,8 @@ func (e *Executor) Execute(ctx context.Context, task *v1.Task) (*TaskExecution,
Progress: 0,
LastUpdate: now,
SessionID: sessionID,
WorktreePath: resp.WorktreePath,
WorktreeBranch: resp.WorktreeBranch,
}
e.mu.Lock()
@@ -351,7 +358,9 @@ func (e *Executor) Execute(ctx context.Context, task *v1.Task) (*TaskExecution,
zap.String("task_id", task.ID),
zap.String("session_id", sessionID),
zap.String("agent_instance_id", resp.AgentInstanceID),
zap.String("container_id", resp.ContainerID))
zap.String("container_id", resp.ContainerID),
zap.String("worktree_path", resp.WorktreePath),
zap.String("worktree_branch", resp.WorktreeBranch))
return execution, nil
}
+14 -14
View File
@@ -162,25 +162,25 @@ export default function TaskPage({ task: initialTask }: TaskPageClientProps) {
try {
// Use task's agent_type if set, otherwise default to 'augment-agent'
const agentType = task.agent_type ?? 'augment-agent';
await client.request('orchestrator.start', {
interface StartResponse {
success: boolean;
task_id: string;
agent_instance_id: string;
status: string;
worktree_path?: string;
worktree_branch?: string;
}
const response = await client.request<StartResponse>('orchestrator.start', {
task_id: task.id,
agent_type: agentType,
}, 15000);
setIsAgentRunning(true);
// Fetch updated task info after agent starts (to get worktree path/branch)
// Wait a bit for the worktree to be created
setTimeout(async () => {
try {
const updatedTask = await client.request<Task>('task.get', { id: task.id }, 5000);
if (updatedTask?.worktree_path) {
setWorktreePath(updatedTask.worktree_path);
setWorktreeBranch(updatedTask.worktree_branch ?? null);
}
} catch (err) {
console.error('Failed to fetch updated task info:', err);
}
}, 1000);
// Update worktree info from response
if (response?.worktree_path) {
setWorktreePath(response.worktree_path);
setWorktreeBranch(response.worktree_branch ?? null);
}
} catch (error) {
console.error('Failed to start agent:', error);
} finally {