Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,166 changes: 368 additions & 798 deletions components/backend/handlers/sessions.go

Large diffs are not rendered by default.

6 changes: 2 additions & 4 deletions components/backend/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ func registerRoutes(r *gin.Engine) {
projectGroup.POST("/agentic-sessions/:sessionName/clone", handlers.CloneSession)
projectGroup.POST("/agentic-sessions/:sessionName/start", handlers.StartSession)
projectGroup.POST("/agentic-sessions/:sessionName/stop", handlers.StopSession)
projectGroup.PUT("/agentic-sessions/:sessionName/status", handlers.UpdateSessionStatus)
projectGroup.POST("/agentic-sessions/:sessionName/workspace/enable", handlers.EnableWorkspaceAccess)
projectGroup.POST("/agentic-sessions/:sessionName/workspace/touch", handlers.TouchWorkspaceAccess)
projectGroup.GET("/agentic-sessions/:sessionName/workspace", handlers.ListSessionWorkspace)
projectGroup.GET("/agentic-sessions/:sessionName/workspace/*path", handlers.GetSessionWorkspaceFile)
projectGroup.PUT("/agentic-sessions/:sessionName/workspace/*path", handlers.PutSessionWorkspaceFile)
Expand All @@ -71,9 +72,6 @@ func registerRoutes(r *gin.Engine) {
projectGroup.POST("/agentic-sessions/:sessionName/git/create-branch", handlers.GitCreateBranchSession)
projectGroup.GET("/agentic-sessions/:sessionName/git/list-branches", handlers.GitListBranchesSession)
projectGroup.GET("/agentic-sessions/:sessionName/k8s-resources", handlers.GetSessionK8sResources)
projectGroup.POST("/agentic-sessions/:sessionName/spawn-content-pod", handlers.SpawnContentPod)
projectGroup.GET("/agentic-sessions/:sessionName/content-pod-status", handlers.GetContentPodStatus)
projectGroup.DELETE("/agentic-sessions/:sessionName/content-pod", handlers.DeleteContentPod)
projectGroup.POST("/agentic-sessions/:sessionName/workflow", handlers.SelectWorkflow)
projectGroup.GET("/agentic-sessions/:sessionName/workflow/metadata", handlers.GetWorkflowMetadata)
projectGroup.POST("/agentic-sessions/:sessionName/repos", handlers.AddRepo)
Expand Down
63 changes: 0 additions & 63 deletions components/backend/types/errors.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
package types

import (
"fmt"
"strings"
)

// GetProviderSpecificGuidance returns remediation guidance for provider-specific errors
func GetProviderSpecificGuidance(provider ProviderType, errorType string) string {
switch provider {
Expand Down Expand Up @@ -34,61 +29,3 @@ func GetProviderSpecificGuidance(provider ProviderType, errorType string) string
return "Check your repository configuration and try again"
}
}

// FormatMixedProviderError formats an error message for mixed-provider scenarios
func FormatMixedProviderError(results []ProviderResult) string {
failedProviders := []string{}
successfulProviders := []string{}

for _, result := range results {
if result.Success {
successfulProviders = append(successfulProviders, string(result.Provider))
} else {
failedProviders = append(failedProviders, string(result.Provider))
}
}

if len(failedProviders) == 0 {
return "All repository operations completed successfully"
} else if len(failedProviders) == len(results) {
return "All repository operations failed. Check your provider configurations and credentials"
} else {
return fmt.Sprintf("Some repository operations failed (%s). Successful: %s",
strings.Join(failedProviders, ", "),
strings.Join(successfulProviders, ", "))
}
}

// CreateProviderResult creates a ProviderResult from an operation outcome
func CreateProviderResult(provider ProviderType, repoURL string, err error) ProviderResult {
result := ProviderResult{
Provider: provider,
RepoURL: repoURL,
}

if err != nil {
result.Success = false
result.Error = err.Error()
} else {
result.Success = true
}

return result
}

// AggregateProviderResults creates a MixedProviderSessionResult from multiple provider results
func AggregateProviderResults(results []ProviderResult) MixedProviderSessionResult {
allSuccess := true
for _, result := range results {
if !result.Success {
allSuccess = false
break
}
}

return MixedProviderSessionResult{
OverallSuccess: allSuccess,
Results: results,
Message: FormatMixedProviderError(results),
}
}
111 changes: 50 additions & 61 deletions components/backend/types/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type AgenticSession struct {
}

type AgenticSessionSpec struct {
Prompt string `json:"prompt" binding:"required"`
InitialPrompt string `json:"initialPrompt,omitempty"`
Interactive bool `json:"interactive,omitempty"`
DisplayName string `json:"displayName"`
LLMSettings LLMSettings `json:"llmSettings"`
Expand All @@ -20,66 +20,44 @@ type AgenticSessionSpec struct {
ResourceOverrides *ResourceOverrides `json:"resourceOverrides,omitempty"`
EnvironmentVariables map[string]string `json:"environmentVariables,omitempty"`
Project string `json:"project,omitempty"`
// Multi-repo support (unified mapping)
Repos []SessionRepoMapping `json:"repos,omitempty"`
MainRepoIndex *int `json:"mainRepoIndex,omitempty"`
// Multi-repo support
Repos []SimpleRepo `json:"repos,omitempty"`
// Active workflow for dynamic workflow switching
ActiveWorkflow *WorkflowSelection `json:"activeWorkflow,omitempty"`
}

// NamedGitRepo represents named repository types for multi-repo session support.
type NamedGitRepo struct {
// SimpleRepo represents a simplified repository configuration
type SimpleRepo struct {
URL string `json:"url"`
Branch *string `json:"branch,omitempty"`
}

type OutputNamedGitRepo struct {
URL string `json:"url"`
Branch *string `json:"branch,omitempty"`
}

// SessionRepoMapping is a unified session repo mapping.
type SessionRepoMapping struct {
Input NamedGitRepo `json:"input"`
Output *OutputNamedGitRepo `json:"output,omitempty"`
Status *string `json:"status,omitempty"`
}

type AgenticSessionStatus struct {
Phase string `json:"phase,omitempty"`
Message string `json:"message,omitempty"`
StartTime *string `json:"startTime,omitempty"`
CompletionTime *string `json:"completionTime,omitempty"`
JobName string `json:"jobName,omitempty"`
StateDir string `json:"stateDir,omitempty"`
// Result summary fields from runner
Subtype string `json:"subtype,omitempty"`
IsError bool `json:"is_error,omitempty"`
NumTurns int `json:"num_turns,omitempty"`
SessionID string `json:"session_id,omitempty"`
TotalCostUSD *float64 `json:"total_cost_usd,omitempty"`
Usage map[string]interface{} `json:"usage,omitempty"`
Result *string `json:"result,omitempty"`
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
Phase string `json:"phase,omitempty"`
StartTime *string `json:"startTime,omitempty"`
CompletionTime *string `json:"completionTime,omitempty"`
ReconciledRepos []ReconciledRepo `json:"reconciledRepos,omitempty"`
ReconciledWorkflow *ReconciledWorkflow `json:"reconciledWorkflow,omitempty"`
SDKSessionID string `json:"sdkSessionId,omitempty"`
SDKRestartCount int `json:"sdkRestartCount,omitempty"`
Conditions []Condition `json:"conditions,omitempty"`
}

type CreateAgenticSessionRequest struct {
Prompt string `json:"prompt" binding:"required"`
InitialPrompt string `json:"initialPrompt,omitempty"`
DisplayName string `json:"displayName,omitempty"`
LLMSettings *LLMSettings `json:"llmSettings,omitempty"`
Timeout *int `json:"timeout,omitempty"`
Interactive *bool `json:"interactive,omitempty"`
WorkspacePath string `json:"workspacePath,omitempty"`
ParentSessionID string `json:"parent_session_id,omitempty"`
// Multi-repo support (unified mapping)
Repos []SessionRepoMapping `json:"repos,omitempty"`
MainRepoIndex *int `json:"mainRepoIndex,omitempty"`
AutoPushOnComplete *bool `json:"autoPushOnComplete,omitempty"`
UserContext *UserContext `json:"userContext,omitempty"`
BotAccount *BotAccountRef `json:"botAccount,omitempty"`
ResourceOverrides *ResourceOverrides `json:"resourceOverrides,omitempty"`
EnvironmentVariables map[string]string `json:"environmentVariables,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
Annotations map[string]string `json:"annotations,omitempty"`
// Multi-repo support
Repos []SimpleRepo `json:"repos,omitempty"`
AutoPushOnComplete *bool `json:"autoPushOnComplete,omitempty"`
UserContext *UserContext `json:"userContext,omitempty"`
EnvironmentVariables map[string]string `json:"environmentVariables,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
Annotations map[string]string `json:"annotations,omitempty"`
}

type CloneSessionRequest struct {
Expand All @@ -88,17 +66,17 @@ type CloneSessionRequest struct {
}

type UpdateAgenticSessionRequest struct {
Prompt *string `json:"prompt,omitempty"`
DisplayName *string `json:"displayName,omitempty"`
Timeout *int `json:"timeout,omitempty"`
LLMSettings *LLMSettings `json:"llmSettings,omitempty"`
InitialPrompt *string `json:"initialPrompt,omitempty"`
DisplayName *string `json:"displayName,omitempty"`
Timeout *int `json:"timeout,omitempty"`
LLMSettings *LLMSettings `json:"llmSettings,omitempty"`
}

type CloneAgenticSessionRequest struct {
TargetProject string `json:"targetProject,omitempty"`
TargetSessionName string `json:"targetSessionName,omitempty"`
DisplayName string `json:"displayName,omitempty"`
Prompt string `json:"prompt,omitempty"`
InitialPrompt string `json:"initialPrompt,omitempty"`
}

// WorkflowSelection represents a workflow to load into the session
Expand All @@ -108,19 +86,30 @@ type WorkflowSelection struct {
Path string `json:"path,omitempty"`
}

// Mixed Provider Support Types
// ReconciledRepo captures reconciliation state for a repository
type ReconciledRepo struct {
URL string `json:"url"`
Branch string `json:"branch"`
Name string `json:"name,omitempty"`
Status string `json:"status,omitempty"`
ClonedAt *string `json:"clonedAt,omitempty"`
}

// ProviderResult contains the result of operations for a specific provider
type ProviderResult struct {
Provider ProviderType `json:"provider"`
Success bool `json:"success"`
Error string `json:"error,omitempty"`
RepoURL string `json:"repoUrl"`
// ReconciledWorkflow captures reconciliation state for the active workflow
type ReconciledWorkflow struct {
GitURL string `json:"gitUrl"`
Branch string `json:"branch"`
Path string `json:"path,omitempty"`
Status string `json:"status,omitempty"`
AppliedAt *string `json:"appliedAt,omitempty"`
}

// MixedProviderSessionResult contains results from multiple providers
type MixedProviderSessionResult struct {
OverallSuccess bool `json:"overallSuccess"`
Results []ProviderResult `json:"results"`
Message string `json:"message"`
// Condition mirrors metav1.Condition for API transport
type Condition struct {
Type string `json:"type"`
Status string `json:"status"`
Reason string `json:"reason,omitempty"`
Message string `json:"message,omitempty"`
LastTransitionTime string `json:"lastTransitionTime,omitempty"`
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
}
12 changes: 0 additions & 12 deletions components/frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";

type Repository = {
input: {
url: string;
branch?: string;
};
url: string;
branch?: string;
};

type RepositoriesAccordionProps = {
Expand Down Expand Up @@ -71,15 +69,15 @@ export function RepositoriesAccordion({
) : (
<div className="space-y-2">
{repositories.map((repo, idx) => {
const repoName = repo.input.url.split('/').pop()?.replace('.git', '') || `repo-${idx}`;
const repoName = repo.url.split('/').pop()?.replace('.git', '') || `repo-${idx}`;
const isRemoving = removingRepo === repoName;

return (
<div key={idx} className="flex items-center gap-2 p-2 border rounded bg-muted/30 hover:bg-muted/50 transition-colors">
<GitBranch className="h-4 w-4 text-muted-foreground flex-shrink-0" />
<div className="flex-1 min-w-0">
<div className="text-sm font-medium truncate">{repoName}</div>
<div className="text-xs text-muted-foreground truncate">{repo.input.url}</div>
<div className="text-xs text-muted-foreground truncate">{repo.url}</div>
</div>
<Button
variant="ghost"
Expand Down
Loading
Loading