Simplify session lifecycle and MCP cleanup
This commit is contained in:
@@ -56,14 +56,12 @@ func (l *Launcher) LaunchAgent(p *preset.Preset, displayName, initialPrompt, par
|
||||
env = append(env, k+"="+v)
|
||||
}
|
||||
|
||||
// Mint a per-spawn MCP config file pointing at the mcp-stdio proxy
|
||||
// with the new child's identity. We don't know the identity until
|
||||
// we've created the child, but the child needs the env/argv at
|
||||
// creation time — so we reserve the identity by pre-creating the
|
||||
// MCP config with a placeholder, then patching it post-spawn.
|
||||
identity, mcpConfigPath, err := l.writeMCPConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
identity := mintIdentity()
|
||||
var cleanupPaths []string
|
||||
cleanup := func() {
|
||||
for _, path := range cleanupPaths {
|
||||
_ = os.RemoveAll(path)
|
||||
}
|
||||
}
|
||||
|
||||
if p.MCPInjection != nil {
|
||||
@@ -72,24 +70,33 @@ func (l *Launcher) LaunchAgent(p *preset.Preset, displayName, initialPrompt, par
|
||||
if p.MCPInjection.Flag == "" {
|
||||
return nil, fmt.Errorf("preset %s: mcp_injection.flag required for kind=flag", p.Name)
|
||||
}
|
||||
mcpConfigPath, err := l.writeMCPConfig(identity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cleanupPaths = append(cleanupPaths, mcpConfigPath)
|
||||
argv = append(argv, p.MCPInjection.Flag, mcpConfigPath)
|
||||
case "env_var":
|
||||
if p.MCPInjection.Var == "" {
|
||||
return nil, fmt.Errorf("preset %s: mcp_injection.var required for kind=env_var", p.Name)
|
||||
}
|
||||
mcpConfigPath, err := l.writeMCPConfig(identity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cleanupPaths = append(cleanupPaths, mcpConfigPath)
|
||||
env = append(env, p.MCPInjection.Var+"="+mcpConfigPath)
|
||||
case "config_file":
|
||||
// Merge patterm's MCP entry into a vendored copy of the
|
||||
// user's existing config file, then point the child at the
|
||||
// vendored copy via the preset's home_var. The real config
|
||||
// file is never modified.
|
||||
envAssign, _, mErr := mcpConfigMerge(p, p.MCPInjection, identity, l.bin, l.mcpSocket)
|
||||
envAssign, homeDir, mErr := mcpConfigMerge(p, p.MCPInjection, identity, l.bin, l.mcpSocket)
|
||||
if mErr != nil {
|
||||
_ = os.Remove(mcpConfigPath)
|
||||
return nil, mErr
|
||||
}
|
||||
cleanupPaths = append(cleanupPaths, homeDir)
|
||||
env = append(env, envAssign)
|
||||
env = append(env, "PATTERM_MCP_CONFIG="+mcpConfigPath)
|
||||
case "cli_override":
|
||||
// Inline -c key=value overrides for agents that accept
|
||||
// them (codex's `-c mcp_servers.patterm.command=...`). No
|
||||
@@ -97,7 +104,6 @@ func (l *Launcher) LaunchAgent(p *preset.Preset, displayName, initialPrompt, par
|
||||
// are untouched.
|
||||
extra, err := mcpCLIOverrideArgs(p, p.MCPInjection, identity, l.bin, l.mcpSocket)
|
||||
if err != nil {
|
||||
_ = os.Remove(mcpConfigPath)
|
||||
return nil, err
|
||||
}
|
||||
argv = append(argv, extra...)
|
||||
@@ -108,11 +114,11 @@ func (l *Launcher) LaunchAgent(p *preset.Preset, displayName, initialPrompt, par
|
||||
// XDG_CONFIG_HOME stays as the user set it.
|
||||
assignment, err := mcpConfigEnv(p, p.MCPInjection, identity, l.bin, l.mcpSocket)
|
||||
if err != nil {
|
||||
_ = os.Remove(mcpConfigPath)
|
||||
return nil, err
|
||||
}
|
||||
env = append(env, assignment)
|
||||
default:
|
||||
cleanup()
|
||||
return nil, fmt.Errorf("preset %s: unknown mcp_injection.kind %q", p.Name, p.MCPInjection.Kind)
|
||||
}
|
||||
}
|
||||
@@ -120,16 +126,17 @@ func (l *Launcher) LaunchAgent(p *preset.Preset, displayName, initialPrompt, par
|
||||
// Spawn with the chosen identity.
|
||||
cols, rows := l.size()
|
||||
c, err := l.sess.Spawn(SpawnSpec{
|
||||
Kind: KindAgent,
|
||||
Argv: argv,
|
||||
Env: env,
|
||||
Name: displayName,
|
||||
ParentID: parentID,
|
||||
PresetRef: p.Name,
|
||||
Identity: identity,
|
||||
Kind: KindAgent,
|
||||
Argv: argv,
|
||||
Env: env,
|
||||
Name: displayName,
|
||||
ParentID: parentID,
|
||||
PresetRef: p.Name,
|
||||
Identity: identity,
|
||||
CleanupPaths: cleanupPaths,
|
||||
}, cols, rows)
|
||||
if err != nil {
|
||||
_ = os.Remove(mcpConfigPath)
|
||||
cleanup()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -219,17 +226,16 @@ func (l *Launcher) LaunchTerminal(argv []string, displayName, parentID, workDir
|
||||
}, cols, rows)
|
||||
}
|
||||
|
||||
func (l *Launcher) writeMCPConfig() (identity, path string, err error) {
|
||||
identity = mintIdentity()
|
||||
func (l *Launcher) writeMCPConfig(identity string) (string, error) {
|
||||
dir, err := preset.ConfigDir()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return "", err
|
||||
}
|
||||
dir = filepath.Join(dir, "mcp")
|
||||
if err := os.MkdirAll(dir, 0o700); err != nil {
|
||||
return "", "", err
|
||||
return "", err
|
||||
}
|
||||
path = filepath.Join(dir, identity+".json")
|
||||
path := filepath.Join(dir, identity+".json")
|
||||
cfg := map[string]any{
|
||||
"mcpServers": map[string]any{
|
||||
"patterm": map[string]any{
|
||||
@@ -240,13 +246,13 @@ func (l *Launcher) writeMCPConfig() (identity, path string, err error) {
|
||||
}
|
||||
body, err := json.MarshalIndent(cfg, "", " ")
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return "", err
|
||||
}
|
||||
body = append(body, '\n')
|
||||
if err := os.WriteFile(path, body, 0o600); err != nil {
|
||||
return "", "", err
|
||||
return "", err
|
||||
}
|
||||
return identity, path, nil
|
||||
return path, nil
|
||||
}
|
||||
|
||||
// waitForIdle polls the child's IdleMS until it exceeds idle, or until
|
||||
|
||||
Reference in New Issue
Block a user