fix(handoff): preserve GT_AGENT across session restarts (#788)
Adds GT_AGENT env var to track agent override when using --agent flag. Handoff reads and preserves GT_AGENT so non-default agents persist across restarts. Co-authored-by: joshuavial <git@codewithjv.com>
This commit is contained in:
@@ -384,7 +384,20 @@ func buildRestartCommand(sessionName string) (string, error) {
|
|||||||
// 3. export Claude-related env vars (not inherited by fresh shell)
|
// 3. export Claude-related env vars (not inherited by fresh shell)
|
||||||
// 4. run claude with the startup beacon (triggers immediate context loading)
|
// 4. run claude with the startup beacon (triggers immediate context loading)
|
||||||
// Use exec to ensure clean process replacement.
|
// Use exec to ensure clean process replacement.
|
||||||
runtimeCmd := config.GetRuntimeCommandWithPrompt("", beacon)
|
//
|
||||||
|
// Check if current session is using a non-default agent (GT_AGENT env var).
|
||||||
|
// If so, preserve it across handoff by using the override variant.
|
||||||
|
currentAgent := os.Getenv("GT_AGENT")
|
||||||
|
var runtimeCmd string
|
||||||
|
if currentAgent != "" {
|
||||||
|
var err error
|
||||||
|
runtimeCmd, err = config.GetRuntimeCommandWithPromptAndAgentOverride("", beacon, currentAgent)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("resolving agent config: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
runtimeCmd = config.GetRuntimeCommandWithPrompt("", beacon)
|
||||||
|
}
|
||||||
|
|
||||||
// Build environment exports - role vars first, then Claude vars
|
// Build environment exports - role vars first, then Claude vars
|
||||||
var exports []string
|
var exports []string
|
||||||
@@ -398,6 +411,11 @@ func buildRestartCommand(sessionName string) (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Preserve GT_AGENT across handoff so agent override persists
|
||||||
|
if currentAgent != "" {
|
||||||
|
exports = append(exports, "GT_AGENT="+currentAgent)
|
||||||
|
}
|
||||||
|
|
||||||
// Add Claude-related env vars from current environment
|
// Add Claude-related env vars from current environment
|
||||||
for _, name := range claudeEnvVars {
|
for _, name := range claudeEnvVars {
|
||||||
if val := os.Getenv(name); val != "" {
|
if val := os.Getenv(name); val != "" {
|
||||||
|
|||||||
@@ -1353,6 +1353,10 @@ func BuildStartupCommandWithAgentOverride(envVars map[string]string, rigPath, pr
|
|||||||
if rc.Session != nil && rc.Session.SessionIDEnv != "" {
|
if rc.Session != nil && rc.Session.SessionIDEnv != "" {
|
||||||
resolvedEnv["GT_SESSION_ID_ENV"] = rc.Session.SessionIDEnv
|
resolvedEnv["GT_SESSION_ID_ENV"] = rc.Session.SessionIDEnv
|
||||||
}
|
}
|
||||||
|
// Record agent override so handoff can preserve it
|
||||||
|
if agentOverride != "" {
|
||||||
|
resolvedEnv["GT_AGENT"] = agentOverride
|
||||||
|
}
|
||||||
|
|
||||||
// Build environment export prefix
|
// Build environment export prefix
|
||||||
var exports []string
|
var exports []string
|
||||||
|
|||||||
@@ -2670,3 +2670,63 @@ func TestQuoteForShell(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuildStartupCommandWithAgentOverride_SetsGTAgent(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
townRoot := t.TempDir()
|
||||||
|
rigPath := filepath.Join(townRoot, "testrig")
|
||||||
|
|
||||||
|
// Create necessary config files
|
||||||
|
townSettings := NewTownSettings()
|
||||||
|
if err := SaveTownSettings(TownSettingsPath(townRoot), townSettings); err != nil {
|
||||||
|
t.Fatalf("SaveTownSettings: %v", err)
|
||||||
|
}
|
||||||
|
if err := SaveRigSettings(RigSettingsPath(rigPath), NewRigSettings()); err != nil {
|
||||||
|
t.Fatalf("SaveRigSettings: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd, err := BuildStartupCommandWithAgentOverride(
|
||||||
|
map[string]string{"GT_ROLE": constants.RoleWitness},
|
||||||
|
rigPath,
|
||||||
|
"",
|
||||||
|
"gemini",
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("BuildStartupCommandWithAgentOverride: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should include GT_AGENT=gemini in export so handoff can preserve it
|
||||||
|
if !strings.Contains(cmd, "GT_AGENT=gemini") {
|
||||||
|
t.Errorf("expected GT_AGENT=gemini in command, got: %q", cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildStartupCommandWithAgentOverride_NoGTAgentWhenNoOverride(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
townRoot := t.TempDir()
|
||||||
|
rigPath := filepath.Join(townRoot, "testrig")
|
||||||
|
|
||||||
|
// Create necessary config files
|
||||||
|
townSettings := NewTownSettings()
|
||||||
|
if err := SaveTownSettings(TownSettingsPath(townRoot), townSettings); err != nil {
|
||||||
|
t.Fatalf("SaveTownSettings: %v", err)
|
||||||
|
}
|
||||||
|
if err := SaveRigSettings(RigSettingsPath(rigPath), NewRigSettings()); err != nil {
|
||||||
|
t.Fatalf("SaveRigSettings: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd, err := BuildStartupCommandWithAgentOverride(
|
||||||
|
map[string]string{"GT_ROLE": constants.RoleWitness},
|
||||||
|
rigPath,
|
||||||
|
"",
|
||||||
|
"", // No override
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("BuildStartupCommandWithAgentOverride: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should NOT include GT_AGENT when no override is used
|
||||||
|
if strings.Contains(cmd, "GT_AGENT=") {
|
||||||
|
t.Errorf("expected no GT_AGENT in command when no override, got: %q", cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user