Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
609e842
feat: wire chat lifecycle hooks into chatd
ibetitsmike Jul 23, 2026
4873805
fix(codersdk): skip chat hook timeout validation when no hook URL is set
ibetitsmike Jul 23, 2026
7c287b0
refactor(coderd/x/chatd): use FinishError for idle hook dispatch fail…
ibetitsmike Jul 23, 2026
d67b92f
refactor(coderd/x/chatd): rewrite tool-call inputs through Tx.UpdateM…
ibetitsmike Jul 23, 2026
cbc1ef6
fix(coderd/x/chatd/chatprompt): keep tool results adjacent to their c…
ibetitsmike Jul 23, 2026
1bda0b9
refactor(coderd/x/chatd): decode hook payloads directly in tests
ibetitsmike Jul 23, 2026
b552849
refactor: dispatch a fresh pre_tool_use hook for every tool call
ibetitsmike Jul 23, 2026
e623cbb
fix(coderd/x/chatd): fail the turn when subagent spawn hook dispatch …
ibetitsmike Jul 23, 2026
fe74536
refactor(coderd/x/chatd): route all lifecycle hook dispatch through o…
ibetitsmike Jul 23, 2026
2ca1287
refactor(coderd/x/chatd): remove redundant hook comment
ibetitsmike Jul 24, 2026
4417f45
style(coderd/x/chatd): trim redundant hook comments
ibetitsmike Jul 24, 2026
1ae7ad4
style: remove redundant hook comments
ibetitsmike Jul 24, 2026
cd05821
refactor: adopt coderd/x/hooks import paths
ibetitsmike Jul 24, 2026
be688da
refactor(coderd): deduplicate chat hook error handling
ibetitsmike Jul 24, 2026
10a7fec
style: use errors.AsType for hook error matching and rewrap comments
ibetitsmike Jul 24, 2026
3193c91
fix(coderd/x/chatd): make hook tool denials self-explanatory to the m…
ibetitsmike Jul 24, 2026
75ec5cb
refactor(coderd/x/chatd): extract hook integration into chathooks sub…
ibetitsmike Jul 24, 2026
84addb9
refactor: adopt codersdk/x/agenthooks import paths
ibetitsmike Jul 24, 2026
fa905f0
chore: hide experimental chat lifecycle hook flags from server help
ibetitsmike Jul 24, 2026
18dbe0d
fix(coderd/x/chatd): clarify that denied tool calls were blocked by e…
ibetitsmike Jul 24, 2026
7aeed00
fix(coderd/x/chatd/chathooks): state that denied tool calls were neve…
ibetitsmike Jul 24, 2026
e818587
docs: align chat lifecycle hook docs with dispatch behavior
ibetitsmike Jul 24, 2026
97c8667
fix: keep denied pre_tool_use hook context out of the client transcript
ibetitsmike Jul 24, 2026
f1398a3
refactor(coderd/x/chatd): reuse chatstate tool-result validation in t…
ibetitsmike Jul 25, 2026
240c487
docs(docs/admin/setup): frame the chat lifecycle hooks page as a how-to
ibetitsmike Jul 25, 2026
04df968
fix(coderd/x/chatd): keep exclusive-tool batches rejected when a hook…
ibetitsmike Jul 25, 2026
60a2985
docs(docs/admin/setup): describe hook delivery as best-effort
ibetitsmike Jul 25, 2026
4a85e8f
docs(coderd/x/chatd): describe hook delivery as best-effort
ibetitsmike Jul 25, 2026
a3a659a
fix(coderd/x/chatd): ignore blank stop-hook model context
ibetitsmike Jul 25, 2026
f02e3f0
docs(docs/admin/setup): document the configured hook audience
ibetitsmike Jul 25, 2026
c4d01ef
fix(coderd/x/chatd): commit tool results when a spawn hook dispatch f…
ibetitsmike Jul 25, 2026
0b18505
docs(coderd/x/chatd): document that post_compact effects can resume g…
ibetitsmike Jul 25, 2026
90397ca
fix(coderd/x/chatd): reject ambiguous built-in tool input
ibetitsmike Jul 27, 2026
dd22942
docs(docs/admin/setup): document built-in tool input validation
ibetitsmike Jul 27, 2026
bf3fb4d
fix(coderd/x/chatd): admit tool calls before the step is persisted
ibetitsmike Jul 27, 2026
8c47067
style(coderd/x): simplify chat hook admission cleanup
ibetitsmike Jul 27, 2026
f5a680f
fix(coderd/x/chatd): reject invalid tool input JSON before hook dispatch
ibetitsmike Jul 27, 2026
284daa3
fix(coderd/x/chatd): reject duplicate tool-use IDs before filtering t…
ibetitsmike Jul 27, 2026
e5ece2b
feat: classify chat lifecycle hook denials with a hook_denied kind
ibetitsmike Jul 27, 2026
ba0fd22
test(coderd/x/chatd): drop usage-limit cases from hook admission pref…
ibetitsmike Jul 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions cli/testdata/server-config.yaml.golden
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,17 @@ chat:
# opt-in settings.
# (default: false, type: bool)
debugLoggingEnabled: false
# HTTPS URL to receive chat agent lifecycle hook events. Hooks are disabled when
# unset. Requires the agent-lifecycle-hooks experiment.
# (default: <unset>, type: url)
hookURL:
# Maximum time to wait for a chat agent lifecycle hook response.
# (default: 1.5s, type: duration)
hookTimeout: 1.5s
# Whether to dispatch chat agent lifecycle hooks when a hook URL is configured.
# Requires the agent-lifecycle-hooks experiment.
# (default: true, type: bool)
hookEnabled: true
# Deprecated: AI Gateway routing is now the only routing path. Setting this value
# has no effect. This option will be removed in a future release.
# (default: true, type: bool)
Expand Down
59 changes: 52 additions & 7 deletions coderd/apidoc/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

59 changes: 52 additions & 7 deletions coderd/apidoc/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions coderd/coderd.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ import (
"github.com/coder/coder/v2/coderd/workspacestats"
"github.com/coder/coder/v2/coderd/wsbuilder"
"github.com/coder/coder/v2/coderd/wsbuildorchestrator"
"github.com/coder/coder/v2/coderd/x/agenthooks/dispatch"
"github.com/coder/coder/v2/coderd/x/chatd"
"github.com/coder/coder/v2/coderd/x/chatd/chatprovider"
"github.com/coder/coder/v2/coderd/x/chatd/mcpclient"
Expand Down Expand Up @@ -879,6 +880,27 @@ func New(options *Options) *API {
// the chat daemon stays nil and chat HTTP handlers return a
// service-unavailable error with a clear remediation message.
if options.DeploymentValues.AI.BridgeConfig.Enabled.Value() {
var hookDispatcher *dispatch.Dispatcher
chatConfig := options.DeploymentValues.AI.Chat
hooksConfigured := chatConfig.HookURL.String() != "" && chatConfig.HookEnabled.Value()
Comment thread
ibetitsmike marked this conversation as resolved.
hooksExperimentEnabled := experiments.Enabled(codersdk.ExperimentAgentLifecycleHooks)
if hooksConfigured && !hooksExperimentEnabled {
options.Logger.Warn(ctx, "chat lifecycle hooks are configured but inactive; enable the agent-lifecycle-hooks experiment to activate them",
slog.F("experiment", codersdk.ExperimentAgentLifecycleHooks),
)
}
if hooksConfigured && hooksExperimentEnabled {
hookDispatcher = dispatch.New(
options.Logger,
nil,
chatConfig.HookURL.String(),
chatConfig.HookSecret.Value(),
chatConfig.HookTimeout.Value(),
api.DeploymentID,
buildinfo.Version(),
options.PrometheusRegistry,
)
}
api.chatDaemon = chatd.New(options.Pubsub, chatd.Config{
Logger: options.Logger.Named("chatd"),
Database: options.Database,
Expand All @@ -898,6 +920,7 @@ func New(options *Options) *API {
StartWorkspace: api.chatStartWorkspace,
StopWorkspace: api.chatStopWorkspace,
WebpushDispatcher: options.WebPushDispatcher,
HookDispatcher: hookDispatcher,
UsageTracker: options.WorkspaceUsageTracker,
PrometheusRegistry: options.PrometheusRegistry,
OIDCTokenSource: oidcMCPSrc,
Expand Down
10 changes: 8 additions & 2 deletions coderd/database/db2sdk/db2sdk.go
Original file line number Diff line number Diff line change
Expand Up @@ -1641,11 +1641,17 @@ func chatMessageParts(m database.ChatMessage) ([]codersdk.ChatMessagePart, error
if err != nil {
return nil, err
}
// Strip internal-only fields before API responses.
// Strip internal-only fields before API responses. Hook context
// parts are model-only and must never reach clients.
filtered := parts[:0]
for i := range parts {
if parts[i].Type == codersdk.ChatMessagePartTypeHookContext {
continue
}
parts[i].StripInternal()
filtered = append(filtered, parts[i])
}
return parts, nil
return filtered, nil
}

func nullUUIDPtr(v uuid.NullUUID) *uuid.UUID {
Expand Down
Loading
Loading