From 2068d95943cfc3dfcfbdf3ed4cbdce308323c236 Mon Sep 17 00:00:00 2001 From: Clint Berry Date: Tue, 9 Jun 2026 16:00:51 +0000 Subject: [PATCH] feat(agent): stronger ask_user steering + launch log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rewrite DefaultBaseSystemPrompt to explain the mechanism — a plain-text question ends the run and returns no answer, so ask_user is the only real channel — and make it imperative (never end a turn with a prose question, even for a single missing detail). Add an Info log at Pi launch recording whether a system prompt was applied, to verify wiring from server logs. --- server/internal/agent/pirun/devpod_launcher.go | 2 ++ server/internal/agent/runtime.go | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/server/internal/agent/pirun/devpod_launcher.go b/server/internal/agent/pirun/devpod_launcher.go index 6c816fe..114e28c 100644 --- a/server/internal/agent/pirun/devpod_launcher.go +++ b/server/internal/agent/pirun/devpod_launcher.go @@ -39,6 +39,8 @@ func NewDevpodLauncher(wm *workspace.Manager, provider, model string) *DevpodLau func (l *DevpodLauncher) Launch(ctx context.Context, workspaceID string, env []string, systemPrompt string) (Handle, error) { inner, extraEnv := piLaunchSpec(l.provider, l.model, systemPrompt) env = append(env, extraEnv...) + slog.Info("pirun: launching pi", "workspace", workspaceID, + "systemPromptApplied", systemPrompt != "", "systemPromptLen", len(systemPrompt)) // Run pi through a login shell so its install location is on PATH. The // pi.dev installer is npm-based and puts the binary in the npm-global bin // (added to the user's profile), NOT $HOME/.local/bin — and a diff --git a/server/internal/agent/runtime.go b/server/internal/agent/runtime.go index 48627bc..581b414 100644 --- a/server/internal/agent/runtime.go +++ b/server/internal/agent/runtime.go @@ -77,9 +77,13 @@ const ( // to steer agents to the ask_user tool when blocked on a human decision (which // is what surfaces the interactive typed prompt) instead of guessing or asking // in a normal chat reply. Overridable via DEUCE_AGENT_SYSTEM_PROMPT. -const DefaultBaseSystemPrompt = `You are an AI agent collaborating with people and other agents in a shared Deuce workspace. +const DefaultBaseSystemPrompt = `You are an AI agent working in a shared Deuce workspace alongside people and other agents. Your messages appear in a chat channel. -Ask before you guess. When you need a decision, clarification, or approval that only a human can give — ambiguous requirements, a missing detail like a filename or value, or a risky or destructive action — call the ask_user tool with a clear question and wait for the answer. Do not answer such a question in a normal chat reply, and do not assume a default. When the answer is one of a small set of choices, set kind to "select" and provide the options; for a yes/no decision set kind to "confirm". Only ask when you are genuinely blocked — otherwise keep working.` +CRITICAL — how to ask the user something. The ONLY reliable way to get an answer from a human is the ask_user tool. It pauses your run, shows the user a real prompt, and returns their answer to you so you can continue. If you instead write a question as ordinary chat text and end your turn, your run is marked complete — you are NOT waiting, the user is not shown a prompt, and you will not receive a clean answer to act on. A plain-text question is a dead end. + +Therefore, whenever you need a decision, clarification, a missing detail (a filename, a value, a path), or approval that only the user can give, you MUST call the ask_user tool with your question and let it block for the answer. Never end your turn with a question written as plain text, and never guess or assume a default to avoid asking. Even a single missing detail like a filename means you call ask_user instead of describing what you need. + +Use the kind parameter: "select" with options when the answer is one of a few choices, "confirm" for a yes/no decision, or omit it for a free-text answer. Only ask when you are genuinely blocked — otherwise keep working without asking.` // NewRuntime builds the runtime. Call Start to begin consuming process exits. // baseSystemPrompt is prepended to every agent's own system_prompt at Pi launch