chore: add client_type field to chats#24342
Conversation
65f5416 to
32f804b
Compare
|
@codex review |
There was a problem hiding this comment.
Pull request overview
Adds a client_type classification to chats so the system can distinguish chats created via the web UI vs. programmatic/API creation, flowing from DB → server → SDK → UI types.
Changes:
- Add
chat_client_typeenum andchats.client_typecolumn with default/backfill migration. - Thread
client_typethrough chat creation (CreateChatRequest→chatd.CreateOptions→InsertChat) and include it in chat responses (db2sdk). - Update generated types and tests/stories to include the new required
client_typefield onChat.
Reviewed changes
Copilot reviewed 37 out of 37 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.test.tsx | Update chat test fixtures to include client_type. |
| site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.stories.tsx | Update story fixtures to include client_type. |
| site/src/pages/AgentsPage/components/ChatTopBar.stories.tsx | Update story fixtures to include client_type. |
| site/src/pages/AgentsPage/components/ChatConversation/chatStore.test.tsx | Update chat fixtures to include client_type. |
| site/src/pages/AgentsPage/AgentsPageView.stories.tsx | Update story fixtures to include client_type. |
| site/src/pages/AgentsPage/AgentChatPageView.stories.tsx | Update story fixtures to include client_type. |
| site/src/pages/AgentsPage/AgentChatPage.stories.tsx | Update story fixtures to include client_type. |
| site/src/api/typesGenerated.ts | Add ChatClientType, Chat.client_type, and CreateChatRequest.client_type. |
| site/src/api/queries/chats.test.ts | Update chat fixtures to include client_type. |
| enterprise/coderd/x/chatd/usagelimit_test.go | Set ClientType when seeding chats in tests. |
| enterprise/coderd/x/chatd/chatd_test.go | Set ClientType when seeding chats in tests. |
| codersdk/chats.go | Introduce SDK ChatClientType; add client_type to Chat and CreateChatRequest. |
| coderd/x/gitsync/worker_test.go | Set ClientType when seeding chats in tests. |
| coderd/x/chatd/subagent.go | Ensure child chats inherit parent ClientType. |
| coderd/x/chatd/recording_internal_test.go | Set ClientType when seeding chats in tests. |
| coderd/x/chatd/chattool/startworkspace_test.go | Set ClientType when seeding chats in tests. |
| coderd/x/chatd/chatprompt/chatprompt_test.go | Set ClientType when seeding chats in tests. |
| coderd/x/chatd/chatd_test.go | Set ClientType when seeding chats in tests. |
| coderd/x/chatd/chatd.go | Add ClientType to CreateOptions, default it, and persist it on insert. |
| coderd/workspaceagents_chat_context_test.go | Set ClientType when seeding chats in tests. |
| coderd/workspaceagents_active_chat_internal_test.go | Set ClientType when seeding chats in tests. |
| coderd/telemetry/telemetry_test.go | Set ClientType when seeding chats in tests. |
| coderd/httpmw/chatparam_test.go | Set ClientType when seeding chats in tests. |
| coderd/exp_chats_test.go | Set ClientType when seeding chats in tests. |
| coderd/exp_chats.go | Map request client_type into chat creation options (with default). |
| coderd/database/queries/chats.sql | Add client_type to InsertChat. |
| coderd/database/queries.sql.go | Regenerate queries to include client_type in scans/returns and insert params. |
| coderd/database/querier_test.go | Set ClientType when seeding chats in tests. |
| coderd/database/models.go | Add ChatClientType enum type + Chat.ClientType field. |
| coderd/database/modelqueries.go | Update authorized chats scanning to include client_type. |
| coderd/database/migrations/000469_chat_client_type.up.sql | Create enum, add column w/ default, and backfill existing rows. |
| coderd/database/migrations/000469_chat_client_type.down.sql | Rollback: drop column and enum type. |
| coderd/database/dump.sql | Update schema dump with new enum and column. |
| coderd/database/dbpurge/dbpurge_test.go | Set ClientType when seeding chats in tests. |
| coderd/database/dbauthz/dbauthz_test.go | Update mocked InsertChat args to include ClientType. |
| coderd/database/db2sdk/db2sdk_test.go | Set ClientType in db2sdk mapping test fixture. |
| coderd/database/db2sdk/db2sdk.go | Map DB ClientType into SDK Chat.ClientType. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
32f804b to
688c690
Compare
mafredri
left a comment
There was a problem hiding this comment.
First-pass review (Netero only). One P2 (input validation), one P3 (test gap). The full review panel has not yet reviewed this PR; these are mechanical findings worth addressing before the panel spends parallel review time.
The copilot reviewer flagged the same validation gap inline. The codebase already uses .Valid() for other enums in this file (e.g., Period.Valid() at lines 1049 and 1071). Applying the same pattern here is straightforward.
P3 coderd/exp_chats_test.go — All test changes add ClientType: database.ChatClientTypeUi to InsertChatParams for existing setup, but no test exercises the handler path: creating a chat via the API and asserting the response contains the correct client_type, the omitted-field default, or rejection of an invalid value. The TestChat_AllFieldsPopulated reflective test confirms db2sdk maps the field, but the handler-to-response round-trip is uncovered. (Netero)
"If a client sends
"evil", the value propagates to InsertChat, where Postgres rejects it as an invalid chat_client_type enum value. The result is a 500." — Netero
🤖 This review was automatically generated with Coder Agents.
mafredri
left a comment
There was a problem hiding this comment.
Round 2 review is blocked. F1 (P2, input validation) was addressed in 9fdd60f with a .Valid() check. Thank you.
F2 (P3, no handler-level test for client_type) is still open with no author response or code change. The finding was posted in the R1 review body: no test exercises creating a chat via the API and asserting the response contains the correct client_type, the omitted-field default, or rejection of an invalid value.
Further review (including the full panel) is blocked until F2 is addressed, acknowledged, or contested.
🤖 This review was automatically generated with Coder Agents.
Documentation CheckUpdates Needed
Automated review via Coder Tasks |
67e407b to
884f3d1
Compare
884f3d1 to
3453753
Compare
ethanndickson
left a comment
There was a problem hiding this comment.
Clean feature — the three-layer validation (handler → service → DB NOT NULL), immutable write-once column, and child-chat propagation are all well done. The round-trip tests added in TestPostChats_ClientType directly address the prior P3 finding.
One correctness issue: the web UI never sends client_type: "ui", defeating the feature for new chats. Plus a few formatting artifacts from the field insertion. 1 P1, 2 P3, 1 Nit, 2 Obs across 5 inline comments.
This review contains a finding that may need attention before merge.
Prior round 1 findings: P2 (input validation) ✅ resolved in 9fdd60f. P3 (handler tests) ✅ resolved by new TestPostChats_ClientType suite.
🤖 This review was generated with Coder Agents.
| } | ||
|
|
||
| clientType := database.ChatClientTypeApi | ||
| if req.ClientType != "" { |
There was a problem hiding this comment.
P1 Web UI never sends client_type: "ui" — all new browser-created chats will be mislabeled as "api" (Edge Case Analyst P1, Frontend Reviewer P1)
AgentCreatePage.tsxis the only UI code path that callscreateMutation.mutateAsync(...). The request object setsorganization_id,content,workspace_id,model_config_id,mcp_server_ids, andplan_mode, but never setsclient_type. Because the SDK field isjson:"client_type,omitempty"and the Go zero-value for the string type is"", this omits the field entirely. The handler treatsreq.ClientType == ""as "default to api".
Frontend Reviewer adds: This is the only place the UI calls
createChat. The migration backfills existing rows correctly to"ui", but all new UI-created chats will be tagged as"api"from the moment this merges.
This defeats the entire purpose of the enum for newly created chats. Fix: add client_type: "ui" to the mutateAsync call in AgentCreatePage.tsx (lines 61–69).
| ModelConfigID: modelConfigID, | ||
| PlanMode: planModeToNullChatPlanMode(req.PlanMode), | ||
| ClientType: clientType, SystemPrompt: req.SystemPrompt, | ||
| InitialUserContent: contentBlocks, |
There was a problem hiding this comment.
P3 Struct field crammed onto same line as SystemPrompt, breaking one-field-per-line convention (Go Architect P2, Contract Auditor P3, Database Reviewer P3, Style Reviewer Nit)
ClientType: clientType, SystemPrompt: req.SystemPrompt,packs two struct fields onto one line. Every other field in this literal occupies its own line with aligned values. This is likely an artifact of inserting the new field without reflowing the struct.
Same issue appears in two other locations (see comments on chatd.go and subagent.go). In chatd.go:915 the trailing comment // Chats created with an initial user message start pending. originally described Status but now appears to annotate ClientType — which it doesn't. Worth a quick reformat: each field on its own line, comment stays with Status.
| effectivePlanMode := opts.PlanMode | ||
|
|
||
| if opts.ClientType == "" { | ||
| opts.ClientType = database.ChatClientTypeApi |
There was a problem hiding this comment.
P3 Redundant defensive default — could use cmp.Or (Contract Auditor P3, Go Architect P3, Modernization Reviewer Nit)
The HTTP handler (
exp_chats.go:571) already defaults toChatClientTypeApiwhenreq.ClientTypeis empty and validates the value with.Valid(). Thenchatd.go:890re-applies the same empty-string-to-api default. This isn't a bug — it's defense-in-depth. But it means any new internal caller that forgets to setClientTypewill silently get"api"without any error signal.
Modernization Reviewer adds: This
if opts.ClientType == "" { ... }pattern can be replaced withopts.ClientType = cmp.Or(opts.ClientType, database.ChatClientTypeApi), which is already established in this codebase (e.g.chatd/chattool/skill.go,agent/agentcontextconfig/api.go).
Acceptable as-is — the defense-in-depth is reasonable with a single caller. If keeping it, the cmp.Or form is more idiomatic for this repo.
| PlanMode: effectivePlanMode, | ||
| // Chats created with an initial user message start pending. | ||
| ClientType: opts.ClientType, // Chats created with an initial user message start pending. | ||
| // Waiting is reserved for idle chats with no pending work. |
There was a problem hiding this comment.
Nit Misattributed comment — // Chats created with an initial user message start pending. now trails ClientType instead of Status (Go Architect, Style Reviewer)
The comment was originally on its own line describing
Status. The insertion ofClientTypeon the same line shifted it so it now appears to annotateClientType. Move the comment back to its own line aboveStatus, and giveClientTypeits own line.
Also: subagent.go:489 has the same two-fields-on-one-line issue (ClientType: parent.ClientType, Status: database.ChatStatusPending,).
| } | ||
|
|
||
| clientType := database.ChatClientTypeApi | ||
| if req.ClientType != "" { |
There was a problem hiding this comment.
Obs client_type not included in telemetry (Contract Auditor)
The telemetry
Chatstruct includesStatus,Mode,HasParent, etc., but notClientType. If the purpose ofclient_typeis to distinguish UI-created from API-created chats, telemetry is the main place where that distinction would be analytically valuable — e.g., tracking API adoption rates.
Not blocking, but worth considering as a follow-up if the goal is fleet-wide visibility into client-type distribution.
Obs Child chat inheritance untested (Test Auditor)
subagent.gocopiesparent.ClientTypeto the childInsertChatParams, but no existing test creates a parent with one client type and verifies the child got the same value. The code is straightforward (a single field copy), so this is low risk, but a refactor could silently drop it.
Add a chat_client_type enum (ui | api) and client_type column to the chats table. The column defaults to 'api' for new rows so API callers don't need to set it explicitly. Existing rows are backfilled to 'ui'. The field flows through CreateChatRequest, chatd.CreateOptions, InsertChat, and is returned in the Chat response via db2sdk. Invalid values are rejected with a 400 in the handler.
3453753 to
22ac022
Compare
- UI sends client_type: "ui" in AgentCreatePage.tsx - Fix struct field formatting (one field per line) - Use cmp.Or for idiomatic default in chatd.go - Add Valid() check in chatd.CreateChat - Use IF EXISTS in down migration - Fix misattributed comment on Status field
ethanndickson
left a comment
There was a problem hiding this comment.
All findings from the previous round are addressed — thanks for the quick fixes.
- P1 (UI never sends
client_type: "ui") ✅ fixed inAgentCreatePage.tsx - P3 (struct formatting, 3 locations) ✅ all three reflowed to one-field-per-line
- P3 (redundant defaulting) ✅ replaced with
cmp.Or, plus added.Valid()in the service layer — nice improvement over what I suggested, since internal callers now get a proper error instead of a silent DB failure - Nit (misattributed comment) ✅ comment back on its own line above
Status - Bonus: down migration now uses
IF EXISTS(Copilot R1 observation)
No new findings. Looks good.
🤖 This review was generated with Coder Agents.
Add a
chat_client_typeenum (ui|api) andclient_typecolumn to thechatstable. The column defaults toapifor new rows so API callers don't need to set it explicitly. Existing rows are backfilled toui.The field flows through
CreateChatRequest,chatd.CreateOptions,InsertChat, and is returned in theChatresponse viadb2sdk.Implementation notes (Coder Agents generated)
Changes
Database migration (000469)
chat_client_typewith valuesui,api.client_typecolumn,NOT NULL DEFAULT 'api'.UPDATE chats SET client_type = 'ui'.SQL query —
InsertChatnow includesclient_type.SDK —
ChatClientTypetype added;ClientTypefield added to bothCreateChatRequest(optional, defaults server-side toapi) andChatresponse.Handler —
postChatsmaps the request field (defaulting toapi) and passes it throughchatd.CreateOptions.Sub-agent — Child chats inherit their parent's
client_type.db2sdk — Maps the database value to the SDK type.
Decision log
api(notui) so existing API integrations get the correct value without code changes.uiper requirement.client_typefrom parent rather than defaulting.