Skip to content

Add hooks implementation and wire up to notify#9691

Merged
gt-oai merged 1 commit intomainfrom
gt/hooks-1
Feb 5, 2026
Merged

Add hooks implementation and wire up to notify#9691
gt-oai merged 1 commit intomainfrom
gt/hooks-1

Conversation

@gt-oai
Copy link
Contributor

@gt-oai gt-oai commented Jan 22, 2026

This introduces a Hooks service. It registers hooks from config and dispatches hook events at runtime.

N.B. The hook config is not wired up to this yet. But for legacy reasons, we wire up notify from config and power it using hooks now. Nothing about the notify interface has changed.

I'd start by reviewing hooks/types.rs

Some things to note:

  • hook names subject to change
  • no hook result yet
  • stopping semantics yet to be introduced
  • additional hooks yet to be introduced

@gt-oai gt-oai force-pushed the gt/hooks-1 branch 11 times, most recently from eb4aa5d to 917a82c Compare January 27, 2026 00:12
@gt-oai gt-oai marked this pull request as ready for review January 27, 2026 00:29
@jif-oai
Copy link
Collaborator

jif-oai commented Jan 27, 2026

@codex review

Copy link
Contributor

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

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: 917a82c82a

ℹ️ 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 +60 to +62
command
.current_dir(&payload.cwd)
.stdin(Stdio::null())
Copy link
Contributor

Choose a reason for hiding this comment

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

P2 Badge Avoid changing notify command resolution to turn cwd

Setting current_dir to the turn’s cwd changes how relative notify commands are resolved. If a user configures notify = ["./scripts/notify.sh"] (relative to the directory where Codex was started), this now resolves relative to the per-turn working directory instead, so the hook can fail with “No such file or directory” after the user switches to a different repo or deletes the cwd. The legacy notifier didn’t change current_dir, so this is a behavior regression that breaks existing setups depending on relative paths.

Useful? React with 👍 / 👎.

return;
};

for hook in hooks {
Copy link
Collaborator

Choose a reason for hiding this comment

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

The order matter so we should probably document it at some point

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will document in the docs

pub(crate) fn new(config: &Config) -> Self {
let after_agent_hooks = get_notify_hook(config).into_iter().collect();
Self {
hooks: HashMap::from([(HookEventKind::AfterAgent, after_agent_hooks)]),
Copy link
Collaborator

Choose a reason for hiding this comment

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

We have a finite set of possible hooks (after/before agent, after/before session, ...). Instead of using a HashMap with a finite enum, should we have a well defined structure? It would make this easier to read and to document

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed the map and replaced with strongly-typed struct field.

@gt-oai gt-oai force-pushed the gt/hooks-1 branch 7 times, most recently from 2d6eb8d to ec59a3d Compare February 5, 2026 12:31
last_assistant_message: last_agent_message.clone(),
});
sess.hooks()
.dispatch(crate::hooks::HookPayload {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe we should think about a timeout on those hooks. Otherwise a badly configured hook will prevent the turn from finishing. Ideally we can make it configurable

Can be in a follow-up PR

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yup timeout is in the design doc

pub(crate) async fn dispatch(&self, hook_payload: HookPayload) {
// TODO(gt): support interrupting program execution by returning a result here.
for hook in self.hooks_for_event(&hook_payload.hook_event) {
let outcome = hook.execute(&hook_payload).await;
Copy link
Collaborator

Choose a reason for hiding this comment

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

It would be cool to measure and log hook failures

// TODO(gt): support interrupting program execution by returning a result here.
for hook in self.hooks_for_event(&hook_payload.hook_event) {
let outcome = hook.execute(&hook_payload).await;
if matches!(outcome, HookOutcome::Stop) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I guess it's on purpose that the Stop does not actually stop anything but the dispatch of the other hooks?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

not right now, will propagate the error up in future PRs

@gt-oai gt-oai merged commit 3b54fd7 into main Feb 5, 2026
51 of 53 checks passed
@gt-oai gt-oai deleted the gt/hooks-1 branch February 5, 2026 16:49
@github-actions github-actions bot locked and limited conversation to collaborators Feb 5, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants