Skip to content
700 linesCodeBlameRaw
1/**
2 * Every state change in g1t is published as an event. Services react to
3 * each other through events rather than direct calls, and the same stream
4 * feeds timelines, webhooks and workflows.
5 *
6 * The envelope follows CloudEvents: `type` says what happened, `subject`
7 * says to what, `data` is the type-specific payload.
8 */
9
10import type { Release } from "./about";
11import type { RepoRole } from "./access";
12import type { CheckRunEventData, CheckSuiteEventData, StatusEventData } from "./checks";
13import type { RepoMirror } from "./mirrors";
14import type { DeploymentStatus, RepoDeployment } from "./deployments";
15import type { TeamRole, TeamVisibility } from "./teams";
16import type { AgentRef, Confidence, Verdict } from "./work";
17
18/** What every `package.*` event names. */
19export type PackageEventData = {
20 packageId: string;
21 workspace: string;
22 ecosystem: string;
23 name: string;
24 repoId: string | null;
25};
26
27/** What `deployment.succeeded` and `deployment.failed` carry. */
28export type DeploymentEventData = {
29 deploymentId: string;
30 projectId: string;
31 repoId: string;
32 workspace: string;
33 project: string;
34 kind: "production" | "preview";
35 branch: string | null;
36 number: number | null;
37 commit: string;
38 path: string;
39 error: string | null;
40 recovered: boolean;
41 /** A username, or `g1t`. */
42 triggeredBy: string;
43};
44
45/** What every `release.*` event carries. */
46export type ReleaseEventData = {
47 releaseId: string;
48 repoId: string;
49 tagName: string;
50 release: Release;
51 /** On `release.edited`: what the title and notes were before. */
52 changes?: { name?: { from: string | null }; body?: { from: string } };
53 /** Set when a workflow job's token made the change: the run's id. */
54 causedByJob?: string;
55};
56
57/** The payload of the `repo.collaborator_*` events. */
58export type RepoCollaboratorData = {
59 repoId: string;
60 namespace: string;
61 name: string;
62 username: string;
63 role: RepoRole | null;
64 previousRole: RepoRole | null;
65};
66/** A team asked to review a pull request. */
67export type TeamRequested = { team: string; notified: string[]; assigned: string[] };
68
69/** The payload of the `team.*` events; each sets the fields that apply. */
70/** How someone came to be a member of a workspace (`workspace.member_joined`). */
71export type JoinedHow = "invitation" | "created" | "joined";
72
73/** The payload of `workspace.member_joined`. Mirrors `MemberJoined` in events.rs. */
74export type MemberJoinedData = {
75 workspaceId: string;
76 /** The workspace's slug. */
77 workspace: string;
78 userId: string;
79 username: string;
80 role: "owner" | "member";
81 how: JoinedHow;
82};
83
84export type TeamChangedData = {
85 workspace: string;
86 teamId: string;
87 team: string;
88 name: string;
89 visibility: TeamVisibility | null;
90 parent?: string;
91 changes?: string[];
92 username?: string;
93 role?: TeamRole;
94 previousRole?: TeamRole;
95 repoId?: string;
96 repo?: string;
97 repoRole?: RepoRole;
98 previousRepoRole?: RepoRole;
99};
100
101export type EventPayloads = {
102 "repo.created": { repoId: string; namespace: string; name: string; isPrivate: boolean };
103 "repo.forked": { repoId: string; sourceRepoId: string; pullId: string };
104 /**
105 * A repository's description, topics or visibility changed.
106 * `visibilityChanged` says whether it went public or private, which
107 * `repo.visibility_changed` also announces on its own.
108 */
109 "repo.updated": { repoId: string; namespace: string; name: string; isPrivate: boolean; visibilityChanged: boolean };
110 "repo.visibility_changed": { repoId: string; isPrivate: boolean };
111 /**
112 * A repository's name changed within its workspace `namespace`, from
113 * `from` to `to`, keeping its id. A path change like `repo.transferred`:
114 * services move rows kept under its path to its *current* path (see
115 * `repoMove`, `currentMovedPath`).
116 */
117 "repo.renamed": { repoId: string; namespace: string; from: string; to: string };
118 /**
119 * A repository was deleted. It is hidden and git refuses it, but it can
120 * be restored until `purgeAfter`: services stop what runs for it and hide
121 * it, and keep their rows until `repo.purged`.
122 */
123 "repo.deleted": {
124 repoId: string;
125 namespace: string;
126 name: string;
127 isPrivate: boolean;
128 purgeAfter: string;
129 /** It went with its workspace (`workspace.deleting`); deployments leaves it to that. */
130 withWorkspace?: boolean;
131 };
132 /** A deleted repository is back, as it was. Services start again what they stopped. */
133 "repo.restored": {
134 repoId: string;
135 namespace: string;
136 name: string;
137 isPrivate: boolean;
138 /** It came back with its workspace (`workspace.restored`). */
139 withWorkspace?: boolean;
140 };
141 /**
142 * A deleted repository is gone for good, its git data with it. Services
143 * drop every row they keep for it, except a workspace's history (ledgers,
144 * invoices, the audit log).
145 */
146 "repo.purged": { repoId: string; namespace: string; name: string };
147 /**
148 * A repository was archived (read-only: pushes, merges, agents and
149 * workflows refused; issues and pull requests locked; deployments keep
150 * serving), or unarchived.
151 */
152 "repo.archived": { repoId: string; namespace: string; name: string; archived: true };
153 "repo.unarchived": { repoId: string; namespace: string; name: string; archived: false };
154 /** The default branch is now `to`; `renamed` when `from` was renamed to it. */
155 "repo.default_branch_changed": { repoId: string; from: string; to: string; renamed: boolean };
156 /** A branch was renamed. Pull requests from it follow. */
157 "branch.renamed": { repoId: string; from: string; to: string; defaultBranch: boolean };
158 /** An account was made, or changed what its profile shows. Ask identity for the profile. */
159 "user.updated": { username: string };
160 /**
161 * An account was deleted, by the person or by g1t's staff; staff can
162 * restore it until `purgeAfter`. Its sessions, tokens and keys have ended
163 * and it has left every workspace. Services stop what they do for it and
164 * keep their rows; `user.restored` undoes that, and `user.deleted` follows
165 * once `purgeAfter` passes.
166 */
167 "user.deleting": { userId: string; username: string; byStaff: boolean; purgeAfter: string };
168 /** Staff brought a deleted account back. Services undo what they did on `user.deleting`. */
169 "user.restored": { userId: string; username: string };
170 /**
171 * An account is gone for good. Services drop what they keep for it alone
172 * and show what it wrote as `ghost` (`GHOST_USERNAME`, `GHOST_ID`).
173 * Ledgers, invoices and audit logs keep its username, which is never given
174 * to anyone again.
175 */
176 "user.deleted": { userId: string; username: string };
177 /** A workspace was made, or its name, description or icon changed. */
178 "workspace.updated": { workspaceId: string; slug: string };
179 /**
180 * A person became a member of a workspace. `how` says by which door;
181 * the workspace's creator is `created`, so a listener that greets new
182 * members leaves them out (the agents service's @g1t welcomes everyone
183 * else, once). Never for an agent or a token.
184 */
185 "workspace.member_joined": MemberJoinedData;
186 /** Someone, or g1t staff, made an invite. Never the code or the address. */
187 "invite.created": { inviteId: string; inviterId: string | null; workspaceId: string | null; bound: boolean };
188 /** An invite was used: by a new account, or by an account joining a workspace. */
189 "invite.redeemed": {
190 inviteId: string;
191 userId: string;
192 inviterId: string | null;
193 workspaceId: string | null;
194 createdAccount: boolean;
195 };
196 /** Someone asked for access while registration is invite-only. The address is not in the event. */
197 "waitlist.requested": { entryId: string };
198 /**
199 * One branch moved by a push. `ref` is the full ref, `after` the commit it
200 * points to now, and `defaultBranch` whether it is the default branch.
201 */
202 /**
203 * `before` is where the ref pointed before; absent for a new branch or
204 * tag. `causedByJob` is set when a workflow job's token pushed: the run's id.
205 */
206 "git.push": {
207 repoId: string;
208 ref: string;
209 before?: string;
210 after: string;
211 defaultBranch: boolean;
212 causedByJob?: string;
213 /** Set when it was copied in from the remote a mirror follows, not made on g1t. */
214 mirrored?: boolean;
215 /** The repository's mirror state when it landed; absent for one that leads. */
216 mirror?: RepoMirror;
217 /**
218 * How many commits the push brought to the branch, along its
219 * first-parent line from `after` back to `before` (a merge commit is
220 * one), at most 50; one for a new branch. Absent when it was not
221 * counted: a tag, a branch deleted, a pull request landed, a push the
222 * store could not read back, or one from before this was recorded.
223 */
224 commits?: number;
225 };
226 /**
227 * `author` is who opened it: g1t, for one its agent filed while at work,
228 * with `requestedBy` the person it was working for. Every issue and pull
229 * request event carries both.
230 */
231 "issue.opened": {
232 issueId: string;
233 repoId: string;
234 number: number;
235 title: string;
236 author?: { id: string; username: string };
237 requestedBy?: { id: string; username: string };
238 };
239 "issue.updated": { issueId: string; repoId: string; number: number };
240 /** The people an issue is assigned to changed; `assignees` is the new set, `added` those newly assigned. */
241 "issue.assigned": { issueId: string; repoId: string; number: number; assignees: string[]; added?: string[] };
242 /** `resolvedBy` is the number of the pull request whose merge closed it. */
243 "issue.closed": {
244 issueId: string;
245 repoId: string;
246 number: number;
247 reason: "completed" | "not_planned";
248 resolvedBy?: number;
249 };
250 "issue.reopened": { issueId: string; repoId: string; number: number };
251 /**
252 * `issue` is the number of the issue the pull request is for. `author` is
253 * who opened it: g1t, for a change g1t made, with `requestedBy` the person
254 * who asked for it.
255 */
256 "pull.opened": {
257 pullId: string;
258 repoId: string;
259 number: number;
260 issue?: number;
261 agent: string;
262 author?: { id: string; username: string };
263 requestedBy?: { id: string; username: string };
264 };
265 /** `confidence`, on a g1t agent's change once g1t has worked it out, is on every pull request event. */
266 "pull.ready": { pullId: string; repoId: string; number: number; issue?: number; confidence?: Confidence };
267 /** A push moved the head of a pull request that is ready for review. */
268 "pull.updated": { pullId: string; repoId: string; number: number; issue?: number; commit: string; confidence?: Confidence };
269 /** A merge was asked for while the pull request was behind; it has to catch up first. */
270 "pull.merge_requested": { pullId: string; repoId: string; number: number; issue?: number; confidence?: Confidence };
271 "pull.closed": { pullId: string; repoId: string; number: number; issue?: number; confidence?: Confidence };
272 /** A closed pull request was opened again. `commit` is its head. */
273 "pull.reopened": { pullId: string; repoId: string; number: number; issue?: number; commit?: string; confidence?: Confidence };
274 /** A pull request that was ready for review was turned back into a draft. */
275 "pull.converted_to_draft": { pullId: string; repoId: string; number: number; issue?: number; confidence?: Confidence };
276 /** People were assigned to a pull request: `assignees` is the new set, `added` those newly assigned. */
277 "pull.assigned": { pullId: string; repoId: string; number: number; issue?: number; assignees: string[]; added: string[] };
278 /** Reviewers were asked for a pull request (`reviewers`), or no longer are. */
279 "pull.review_requested": {
280 pullId: string;
281 repoId: string;
282 number: number;
283 issue?: number;
284 reviewers?: string[];
285 /** Teams asked: `team` is `workspace/slug`, `notified` who is told, `assigned` who review assignment picked. */
286 teams?: TeamRequested[];
287 /** Asked because they own files it changes (its CODEOWNERS file). */
288 codeOwners?: boolean;
289 };
290 "pull.review_request_removed": {
291 pullId: string;
292 repoId: string;
293 number: number;
294 issue?: number;
295 reviewers?: string[];
296 teams?: TeamRequested[];
297 };
298 /** g1t stopped seeing a pull request through until a person steps in; `detail` says why. */
299 "pull.stalled": { pullId: string; repoId: string; number: number; issue?: number; detail: string };
300 /** A pull request g1t had stopped on is going again. */
301 "pull.resumed": { pullId: string; repoId: string; number: number; issue?: number };
302 /**
303 * The pull request's head or its target moved and the files both changed
304 * overlap: a sandbox should find out whether it still merges cleanly.
305 * `commit` is its head.
306 */
307 "pull.mergecheck": { pullId: string; repoId: string; number: number; issue?: number; commit: string };
308 /** Whether the pull request merges cleanly was settled. */
309 "pull.mergeability": { pullId: string; repoId: string; number: number; issue?: number };
310 /** Another agent asked the agent on a pull request, which was not at work, a question or handed it work. */
311 "agent.asked": { pullId: string; repoId: string; number: number; issue?: number };
312 "pull.merged": { pullId: string; repoId: string; number: number; issue?: number; commit: string; confidence?: Confidence };
313 /** A run of the acceptance checks finished. `commit` is what was checked. */
314 "checks.completed": {
315 pullId: string;
316 repoId: string;
317 number: number;
318 status: "passed" | "failed" | "errored";
319 commit: string;
320 };
321 /** A status was set on a commit through the API. */
322 "status.created": StatusEventData;
323 /** A check run was reported on a commit through the API, completed, asked to run again, or had one of its buttons pressed (`requestedAction`). */
324 "check_run.created": CheckRunEventData;
325 "check_run.completed": CheckRunEventData;
326 "check_run.rerequested": CheckRunEventData;
327 "check_run.requested_action": CheckRunEventData;
328 /** A reporter's check runs on a commit all completed, or it was asked to run them again. */
329 "check_suite.completed": CheckSuiteEventData;
330 "check_suite.rerequested": CheckSuiteEventData;
331 /** A g1t agent finished reviewing a pull request; no verdict if it could not. */
332 "review.completed": {
333 pullId: string;
334 repoId: string;
335 number: number;
336 verdict?: "approve" | "request_changes";
337 };
338 /** A person was given a role on one repository directly, had it changed, or lost it. `role` is null once removed. */
339 "repo.collaborator_added": RepoCollaboratorData;
340 "repo.collaborator_removed": RepoCollaboratorData;
341 "repo.collaborator_role_changed": RepoCollaboratorData;
342 /** A team of a workspace was created, changed (`changes` says what) or deleted. */
343 "team.created": TeamChangedData;
344 "team.edited": TeamChangedData;
345 "team.deleted": TeamChangedData;
346 /** Someone joined a team, had their role in it changed, or left it. */
347 "team.member_added": TeamChangedData;
348 "team.member_role_changed": TeamChangedData;
349 "team.member_removed": TeamChangedData;
350 /** A team was given a role on a repository, had it changed, or lost it. */
351 "team.repo_added": TeamChangedData;
352 "team.repo_role_changed": TeamChangedData;
353 "team.repo_removed": TeamChangedData;
354 /** A repository's merge queue gained, lost or settled an entry. */
355 "queue.changed": { repoId: string };
356 /** `number` is the issue or pull request commented on. */
357 "comment.created": {
358 commentId: string;
359 repoId: string;
360 number: number;
361 /** Set when the comment is on a pull request. */
362 pullId?: string;
363 /** Set when the comment is a review. */
364 verdict?: Verdict;
365 /**
366 * Set when one of the workspace's agents wrote it, as itself; the
367 * event's actor is then the person it acted for (`actingFor`).
368 */
369 agent?: AgentRef;
370 actingFor?: { id: string; username: string };
371 /** An agent's review: its verdict is advisory and counts toward nothing. */
372 advisory?: boolean;
373 };
374 /** A comment's text changed; `changes.body.from` is what it said before. */
375 "comment.edited": {
376 commentId: string;
377 repoId: string;
378 number: number;
379 /** Set when the comment is on a pull request. */
380 pullId?: string;
381 changes: { body: { from: string } };
382 };
383 /** A comment was deleted; `comment` is the comment as it was. */
384 "comment.deleted": {
385 commentId: string;
386 repoId: string;
387 number: number;
388 /** Set when the comment was on a pull request. */
389 pullId?: string;
390 comment: {
391 id: string;
392 body: string;
393 author: { id: string; username: string };
394 createdAt: string;
395 path: string | null;
396 line: number | null;
397 };
398 };
399 "session.appended": { pullId: string; repoId: string; number: number; count: number };
400 /**
401 * A build of a project finished, for production or one pull request's
402 * preview (`number`). `path` is the deployment's page on the site;
403 * `recovered`, on a success, says the build before it failed.
404 */
405 "deployment.succeeded": DeploymentEventData;
406 "deployment.failed": DeploymentEventData;
407 /**
408 * A deployment was made, wherever it runs: reported through the API, by
409 * a g1t Actions job with an `environment:`, or a g1t.page build. Its
410 * `payload` is left out: read the deployment for it.
411 */
412 "deployment.created": { repoId: string; deployment: Omit<RepoDeployment, "payload">; causedByJob?: string };
413 /**
414 * A deployment has a new status; `deployment` is as it is now.
415 * `causedByJob` (as on every event a workflow job's token causes) is the
416 * run whose job made it, so no workflow starts for it.
417 */
418 "deployment_status.created": {
419 repoId: string;
420 deployment: Omit<RepoDeployment, "payload">;
421 deploymentStatus: DeploymentStatus;
422 causedByJob?: string;
423 };
424 /**
425 * A release changed, one event per GitHub release activity it amounts
426 * to: made (`created`; a published one is also `published`, and
427 * `released` or `prereleased`), a draft published, edited, made a draft
428 * again (`unpublished`) or deleted. `release` is as it is now (as it was,
429 * for `release.deleted`).
430 */
431 "release.created": ReleaseEventData;
432 "release.published": ReleaseEventData;
433 "release.released": ReleaseEventData;
434 "release.prereleased": ReleaseEventData;
435 "release.edited": ReleaseEventData;
436 "release.unpublished": ReleaseEventData;
437 "release.deleted": ReleaseEventData;
438 /**
439 * A package version was published, such as an image pushed by
440 * `docker push`. `tags` are the tags that now point to it; `repoId` (and
441 * the event's) is the repository the package is linked to, if any.
442 */
443 "package.published": PackageEventData & { version: string; digest: string; size: number; tags: string[] };
444 /** One version of a package was deleted, with the tags that pointed to it. */
445 "package.version_deleted": PackageEventData & { version: string; digest: string };
446 /** A package was deleted with every version it had. */
447 "package.deleted": PackageEventData;
448 /** A package became public or private. */
449 "package.visibility_changed": PackageEventData & { visibility: "public" | "private" };
450 /**
451 * A workspace's slug changed from `from` to `to`. Services that store a
452 * slug move their rows to the workspace's *current* slug (see
453 * `currentWorkspaceSlug`), so a repeated or late delivery after a second
454 * rename still lands in the right place.
455 */
456 "workspace.renamed": { workspaceId: string; from: string; to: string };
457 /**
458 * A repository moved from workspace `from` to `to`, keeping its id and
459 * name. Services that store its path or its workspace's slug move those
460 * rows to its *current* path (ask repos `path_by_id`), so a repeated or
461 * late delivery after a second transfer still lands in the right place.
462 */
463 "repo.transferred": { repoId: string; name: string; from: string; to: string };
464 /**
465 * A workspace is gone. Services drop what they keep for it alone; ledgers,
466 * invoices and the audit log stay under its slug, which is never reused.
467 */
468 "workspace.deleted": { workspaceId: string; slug: string };
469 /**
470 * An owner deleted a workspace; staff can restore it until `purgeAfter`,
471 * and nobody can reach it meanwhile. Services hide what they keep for it
472 * and stop what runs for it, keeping their rows: repos deletes its
473 * repositories softly (`repo.deleted` with `withWorkspace`), deployments
474 * pauses its apps, search drops it. `workspace.restored` undoes exactly
475 * that; `workspace.deleted` follows once `purgeAfter` passes. `by` is the
476 * owner's username.
477 */
478 "workspace.deleting": { workspaceId: string; slug: string; by: string; purgeAfter: string };
479 /**
480 * Staff brought a deleted workspace back with its members and tokens.
481 * Services undo what they did on `workspace.deleting`, and only that.
482 */
483 "workspace.restored": { workspaceId: string; slug: string };
484 /**
485 * A memory was added, changed, reviewed or forgotten. No text: ask the
486 * work service for it by id. `repoId` is the project's, or null for the
487 * workspace's memory. `status` is `deleted` once forgotten.
488 */
489 "memory.changed": { memoryId: string; workspace: string; status: "candidate" | "kept" | "dismissed" | "deleted" };
490 /**
491 * A sandbox was stopped because it looked like it was mining: CPU pinned
492 * with little I/O and no progress, or a miner seen by name. For g1t's
493 * staff, in sudo; published with no `repoId` so it never reaches a
494 * repository's timeline or webhooks. `metrics` is what the sandbox
495 * measured (`Verdict` in crates/runner abuse.rs), or null if it could
496 * not say.
497 */
498 "abuse.flagged": {
499 workspace: string;
500 repo: string | null;
501 /** The agent run, when the sandbox had one. */
502 run: string | null;
503 /** What the sandbox was for: agent, checks, queue, actions, deploy... */
504 kind: string;
505 sandbox: string;
506 metrics: Record<string, unknown> | null;
507 };
508 /**
509 * Docs (services/artifacts): a page was made. Published with no `repoId`, so
510 * a page (which may be in a private space) never reaches a repository's
511 * timeline or webhooks; `actor` is the user's or agent's id. Readers
512 * check access with the artifacts service before showing anything of it.
513 */
514 "doc.page.created": DocPageEventData;
515 /**
516 * A page's content changed: at most once per page every ten minutes of
517 * editing (when its history records a version), and for every agent
518 * edit, accepted suggestion and restore. `authors` are member keys
519 * (`user:<id>`, `agent:<id>`) of everyone whose changes are in it.
520 */
521 "doc.page.updated": DocPageEventData & { versionId: string; kind: "edit" | "agent" | "suggestion" | "restore"; authors: string[] };
522 /** A page went to the trash (with every page under it; one event for the page asked about). */
523 "doc.page.archived": DocPageEventData;
524 /**
525 * A page became possibly out of date: a merged pull request or a push to
526 * a repository's default branch changed code it cites. `repoId` is in
527 * `data`, not on the event, for the same reason as above. `owners` are
528 * member keys; an agent that owns the page can update it
529 * (`stalePagesForAgent` in docs.ts).
530 */
531 "doc.page.stale": DocPageEventData & { repoId: string; repo: string; commit: string; pull: number | null; paths: string[]; owners: string[] };
532 /**
533 * Artifacts (folios, services/artifacts): a folio was made. Like `doc.page.*`,
534 * published with no `repoId`, never offered to webhooks, and readers check
535 * access with the artifacts service before showing anything of it.
536 */
537 "folio.created": FolioEventData;
538 /** A folio's content changed: a version (`versionKind`) with everyone whose changes are in it. */
539 "folio.updated": FolioEventData & { versionId: string; versionKind: "edit" | "agent" | "suggestion" | "proposal" | "restore"; authors: string[] };
540 /** A folio went to the trash (with everything under it; one event for the folio asked about). */
541 "folio.trashed": FolioEventData;
542 "folio.restored": FolioEventData;
543 /** Someone was given access: who (member keys) and the role. Never content. */
544 "folio.shared": FolioEventData & { principals: string[]; role: "view" | "comment" | "edit" | "manage" };
545 /** Code a folio cites changed. The repository is in `data` as `owner/name` only. */
546 "folio.stale": FolioEventData & { repo: string; commit: string; pull: number | null; paths: string[]; owners: string[] };
547};
548
549/**
550 * What every `folio.*` event carries. `title` is null unless every member
551 * of the workspace can read the folio, so a private folio's name never
552 * travels.
553 */
554export type FolioEventData = {
555 workspace: string;
556 workspaceId: string;
557 folioId: string;
558 kind: "doc" | "slides" | "design" | "dashboard";
559 spaceId: string | null;
560 title: string | null;
561};
562
563/** What every `doc.page.*` event carries. */
564export type DocPageEventData = {
565 workspace: string;
566 workspaceId: string;
567 pageId: string;
568 spaceId: string;
569 title: string;
570 /** The page's address on the site. */
571 path: string;
572};
573
574export type EventType = keyof EventPayloads;
575
576export type G1tEvent<T extends EventType = EventType> = {
577 [K in T]: {
578 id: string;
579 type: K;
580 /** The service that published it. */
581 source: string;
582 /** RFC 3339. */
583 time: string;
584 /** The repo the event concerns, used to scope timelines and deliveries. */
585 repoId: string | null;
586 /** The user or agent that caused it, if any. */
587 actor: string | null;
588 data: EventPayloads[K];
589 };
590}[T];
591
592/** What a publisher supplies; the bus fills in `id` and `time`. */
593export type NewEvent<T extends EventType = EventType> = {
594 [K in T]: Omit<G1tEvent<K>, "id" | "time">;
595}[T];
596
597export type EventQuery = {
598 repoId?: string;
599 types?: EventType[];
600 /** Return events older than this event id. */
601 before?: string;
602 /** Only events by this account id. */
603 actor?: string;
604 /** Only events about these issues or pull requests (`number`, or the `issue` a comment or review is on). */
605 numbers?: number[];
606 /** Only events at or after this RFC 3339 time. */
607 since?: string;
608 limit?: number;
609};
610
611/**
612 * `activity_digest`: what happened in some repositories, and to a
613 * workspace's artifacts, over `[from, until)`, counted by who did it. Home
614 * reads it for what people and agents did since you were last there.
615 * snake_case, as the Rust service reads it
616 * (`g1t_contracts::events::ActivityDigestArgs`).
617 */
618export type ActivityDigestArgs = {
619 /** By id; the caller has checked the viewer may read each. At most `MAX_DIGEST_REPOS` are read. */
620 repo_ids: string[];
621 /** The workspace's slug, for its `folio.*` events (which carry no repository); null leaves artifacts out. */
622 workspace: string | null;
623 /** RFC 3339, inclusive. */
624 from: string;
625 /** RFC 3339, exclusive. */
626 until: string;
627};
628
629/** The most repositories one digest reads. */
630export const MAX_DIGEST_REPOS = 50;
631
632/**
633 * How many times one actor did something. The actor is a member key:
634 * `user:<id>` for a person (or g1t, `user:usr_g1t_agent`), `agent:<id>` for
635 * one of the workspace's agents acting as itself, or `""` when the event
636 * named nobody (a push copied in by a mirror).
637 */
638export type ActorCount = { actor: string; count: number };
639
640/** Pushes by one actor, with the commits they brought. */
641export type PushesBy = { actor: string; count: number; commits: number };
642
643/** Pushes to a repository's branches (tags are not pushes here). A push whose commits were not counted counts as one commit. */
644export type PushDigest = {
645 count: number;
646 commits: number;
647 /** The branches pushed to, by name, sorted. */
648 branches: string[];
649 /** Most pushes first. */
650 by: PushesBy[];
651 /** Of those, the pushes straight to the default branch: what landed without a pull request. */
652 default_branch: { count: number; commits: number; by: PushesBy[]; last_at: string | null };
653};
654
655export type RepoDigest = {
656 repo_id: string;
657 pushes: PushDigest;
658 /** Pull requests opened, merged and closed without merging, each by who did it. */
659 pulls: { opened: ActorCount[]; merged: ActorCount[]; closed: ActorCount[] };
660 issues: { opened: ActorCount[]; closed: ActorCount[] };
661 /** `review.completed` (g1t's), and a comment with a verdict (a person's). */
662 reviews: ActorCount[];
663 /** Comments without a verdict; one by an agent as itself is the agent's. */
664 comments: ActorCount[];
665 /** Builds that finished, by who set them off; `production` is how many successes were production. */
666 deployments: { succeeded: ActorCount[]; failed: ActorCount[]; production: number };
667 /** Releases published, newest first, at most 50. */
668 releases: { tag: string; name: string | null; actor: string; at: string }[];
669 /** Package versions published, newest first, at most 50. */
670 packages: { ecosystem: string; name: string; version: string; actor: string; at: string }[];
671};
672
673/** A workspace's artifacts over the span. Never a title. */
674export type FolioDigest = {
675 created: ActorCount[];
676 /** Each folio whose content changed, once, with everyone whose changes are in it (member keys); at most 50. */
677 edited: { folio_id: string; kind: "doc" | "slides" | "design" | "dashboard" | string; authors: string[] }[];
678 /** How many folios changed, counting those past the cap. */
679 edited_count: number;
680};
681
682export type ActivityDigest = {
683 from: string;
684 until: string;
685 /** One per repository that had any event counted; none for a quiet one. */
686 repos: RepoDigest[];
687 /** When a workspace was named. */
688 folios: FolioDigest | null;
689 /** False when more events fell in the span than were read, or more repositories were named than are read, so counts are low. */
690 complete: boolean;
691};
692
693/** The event bus and its durable log. */
694export interface EventsApi {
695 publish(events: NewEvent[]): Promise<void>;
696 /** Newest first. */
697 list(query: EventQuery): Promise<G1tEvent[]>;
698 /** A span of some repositories' and a workspace's artifacts' events, counted by who did it. */
699 activityDigest(args: ActivityDigestArgs): Promise<ActivityDigest>;
700}