-
Notifications
You must be signed in to change notification settings - Fork 66.8k
Document agentStop, subagentStop, and preCompact hooks for Copilot CLI #43876
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -257,6 +257,158 @@ if [ "$RESULT_TYPE" = "failure" ]; then | |||||
| fi | ||||||
| ``` | ||||||
|
|
||||||
| ### Agent stop hook | ||||||
|
|
||||||
| Executed when the main agent finishes responding to a prompt and is about to stop. Use this hook to log session completion or to inject a follow-up instruction by blocking the stop. When you block, the `reason` you provide is enqueued as the next user prompt, so the agent continues with that input. | ||||||
|
|
||||||
| **Example input JSON:** | ||||||
|
|
||||||
| ```json copy | ||||||
| { | ||||||
| "timestamp": 1704614750000, | ||||||
| "cwd": "/path/to/project", | ||||||
| "sessionId": "01HW2X3Y4Z5...", | ||||||
| "transcriptPath": "/path/to/transcript.jsonl", | ||||||
| "stopReason": "end_turn" | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| **Fields:** | ||||||
|
|
||||||
| * `timestamp`: Unix timestamp in milliseconds | ||||||
| * `cwd`: Current working directory | ||||||
| * `sessionId`: The unique identifier of the current session | ||||||
| * `transcriptPath`: Path to the JSONL transcript file for the session | ||||||
| * `stopReason`: Why the agent is stopping (currently always `"end_turn"`) | ||||||
|
|
||||||
| **Output JSON (optional):** | ||||||
|
|
||||||
| ```json copy | ||||||
| { | ||||||
| "decision": "block", | ||||||
| "reason": "Run the test suite before stopping." | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| **Output fields:** | ||||||
|
|
||||||
| * `decision`: Set to `"block"` to keep the agent running by enqueueing `reason` as the next user prompt. Omit the field, or return `{}`, to allow the stop. | ||||||
| * `reason`: The text to feed back into the agent as a new prompt when blocking. Required when `decision` is `"block"`. | ||||||
|
|
||||||
| **Example script that asks the agent to summarize before stopping:** | ||||||
|
|
||||||
| ```shell copy | ||||||
| #!/bin/bash | ||||||
| INPUT=$(cat) | ||||||
|
|
||||||
| # Avoid an infinite loop: only inject a follow-up if no summary marker exists yet | ||||||
| TRANSCRIPT=$(echo "$INPUT" | jq -r '.transcriptPath') | ||||||
| if [ -f "$TRANSCRIPT" ] && grep -q "## Session summary" "$TRANSCRIPT"; then | ||||||
| echo "{}" | ||||||
| exit 0 | ||||||
| fi | ||||||
|
|
||||||
| echo '{"decision":"block","reason":"Before you stop, write a one-paragraph session summary under a `## Session summary` heading."}' | ||||||
| ``` | ||||||
|
|
||||||
| ### Subagent stop hook | ||||||
|
|
||||||
| Executed when a subagent finishes its turn, before its output is returned to the parent agent. Use this hook to log subagent activity or to keep the subagent running by injecting follow-up instructions. | ||||||
|
|
||||||
| **Example input JSON:** | ||||||
|
|
||||||
| ```json copy | ||||||
| { | ||||||
| "timestamp": 1704614760000, | ||||||
| "cwd": "/path/to/project", | ||||||
| "sessionId": "01HW2X3Y4Z5...", | ||||||
| "transcriptPath": "/path/to/subagent-transcript.jsonl", | ||||||
| "agentName": "researcher", | ||||||
| "agentDisplayName": "Research Agent", | ||||||
| "stopReason": "end_turn" | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| **Fields:** | ||||||
|
|
||||||
| * `timestamp`: Unix timestamp in milliseconds | ||||||
| * `cwd`: Current working directory | ||||||
| * `sessionId`: The unique identifier of the subagent session | ||||||
| * `transcriptPath`: Path to the JSONL transcript file for the subagent | ||||||
| * `agentName`: The internal name of the subagent | ||||||
| * `agentDisplayName`: The human-readable display name of the subagent | ||||||
|
||||||
| * `agentDisplayName`: The human-readable display name of the subagent | |
| * `agentDisplayName` (optional): The human-readable display name of the subagent. This field is omitted for some subagents. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion. I checked three surfaces in the @github/copilot v1.0.32 bundle and they all agree this field is required:
- The CLI runtime schema marks it as
Se().describe("Human-readable display name of the sub-agent")(no.optional()). - The exported SDK types (
@github/copilot/sdk) declare it asagentDisplayName: z.ZodStringand the inferred TypeScript type isagentDisplayName: string(notstring | undefined). - The public JSON Schema (
schemas/session-events.schema.json) listsagentDisplayNamein therequiredarray for bothsubagent.startedandsubagent.completedevent data.
For comparison, sibling fields like model and durationMs are explicitly marked optional in all three surfaces, so the absence of optional markers on agentDisplayName is intentional. Going to leave the description as is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The intro implies
agentStopindicates “session completion”, but this hook fires when the main agent finishes a turn (the session can continue on the next prompt). Consider rephrasing to avoid conflating turn end with session end (for session completion,sessionEndis the right hook).