feat: wire chat lifecycle hooks into chatd#27275
Conversation
Docs previewCheck 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. |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bb5ad536d0
ℹ️ 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".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4c3deb6255
ℹ️ 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".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: da9e31ed4c
ℹ️ 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".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8775de5b78
ℹ️ 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".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c833f11a2e
ℹ️ 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".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 45d4b5796f
ℹ️ 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".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fa8e9fe012
ℹ️ 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".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c363878f35
ℹ️ 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".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 35526b5c9f
ℹ️ 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".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ae7ab19b96
ℹ️ 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".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 48d613f3f3
ℹ️ 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".
|
@codex review
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7f8cd31e8c
ℹ️ 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".
|
@codex review
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d06b97ccd7
ℹ️ 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".
|
@codex review
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4060962c72
ℹ️ 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".
|
|
||
| // endDescendantChats uses nested machines so descendant snapshots and publications | ||
| // share the outer transaction. ID order follows the family lock order. | ||
| func (tx *Tx) endDescendantChats(chat database.Chat) ([]database.Chat, error) { |
There was a problem hiding this comment.
instead of forcing ending on all the children, could we go down the route of dispatching the end for all children of the root agent?
There was a problem hiding this comment.
Restructured in 4b2efba: the multi-chat entry is now EndChatFamily, an explicit orchestrator that dispatches the single-chat EndChat transition per descendant through nested machines (per-chat lock, per-chat matrix validation, shared publish buffer, one outer transaction). That is the dispatch-per-child model, kept inside one transaction so a partial family end cannot commit.
Posted by Mux on behalf of Mike.
| } | ||
| if overridden { | ||
| prompt = override | ||
| // Avoid deriving titles from the prompt that policy replaced. |
There was a problem hiding this comment.
the prompt should already be redacted before we reach this point
There was a problem hiding this comment.
There is no earlier redaction on this path: pre_tool_use can override the spawn_agent input, but nothing guarantees it, and the user_prompt_submit override lands exactly here. The real defect was deriving the title from the original prompt before the hook ran, which forced this recompute. Fixed in f47c86b: the fallback title is now derived once, after hooks settle the final prompt.
Posted by Mux on behalf of Mike.
| if err != nil { | ||
| return database.Chat{}, err | ||
| } | ||
| // With no child to archive, end_chat refuses the spawn. |
There was a problem hiding this comment.
subagent should never have a child chat
There was a problem hiding this comment.
The synthetic chat here is the prospective child itself (the spawn target), not a child of a subagent; spawn parents are validated root-only. Since the hook fires before the row exists, end_chat has nothing to archive and denies the spawn. Reworded the comment in f47c86b to say that directly.
Posted by Mux on behalf of Mike.
| const ( | ||
| TransitionCreateChat Transition = "CreateChat" | ||
| TransitionSetArchived Transition = "SetArchived" | ||
| TransitionEndChat Transition = "EndChat" |
There was a problem hiding this comment.
is this transition absolutely necessary considering we are intending to archive the chat on chat_end
There was a problem hiding this comment.
EndChat transition violates a central assumption about the chat state machine: a state machine transition only changes the state of one chat. EndChat affects multiple chats at the same time
There was a problem hiding this comment.
EndChat is still needed because SetArchived cannot end active chats: it only allows idle/error states and preserves queue/ownership, while end_chat must end running chats, clear queue/worker/errors/deadlines, and synthesize tool cancellations. The single-transition-single-chat assumption is restored in 4b2efba: TransitionEndChat is validated and applied strictly per chat (applyEndChat touches only tx.chatID), and the multi-chat behavior moved to the named EndChatFamily orchestrator, which dispatches that single-chat transition per descendant inside the shared outer transaction.
Posted by Mux on behalf of Mike.
| ALTER TABLE chat_queued_messages | ||
| ADD COLUMN turn_id uuid, | ||
| ADD COLUMN hook_prefix jsonb, | ||
| ADD COLUMN hook_allowed_tools jsonb; |
There was a problem hiding this comment.
Could we store this as a dedicated table instead? cc @spikecurtis
76c6ef6 to
73e757f
Compare
|
@codex review |
|
@codex review
|
|
Codex Review: Something went wrong. Try again later by commenting “@codex review”. ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
…subagent spawn Reuse textFromParts instead of the duplicate promptText helper, move UserPromptDeniedError next to its producer, inline the four trivial per-event dispatch wrappers into dispatchLifecycleHook calls, and derive the subagent fallback title once after hooks settle the final prompt.
… and family orchestration Rename Tx.EndChat to Tx.EndChatFamily to make explicit that it orchestrates the single-chat EndChat transition across the addressed chat and its descendants. TransitionEndChat itself now only ever changes one chat: applyEndChat archives exactly one chat, and endDescendantChats dispatches it per descendant through nested machines sharing the outer transaction and publish buffer.
|
@codex review
|
7424289 to
874d219
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 874d2198f8
ℹ️ 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".
| return s.finishSessionStartEnd(ctx, input, result) | ||
| } | ||
| input.HistoryVersion = result.Chat.HistoryVersion | ||
| continue |
There was a problem hiding this comment.
Return after applying session_start effects
When a session_start hook returns a user-visible or model-context message, applySessionStartResponse commits those rows and publishes a chat:update with a new history_version. The runner treats that history change as new work, cancels the active task, and spawns another generation task keyed to the new history; this task then mutates input.HistoryVersion and loops anyway, so both tasks can race until the generation-attempt fence aborts one. This can issue duplicate model/tool work for every session_start response that changes history; after applying the hook effects, let the runner-spawned task own the new history instead of continuing here.
Useful? React with 👍 / 👎.
01ed9a1 to
fb591a8
Compare
|
Closing in favor of a restacked, smaller-PR version of this change. The tree at the top of the new stack is identical to this PR's final state (modulo folding the two migrations into a single New stack (bottom to top):
All review feedback resolved here is incorporated in the new stack.
|
Stack Context
Second of a 2-PR stack adding chat lifecycle hook webhooks. #27401 landed the standalone backend (dispatcher package
coderd/x/chathooks,codersdk/agenthookswire contract,chat_hook_dispatchestable, config knobs, docs). This PR activates it.What?
Wires lifecycle hook dispatch into chatd and the chat API, letting one globally configured external HTTP consumer observe and control chat agent lifecycles across seven events:
session_start,user_prompt_submit,pre_tool_use,post_tool_use,stop,pre_compact, andpost_compact.Requires both the hook configuration and the
agent-lifecycle-hooksexperiment (CODER_EXPERIMENTS=agent-lifecycle-hooks, excluded from*), all introduced in #27401.How it works
Responses can deny or rewrite prompts and tool inputs, inject model context, surface user notices, restrict the tool set, end a chat, or request one continuation nudge per turn. Dispatch is fail-closed: hook outages surface as HTTP 502 on gated API requests or move the chat to an error state, and no gated action proceeds unreviewed.
chatd.Newalso refuses an injected dispatcher without the experiment.user_prompt_submitgates sends, queued sends, edits, and chat creation before persistence;pre_tool_userewrites or denies tool calls before the assistant step persists;post_tool_useruns before tool results commit and its fail-closed error lands atomically with the step;stopsupports a single continuation nudge; compaction hooks dispatch before the compaction commit so their effects land atomically with the step.chats.hook_allowed_tools(migration 000552) persists tool restrictions and intersects with future tool exposure, and an active policy injects a system notice so the model does not fabricate calls to stripped tools;chat_messages.turn_ididentifies the user message that started each turn. Send and edit responses return every inserted message (hook notices included) so client caches never miss lower-ID rows.NOTE: Migration 000552 adds
chats.hook_allowed_tools,chat_messages.turn_id, andchat_queued_messages.{turn_id,hook_prefix,hook_allowed_tools}, and recreates thechats_expandedview. Thechat_hook_dispatchestable landed in #27401.