chore: add client_type field to chats and telemetry (#24342)

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`.

<details>
<summary>Implementation notes (Coder Agents generated)</summary>

### Changes

**Database migration (000469)**
- New enum `chat_client_type` with values `ui`, `api`.
- New `client_type` column, `NOT NULL DEFAULT 'api'`.
- Backfill: `UPDATE chats SET client_type = 'ui'`.

**SQL query** — `InsertChat` now includes `client_type`.

**SDK** — `ChatClientType` type added; `ClientType` field added to both
`CreateChatRequest` (optional, defaults server-side to `api`) and `Chat`
response.

**Handler** — `postChats` maps the request field (defaulting to `api`)
and passes it through `chatd.CreateOptions`.

**Sub-agent** — Child chats inherit their parent's `client_type`.

**db2sdk** — Maps the database value to the SDK type.

### Decision log
- Default is `api` (not `ui`) so existing API integrations get the
correct value without code changes.
- Backfill sets existing rows to `ui` per requirement.
- Child chats inherit `client_type` from parent rather than defaulting.
</details>
This commit is contained in:
Dean Sheather
2026-04-16 23:57:05 +10:00
committed by GitHub
parent b9bc0ad6df
commit 3452ab3166
40 changed files with 375 additions and 47 deletions
+59
View File
@@ -1107,6 +1107,64 @@ func AllBuildReasonValues() []BuildReason {
}
}
type ChatClientType string
const (
ChatClientTypeUi ChatClientType = "ui"
ChatClientTypeApi ChatClientType = "api"
)
func (e *ChatClientType) Scan(src interface{}) error {
switch s := src.(type) {
case []byte:
*e = ChatClientType(s)
case string:
*e = ChatClientType(s)
default:
return fmt.Errorf("unsupported scan type for ChatClientType: %T", src)
}
return nil
}
type NullChatClientType struct {
ChatClientType ChatClientType `json:"chat_client_type"`
Valid bool `json:"valid"` // Valid is true if ChatClientType is not NULL
}
// Scan implements the Scanner interface.
func (ns *NullChatClientType) Scan(value interface{}) error {
if value == nil {
ns.ChatClientType, ns.Valid = "", false
return nil
}
ns.Valid = true
return ns.ChatClientType.Scan(value)
}
// Value implements the driver Valuer interface.
func (ns NullChatClientType) Value() (driver.Value, error) {
if !ns.Valid {
return nil, nil
}
return string(ns.ChatClientType), nil
}
func (e ChatClientType) Valid() bool {
switch e {
case ChatClientTypeUi,
ChatClientTypeApi:
return true
}
return false
}
func AllChatClientTypeValues() []ChatClientType {
return []ChatClientType{
ChatClientTypeUi,
ChatClientTypeApi,
}
}
type ChatMessageRole string
const (
@@ -4303,6 +4361,7 @@ type Chat struct {
DynamicTools pqtype.NullRawMessage `db:"dynamic_tools" json:"dynamic_tools"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
PlanMode NullChatPlanMode `db:"plan_mode" json:"plan_mode"`
ClientType ChatClientType `db:"client_type" json:"client_type"`
}
type ChatDebugRun struct {