fix: reapply system prompts on restore

This commit is contained in:
whoisasx 2026-07-04 00:34:49 +05:30
parent fa68474e6f
commit fd04e11061
18 changed files with 98 additions and 26 deletions

View File

@ -122,6 +122,11 @@ func (p *Plugin) GetRestoreCommand(ctx context.Context, cfg ports.RestoreConfig)
cmd = make([]string, 0, 5)
cmd = append(cmd, binary)
appendPermissionFlags(&cmd, cfg.Permissions)
if cfg.SystemPrompt != "" {
cmd = append(cmd, "--append-system-prompt", cfg.SystemPrompt)
} else if cfg.SystemPromptFile != "" {
cmd = append(cmd, "--append-system-prompt-file", cfg.SystemPromptFile)
}
cmd = append(cmd, "--resume", agentSessionID)
return cmd, true, nil
}

View File

@ -138,7 +138,9 @@ func TestGetRestoreCommand(t *testing.T) {
Session: ports.SessionRef{
Metadata: map[string]string{ports.MetadataKeyAgentSessionID: "T-abc123"},
},
Permissions: ports.PermissionModeBypassPermissions,
Permissions: ports.PermissionModeBypassPermissions,
SystemPrompt: "restore inline wins",
SystemPromptFile: "/tmp/system.md",
})
if err != nil {
t.Fatal(err)
@ -147,7 +149,7 @@ func TestGetRestoreCommand(t *testing.T) {
t.Fatal("ok=false, want true")
}
want := []string{"amp", "--permission-mode", "bypassPermissions", "--resume", "T-abc123"}
want := []string{"amp", "--permission-mode", "bypassPermissions", "--append-system-prompt", "restore inline wins", "--resume", "T-abc123"}
if !reflect.DeepEqual(cmd, want) {
t.Fatalf("cmd = %#v, want %#v", cmd, want)
}

View File

@ -152,7 +152,13 @@ func (p *Plugin) GetRestoreCommand(ctx context.Context, cfg ports.RestoreConfig)
if err != nil {
return nil, false, err
}
cmd = []string{binary, "--print", "--resume", agentSessionID}
cmd = []string{binary, "--print"}
if cfg.SystemPrompt != "" {
cmd = append(cmd, "--instruction", cfg.SystemPrompt)
} else if cfg.SystemPromptFile != "" {
cmd = append(cmd, "--instruction-file", cfg.SystemPromptFile)
}
cmd = append(cmd, "--resume", agentSessionID)
return cmd, true, nil
}

View File

@ -134,7 +134,9 @@ func TestGetRestoreCommand(t *testing.T) {
Session: ports.SessionRef{
Metadata: map[string]string{ports.MetadataKeyAgentSessionID: "sess-abc123"},
},
Permissions: ports.PermissionModeBypassPermissions,
Permissions: ports.PermissionModeBypassPermissions,
SystemPrompt: "restore inline wins",
SystemPromptFile: "/tmp/system.md",
})
if err != nil {
t.Fatal(err)
@ -143,7 +145,7 @@ func TestGetRestoreCommand(t *testing.T) {
t.Fatal("ok=false, want true")
}
want := []string{"auggie", "--print", "--resume", "sess-abc123"}
want := []string{"auggie", "--print", "--instruction", "restore inline wins", "--resume", "sess-abc123"}
if !reflect.DeepEqual(cmd, want) {
t.Fatalf("cmd = %#v, want %#v", cmd, want)
}

View File

@ -110,8 +110,9 @@ func (p *Plugin) GetPromptDeliveryStrategy(ctx context.Context, cfg ports.Launch
// GetRestoreCommand rebuilds the argv that continues an existing Autohand
// session: `autohand resume [--path <workspace>] <sessionId>`. ok is false when
// the hook-derived native session id has not landed yet, so callers can fall
// back to fresh launch behavior. Autohand's resume sub-command does not accept
// approval flags, so none are appended here.
// back to fresh launch behavior. Autohand's resume sub-command only accepts the
// workspace path and session id, so approval and system-prompt flags are not
// re-applied here.
func (p *Plugin) GetRestoreCommand(ctx context.Context, cfg ports.RestoreConfig) (cmd []string, ok bool, err error) {
if err := ctx.Err(); err != nil {
return nil, false, err

View File

@ -152,7 +152,8 @@ func TestGetRestoreCommandReadsAgentSessionID(t *testing.T) {
plugin := &Plugin{resolvedBinary: "autohand"}
cmd, ok, err := plugin.GetRestoreCommand(context.Background(), ports.RestoreConfig{
Permissions: ports.PermissionModeAuto,
Permissions: ports.PermissionModeAuto,
SystemPrompt: "restore instructions ignored by resume",
Session: ports.SessionRef{
WorkspacePath: "/work/space",
Metadata: map[string]string{ports.MetadataKeyAgentSessionID: "sess-123"},

View File

@ -122,6 +122,9 @@ func (p *Plugin) GetRestoreCommand(ctx context.Context, cfg ports.RestoreConfig)
cmd = make([]string, 0, 8)
cmd = append(cmd, binary, "--json")
appendApprovalFlags(&cmd, cfg.Permissions)
if cfg.SystemPrompt != "" {
cmd = append(cmd, "-s", cfg.SystemPrompt)
}
cmd = append(cmd, "--id", agentSessionID)
return cmd, true, nil
}

View File

@ -241,7 +241,8 @@ func TestGetRestoreCommandReadsAgentSessionID(t *testing.T) {
plugin := &Plugin{resolvedBinary: "cline"}
cmd, ok, err := plugin.GetRestoreCommand(context.Background(), ports.RestoreConfig{
Permissions: ports.PermissionModeAuto,
Permissions: ports.PermissionModeAuto,
SystemPrompt: "restore instructions",
Session: ports.SessionRef{
Metadata: map[string]string{ports.MetadataKeyAgentSessionID: "session-123"},
},
@ -256,6 +257,7 @@ func TestGetRestoreCommandReadsAgentSessionID(t *testing.T) {
"cline",
"--json",
"--auto-approve", "true",
"-s", "restore instructions",
"--id", "session-123",
}
if !reflect.DeepEqual(cmd, want) {

View File

@ -125,6 +125,11 @@ func (p *Plugin) GetRestoreCommand(ctx context.Context, cfg ports.RestoreConfig)
appendSessionHookFlags(&cmd)
appendTerminalCompatibilityFlags(&cmd)
appendWorkspaceTrustFlag(&cmd, cfg.Session.WorkspacePath)
if cfg.SystemPrompt != "" {
cmd = append(cmd, "-c", "developer_instructions="+codexTOMLConfigString(cfg.SystemPrompt))
} else if cfg.SystemPromptFile != "" {
cmd = append(cmd, "-c", "model_instructions_file="+cfg.SystemPromptFile)
}
cmd = append(cmd, agentSessionID)
return cmd, true, nil
}

View File

@ -398,7 +398,9 @@ func TestGetRestoreCommandReadsAgentSessionID(t *testing.T) {
workspace := canonicalTempDir(t)
cmd, ok, err := plugin.GetRestoreCommand(context.Background(), ports.RestoreConfig{
Permissions: ports.PermissionModeAuto,
Permissions: ports.PermissionModeAuto,
SystemPrompt: "restore inline wins",
SystemPromptFile: filepath.Join("tmp", "restore-system.md"),
Session: ports.SessionRef{
Metadata: map[string]string{ports.MetadataKeyAgentSessionID: "thread-123"},
WorkspacePath: workspace,
@ -425,6 +427,7 @@ func TestGetRestoreCommandReadsAgentSessionID(t *testing.T) {
}
want = append(want,
"-c", `projects={`+codexTOMLConfigString(workspace)+`={trust_level="trusted"}}`,
"-c", "developer_instructions="+codexTOMLConfigString("restore inline wins"),
"thread-123",
)
if !reflect.DeepEqual(cmd, want) {

View File

@ -151,6 +151,11 @@ func (p *Plugin) GetRestoreCommand(ctx context.Context, cfg ports.RestoreConfig)
return nil, false, err
}
cmd = append(cmd, settingsArgs...)
if cfg.SystemPrompt != "" {
cmd = append(cmd, "--append-system-prompt", cfg.SystemPrompt)
} else if cfg.SystemPromptFile != "" {
cmd = append(cmd, "--append-system-prompt-file", cfg.SystemPromptFile)
}
cmd = append(cmd, "-r", agentSessionID)
return cmd, true, nil
}

View File

@ -141,6 +141,8 @@ func TestGetLaunchCommandSystemPrompt(t *testing.T) {
func TestGetRestoreCommand(t *testing.T) {
plugin := &Plugin{resolvedBinary: "droid"}
cmd, ok, err := plugin.GetRestoreCommand(context.Background(), ports.RestoreConfig{
SystemPrompt: "restore inline wins",
SystemPromptFile: "/tmp/system.md",
Session: ports.SessionRef{
ID: "mer-4",
Metadata: map[string]string{
@ -154,7 +156,7 @@ func TestGetRestoreCommand(t *testing.T) {
if !ok {
t.Fatal("ok=false, want true")
}
want := []string{"droid", "-r", "droid-ses-1"}
want := []string{"droid", "--append-system-prompt", "restore inline wins", "-r", "droid-ses-1"}
if !reflect.DeepEqual(cmd, want) {
t.Fatalf("cmd = %#v, want %#v", cmd, want)
}

View File

@ -142,7 +142,15 @@ func (p *Plugin) GetRestoreCommand(ctx context.Context, cfg ports.RestoreConfig)
return nil, false, err
}
cmd = append(gooseModeEnvPrefix(cfg.Permissions), binary, "run", "--resume", "--session-id", agentSessionID)
cmd = append(gooseModeEnvPrefix(cfg.Permissions), binary, "run")
systemPrompt, err := restoreSystemPromptText(cfg)
if err != nil {
return nil, false, err
}
if systemPrompt != "" {
cmd = append(cmd, "--system", systemPrompt)
}
cmd = append(cmd, "--resume", "--session-id", agentSessionID)
return cmd, true, nil
}
@ -167,17 +175,26 @@ func (p *Plugin) SessionInfo(ctx context.Context, session ports.SessionRef) (por
// flag takes inline text only (no file variant), so a system-prompt file is read
// from disk only when inline instructions are unavailable.
func systemPromptText(cfg ports.LaunchConfig) (string, error) {
if cfg.SystemPrompt != "" {
return cfg.SystemPrompt, nil
return systemPromptTextFrom(cfg.SystemPrompt, cfg.SystemPromptFile)
}
func restoreSystemPromptText(cfg ports.RestoreConfig) (string, error) {
return systemPromptTextFrom(cfg.SystemPrompt, cfg.SystemPromptFile)
}
func systemPromptTextFrom(inline, file string) (string, error) {
if inline != "" {
return inline, nil
}
if cfg.SystemPromptFile != "" {
data, err := os.ReadFile(cfg.SystemPromptFile) //nolint:gosec // path is AO-owned launch config
if err != nil {
return "", fmt.Errorf("read %s: %w", cfg.SystemPromptFile, err)
}
if text := strings.TrimSpace(string(data)); text != "" {
return text, nil
}
if file == "" {
return "", nil
}
data, err := os.ReadFile(file) //nolint:gosec // path is AO-owned launch config
if err != nil {
return "", fmt.Errorf("read %s: %w", file, err)
}
if text := strings.TrimSpace(string(data)); text != "" {
return text, nil
}
return "", nil
}

View File

@ -278,7 +278,9 @@ func TestGetRestoreCommandReadsAgentSessionID(t *testing.T) {
plugin := &Plugin{resolvedBinary: "goose"}
cmd, ok, err := plugin.GetRestoreCommand(context.Background(), ports.RestoreConfig{
Permissions: ports.PermissionModeAuto,
Permissions: ports.PermissionModeAuto,
SystemPrompt: "restore inline wins",
SystemPromptFile: filepath.Join(t.TempDir(), "missing.md"),
Session: ports.SessionRef{
Metadata: map[string]string{ports.MetadataKeyAgentSessionID: "thread-123"},
},
@ -291,7 +293,7 @@ func TestGetRestoreCommandReadsAgentSessionID(t *testing.T) {
}
want := []string{
"env", "GOOSE_MODE=auto",
"goose", "run", "--resume", "--session-id", "thread-123",
"goose", "run", "--system", "restore inline wins", "--resume", "--session-id", "thread-123",
}
if !reflect.DeepEqual(cmd, want) {
t.Fatalf("restore cmd\nwant: %#v\n got: %#v", want, cmd)

View File

@ -146,6 +146,15 @@ func (p *Plugin) GetRestoreCommand(ctx context.Context, cfg ports.RestoreConfig)
return nil, false, err
}
cmd = []string{binary, "--print", "--session", agentSessionID}
if cfg.SystemPrompt != "" {
cmd = append(cmd, "--append-system-prompt", cfg.SystemPrompt)
} else if cfg.SystemPromptFile != "" {
data, err := os.ReadFile(cfg.SystemPromptFile) //nolint:gosec // path is AO-owned launch config
if err != nil {
return nil, false, err
}
cmd = append(cmd, "--append-system-prompt", string(data))
}
return cmd, true, nil
}

View File

@ -148,6 +148,8 @@ func TestGetLaunchCommandSystemPromptFileReadError(t *testing.T) {
func TestGetRestoreCommand(t *testing.T) {
p := &Plugin{resolvedBinary: "pi"}
cmd, ok, err := p.GetRestoreCommand(context.Background(), ports.RestoreConfig{
SystemPrompt: "restore inline wins",
SystemPromptFile: filepath.Join(t.TempDir(), "missing.md"),
Session: ports.SessionRef{
Metadata: map[string]string{ports.MetadataKeyAgentSessionID: "019e950e-52e0-7411-961b-d380ca7e610f"},
},
@ -160,7 +162,7 @@ func TestGetRestoreCommand(t *testing.T) {
t.Fatal("ok=false, want true")
}
want := []string{"pi", "--print", "--session", "019e950e-52e0-7411-961b-d380ca7e610f"}
want := []string{"pi", "--print", "--session", "019e950e-52e0-7411-961b-d380ca7e610f", "--append-system-prompt", "restore inline wins"}
if !reflect.DeepEqual(cmd, want) {
t.Fatalf("cmd = %#v, want %#v", cmd, want)
}

View File

@ -122,6 +122,9 @@ func (p *Plugin) GetRestoreCommand(ctx context.Context, cfg ports.RestoreConfig)
cmd = make([]string, 0, 6)
cmd = append(cmd, binary)
appendApprovalFlags(&cmd, cfg.Permissions)
if cfg.SystemPrompt != "" {
cmd = append(cmd, "--append-system-prompt", cfg.SystemPrompt)
}
cmd = append(cmd, "-r", agentSessionID)
return cmd, true, nil
}

View File

@ -271,7 +271,8 @@ func TestGetRestoreCommandReadsAgentSessionID(t *testing.T) {
plugin := &Plugin{resolvedBinary: "qwen"}
cmd, ok, err := plugin.GetRestoreCommand(context.Background(), ports.RestoreConfig{
Permissions: ports.PermissionModeAuto,
Permissions: ports.PermissionModeAuto,
SystemPrompt: "restore instructions",
Session: ports.SessionRef{
Metadata: map[string]string{ports.MetadataKeyAgentSessionID: "sess-123"},
},
@ -285,6 +286,7 @@ func TestGetRestoreCommandReadsAgentSessionID(t *testing.T) {
want := []string{
"qwen",
"--approval-mode", "auto",
"--append-system-prompt", "restore instructions",
"-r", "sess-123",
}
if !reflect.DeepEqual(cmd, want) {