Documentation
¶
Overview ¶
Package chatd implements the internal chat service used by Agents.
Provider configuration glossary ¶
This package uses AI Provider language for new provider configuration code:
- AI Provider: a database-backed LLM provider configuration stored in ai_providers. It is the source of truth for Agents provider identity.
- Legacy Chat Provider: the pre-migration chat-specific provider source. Legacy rows only exist as migration input during the stack.
- Provider Type: the provider implementation family stored in ai_providers.type, such as openai, anthropic, azure, bedrock, google, openai-compat, openrouter, and vercel.
- Provider Name: the unique instance identifier stored in ai_providers.name. It is not the implementation family.
- Model Config: an Agents model selection record. In the target state it references one concrete AI Provider by ID.
- Provider-scoped AI Provider Key: an administrator-managed credential in ai_provider_keys, attached to one AI Provider.
- User AI Provider Key: a user-owned credential attached to one user and one AI Provider.
- BYOK: the deployment-level AI Bridge policy that controls whether user keys may be written or used. Disabling BYOK does not delete stored user keys.
- AI Bridge: the product area that introduced AI provider records. Agents consume the same records through chatd, but this package does not define the full AI Bridge runtime roadmap.
Model configs should use provider IDs for identity. Provider types choose runtime implementation details. Provider names are instance identifiers for administrators and APIs.
When BYOK is enabled, a user key for the selected provider takes precedence over provider-scoped keys. When BYOK is disabled, chatd ignores user keys and uses provider-scoped keys only.
Index ¶
- Constants
- Variables
- func BuildSingleChatMessageInsertParams(chatID uuid.UUID, role database.ChatMessageRole, content pqtype.NullRawMessage, ...) database.InsertChatMessagesParams
- func BuildSingleUserChatMessageInsertParams(chatID uuid.UUID, apiKeyID string, content pqtype.NullRawMessage, ...) database.InsertChatMessagesParams
- func ChatPersonalModelOverrideKey(overrideContext codersdk.ChatPersonalModelOverrideContext) string
- func ComputeUsagePeriodBounds(now time.Time, period codersdk.ChatUsageLimitPeriod) (start, end time.Time)
- func PlanningOverlayPrompt() string
- func ResolveUsageLimitStatus(ctx context.Context, db database.Store, userID uuid.UUID, ...) (*codersdk.ChatUsageLimitStatus, error)
- func SanitizePromptText(s string) string
- func ValidateAIGatewayProviderModel(provider database.AIProvider, model string) error
- func WaitUntilIdleForTest(server *Server)
- type AgentConnFunc
- type Config
- type CreateOptions
- type DialFunc
- type DialResult
- type EditMessageOptions
- type EditMessageResult
- type LocalStreamPartsDialerConfig
- type ParsedChatPersonalModelOverride
- type PromoteQueuedOptions
- type PromoteQueuedResult
- type SendMessageBusyBehavior
- type SendMessageOptions
- type SendMessageResult
- type Server
- func (p *Server) ArchiveChat(ctx context.Context, chat database.Chat) error
- func (p *Server) Close() error
- func (server *Server) ContextResources(ctx context.Context, chat database.Chat) ([]codersdk.ChatContextResource, error)
- func (p *Server) CreateChat(ctx context.Context, opts CreateOptions) (database.Chat, error)
- func (p *Server) DeleteQueued(ctx context.Context, chatID uuid.UUID, queuedMessageID int64) error
- func (p *Server) EditMessage(ctx context.Context, opts EditMessageOptions) (EditMessageResult, error)
- func (p *Server) GenerateChatTitleAsync(ctx context.Context, chat database.Chat)
- func (p *Server) HydrateAndMarkChatsDirty(ctx context.Context, tx database.Store, agentID uuid.UUID, ...) (func(), error)
- func (p *Server) InterruptChat(ctx context.Context, chat database.Chat) (database.Chat, error)
- func (p *Server) PromoteQueued(ctx context.Context, opts PromoteQueuedOptions) (PromoteQueuedResult, error)
- func (p *Server) ProposeChatTitle(ctx context.Context, chat database.Chat) (string, error)
- func (p *Server) PublishDiffStatusChange(ctx context.Context, chatID uuid.UUID) error
- func (p *Server) PublishTitleChange(chat database.Chat)
- func (p *Server) ReconcileInvalidStateChat(ctx context.Context, chat database.Chat) (database.Chat, error)
- func (p *Server) RefreshChatContext(ctx context.Context, chat database.Chat) (database.Chat, error)
- func (p *Server) RegenerateChatTitle(ctx context.Context, chat database.Chat) (database.Chat, error)
- func (p *Server) RenameChatTitle(ctx context.Context, chat database.Chat, newTitle string) (updated database.Chat, wrote bool, err error)
- func (p *Server) SendMessage(ctx context.Context, opts SendMessageOptions) (SendMessageResult, error)
- func (p *Server) ServeStreamPartsAuthorized(rw http.ResponseWriter, r *http.Request, chat database.Chat) error
- func (p *Server) Start() *Server
- func (p *Server) SubmitToolResults(ctx context.Context, opts SubmitToolResultsOptions) error
- func (p *Server) Subscribe(ctx context.Context, chatID uuid.UUID, requestHeader http.Header, ...) ([]codersdk.ChatStreamEvent, <-chan codersdk.ChatStreamEvent, func(), bool)
- func (p *Server) SubscribeAuthorized(ctx context.Context, chat database.Chat, requestHeader http.Header, ...) ([]codersdk.ChatStreamEvent, <-chan codersdk.ChatStreamEvent, func(), bool)
- func (p *Server) UnarchiveChat(ctx context.Context, chat database.Chat) error
- type StreamPart
- type StreamPartsDialInput
- type StreamPartsDialer
- type StreamPartsJSONSession
- type StreamPartsSession
- type SubmitToolResultsOptions
- type ToolResultStatusConflictError
- type ToolResultValidationError
- type UsageLimitExceededError
- type ValidateFunc
Constants ¶
const ( // DefaultPendingChatAcquireInterval is the default time between attempts to // acquire pending chats. DefaultPendingChatAcquireInterval = time.Second // DefaultInFlightChatStaleAfter is the default age after which a running // chat is considered stale and should be recovered. DefaultInFlightChatStaleAfter = 5 * time.Minute // DefaultChatHeartbeatInterval is the default time between chat // heartbeat updates while a chat is being processed. DefaultChatHeartbeatInterval = 30 * time.Second // DefaultMaxChatsPerAcquire is the maximum number of chats to // acquire in a single processOnce call. Batching avoids // waiting a full polling interval between acquisitions // when many chats are pending. DefaultMaxChatsPerAcquire int32 = 10 )
const AgentChatContextSentinelPath = ".coder/agent-chat-context-sentinel"
AgentChatContextSentinelPath is the canonical path of the synthetic empty context-file part that legacy chats used to mark skill-only workspace-agent context. New turns no longer emit it; it is retained as the canonical value so historical-message handling and the chatopenai chain-mode tests stay in sync.
const ChatPersonalModelOverrideKeyPrefix = "chat_personal_model_override:"
ChatPersonalModelOverrideKeyPrefix is the user config key prefix for chat personal model overrides. Values under this prefix should be parsed with ParseChatPersonalModelOverride so malformed values use one fallback.
const DefaultSystemPrompt = `You are the Coder agent — an interactive chat tool that helps users with software-engineering tasks inside of the Coder product. Use the instructions below and the tools available to you to assist User. IMPORTANT — obey every rule in this prompt before anything else. Do EXACTLY what the User asked, never more, never less. <behavior> You MUST execute AS MANY TOOLS to help the user accomplish their task. You are COMFORTABLE with vague tasks - using your tools to collect the most relevant answer possible. If a user asks how something works, no matter how vague, you MUST use your tools to collect the most relevant answer possible. Use tools first to gather context and make progress. When no workspace is attached, use available non-workspace tools first. Do not create a workspace by default. Reuse existing chat and workspace context. Do not clone repositories already present in the workspace. Treat injected <workspace-context> files, including AGENTS.md, as read; re-read only for exact current contents or suspected changes. Do not ask clarifying questions if the answer can be obtained from the codebase, workspace, or existing project conventions. Ask concise clarifying questions only when: - the user's intent is materially ambiguous; - architecture, tooling, or style preferences would change the implementation; - the action is destructive, irreversible, or expensive; or - you cannot make progress with confidence. If a task is too ambiguous to implement with confidence, ask for clarification before proceeding. </behavior> <version-control-safety> Before committing or pushing in a Git repository, check the current branch and push target. Do not commit directly to default or protected branches, including main, master, trunk, or the repository's remote default branch, unless the user explicitly confirms after you identify the exact branch. Do not push when the target would update a default or protected branch unless the user explicitly confirms. Before asking for confirmation, warn that the push bypasses the normal feature branch or pull request workflow and state the exact remote ref that would be updated. Do not run plain git push while checked out on a default or protected branch. When pushing after explicit confirmation, use an explicit refspec. If the user asks you to commit or push from a default or protected branch without that confirmation, create and switch to a feature branch first. If a branch name is not obvious, choose a concise descriptive branch name that follows the repository's conventions, or ask when the choice is material. Never treat the original request as confirmation. Confirmation must be separate and must name the exact protected branch or accept the exact branch you named. </version-control-safety> <personality> Analytical — You break problems into measurable steps, relying on tool output and data rather than intuition. Organized — You structure every interaction with clear tags, TODO lists, and section boundaries. Precision-Oriented — You insist on exact formatting, package-manager choice, and rule adherence. Efficiency-Focused — You minimize chatter, run tasks in parallel, and favor small, complete answers. Clarity-Seeking — You resolve ambiguity with tools when possible and ask focused questions only when necessary. </personality> <communication> Be concise, direct, and to the point. NO emojis unless the User explicitly asks for them. If a task appears incomplete or ambiguous, first use your tools to gather context. **Pause and ask the User** only if material ambiguity remains rather than guessing or marking "done". Prefer accuracy over reassurance; confirm facts with tool calls instead of assuming the User is right. If you face an architectural, tooling, or package-manager choice, **ask the User's preference first**. Default to the project's existing package manager / tooling; never substitute without confirmation. You MUST avoid text before/after your response, such as "The answer is" or "Short answer:", "Here is the content of the file..." or "Based on the information provided, the answer is..." or "Here is what I will do next...". Mimic the style of the User's messages. Do not remind the User you are happy to help. Do not inherently assume the User is correct; they may be making assumptions. If you are not confident in your answer, DO NOT provide an answer. Use your tools to collect more information, or ask the User for help. Do not act with sycophantic flattery or over-the-top enthusiasm. Here are examples to demonstrate appropriate communication style and level of verbosity: <example> user: find me a good issue to work on assistant: Issue [#1234](https://example) indicates a bug in the frontend, which you've contributed to in the past. </example> <example> user: work on this issue <url> ...assistant does work... assistant: I've put up this pull request: https://github.com/example/example/pull/1824. Please let me know your thoughts! </example> <example> user: what is 2+2? assistant: 4 </example> <example> user: how does X work in <popular-repository-name>? assistant: Let me take a look at the code... [tool calls to investigate the repository] </example> </communication> <collaboration> When clarification is necessary, ask concise questions to understand: - What specific aspect they want to focus on - Their goals and vision for the changes - Their preferences for approach or style - What problems they're trying to solve Do not start with clarifying questions if the codebase or tools can answer them. Ask the minimum number of questions needed to define the scope together. </collaboration> <workspace-template-selection> When no workspace is attached and you need to create one: - Call list_templates with concise search terms from the user's task, then follow its ` + chattool.NextStepField + `: use the recommended template, or ask the user to choose when none is recommended. - Call read_template only when you need parameter or preset details before create_workspace. </workspace-template-selection> <planning> Propose a plan when: - The task is too ambiguous to implement with confidence. - The user asks for a plan. If no workspace is attached to this chat yet, do not create one as the first action merely because you are planning. First use the conversation, provider tools such as web_search when available, configured external MCP tools, and template metadata when they are sufficient. Create and start a workspace only when the plan requires inspecting, editing, or running workspace files, or before writing the required plan artifact if no other valid plan path is available. Once a workspace is available: ` + defaultSystemPromptPlanningGuidance + ` 2. Use write_file to create a Markdown plan file at the absolute chat-specific path from the <plan-file-path> block below when it is available. 3. Iterate on the plan with edit_files if needed. 4. Present the plan to the user and wait for review before starting implementation. Write the file first, then present it. All file paths must be absolute. When the <plan-file-path> block below is present, use that exact path. ` + defaultSystemPromptPlanPathBlockPlaceholder + ` </planning> ` + subagentOrchestrationPromptBlock
DefaultSystemPrompt is used for new chats when no deployment override is configured.
const ExploreSubagentOverlayPrompt = `` /* 313-byte string literal not displayed */
ExploreSubagentOverlayPrompt contains Explore-mode instructions for delegated child chats.
const PlanningSubagentOverlayPrompt = `` /* 413-byte string literal not displayed */
PlanningSubagentOverlayPrompt contains plan-mode instructions for delegated child chats. Child chats may investigate with shell tools but should return findings to the parent instead of authoring the final plan.
Variables ¶
var ( // ErrInvalidModelConfigID indicates the requested model config does not exist. ErrInvalidModelConfigID = xerrors.New("invalid model config ID") // ErrEditedMessageNotFound indicates the edited message does not exist // in the target chat. ErrEditedMessageNotFound = xerrors.New("edited message not found") // ErrEditedMessageNotUser indicates a non-user message edit attempt. ErrEditedMessageNotUser = xerrors.New("only user messages can be edited") // ErrChatArchived indicates the chat is archived and cannot // accept modifications (messages, edits, promotions, or // tool-result submissions). ErrChatArchived = xerrors.New("chat is archived") )
var ErrArchiveRequiresRootChat = xerrors.New(
"chat archive state can only be changed on the root chat",
)
ErrArchiveRequiresRootChat is returned by Server.ArchiveChat and Server.UnarchiveChat when the supplied chat is a child chat. Archive state changes must always target the root chat so the whole family flips together.
var ErrManualTitleRegenerationInProgress = xerrors.New(
"manual title regeneration already in progress",
)
var ErrSubagentNotDescendant = xerrors.New("target chat is not a descendant of current chat")
var ErrSubagentWaitTimeout = xerrors.New("timed out waiting for delegated subagent completion")
ErrSubagentWaitTimeout is returned by awaitSubagentCompletion when the wait deadline elapses before the subagent reaches a terminal status. The agent is still working and the wait can be retried.
Functions ¶
func BuildSingleChatMessageInsertParams ¶ added in v2.33.0
func BuildSingleChatMessageInsertParams( chatID uuid.UUID, role database.ChatMessageRole, content pqtype.NullRawMessage, visibility database.ChatMessageVisibility, modelConfigID uuid.UUID, contentVersion int16, createdBy uuid.UUID, ) database.InsertChatMessagesParams
BuildSingleUserChatMessageInsertParams creates batch insert params for one user message, requiring an apiKeyID for AI Gateway attribution. BuildSingleChatMessageInsertParams creates batch insert params for one non-user message using the shared chat message builder.
func BuildSingleUserChatMessageInsertParams ¶ added in v2.34.0
func BuildSingleUserChatMessageInsertParams( chatID uuid.UUID, apiKeyID string, content pqtype.NullRawMessage, visibility database.ChatMessageVisibility, modelConfigID uuid.UUID, contentVersion int16, createdBy uuid.UUID, ) database.InsertChatMessagesParams
func ChatPersonalModelOverrideKey ¶ added in v2.34.0
func ChatPersonalModelOverrideKey( overrideContext codersdk.ChatPersonalModelOverrideContext, ) string
ChatPersonalModelOverrideKey returns the user config key for a chat personal model override context. Values stored at the returned key should use ParseChatPersonalModelOverride so malformed values fall back safely.
func ComputeUsagePeriodBounds ¶
func ComputeUsagePeriodBounds(now time.Time, period codersdk.ChatUsageLimitPeriod) (start, end time.Time)
ComputeUsagePeriodBounds returns the UTC-aligned start and end bounds for the active usage-limit period containing now.
func PlanningOverlayPrompt ¶ added in v2.33.0
func PlanningOverlayPrompt() string
PlanningOverlayPrompt returns the plan-mode-only instructions appended when the chat is in plan mode.
func ResolveUsageLimitStatus ¶
func ResolveUsageLimitStatus(ctx context.Context, db database.Store, userID uuid.UUID, organizationID uuid.NullUUID, now time.Time) (*codersdk.ChatUsageLimitStatus, error)
ResolveUsageLimitStatus resolves the current usage-limit status for userID within organizationID. When organizationID is invalid (Valid == false), limits and spend are computed globally across all organizations (legacy behavior).
Note: There is a potential race condition where two concurrent messages from the same user can both pass the limit check if processed in parallel, allowing brief overage. This is acceptable because:
- Cost is only known after the LLM API returns.
- Overage is bounded by message cost × concurrency.
- Fail-open is the deliberate design choice for this feature.
Architecture note: today this path enforces one period globally (day/week/month) from config. To support simultaneous periods, add nullable daily/weekly/monthly_limit_micros columns on override tables, where NULL means no limit for that period. Then scan spend once over the widest active window with conditional SUMs for each period and compare each spend/limit pair Go-side, blocking on whichever period is tightest.
func SanitizePromptText ¶
SanitizePromptText strips invisible Unicode characters that could hide prompt-injection content from human reviewers, normalizes line endings, collapses excessive blank lines, and trims surrounding whitespace.
The stripped codepoints are truly invisible and have no legitimate use in prompt text. An explicit codepoint list is used rather than blanket unicode.Cf stripping to avoid breaking subdivision flag emoji (🏴) and other legitimate format characters.
Note: U+200D (ZWJ) is stripped even though it joins compound emoji (e.g. 👨👩👦 → 👨👩👦). This is an acceptable trade-off because system prompts are not emoji art, and ZWJ is actively exploited in zero-width steganography schemes as a delimiter character.
func ValidateAIGatewayProviderModel ¶ added in v2.34.1
func ValidateAIGatewayProviderModel(provider database.AIProvider, model string) error
ValidateAIGatewayProviderModel rejects slash-namespaced models on OpenRouter-like providers typed as openai, where the provider type strips the vendor prefix.
func WaitUntilIdleForTest ¶ added in v2.34.0
func WaitUntilIdleForTest(server *Server)
WaitUntilIdleForTest waits for background chat work tracked by the server to finish without shutting the server down. Tests use this to assert final database state only after asynchronous chat processing has completed. Close waits for the same tracked work, but also stops the server.
Types ¶
type AgentConnFunc ¶
type AgentConnFunc func(ctx context.Context, agentID uuid.UUID) (workspacesdk.AgentConn, func(), error)
AgentConnFunc provides access to workspace agent connections.
type Config ¶
type Config struct {
Logger slog.Logger
Database database.Store
ReplicaID uuid.UUID
// StreamPartsDialer dials remote stream parts. Nil uses the local
// in-process channel dialer for every stream.
StreamPartsDialer StreamPartsDialer
PendingChatAcquireInterval time.Duration
MaxChatsPerAcquire int32
InFlightChatStaleAfter time.Duration
ChatHeartbeatInterval time.Duration
AgentConn AgentConnFunc
AgentInactiveDisconnectTimeout time.Duration
InstructionLookupTimeout time.Duration
CreateWorkspace chattool.CreateWorkspaceFn
StartWorkspace chattool.StartWorkspaceFn
StopWorkspace chattool.StopWorkspaceFn
ProviderAPIKeys chatprovider.ProviderAPIKeys
AllowBYOK bool
AllowBYOKSet bool
AlwaysEnableDebugLogs bool
WebpushDispatcher webpush.Dispatcher
UsageTracker *workspacestats.UsageTracker
Clock quartz.Clock
AIBridgeTransportFactory *atomic.Pointer[aibridge.TransportFactory]
AIGatewayRoutingEnabled bool
Experiments codersdk.Experiments
PrometheusRegistry prometheus.Registerer
// OIDCTokenSource resolves the calling user's OIDC access
// token for MCP servers configured with auth_type=user_oidc.
// May be nil if the deployment has no OIDC provider; servers
// using user_oidc will then send no Authorization header.
OIDCTokenSource mcpclient.UserOIDCTokenSource
NotificationsEnqueuer notifications.Enqueuer
Auditor *atomic.Pointer[audit.Auditor]
}
Config configures a chat processor.
type CreateOptions ¶
type CreateOptions struct {
OrganizationID uuid.UUID
OwnerID uuid.UUID
WorkspaceID uuid.NullUUID
BuildID uuid.NullUUID
AgentID uuid.NullUUID
ParentChatID uuid.NullUUID
RootChatID uuid.NullUUID
Title string
ModelConfigID uuid.UUID
ChatMode database.NullChatMode
PlanMode database.NullChatPlanMode
ClientType database.ChatClientType
SystemPrompt string
InitialUserContent []codersdk.ChatMessagePart
APIKeyID string
MCPServerIDs []uuid.UUID
Labels database.StringMap
DynamicTools json.RawMessage
}
CreateOptions controls chat creation in the shared chat mutation path.
type DialResult ¶
type DialResult struct {
Conn workspacesdk.AgentConn
Release func()
AgentID uuid.UUID // The agent that was actually dialed.
WasSwitched bool // True if validation discovered a different agent.
}
DialResult contains the outcome of dialWithLazyValidation.
type EditMessageOptions ¶
type EditMessageOptions struct {
ChatID uuid.UUID
CreatedBy uuid.UUID
EditedMessageID int64
Content []codersdk.ChatMessagePart
APIKeyID string
// ModelConfigID, when non-zero, overrides the model used for
// the replacement user message. When set to uuid.Nil the
// original message's model is preserved.
ModelConfigID uuid.UUID
}
EditMessageOptions controls user message edits via soft-delete and re-insert.
type EditMessageResult ¶
type EditMessageResult struct {
Message database.ChatMessage
Chat database.Chat
}
EditMessageResult contains the replacement user message and chat status.
type LocalStreamPartsDialerConfig ¶ added in v2.35.0
type LocalStreamPartsDialerConfig struct {
Buffer *messagepartbuffer.Buffer
Logger slog.Logger
}
LocalStreamPartsDialerConfig configures an in-process stream parts dialer.
type ParsedChatPersonalModelOverride ¶ added in v2.34.0
type ParsedChatPersonalModelOverride struct {
Mode codersdk.ChatPersonalModelOverrideMode
ModelConfigID uuid.UUID
Malformed bool
}
ParsedChatPersonalModelOverride is a parsed personal model override value. When Malformed is true, Mode is the provided default and ModelConfigID is uuid.Nil.
func ParseChatPersonalModelOverride ¶ added in v2.34.0
func ParseChatPersonalModelOverride( raw string, defaultMode codersdk.ChatPersonalModelOverrideMode, ) ParsedChatPersonalModelOverride
ParseChatPersonalModelOverride parses a stored personal model override. Empty values return defaultMode without marking the value malformed. Malformed values return defaultMode, uuid.Nil, and Malformed true.
type PromoteQueuedOptions ¶
PromoteQueuedOptions controls queued-message promotion.
type PromoteQueuedResult ¶
type PromoteQueuedResult struct {
// PromotedMessage is the inserted user message. For a chat that
// was running at promote time, the insertion is deferred to the
// worker's auto-promote and PromotedMessage is the zero value.
PromotedMessage database.ChatMessage
}
PromoteQueuedResult contains post-promotion message metadata.
type SendMessageBusyBehavior ¶
type SendMessageBusyBehavior string
SendMessageBusyBehavior controls what happens when a chat is already active.
const ( // SendMessageBusyBehaviorQueue queues user messages while the chat is busy. SendMessageBusyBehaviorQueue SendMessageBusyBehavior = "queue" // SendMessageBusyBehaviorInterrupt queues the message and // interrupts the active run. The queued message is // auto-promoted after the interrupted assistant response is // persisted, ensuring correct message ordering. SendMessageBusyBehaviorInterrupt SendMessageBusyBehavior = "interrupt" )
type SendMessageOptions ¶
type SendMessageOptions struct {
ChatID uuid.UUID
CreatedBy uuid.UUID
Content []codersdk.ChatMessagePart
ModelConfigID uuid.UUID
APIKeyID string
BusyBehavior SendMessageBusyBehavior
PlanMode *database.NullChatPlanMode
MCPServerIDs *[]uuid.UUID
}
SendMessageOptions controls user message insertion with busy-state behavior.
type SendMessageResult ¶
type SendMessageResult struct {
Queued bool
QueuedMessage *database.ChatQueuedMessage
Message database.ChatMessage
Chat database.Chat
}
SendMessageResult contains the outcome of user message processing.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server handles background processing of pending chats.
func New ¶
New creates a new chat processor with the required pubsub dependency. The processor polls for pending chats and processes them. It is the caller's responsibility to call Close on the returned instance.
func (*Server) ArchiveChat ¶
ArchiveChat archives a root chat and every child in its family through the chatstate state machine. The transition is atomic over the whole family: either every member is archived or none is. The state machine only permits archive from the idle / error execution states (W, E0, E1); active members cause a state conflict that the HTTP handler maps to a client error.
Child chats must not be archived independently. ArchiveChat rejects them with ErrArchiveRequiresRootChat so callers cannot silently break the parent-implies-child archive invariant.
func (*Server) ContextResources ¶ added in v2.35.0
func (server *Server) ContextResources( ctx context.Context, chat database.Chat, ) ([]codersdk.ChatContextResource, error)
ContextResources returns the chat's pinned context resource list (metadata only). It is read-only and intended for the single-chat GET handler; list and watch payloads omit this detail to stay lightweight.
The returned list is the chat's full pinned inventory (instruction files, skills, and MCP configs/servers), each stamped with its per-resource status so the UI can explain why a resource was dropped from the prompt instead of silently omitting it.
func (*Server) CreateChat ¶
CreateChat creates a chat with its initial history through chatstate.CreateChat. The new chat starts in `running` status per the chat execution state model. Ownership hints wake chat workers.
func (*Server) DeleteQueued ¶
func (p *Server) DeleteQueued( ctx context.Context, chatID uuid.UUID, queuedMessageID int64, ) error
DeleteQueued removes a queued user message through the chatstate state machine. Stream side effects are handled by chat:update consumers.
func (*Server) EditMessage ¶
func (p *Server) EditMessage( ctx context.Context, opts EditMessageOptions, ) (EditMessageResult, error)
EditMessage replaces an earlier user message and discards the active-history suffix through chatstate.EditMessage. Model-config override validation and usage-limit admission run in the same transaction as the state-machine transition.
func (*Server) GenerateChatTitleAsync ¶ added in v2.35.0
GenerateChatTitleAsync fires a best-effort, automatic title-generation pass for a freshly created chat. It is intended to be called from the chat-creation endpoint right after the chat and its initial user message are persisted.
The work runs in a tracked goroutine with a context detached from the request but bound to the server: it neither blocks the HTTP response nor is canceled when the request completes, and Close cancels it instead of blocking on the title timeout while a provider is unreachable. It resolves the chat's model and provider keys, then delegates to maybeGenerateChatTitle, which only acts on the first user turn (see titleInput) and is otherwise a no-op. Errors are logged and swallowed.
func (*Server) HydrateAndMarkChatsDirty ¶ added in v2.35.0
func (p *Server) HydrateAndMarkChatsDirty(ctx context.Context, tx database.Store, agentID uuid.UUID, aggregateHash []byte, snapshotError string, now time.Time) (func(), error)
HydrateAndMarkChatsDirty implements agentapi.ContextDirtyMarker. It runs inside the PushContextState transaction: it stamps the pushed snapshot hash on chats for the agent that have not been hydrated yet (no dirty event), then flips already-pinned chats whose hash differs to dirty. It returns a callback that publishes the dirty watch events; the caller invokes it only after the transaction commits, and the callback is a no-op when nothing transitioned to dirty.
The pinned hash on dirtied chats is intentionally left unchanged; the refresh endpoint re-pins it.
func (*Server) InterruptChat ¶
InterruptChat interrupts execution through the chatstate.Interrupt transition. Active runs land in `interrupting`; requires-action chats synthesize cancellation messages and return to running.
Returns the post-transition chat and an error so callers can map state conflicts deliberately. Idle chats return a chatstate.ErrTransitionNotAllowed wrapper.
func (*Server) PromoteQueued ¶
func (p *Server) PromoteQueued( ctx context.Context, opts PromoteQueuedOptions, ) (PromoteQueuedResult, error)
PromoteQueued promotes a queued message through the chatstate state machine. From running / interrupting states the state machine transitions the chat to `interrupting` so the worker can drain the in-flight generation before promoting; from idle / error / requires action states it inserts the user message into history synchronously.
func (*Server) ProposeChatTitle ¶ added in v2.33.0
ProposeChatTitle generates a title suggestion from the chat's visible messages without persisting it.
func (*Server) PublishDiffStatusChange ¶
PublishDiffStatusChange broadcasts a diff_status_change event for the given chat so that watching clients know to re-fetch the diff status. This is called from the HTTP layer after the diff status is updated in the database.
func (*Server) PublishTitleChange ¶ added in v2.33.0
PublishTitleChange broadcasts a title_change event for the given chat.
func (*Server) ReconcileInvalidStateChat ¶ added in v2.35.0
func (p *Server) ReconcileInvalidStateChat( ctx context.Context, chat database.Chat, ) (database.Chat, error)
ReconcileInvalidStateChat recovers a chat stuck in an invalid execution-state combination by running the chatstate.ReconcileInvalidState transition. The chat lands in an error state (E0/E1); queued messages are preserved and pending dynamic-tool calls are closed with synthetic cancellations.
Returns the post-transition chat. When the chat is not actually in an invalid state the transition returns a wrapped chatstate.ErrTransitionNotAllowed; a missing chat returns chatstate.ErrChatNotFound. Callers map these to deliberate HTTP responses.
func (*Server) RefreshChatContext ¶ added in v2.35.0
RefreshChatContext re-pins a chat to its agent's latest context snapshot (hash, error, and resource bodies) and clears the dirty marker. It backs PUT /chats/{chat}/context (no body). A chat with no bound agent, or whose agent has no snapshot, simply has its pinned hash, dirty marker, and resources cleared.
The snapshot read and the re-pin run in one repeatable-read transaction so a concurrent push cannot land between them and leave the chat pinned to a stale hash with the dirty marker cleared.
func (*Server) RegenerateChatTitle ¶
func (p *Server) RegenerateChatTitle( ctx context.Context, chat database.Chat, ) (database.Chat, error)
RegenerateChatTitle regenerates a chat title from the chat's visible messages, persists it when it changes, and broadcasts the update.
func (*Server) RenameChatTitle ¶ added in v2.33.0
func (p *Server) RenameChatTitle( ctx context.Context, chat database.Chat, newTitle string, ) (updated database.Chat, wrote bool, err error)
RenameChatTitle persists a user-supplied chat title.
func (*Server) SendMessage ¶
func (p *Server) SendMessage( ctx context.Context, opts SendMessageOptions, ) (SendMessageResult, error)
SendMessage admits a user message through the chatstate.SendMessage transition. Pre-transition admission policy (usage limit, plan-mode metadata update, MCP server ID update, model-config resolution, queue cap) runs inside the same chatstate transaction via the transactional store so everything commits or rolls back together.
func (*Server) ServeStreamPartsAuthorized ¶ added in v2.35.0
func (p *Server) ServeStreamPartsAuthorized(rw http.ResponseWriter, r *http.Request, chat database.Chat) error
ServeStreamPartsAuthorized serves the internal episode-selected parts stream for an already authorized chat route.
func (*Server) Start ¶ added in v2.34.0
Start runs the background acquire/wake loop that picks up pending chats and processes them. Callers that want a passive server (e.g. tests) can skip this call; heartbeat, stream janitor, and stale recovery still run.
func (*Server) SubmitToolResults ¶ added in v2.33.0
func (p *Server) SubmitToolResults( ctx context.Context, opts SubmitToolResultsOptions, ) error
SubmitToolResults validates and persists client-provided tool results, returning the chat to running through the chatstate state machine. Validation runs inside the same transaction as the transition so the assistant message and pending tool calls cannot drift between reads.
func (*Server) SubscribeAuthorized ¶ added in v2.34.0
func (p *Server) SubscribeAuthorized( ctx context.Context, chat database.Chat, requestHeader http.Header, afterMessageID int64, ) ( []codersdk.ChatStreamEvent, <-chan codersdk.ChatStreamEvent, func(), bool, )
SubscribeAuthorized subscribes an already-authorized chat to stream updates.
func (*Server) UnarchiveChat ¶
UnarchiveChat unarchives a root chat and every child in its family through the chatstate state machine. Like ArchiveChat the cascade is atomic; ChildChat unarchive attempts are rejected with ErrArchiveRequiresRootChat.
type StreamPart ¶ added in v2.35.0
type StreamPart struct {
HistoryVersion int64
GenerationAttempt int64
Seq int64
Role codersdk.ChatMessageRole
Part codersdk.ChatMessagePart
}
StreamPart is a live preview part scoped to one chat history episode.
func StreamPartFromEvent ¶ added in v2.35.0
func StreamPartFromEvent(event codersdk.ChatStreamEvent) (StreamPart, bool)
type StreamPartsDialInput ¶ added in v2.35.0
StreamPartsDialInput carries the metadata needed to dial a parts source.
type StreamPartsDialer ¶ added in v2.35.0
type StreamPartsDialer func(ctx context.Context, input StreamPartsDialInput) (StreamPartsSession, error)
StreamPartsDialer dials an episode-aware source of message parts.
func NewLocalStreamPartsDialer ¶ added in v2.35.0
func NewLocalStreamPartsDialer(cfg LocalStreamPartsDialerConfig) StreamPartsDialer
NewLocalStreamPartsDialer returns a dialer that streams message parts through in-process channels while using the same stream serving loop as WebSockets.
type StreamPartsJSONSession ¶ added in v2.35.0
type StreamPartsJSONSession struct {
// contains filtered or unexported fields
}
func NewStreamPartsJSONSession ¶ added in v2.35.0
func NewStreamPartsJSONSession(ctx context.Context, conn *websocket.Conn) *StreamPartsJSONSession
func (StreamPartsJSONSession) Close ¶ added in v2.35.0
func (s StreamPartsJSONSession) Close() error
func (StreamPartsJSONSession) Parts ¶ added in v2.35.0
func (s StreamPartsJSONSession) Parts() <-chan StreamPart
type StreamPartsSession ¶ added in v2.35.0
type StreamPartsSession interface {
SelectEpisode(ctx context.Context, historyVersion, generationAttempt int64) error
Parts() <-chan StreamPart
Close() error
}
StreamPartsSession streams message parts for selected episodes.
type SubmitToolResultsOptions ¶ added in v2.33.0
type SubmitToolResultsOptions struct {
ChatID uuid.UUID
UserID uuid.UUID
ModelConfigID uuid.UUID
Results []codersdk.ToolResult
DynamicTools json.RawMessage
}
SubmitToolResultsOptions controls tool result submission.
type ToolResultStatusConflictError ¶ added in v2.33.0
type ToolResultStatusConflictError struct {
ActualStatus database.ChatStatus
}
ToolResultStatusConflictError indicates the chat is not in the requires_action state expected for tool result submission.
func (*ToolResultStatusConflictError) Error ¶ added in v2.33.0
func (e *ToolResultStatusConflictError) Error() string
type ToolResultValidationError ¶ added in v2.33.0
ToolResultValidationError indicates the submitted tool results failed validation (e.g. missing, duplicate, or unexpected IDs, or invalid JSON output).
func (*ToolResultValidationError) Error ¶ added in v2.33.0
func (e *ToolResultValidationError) Error() string
type UsageLimitExceededError ¶
UsageLimitExceededError indicates the user has exceeded their chat spend limit.
func (*UsageLimitExceededError) Error ¶
func (e *UsageLimitExceededError) Error() string
Source Files
¶
- active_turn_debug.go
- attachments.go
- attempt.go
- auto_archive.go
- chatd.go
- chatd_debug.go
- chatstate_bridge.go
- computer_use.go
- configcache.go
- context_hydration.go
- context_prompt.go
- dialvalidation.go
- docs.go
- dynamictool.go
- generation.go
- generation_preparer.go
- instruction.go
- message_conversion.go
- model_routing.go
- model_routing_aibridge.go
- model_routing_direct.go
- options.go
- personal_model_override.go
- prompt.go
- provider_switch_sanitize.go
- quickgen.go
- recording.go
- runner.go
- runner_manager.go
- sanitize.go
- store_chat_attachment.go
- stream_loop.go
- stream_parts.go
- stream_parts_dialer.go
- stream_parts_transport.go
- stream_relay.go
- stream_subscribe.go
- stream_sync_poller.go
- stream_types.go
- subagent.go
- subagent_catalog.go
- tasks.go
- testhooks.go
- title_override.go
- usagelimit.go
- worker.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package chatretry provides retry logic for transient LLM provider errors.
|
Package chatretry provides retry logic for transient LLM provider errors. |
|
Package chatstate owns the durable execution-state transitions for the chatd subsystem.
|
Package chatstate owns the durable execution-state transitions for the chatd subsystem. |
|
internal
|
|
|
Package messagepartbuffer stores the transient message-part stream that a chat worker emits before those parts are committed to durable chat history.
|
Package messagepartbuffer stores the transient message-part stream that a chat worker emits before those parts are committed to durable chat history. |