Skip to content
317 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides1//! The webhooks service: events, delivered to the addresses a repository or
2//! a workspace registers.
3//!
4//! Every event g1t publishes can be delivered: an HTTPS `POST` of JSON,
5//! signed with the webhook's secret in `X-G1t-Signature-256`, and retried
6//! with growing waits when the receiver does not answer with a 2xx. Each
7//! delivery is kept, with what was sent and what came back, and can be sent
8//! again.
9//!
10//! Mirrors `packages/contracts/src/webhooks.ts`.
11
12use serde::{Deserialize, Serialize};
13
14use crate::repos::RepoPath;
15use crate::{User, Viewer};
16
17/// Every event a webhook can be sent, in the order people are shown them.
Merge branch 'worktree-agent-a6ec0a89d7fe7a9ae'18pub const EVENT_TYPES: [&str; 118] = [
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides19 "git.push",
Merge branch 'worktree-agent-a8b78c62188153aa3' into claude/cool-newton-7nm8r920 "git.deleted",
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides21 "branch.renamed",
22 "repo.created",
23 "repo.forked",
24 "repo.updated",
25 "repo.visibility_changed",
26 "repo.renamed",
27 "repo.transferred",
28 "repo.default_branch_changed",
29 "repo.archived",
30 "repo.unarchived",
31 "repo.deleted",
32 "repo.restored",
33 "repo.purged",
34 "repo.collaborator_added",
35 "repo.collaborator_removed",
36 "repo.collaborator_role_changed",
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar37 "team.created",
38 "team.edited",
39 "team.deleted",
40 "team.member_added",
41 "team.member_role_changed",
42 "team.member_removed",
43 "team.repo_added",
44 "team.repo_role_changed",
45 "team.repo_removed",
@g1t welcomes you when you join a workspace, and that is the one introduction any agent makes. Identity now says when a person becomes a member, workspace.member_joined, by invitation, by an invite code, or by creating the workspace, and webhooks can hear it; the agents service hears it too, and for anyone who joined, never the creator, the workspace's @g1t opens your direct message and says in its own voice who it is, what to ask it, how agents answer, and where things are, once per person per workspace and charged to @g1t like any reply, with a fixed welcome when no model can be used. The agents, workspaces, authentication and webhooks guides say so.46 "workspace.member_joined",
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge47 "ruleset.created",
48 "ruleset.updated",
49 "ruleset.deleted",
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides50 "issue.opened",
51 "issue.updated",
52 "issue.assigned",
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar53 "issue.labeled",
54 "issue.unlabeled",
55 "issue.milestoned",
56 "issue.demilestoned",
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides57 "issue.closed",
58 "issue.reopened",
59 "comment.created",
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts60 "comment.edited",
61 "comment.deleted",
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides62 "pull.opened",
63 "pull.ready",
64 "pull.updated",
65 "pull.merge_requested",
66 "pull.merged",
67 "pull.closed",
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts68 "pull.reopened",
69 "pull.converted_to_draft",
Events: review requests, assignments, stops and deployments are published70 "pull.assigned",
71 "pull.review_requested",
72 "pull.review_request_removed",
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar73 "pull.labeled",
74 "pull.unlabeled",
75 "pull.milestoned",
76 "pull.demilestoned",
77 "pull.base_changed",
Events: review requests, assignments, stops and deployments are published78 "pull.stalled",
79 "pull.resumed",
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts80 "release.created",
81 "release.published",
82 "release.released",
83 "release.prereleased",
84 "release.edited",
85 "release.unpublished",
86 "release.deleted",
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides87 "checks.completed",
Merge checks: statuses and check runs on every commit88 "status.created",
89 "check_run.created",
90 "check_run.completed",
91 "check_run.rerequested",
92 "check_run.requested_action",
93 "check_suite.completed",
94 "check_suite.rerequested",
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides95 "workflow.completed",
Events: review requests, assignments, stops and deployments are published96 "deployment.succeeded",
97 "deployment.failed",
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb9798 "deployment.created",
99 "deployment_status.created",
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides100 "queue.changed",
Merge branch 'worktree-agent-ab8033ab973f8d99a'101 "integration.failed",
102 "integration.passed",
103 "decision.recorded",
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides104 "session.appended",
Merge branch 'worktree-agent-a6ec0a89d7fe7a9ae'105 "session.started",
106 "session.ended",
107 "work.claimed",
108 "work.released",
109 "work.claim_expired",
110 "work.handoff_requested",
111 "dependency.changed",
112 "overlap.detected",
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides113 "package.published",
114 "package.version_deleted",
115 "package.deleted",
116 "package.visibility_changed",
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar117 "secret_scanning_alert.created",
118 "secret_scanning_alert.fixed",
119 "secret_scanning_alert.dismissed",
120 "secret_scanning_alert.reopened",
121 "secret_scanning.bypass_requested",
122 "secret_scanning.bypass_reviewed",
123 "code_scanning_alert.created",
124 "code_scanning_alert.fixed",
125 "code_scanning_alert.dismissed",
126 "code_scanning_alert.reopened",
127 "vulnerability_alert.created",
128 "vulnerability_alert.fixed",
129 "vulnerability_alert.dismissed",
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9130 "secret_scanning_alert.summary",
131 "code_scanning_alert.summary",
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar132 "vulnerability_alert.reopened",
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9133 "vulnerability_alert.summary",
134 "security_advisory.published",
135 "security_advisory.withdrawn",
136 "security_advisory.reported",
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides137];
138
The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens.139/// Event types webhooks were once offered that g1t no longer sends: they
140/// came from g1t's own agents, since removed. A webhook saved with one
141/// still reads, and saving its list again leaves them out.
142pub const RETIRED_EVENT_TYPES: [&str; 2] = ["agent.asked", "review.completed"];
143
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides144/// What a webhook belongs to.
145#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
146#[serde(rename_all = "snake_case")]
147pub enum HookScope {
148 /// One repository's events.
149 Repo,
150 /// The events of every repository in a workspace.
151 Workspace,
152}
153
154#[derive(Clone, Debug, Serialize, Deserialize)]
155#[serde(rename_all = "camelCase")]
156pub struct Hook {
157 pub id: String,
158 pub scope: HookScope,
159 pub workspace: String,
160 /// For a repository's webhook: `owner/name`.
161 pub repo: Option<String>,
162 pub url: String,
163 /// The event types it is sent, or `["*"]` for all.
164 pub events: Vec<String>,
165 pub active: bool,
166 /// The last four characters of its secret.
167 pub secret_hint: String,
168 pub created_by: String,
169 /// RFC 3339.
170 pub created_at: String,
171 /// How its latest delivery went: `delivered`, `pending` or `failed`.
172 pub last_status: Option<String>,
173 pub last_delivered_at: Option<String>,
174}
175
176/// One event sent, or being sent, to a webhook.
177#[derive(Clone, Debug, Serialize, Deserialize)]
178#[serde(rename_all = "camelCase")]
179pub struct HookDelivery {
180 pub id: String,
181 pub hook_id: String,
182 /// The event's id, or empty for a ping.
183 pub event_id: String,
184 pub event: String,
185 /// `pending` while it will be tried again, `delivered`, or `failed` once
186 /// it has been tried as often as it will be.
187 pub status: String,
188 pub attempts: u32,
189 /// The receiver's HTTP status, the last time it answered.
190 pub response_status: Option<u16>,
191 /// The start of what it answered.
192 pub response_body: Option<String>,
193 /// Why the last attempt failed, when the receiver could not be reached.
194 pub error: Option<String>,
195 pub duration_ms: Option<u32>,
196 /// The JSON that was sent.
197 pub payload: String,
198 /// RFC 3339.
199 pub created_at: String,
200 pub delivered_at: Option<String>,
201 pub next_attempt_at: Option<String>,
202}
203
204/// Which webhooks a call is about: a repository's, or with `repo` left out,
205/// the workspace's own.
206#[derive(Clone, Debug, Serialize, Deserialize)]
207pub struct HookOwner {
208 pub workspace: String,
209 #[serde(default)]
210 pub repo: Option<RepoPath>,
211}
212
213/// `list`. Returns `Outcome<Vec<Hook>>`. Members of the workspace only.
214#[derive(Debug, Serialize, Deserialize)]
215pub struct ListArgs {
216 pub viewer: Viewer,
217 #[serde(flatten)]
218 pub owner: HookOwner,
219}
220
221/// `create`. Returns `Outcome<CreatedHook>`. Members, for a repository's
222/// webhooks; owners, for the workspace's.
223#[derive(Debug, Serialize, Deserialize)]
224pub struct CreateArgs {
225 pub actor: User,
226 #[serde(flatten)]
227 pub owner: HookOwner,
228 pub url: String,
229 /// Event types, or `["*"]` for all. All when empty.
230 #[serde(default)]
231 pub events: Vec<String>,
232 /// Made by g1t when left out.
233 #[serde(default)]
234 pub secret: Option<String>,
235}
236
237#[derive(Clone, Debug, Serialize, Deserialize)]
238pub struct CreatedHook {
239 pub hook: Hook,
240 /// The secret, when g1t made it: shown this once.
241 pub secret: Option<String>,
242}
243
244/// `update`: only the fields given change. Returns `Outcome<Hook>`.
245#[derive(Debug, Serialize, Deserialize)]
246pub struct UpdateArgs {
247 pub actor: User,
248 #[serde(flatten)]
249 pub owner: HookOwner,
250 pub id: String,
251 #[serde(default)]
252 pub url: Option<String>,
253 #[serde(default)]
254 pub events: Option<Vec<String>>,
255 #[serde(default)]
256 pub active: Option<bool>,
257}
258
259/// `delete` (returns `Outcome<bool>`) and `ping` (sends a `ping` event and
260/// returns `Outcome<HookDelivery>`).
261#[derive(Debug, Serialize, Deserialize)]
262pub struct HookArgs {
263 pub actor: User,
264 #[serde(flatten)]
265 pub owner: HookOwner,
266 pub id: String,
267}
268
269/// `deliveries`: a webhook's latest deliveries, newest first. Returns
270/// `Outcome<Vec<HookDelivery>>`.
271#[derive(Debug, Serialize, Deserialize)]
272pub struct DeliveriesArgs {
273 pub viewer: Viewer,
274 #[serde(flatten)]
275 pub owner: HookOwner,
276 pub id: String,
277}
278
279/// `redeliver`: sends a delivery's payload again, as a new delivery.
280/// Returns `Outcome<HookDelivery>`.
281#[derive(Debug, Serialize, Deserialize)]
282#[serde(rename_all = "camelCase")]
283pub struct RedeliverArgs {
284 pub actor: User,
285 #[serde(flatten)]
286 pub owner: HookOwner,
287 pub delivery_id: String,
288}
289
290#[cfg(test)]
291mod tests {
The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens.292 use super::{EVENT_TYPES, RETIRED_EVENT_TYPES};
293
294 #[test]
295 fn retired_events_are_not_offered() {
296 for kind in RETIRED_EVENT_TYPES {
297 assert!(!EVENT_TYPES.contains(&kind), "{kind}");
298 }
299 }
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides300
301 /// The TypeScript mirror lists the same events, in the same order.
302 #[test]
303 fn the_typescript_mirror_lists_the_same_events() {
304 let ts = include_str!("../../../packages/contracts/src/webhooks.ts");
305 let list = ts
306 .split_once("export const EVENT_TYPES = [")
307 .and_then(|(_, rest)| rest.split_once("] as const"))
308 .map(|(list, _)| list)
309 .expect("EVENT_TYPES in webhooks.ts");
310 let mirrored: Vec<&str> = list
311 .split(',')
312 .map(|item| item.trim().trim_matches('"'))
313 .filter(|item| !item.is_empty())
314 .collect();
315 assert_eq!(mirrored, EVENT_TYPES);
316 }
317}

This file's history is long; its oldest lines are credited to the oldest commit read.