( ctx context.Context, tools []fantasy.AgentTool, opts rootChatToolsOptions, )
| 6913 | } |
| 6914 | |
| 6915 | func (p *Server) appendRootChatTools( |
| 6916 | ctx context.Context, |
| 6917 | tools []fantasy.AgentTool, |
| 6918 | opts rootChatToolsOptions, |
| 6919 | ) []fantasy.AgentTool { |
| 6920 | onChatUpdated := func(updatedChat database.Chat) { |
| 6921 | opts.workspaceCtx.selectWorkspace(updatedChat) |
| 6922 | // Notify the frontend immediately so it can start streaming |
| 6923 | // build logs before the tool completes. |
| 6924 | p.publishChatPubsubEvent(updatedChat, codersdk.ChatWatchEventKindStatusChange, nil) |
| 6925 | |
| 6926 | // When a workspace is first attached mid-turn (e.g. via |
| 6927 | // create_workspace), fetch and persist instruction files |
| 6928 | // immediately so the LLM has AGENTS.md context for the remainder |
| 6929 | // of this turn. The persisted marker prevents redundant fetches on |
| 6930 | // subsequent turns. |
| 6931 | if *opts.instruction == "" && updatedChat.WorkspaceID.Valid { |
| 6932 | newInstruction, discoveredSkills, persistErr := p.persistInstructionFiles( |
| 6933 | ctx, |
| 6934 | updatedChat, |
| 6935 | opts.modelConfigID, |
| 6936 | opts.workspaceCtx.getWorkspaceAgent, |
| 6937 | opts.workspaceCtx.getWorkspaceConn, |
| 6938 | ) |
| 6939 | if persistErr != nil { |
| 6940 | p.logger.Warn(ctx, "failed to persist instruction files on workspace attach", |
| 6941 | slog.F("chat_id", updatedChat.ID), |
| 6942 | slog.Error(persistErr), |
| 6943 | ) |
| 6944 | } else { |
| 6945 | *opts.instruction = newInstruction |
| 6946 | if len(discoveredSkills) > 0 { |
| 6947 | *opts.skills = discoveredSkills |
| 6948 | } |
| 6949 | } |
| 6950 | } |
| 6951 | |
| 6952 | // Prime the workspace MCP tools cache while the create_workspace |
| 6953 | // or start_workspace tool is still running. The AgentID guard |
| 6954 | // below restricts the primer to the post-ready callback, when |
| 6955 | // the agent is reachable. ListMCPTools may still return an |
| 6956 | // empty list on the first try when the agent's MCP Connect is |
| 6957 | // racing with agent startup; primeWorkspaceMCPCache retries |
| 6958 | // with a short backoff up to workspaceMCPPrimeMaxWait. Priming |
| 6959 | // here lets the next LLM step's PrepareTools hit the cache |
| 6960 | // instead of dialing again on a separate timeout budget. |
| 6961 | // |
| 6962 | // Run asynchronously: the tool itself must not block on the |
| 6963 | // primer because the agent may not advertise any MCP tools at |
| 6964 | // all (e.g. minimal templates), in which case the primer waits |
| 6965 | // the full budget before giving up. PrepareTools on the next |
| 6966 | // step covers the cache miss path; the primer is purely an |
| 6967 | // optimization that warms the cache while the LLM is thinking. |
| 6968 | // inflight tracking ensures server shutdown still waits for any |
| 6969 | // in-progress primer. |
| 6970 | // |
| 6971 | // Guard on both WorkspaceID and AgentID being valid: |
| 6972 | // create_workspace and start_workspace each fire onChatUpdated |
no test coverage detected