From 94f36bce59aafa8dd6fa15fd21d09ee27e34e250 Mon Sep 17 00:00:00 2001 From: harshitsinghbhandari <24b4506@iitb.ac.in> Date: Sat, 13 Jun 2026 13:56:38 +0530 Subject: [PATCH 1/2] fix(session): remove orchestrator kickoff auto-prompt on spawn Spawning a session without an explicit prompt injected a "Get oriented..." kickoff turn for orchestrators, which surfaced as an unsolicited message to the orchestrator. Drop the auto-prompt so a promptless spawn delivers nothing to the agent, leaving it idle at an empty input box. Closes #226 Co-Authored-By: Claude Opus 4.8 --- backend/internal/session_manager/manager.go | 13 +++---------- backend/internal/session_manager/manager_test.go | 8 ++++---- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/backend/internal/session_manager/manager.go b/backend/internal/session_manager/manager.go index 1ae83837..b2a82c92 100644 --- a/backend/internal/session_manager/manager.go +++ b/backend/internal/session_manager/manager.go @@ -594,25 +594,18 @@ func buildPrompt(cfg ports.SpawnConfig) string { return cfg.Prompt } -// orchestratorKickoffPrompt is the default first turn for an orchestrator -// spawned without an explicit prompt. The role definition rides the system -// prompt, and an interactive agent launched with only a system prompt sits at -// an empty input box; this gives it a turn to act on. -const orchestratorKickoffPrompt = "Get oriented: review the current repo state and any active worker sessions, then report your status and wait for direction." - // buildSpawnTexts returns the user-facing prompt and the system prompt to // deliver separately to the agent. Orchestrator role instructions and worker // coordination hints are placed in the system prompt so they are treated as -// standing instructions rather than part of the human's task request. +// standing instructions rather than part of the human's task request. A +// promptless spawn delivers no user prompt at all: the agent simply lands at an +// empty input box rather than receiving an auto-generated kickoff turn. func (m *Manager) buildSpawnTexts(ctx context.Context, cfg ports.SpawnConfig) (prompt, systemPrompt string, err error) { prompt = buildPrompt(cfg) systemPrompt, err = m.buildSystemPrompt(ctx, cfg.Kind, cfg.ProjectID) if err != nil { return "", "", err } - if cfg.Kind == domain.KindOrchestrator && prompt == "" { - prompt = orchestratorKickoffPrompt - } return prompt, systemPrompt, nil } diff --git a/backend/internal/session_manager/manager_test.go b/backend/internal/session_manager/manager_test.go index 01ee199c..dac81d72 100644 --- a/backend/internal/session_manager/manager_test.go +++ b/backend/internal/session_manager/manager_test.go @@ -635,10 +635,10 @@ func TestSpawnOrchestrator_UsesCoordinatorPrompt(t *testing.T) { t.Fatalf("coordinator role must not be in the user prompt:\n%s", agent.lastLaunch.Prompt) } - // A promptless orchestrator still needs a first turn: with the role in the - // system prompt only, an interactive agent would idle at an empty input box. - if agent.lastLaunch.Prompt != orchestratorKickoffPrompt { - t.Fatalf("prompt = %q, want kick-off prompt", agent.lastLaunch.Prompt) + // A promptless orchestrator gets no auto-generated kickoff turn: spawning + // must deliver nothing to the agent, leaving it idle at an empty input box. + if agent.lastLaunch.Prompt != "" { + t.Fatalf("prompt = %q, want empty (no kickoff turn)", agent.lastLaunch.Prompt) } } From 2a77b32d8eb7502e91f204579d9fba60733fa707 Mon Sep 17 00:00:00 2001 From: harshitsinghbhandari <24b4506@iitb.ac.in> Date: Sat, 13 Jun 2026 14:17:26 +0530 Subject: [PATCH 2/2] fix(session): re-apply derived system prompt on agent resume Restore re-derives the standing system prompt but only handed it to the fresh-launch fallback, not the native GetRestoreCommand path, so a resumed orchestrator/worker lost its role instructions. Pass SystemPrompt through to the restore command too, matching adapters that re-append it on resume. Also fix the recordingAgent test double to return ok=false when there is no agent-session id, mirroring every real adapter, so the fallback-launch path is actually exercised. These three TestRestore_* cases were red on main since #222. Co-Authored-By: Claude Opus 4.8 --- backend/internal/session_manager/manager.go | 2 +- backend/internal/session_manager/manager_test.go | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/backend/internal/session_manager/manager.go b/backend/internal/session_manager/manager.go index b2a82c92..9104a8c7 100644 --- a/backend/internal/session_manager/manager.go +++ b/backend/internal/session_manager/manager.go @@ -851,7 +851,7 @@ func restoreArgv(ctx context.Context, agent ports.Agent, id domain.SessionID, wo WorkspacePath: workspacePath, Metadata: map[string]string{ports.MetadataKeyAgentSessionID: meta.AgentSessionID}, } - cmd, ok, err := agent.GetRestoreCommand(ctx, ports.RestoreConfig{Session: ref, Config: agentConfig, Permissions: agentConfig.Permissions}) + cmd, ok, err := agent.GetRestoreCommand(ctx, ports.RestoreConfig{Session: ref, SystemPrompt: systemPrompt, Config: agentConfig, Permissions: agentConfig.Permissions}) if err != nil { return nil, fmt.Errorf("restore command: %w", err) } diff --git a/backend/internal/session_manager/manager_test.go b/backend/internal/session_manager/manager_test.go index dac81d72..521b72e7 100644 --- a/backend/internal/session_manager/manager_test.go +++ b/backend/internal/session_manager/manager_test.go @@ -168,6 +168,11 @@ func (a *recordingAgent) GetLaunchCommand(_ context.Context, cfg ports.LaunchCon func (a *recordingAgent) GetRestoreCommand(_ context.Context, cfg ports.RestoreConfig) ([]string, bool, error) { a.lastConfig = cfg.Config a.lastRestore = cfg + // Mirror real adapters: with no native agent-session id to resume, signal + // "cannot restore" so the manager falls back to a fresh launch. + if cfg.Session.Metadata[ports.MetadataKeyAgentSessionID] == "" { + return nil, false, nil + } return []string{"resume"}, true, nil }