Files
Michael Suchacz 1cf0354f72 feat: add plan mode with restricted tool boundary (#24236)
> This PR was authored by Mux on behalf of Mike.

## Summary
- add persistent plan mode for chats and the chat-specific plan file
flow
- add structured planning tools such as `ask_user_question` and
`propose_plan`
- keep `write_file` and `edit_files` constrained to the chat-specific
plan file during plan turns
- allow shell exploration in plan mode, including subagents, via
`execute` and `process_output`
- block implementation-oriented, provider-native, MCP, dynamic, and
computer-use tools during plan turns
- update the chat UI, tests, and docs for the new planning flow
2026-04-16 11:12:01 +02:00

55 lines
1.3 KiB
Go

package chattool
import (
"context"
"net/http"
"path"
"path/filepath"
"strings"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/codersdk/workspacesdk"
)
func ensurePlanPathResolvesToItself(
ctx context.Context,
conn workspacesdk.AgentConn,
planPath string,
) error {
if conn == nil {
return xerrors.New("workspace connection is required")
}
normalizedPlanPath := normalizeWorkspacePath(planPath)
resolvedPath, err := conn.ResolvePath(ctx, planPath)
if err != nil {
if resolvePathUnsupported(err) {
// Older workspace agents do not expose /resolve-path yet. Keep
// plan turns working during rolling upgrades, even though they
// cannot enforce the symlink guard until the agent is upgraded.
return nil
}
return xerrors.Errorf("resolve plan path: %w", err)
}
resolvedPath = normalizeWorkspacePath(resolvedPath)
if resolvedPath != normalizedPlanPath {
return xerrors.New(symlinkedPlanPathMessage(normalizedPlanPath, resolvedPath))
}
return nil
}
func resolvePathUnsupported(err error) bool {
var statusErr interface{ StatusCode() int }
return xerrors.As(err, &statusErr) && statusErr.StatusCode() == http.StatusNotFound
}
func normalizeWorkspacePath(pathString string) string {
pathString = strings.TrimSpace(pathString)
if pathString == "" {
return ""
}
return path.Clean(filepath.ToSlash(pathString))
}