Skip to content

feat: surface chat hook outcomes in the chats API and UI#27430

Open
ibetitsmike wants to merge 1 commit into
mike/chat-hooks/chatdfrom
mike/chat-hooks/api
Open

feat: surface chat hook outcomes in the chats API and UI#27430
ibetitsmike wants to merge 1 commit into
mike/chat-hooks/chatdfrom
mike/chat-hooks/api

Conversation

@ibetitsmike

@ibetitsmike ibetitsmike commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Stack Context

Chat lifecycle hooks stack: #27427 (DB schema) -> #27401 (dispatch backend) -> #27428 (chatstate transitions) -> #27429 (chatd wiring) -> #27430 (this, top).

API and UI ship together because the send/edit response shapes change (full messages batches), so the site does not compile against the regenerated types without its client updates.

What

API (coderd/exp_chats.go, codersdk/chats.go)

  • user_prompt_submit denials map to 403 with the hook reason; dispatch failures map to 502 with the hook_dispatch_failed error kind.
  • Send/edit responses return messages (full inserted batch, hooks may prepend notices); successful edits always return the replacement message.
  • File links resolve after prompt overrides so linked files follow the persisted content.

UI (site/src)

  • Chat queries upsert the full messages batch on send and reconcile edit batches.
  • ConversationTimeline renders hook notice messages; stories cover the new states.

Why

Top of the stack: surfaces everything below to clients. The frontend delta is small and inseparable from the response shape change.

This PR was prepared by Mux (AI agent) on Mike's behalf.

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown

Docs preview

Check off each page once it's been reviewed. If a page changes in a later push, its checkbox clears automatically so it gets a fresh look. Pages not yet wired into the docs navigation aren't listed here.

Copy link
Copy Markdown
Collaborator Author

@ibetitsmike

Copy link
Copy Markdown
Collaborator Author

@codex review

@ibetitsmike
ibetitsmike force-pushed the mike/chat-hooks/chatd branch from bbc1484 to d0f7244 Compare July 22, 2026 20:12
@ibetitsmike
ibetitsmike force-pushed the mike/chat-hooks/api branch from 6743ac5 to fdcf4b8 Compare July 22, 2026 20:12
@ibetitsmike

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Can't wait for the next one!

Reviewed commit: fdcf4b843f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ibetitsmike
ibetitsmike force-pushed the mike/chat-hooks/chatd branch from d0f7244 to 5250a86 Compare July 22, 2026 20:24
@ibetitsmike
ibetitsmike force-pushed the mike/chat-hooks/api branch from fdcf4b8 to 44a8e4a Compare July 22, 2026 20:25
@ibetitsmike

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 44a8e4a966

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread coderd/exp_chats.go
Comment on lines +116 to +119
httpapi.Write(ctx, rw, http.StatusBadGateway, codersdk.Response{
Message: "Chat lifecycle hook dispatch failed.",
Detail: fmt.Sprintf("Lifecycle hook dispatch %s failed (%s).", hookErr.DispatchID, hookErr.Class),
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve hook dispatch failure kind

When a lifecycle hook dispatch fails on create/send/edit/tool-result submission, this helper now returns a plain codersdk.Response, so the 502 JSON body only has message and detail and drops the hook_dispatch_failed classification. In the Agents page, that immediate API error is cached as a generic chat error before the follow-up refetch, and cached non-usage errors take precedence over chat.last_error, so users keep seeing a generic request failure instead of the hook-specific failure. Return a chat error payload, or another response type, that includes kind: "hook_dispatch_failed" and preserve it in the client.

Useful? React with 👍 / 👎.

@ibetitsmike
ibetitsmike force-pushed the mike/chat-hooks/chatd branch from 5250a86 to e62369a Compare July 22, 2026 20:37
@ibetitsmike
ibetitsmike force-pushed the mike/chat-hooks/api branch from 44a8e4a to 7b0db9d Compare July 22, 2026 20:37
@ibetitsmike

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7b0db9d844

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread coderd/exp_chats.go
// Return the full inserted batch because hook notices can have lower IDs.
// A client that caches only the user message could skip them when
// reconnecting with after_id.
for _, inserted := range sendResult.InsertedMessages {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include promoted batches in queued send responses

When an errored chat already has a queued prompt, sendMessageE1 queues the new prompt and also promotes the previous queue head into history. Because this new messages population only runs in the non-queued branch, that user-visible promoted batch is omitted from the response; if the WebSocket delivery is missed, the client has no cache update for the inserted prompt until some later transcript refetch. Serialize InsertedMessages regardless of whether the current prompt was queued, while still keeping message reserved for non-queued sends.

Useful? React with 👍 / 👎.

// Keep links in dimmed notices out of accessibility navigation.
inert={isAfterEditingMessage ? true : undefined}
>
<Alert className="my-1">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid live alerts for historical hook notices

When a chat with persisted lifecycle-hook notices is opened or refetched, each historical notice is rendered with the shared Alert component, which hard-codes role="alert". That makes old transcript content an assertive live region for screen-reader users instead of ordinary message history, so loading a conversation with several notices can announce them as urgent new alerts. Use a non-live role/container here, or pass an overridable role to Alert for timeline content.

Useful? React with 👍 / 👎.

Maps user_prompt_submit denials and dispatch failures to HTTP
responses, returns hook-inserted message batches and ended state from
send and edit endpoints, links files after prompt overrides, adds the
hook_dispatch_failed chat error kind, and renders hook notices and
ended sends in the chat UI.
@ibetitsmike
ibetitsmike force-pushed the mike/chat-hooks/chatd branch from e62369a to d835b06 Compare July 22, 2026 20:52
@ibetitsmike
ibetitsmike force-pushed the mike/chat-hooks/api branch from 7b0db9d to 38d0e66 Compare July 22, 2026 20:52
@ibetitsmike

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 38d0e669fd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 136 to 139
const preservedMessages = page.messages.filter(
(message) =>
message.id !== optimisticMessageId && message.id !== responseMessage.id,
message.id !== optimisticMessageId && !responseIDs.has(message.id),
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove stale hook notices when reconciling edits

When editing a user message that already had lifecycle-hook notices, the backend soft-deletes those same-turn prefix rows before inserting the replacement, but this reconciliation only filters the optimistic/replacement IDs and then adds the new response batch. If the edit WebSocket history_reset is missed or the client reconnects with after_id at the replacement, the first-page cache keeps the old system notice and displays it beside the new notice; the response needs to carry deleted IDs or the cache needs to remove prior same-turn hook prefixes as part of edit reconciliation.

Useful? React with 👍 / 👎.

Comment thread coderd/exp_chats.go
Comment on lines +116 to +119
httpapi.Write(ctx, rw, http.StatusBadGateway, codersdk.Response{
Message: "Chat lifecycle hook dispatch failed.",
Detail: fmt.Sprintf("Lifecycle hook dispatch %s failed (%s).", hookErr.DispatchID, hookErr.Class),
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Return the hook failure kind in API errors

For hook dispatch failures during create/send/edit/tool-result submission, this writes a plain codersdk.Response, which has only message/detail/validations; there is no kind field for clients to distinguish hook_dispatch_failed from an ordinary 502. In the create-chat path no chat row is persisted for a later last_error refresh, and in the send/edit UI catch path the synchronous API error is cached as kind generic, so users and API clients lose the specific lifecycle-hook failure classification.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant