Skip to content

Commit 40a5f31

Browse files
authored
Stronger session state (#338)
1 parent 45fad93 commit 40a5f31

39 files changed

+9425
-2149
lines changed

components/backend/handlers/sessions.go

Lines changed: 368 additions & 798 deletions
Large diffs are not rendered by default.

components/backend/routes.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ func registerRoutes(r *gin.Engine) {
5555
projectGroup.POST("/agentic-sessions/:sessionName/clone", handlers.CloneSession)
5656
projectGroup.POST("/agentic-sessions/:sessionName/start", handlers.StartSession)
5757
projectGroup.POST("/agentic-sessions/:sessionName/stop", handlers.StopSession)
58-
projectGroup.PUT("/agentic-sessions/:sessionName/status", handlers.UpdateSessionStatus)
58+
projectGroup.POST("/agentic-sessions/:sessionName/workspace/enable", handlers.EnableWorkspaceAccess)
59+
projectGroup.POST("/agentic-sessions/:sessionName/workspace/touch", handlers.TouchWorkspaceAccess)
5960
projectGroup.GET("/agentic-sessions/:sessionName/workspace", handlers.ListSessionWorkspace)
6061
projectGroup.GET("/agentic-sessions/:sessionName/workspace/*path", handlers.GetSessionWorkspaceFile)
6162
projectGroup.PUT("/agentic-sessions/:sessionName/workspace/*path", handlers.PutSessionWorkspaceFile)
@@ -71,9 +72,6 @@ func registerRoutes(r *gin.Engine) {
7172
projectGroup.POST("/agentic-sessions/:sessionName/git/create-branch", handlers.GitCreateBranchSession)
7273
projectGroup.GET("/agentic-sessions/:sessionName/git/list-branches", handlers.GitListBranchesSession)
7374
projectGroup.GET("/agentic-sessions/:sessionName/k8s-resources", handlers.GetSessionK8sResources)
74-
projectGroup.POST("/agentic-sessions/:sessionName/spawn-content-pod", handlers.SpawnContentPod)
75-
projectGroup.GET("/agentic-sessions/:sessionName/content-pod-status", handlers.GetContentPodStatus)
76-
projectGroup.DELETE("/agentic-sessions/:sessionName/content-pod", handlers.DeleteContentPod)
7775
projectGroup.POST("/agentic-sessions/:sessionName/workflow", handlers.SelectWorkflow)
7876
projectGroup.GET("/agentic-sessions/:sessionName/workflow/metadata", handlers.GetWorkflowMetadata)
7977
projectGroup.POST("/agentic-sessions/:sessionName/repos", handlers.AddRepo)

components/backend/types/errors.go

Lines changed: 0 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
package types
22

3-
import (
4-
"fmt"
5-
"strings"
6-
)
7-
83
// GetProviderSpecificGuidance returns remediation guidance for provider-specific errors
94
func GetProviderSpecificGuidance(provider ProviderType, errorType string) string {
105
switch provider {
@@ -34,61 +29,3 @@ func GetProviderSpecificGuidance(provider ProviderType, errorType string) string
3429
return "Check your repository configuration and try again"
3530
}
3631
}
37-
38-
// FormatMixedProviderError formats an error message for mixed-provider scenarios
39-
func FormatMixedProviderError(results []ProviderResult) string {
40-
failedProviders := []string{}
41-
successfulProviders := []string{}
42-
43-
for _, result := range results {
44-
if result.Success {
45-
successfulProviders = append(successfulProviders, string(result.Provider))
46-
} else {
47-
failedProviders = append(failedProviders, string(result.Provider))
48-
}
49-
}
50-
51-
if len(failedProviders) == 0 {
52-
return "All repository operations completed successfully"
53-
} else if len(failedProviders) == len(results) {
54-
return "All repository operations failed. Check your provider configurations and credentials"
55-
} else {
56-
return fmt.Sprintf("Some repository operations failed (%s). Successful: %s",
57-
strings.Join(failedProviders, ", "),
58-
strings.Join(successfulProviders, ", "))
59-
}
60-
}
61-
62-
// CreateProviderResult creates a ProviderResult from an operation outcome
63-
func CreateProviderResult(provider ProviderType, repoURL string, err error) ProviderResult {
64-
result := ProviderResult{
65-
Provider: provider,
66-
RepoURL: repoURL,
67-
}
68-
69-
if err != nil {
70-
result.Success = false
71-
result.Error = err.Error()
72-
} else {
73-
result.Success = true
74-
}
75-
76-
return result
77-
}
78-
79-
// AggregateProviderResults creates a MixedProviderSessionResult from multiple provider results
80-
func AggregateProviderResults(results []ProviderResult) MixedProviderSessionResult {
81-
allSuccess := true
82-
for _, result := range results {
83-
if !result.Success {
84-
allSuccess = false
85-
break
86-
}
87-
}
88-
89-
return MixedProviderSessionResult{
90-
OverallSuccess: allSuccess,
91-
Results: results,
92-
Message: FormatMixedProviderError(results),
93-
}
94-
}

components/backend/types/session.go

Lines changed: 50 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ type AgenticSession struct {
1010
}
1111

1212
type AgenticSessionSpec struct {
13-
Prompt string `json:"prompt" binding:"required"`
13+
InitialPrompt string `json:"initialPrompt,omitempty"`
1414
Interactive bool `json:"interactive,omitempty"`
1515
DisplayName string `json:"displayName"`
1616
LLMSettings LLMSettings `json:"llmSettings"`
@@ -20,66 +20,44 @@ type AgenticSessionSpec struct {
2020
ResourceOverrides *ResourceOverrides `json:"resourceOverrides,omitempty"`
2121
EnvironmentVariables map[string]string `json:"environmentVariables,omitempty"`
2222
Project string `json:"project,omitempty"`
23-
// Multi-repo support (unified mapping)
24-
Repos []SessionRepoMapping `json:"repos,omitempty"`
25-
MainRepoIndex *int `json:"mainRepoIndex,omitempty"`
23+
// Multi-repo support
24+
Repos []SimpleRepo `json:"repos,omitempty"`
2625
// Active workflow for dynamic workflow switching
2726
ActiveWorkflow *WorkflowSelection `json:"activeWorkflow,omitempty"`
2827
}
2928

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

36-
type OutputNamedGitRepo struct {
37-
URL string `json:"url"`
38-
Branch *string `json:"branch,omitempty"`
39-
}
40-
41-
// SessionRepoMapping is a unified session repo mapping.
42-
type SessionRepoMapping struct {
43-
Input NamedGitRepo `json:"input"`
44-
Output *OutputNamedGitRepo `json:"output,omitempty"`
45-
Status *string `json:"status,omitempty"`
46-
}
47-
4835
type AgenticSessionStatus struct {
49-
Phase string `json:"phase,omitempty"`
50-
Message string `json:"message,omitempty"`
51-
StartTime *string `json:"startTime,omitempty"`
52-
CompletionTime *string `json:"completionTime,omitempty"`
53-
JobName string `json:"jobName,omitempty"`
54-
StateDir string `json:"stateDir,omitempty"`
55-
// Result summary fields from runner
56-
Subtype string `json:"subtype,omitempty"`
57-
IsError bool `json:"is_error,omitempty"`
58-
NumTurns int `json:"num_turns,omitempty"`
59-
SessionID string `json:"session_id,omitempty"`
60-
TotalCostUSD *float64 `json:"total_cost_usd,omitempty"`
61-
Usage map[string]interface{} `json:"usage,omitempty"`
62-
Result *string `json:"result,omitempty"`
36+
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
37+
Phase string `json:"phase,omitempty"`
38+
StartTime *string `json:"startTime,omitempty"`
39+
CompletionTime *string `json:"completionTime,omitempty"`
40+
ReconciledRepos []ReconciledRepo `json:"reconciledRepos,omitempty"`
41+
ReconciledWorkflow *ReconciledWorkflow `json:"reconciledWorkflow,omitempty"`
42+
SDKSessionID string `json:"sdkSessionId,omitempty"`
43+
SDKRestartCount int `json:"sdkRestartCount,omitempty"`
44+
Conditions []Condition `json:"conditions,omitempty"`
6345
}
6446

6547
type CreateAgenticSessionRequest struct {
66-
Prompt string `json:"prompt" binding:"required"`
48+
InitialPrompt string `json:"initialPrompt,omitempty"`
6749
DisplayName string `json:"displayName,omitempty"`
6850
LLMSettings *LLMSettings `json:"llmSettings,omitempty"`
6951
Timeout *int `json:"timeout,omitempty"`
7052
Interactive *bool `json:"interactive,omitempty"`
71-
WorkspacePath string `json:"workspacePath,omitempty"`
7253
ParentSessionID string `json:"parent_session_id,omitempty"`
73-
// Multi-repo support (unified mapping)
74-
Repos []SessionRepoMapping `json:"repos,omitempty"`
75-
MainRepoIndex *int `json:"mainRepoIndex,omitempty"`
76-
AutoPushOnComplete *bool `json:"autoPushOnComplete,omitempty"`
77-
UserContext *UserContext `json:"userContext,omitempty"`
78-
BotAccount *BotAccountRef `json:"botAccount,omitempty"`
79-
ResourceOverrides *ResourceOverrides `json:"resourceOverrides,omitempty"`
80-
EnvironmentVariables map[string]string `json:"environmentVariables,omitempty"`
81-
Labels map[string]string `json:"labels,omitempty"`
82-
Annotations map[string]string `json:"annotations,omitempty"`
54+
// Multi-repo support
55+
Repos []SimpleRepo `json:"repos,omitempty"`
56+
AutoPushOnComplete *bool `json:"autoPushOnComplete,omitempty"`
57+
UserContext *UserContext `json:"userContext,omitempty"`
58+
EnvironmentVariables map[string]string `json:"environmentVariables,omitempty"`
59+
Labels map[string]string `json:"labels,omitempty"`
60+
Annotations map[string]string `json:"annotations,omitempty"`
8361
}
8462

8563
type CloneSessionRequest struct {
@@ -88,17 +66,17 @@ type CloneSessionRequest struct {
8866
}
8967

9068
type UpdateAgenticSessionRequest struct {
91-
Prompt *string `json:"prompt,omitempty"`
92-
DisplayName *string `json:"displayName,omitempty"`
93-
Timeout *int `json:"timeout,omitempty"`
94-
LLMSettings *LLMSettings `json:"llmSettings,omitempty"`
69+
InitialPrompt *string `json:"initialPrompt,omitempty"`
70+
DisplayName *string `json:"displayName,omitempty"`
71+
Timeout *int `json:"timeout,omitempty"`
72+
LLMSettings *LLMSettings `json:"llmSettings,omitempty"`
9573
}
9674

9775
type CloneAgenticSessionRequest struct {
9876
TargetProject string `json:"targetProject,omitempty"`
9977
TargetSessionName string `json:"targetSessionName,omitempty"`
10078
DisplayName string `json:"displayName,omitempty"`
101-
Prompt string `json:"prompt,omitempty"`
79+
InitialPrompt string `json:"initialPrompt,omitempty"`
10280
}
10381

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

111-
// Mixed Provider Support Types
89+
// ReconciledRepo captures reconciliation state for a repository
90+
type ReconciledRepo struct {
91+
URL string `json:"url"`
92+
Branch string `json:"branch"`
93+
Name string `json:"name,omitempty"`
94+
Status string `json:"status,omitempty"`
95+
ClonedAt *string `json:"clonedAt,omitempty"`
96+
}
11297

113-
// ProviderResult contains the result of operations for a specific provider
114-
type ProviderResult struct {
115-
Provider ProviderType `json:"provider"`
116-
Success bool `json:"success"`
117-
Error string `json:"error,omitempty"`
118-
RepoURL string `json:"repoUrl"`
98+
// ReconciledWorkflow captures reconciliation state for the active workflow
99+
type ReconciledWorkflow struct {
100+
GitURL string `json:"gitUrl"`
101+
Branch string `json:"branch"`
102+
Path string `json:"path,omitempty"`
103+
Status string `json:"status,omitempty"`
104+
AppliedAt *string `json:"appliedAt,omitempty"`
119105
}
120106

121-
// MixedProviderSessionResult contains results from multiple providers
122-
type MixedProviderSessionResult struct {
123-
OverallSuccess bool `json:"overallSuccess"`
124-
Results []ProviderResult `json:"results"`
125-
Message string `json:"message"`
107+
// Condition mirrors metav1.Condition for API transport
108+
type Condition struct {
109+
Type string `json:"type"`
110+
Status string `json:"status"`
111+
Reason string `json:"reason,omitempty"`
112+
Message string `json:"message,omitempty"`
113+
LastTransitionTime string `json:"lastTransitionTime,omitempty"`
114+
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
126115
}

components/frontend/package-lock.json

Lines changed: 0 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/accordions/repositories-accordion.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,8 @@ import { Badge } from "@/components/ui/badge";
77
import { Button } from "@/components/ui/button";
88

99
type Repository = {
10-
input: {
11-
url: string;
12-
branch?: string;
13-
};
10+
url: string;
11+
branch?: string;
1412
};
1513

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

7775
return (
7876
<div key={idx} className="flex items-center gap-2 p-2 border rounded bg-muted/30 hover:bg-muted/50 transition-colors">
7977
<GitBranch className="h-4 w-4 text-muted-foreground flex-shrink-0" />
8078
<div className="flex-1 min-w-0">
8179
<div className="text-sm font-medium truncate">{repoName}</div>
82-
<div className="text-xs text-muted-foreground truncate">{repo.input.url}</div>
80+
<div className="text-xs text-muted-foreground truncate">{repo.url}</div>
8381
</div>
8482
<Button
8583
variant="ghost"

0 commit comments

Comments
 (0)