Skip to content
1,153 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.

Chat has sounds. A short warm cue plays for a message in a conversation you have open but aren't looking at, a direct message, a mention of you and an agent finishing something for you, with a soft tick for sending that is off until you turn it on; never for your own messages, a muted conversation, a conversation you are looking at in a window that has the front, or while you have paused notifications, and never twice for one message or more than once in a second and a half. The cues are made by the browser itself in two sets, Soft and Bright, so nothing is downloaded; the Sounds and notifications section of your notification settings turns them on and off, picks the set and the volume, plays each one, and offers desktop notifications for when the window is behind; Do not disturb is the one pause everyone sees, set from the chat sidebar's bell, the settings, or your avatar menu, now with a two-hour choice, and the top bar's bell shows a dot while it's on. Your choices are kept with your account. The sound layer sits behind one small interface so the desktop app can play them natively later. The chat and notifications guides say the rules.1import type { SoundSettings, SoundSettingsChange } from "./sounds";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look2import type { AccessClient, BasePermission, RepoGrant } from "./access";
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers3import type { Permissions, ScopeLevel, ScopeResource } from "./scopes";
Merge main (membership, two-factor, GitHub repo roles) into tokens4import type { MemberPrivileges, OrgRole, PolicyHold } from "./members";
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API5import type { Acting, CreateRunCredentialInput, RunBinding } from "./audit";
Billing accounts, terms and enterprises; g1t is no longer free6import type { RepoPath } from "./repos";
7import type { Result } from "./result";
Merge branch 'worktree-agent-ad7c6d88d93adc817'8import type { TeamCreation, TeamsClient } from "./teams";
People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how.9import type { PeopleClient } from "./people";
Merge branch 'worktree-agent-a5a5de74d8863d554' into worktree-agent-a16631325aecf58ca10import type { DeployKeysClient } from "./deploy-keys";
Merge email confirmation gate: a code and a link, nothing until confirmed (identity 0036)11import type { EmailConfirmed } from "./accounts";
Billing accounts, terms and enterprises; g1t is no longer free12
13export type User = {
14 id: string;
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers15 /** Lowercased: what the person is found, linked and mentioned by. */
Billing accounts, terms and enterprises; g1t is no longer free16 username: string;
17 /**
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers18 * The username as its owner wrote it (`Ana`), when that differs from
19 * `username`: what pages show (`shownUsername`). Set on the signed-in
20 * person and on people looked up by name.
21 */
22 display_username?: string;
23 /**
Billing accounts, terms and enterprises; g1t is no longer free24 * `workspace` when a workspace is acting through one of its own access
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily25 * tokens: `id` is then the workspace's and `username` its slug. `system`
26 * is g1t itself doing platform work, such as a security update
27 * (`username` `g1t`). Absent means `user`.
Billing accounts, terms and enterprises; g1t is no longer free28 */
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily29 kind?: "user" | "workspace" | "agent" | "system";
Billing accounts, terms and enterprises; g1t is no longer free30 /**
31 * Whether the account's email address is confirmed. Only set on users
Merge email confirmation gate: a code and a link, nothing until confirmed (identity 0036)32 * resolved from credentials. An account that has not confirmed it can
33 * only confirm it: see `awaitsConfirmation`.
Billing accounts, terms and enterprises; g1t is no longer free34 */
35 verified?: boolean;
36 /**
37 * The workspaces this user belongs to. Set on users resolved from
38 * credentials, so any service can authorize from it.
39 */
40 workspaces?: Membership[];
Workspace names and icons, and a component kit for every control41 /**
42 * The person's uploaded avatar: the SHA-256 of its bytes, served at
43 * `/avatars/<avatar>`. Only set on the signed-in person; absent means
44 * the generated letter avatar.
45 */
46 avatar?: string;
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API47 /**
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent48 * Set on an agent resolved from its token: who it acts for ("g1t
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API49 * on behalf of syntaqx"), with which credential, and what it may do.
50 */
51 acting?: Acting;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look52 /**
53 * The repositories this user has a role on directly, whether or not they
54 * belong to its workspace. Set with `workspaces`; see `access.ts`.
55 */
56 grants?: RepoGrant[];
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step57 /**
58 * Set on a user resolved from an access token: its scopes (null for full
59 * access) and the workspaces or repositories it reaches. See scopes.ts.
60 */
61 token?: {
62 token_id: string;
63 scopes?: string[] | null;
64 legacy?: boolean;
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens65 /** The token's name, as its owner gave it. */
66 name?: string;
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers67 /**
68 * A token narrowed to one workspace (or none): its workspace and
69 * repositories. The key is kept from before tokens were one kind.
70 */
TS access mirrors the workspace token cap and fine-grained reach71 fine_grained?: {
72 workspace?: string | null;
73 repositories?: "all" | "selected" | "public";
74 repo_ids?: string[];
75 };
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers76 /** A workspace's own token with Repositories: admin. */
TS access mirrors the workspace token cap and fine-grained reach77 admin?: boolean;
78 /** Set on what a deploy key resolves to. */
79 deploy_key?: string;
80 /** The one repository a job's token or a deploy key reaches. */
81 repo?: string;
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts82 /** Set on a workflow job's token (`G1T_TOKEN`): the run and job it was made for. */
83 job?: { run_id: string; job_id: string; pull_requests?: boolean };
Merge main into Artifacts Phase 284 /**
85 * A person's token whose owner let it use the website as them, sent as
86 * `Authorization: Bearer` (apps/web, lib/website-token.ts). Not a scope.
87 */
88 website?: boolean;
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step89 };
Merge main (membership, two-factor, GitHub repo roles) into tokens90 /**
91 * Workspaces the person belongs to but cannot use until they meet its
92 * policy, such as turning on two-factor authentication. Left out of
93 * `workspaces` and `grants` meanwhile.
94 */
95 held?: PolicyHold[];
Billing accounts, terms and enterprises; g1t is no longer free96};
97
98/** What a member may do: an owner also manages the workspace's members. */
99export type Role = "owner" | "member";
100
Workspace names and icons, and a component kit for every control101export type Membership = {
102 /** The workspace's name in URLs: `g1t.sh/<slug>`. */
103 slug: string;
104 role: Role;
105 /** Its display name. Set on users resolved from credentials. */
106 name?: string;
107 /** Its uploaded icon, as `Workspace.avatar`. */
108 avatar?: string;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look109 /** The workspace's base permission: what members get on every repository. Absent means `write`. */
110 base_permission?: BasePermission;
Merge branch 'worktree-agent-ad7c6d88d93adc817'111 /** Who may create its teams. Absent means any member. */
112 team_creation?: TeamCreation;
Merge main (membership, two-factor, GitHub repo roles) into tokens113 /** The roles held besides owner or member. */
114 org_roles?: OrgRole[];
115 /** What the workspace lets its members do. Absent means the defaults. */
116 privileges?: MemberPrivileges;
Chat and workspace agents: channels, DMs and named agents you talk to117 /**
118 * Whether this member uses Code: repositories, issues, pull requests,
119 * checks, deploys. False for people who only use Chat, Docs and agents
120 * (support, sales, finance): they see no repository, whatever the base
121 * permission, and agents treat them as unable to change code. Absent
122 * means true.
123 */
124 code_access?: boolean;
Workspace names and icons, and a component kit for every control125};
Billing accounts, terms and enterprises; g1t is no longer free126
Chat and workspace agents: channels, DMs and named agents you talk to127/** Whether a member uses Code. See `Membership.code_access`. */
128export function hasCodeAccess(membership: Pick<Membership, "code_access"> | null | undefined): boolean {
129 return membership?.code_access !== false;
130}
131
Agents and memory, checks and conflicts, profiles, slug renames, custom domains132/** How long an old workspace slug redirects, and stays reserved for it, after a rename. */
133export const SLUG_HOLD_DAYS = 90;
134
135/** How long a workspace must wait between renames. */
136export const RENAME_COOLDOWN_HOURS = 24;
137
Workspace names and icons, and a component kit for every control138/** The largest avatar that can be uploaded, in bytes. */
139export const MAX_AVATAR_BYTES = 1024 * 1024;
140
Billing accounts, terms and enterprises; g1t is no longer free141/**
Merge branch 'worktree-agent-a2013627e5ea4ab13'142 * Where a workspace keeps its repositories' git data: anywhere g1t stores
143 * it (the default), or in the EU only. It applies to repositories made
144 * after it is set.
145 */
146export type DataResidency = "anywhere" | "eu";
147
148/**
Billing accounts, terms and enterprises; g1t is no longer free149 * A workspace: the owner of repositories, and the first segment of their
150 * URLs. A person's own space and a team's are the same thing.
151 */
152export type Workspace = {
153 id: string;
154 slug: string;
155 name: string;
156 /** One line saying what the workspace is for. */
157 description: string | null;
158 /** RFC 3339. */
159 createdAt: string;
160 memberCount: number;
Workspace names and icons, and a component kit for every control161 /**
162 * The workspace's uploaded icon: the SHA-256 of its bytes, served at
163 * `/avatars/<avatar>`. Null means the generated letter avatar.
164 */
165 avatar: string | null;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look166 /** What every member gets on each repository; owners have Admin. */
167 basePermission?: BasePermission;
Merge branch 'worktree-agent-ad7c6d88d93adc817'168 /** Who may create its teams. Absent means any member. */
169 teamCreation?: TeamCreation;
Merge main (membership, two-factor, GitHub repo roles) into tokens170 /** Whether members and outside collaborators need two-factor authentication. */
171 twoFactorRequirementEnabled?: boolean;
172} & Partial<MemberPrivileges>;
Billing accounts, terms and enterprises; g1t is no longer free173
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look174export type Member = {
175 username: string;
Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar176 /** The username as its owner wrote it (`Ana`), when that differs from `username`. */
177 display_username?: string;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look178 role: Role;
Merge main (membership, two-factor, GitHub repo roles) into tokens179 /** The roles they hold besides `role`. */
180 org_roles?: OrgRole[];
181 /** Whether two-factor authentication is on; owners only, null for anyone else. */
182 two_factor?: boolean | null;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look183 /** Their display name, when they set one. */
184 name?: string | null;
185 /** Their uploaded avatar's hash, served at `/avatars/<avatar>`; null for the generated letter avatar. */
186 avatar?: string | null;
187};
Billing accounts, terms and enterprises; g1t is no longer free188
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace189/** An owner of a workspace, as staff see them. */
190export type AdminOwner = { username: string; email: string | null };
191
192/** A workspace as staff see it. Mirrors `AdminWorkspace` in `crates/contracts/src/identity.rs`. */
193export type AdminWorkspace = {
194 slug: string;
195 name: string;
196 /** RFC 3339. */
197 createdAt: string;
198 owners: AdminOwner[];
199 memberCount: number;
200};
201
202/** A member of a workspace, as staff see them. */
203export type AdminMember = { username: string; email: string | null; role: Role; /** RFC 3339. */ joined: string };
204
205export type AdminWorkspaceDetail = {
206 slug: string;
207 name: string;
208 description: string | null;
209 /** RFC 3339. */
210 createdAt: string;
211 /** Owners first, then by username. */
212 members: AdminMember[];
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member213 /** It can never be deleted, by anyone (identity's `PROTECTED_WORKSPACES`). */
214 protected: boolean;
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace215};
216
217/** The most workspaces one `workspaces` call returns. */
218export const ADMIN_WORKSPACES_LIMIT = 500;
219
220/**
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look221 * Whether anyone may make an account, or only someone with an invite.
222 * Identity's `REGISTRATION_MODE`; unset means `invite`.
223 */
224export type RegistrationMode = "invite" | "open";
225
226/** How many invites a person may have out at once, unless identity's `INVITES_PER_USER` says otherwise. */
227export const INVITES_PER_USER = 5;
228/** How long an invite works, unless identity's `INVITE_TTL_DAYS` says otherwise. */
229export const INVITE_TTL_DAYS = 30;
Workspace is the workspace's settings, in one place. Its sidebar is grouped, General, Access, Money, Compute, Code, Agents, Chat, Artifacts, Security and Integrations, every page one click away with no settings inside settings, the groups folding and the owner-only pages hidden from members; what is not here yet is marked Soon with a hint. Members is a list with search and filters for role, two-factor and team, and Invite opens a dialog: who, by username, name or email, their role and a note that goes into the invitation; nothing is filled in inline any more. Invitations is its own page with All, Pending, Accepted, Declined, Expired and Revoked filters that say how many, and each row's menu can copy the link, send it again or revoke it; identity learned to send an invitation again and to carry the note. Permissions gathers every rule about who may do what, by part: Code's base permission and member privileges, who creates teams, who creates channels, and, marked Soon with what applies today, forking private repositories, adding agents to conversations, messaging agents directly, creating spaces and default sharing, creating workspace agents and raising budgets, deploying to production and publishing packages. General lost what moved. The permissions guide is new, and the workspaces, people and teams, access, chat, teams, authentication and billing guides say where things are now.230/** The most characters an invitation's note keeps (`invite_member`'s `message`). */
231export const MAX_INVITE_MESSAGE = 500;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look232
233/** Only a pending invite can be used or revoked. Revoked and expired ones never used give the invite back. */
Merge email confirmation gate: a code and a link, nothing until confirmed (identity 0036)234/**
235 * `awaiting_confirmation`: used to make an account that has not confirmed its
236 * email address yet; what it gives is joined when the address is confirmed,
237 * unless it is revoked first.
238 */
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)239/**
240 * `awaiting_answer`: the account it made is confirmed, and the workspace it
241 * names waits for the person to accept or decline. `declined`: they said no.
242 */
243export type InviteStatus =
244 | "pending"
245 | "awaiting_confirmation"
246 | "awaiting_answer"
247 | "redeemed"
248 | "declined"
249 | "expired"
250 | "revoked";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look251
252/** One invite. Mirrors `Invite` in `crates/contracts/src/identity.rs`. */
253export type Invite = {
254 id: string;
255 /** `g1t-k7m2-…`: returned when it is made, and to its maker while pending. */
256 code: string | null;
257 /** The code's first group, such as `g1t-k7m2`. */
258 hint: string;
259 /** Only this address can use it. */
260 email: string | null;
261 /** `account` makes an account; `workspace` joins an existing one to `workspace`. */
262 kind: "account" | "workspace";
263 /** The workspace using it joins. */
264 workspace: string | null;
265 status: InviteStatus;
266 /** Whose allowance it used. */
267 chargedTo: "user" | "workspace" | "none";
268 /** Its maker's username; null when g1t staff made it. */
269 invitedBy: string | null;
270 /** The account that used it. */
271 redeemedBy: string | null;
272 /** RFC 3339. */
273 createdAt: string;
274 /** RFC 3339. */
275 expiresAt: string;
276 redeemedAt: string | null;
277 revokedAt: string | null;
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)278 /** The account a workspace invitation is for, by username: someone invited by username, or the account the invite made. */
279 invitee?: string | null;
280 /** The role `workspace` is joined with; null when it names none. */
281 role?: Role | null;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look282 /** The staff member who minted it; only in staff views. */
283 staff?: string | null;
284};
285
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)286/**
287 * A workspace invitation waiting for its person's answer, as they see it.
288 * Mirrors `WorkspaceInvitation` in `crates/contracts/src/identity.rs`.
289 */
290export type WorkspaceInvitation = {
291 id: string;
292 workspace: ProfileWorkspace;
293 /** The role accepting joins with. */
294 role: Role;
295 /** Null when g1t staff sent it. */
296 invitedBy: { username: string; name: string | null; avatar: string | null } | null;
297 createdAt: string;
298 expiresAt: string;
299};
300
301/** Someone to invite, as `findPeople` finds them: never an email address. */
302export type PersonMatch = { username: string; name: string | null; avatar: string | null };
303
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look304/** How many invites someone may have out. `limit` and `remaining` are null for no limit. */
305export type Allowance = { limit: number | null; used: number; remaining: number | null };
306
307export type InvitesOverview = {
308 mode: RegistrationMode;
309 allowance: Allowance;
310 /** Workspaces the person owns that were granted invites to share. */
311 workspaces: { slug: string; allowance: Allowance }[];
312 invites: Invite[];
313};
314
315/** What a valid code is for, before it is used. */
316export type InvitePreview = {
317 kind: "account" | "workspace";
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas318 /** Pending, unless `anyStatus` asked about a code that is spent. */
319 status: InviteStatus;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look320 /** Null when g1t staff sent it. */
321 invitedBy: { username: string; name: string | null; avatar: string | null } | null;
322 workspace: ProfileWorkspace | null;
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas323 /** The repository it accepts an invitation to, such as `{ name: "flagon-io/g1t", role: "write" }`. */
324 repository: { name: string; role: string } | null;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look325 /** Partly hidden, such as `a•••@example.com`. */
326 email: string | null;
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas327 /** The bound address in full, while the invite is pending: it fills in and locks the sign-up form. */
328 address: string | null;
329 /** Whether the bound address has a g1t account already: sign in to accept. */
330 hasAccount: boolean;
331 /** With a viewer: whether it is theirs (for one of their confirmed addresses, or used by them). */
332 forViewer: boolean | null;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look333 expiresAt: string;
Merge shared invite links: label, uses, expiry, domains; joined through recorded (identity 0038)334 /** A shared invite link's group, such as `Cloudflare judges`; null for a one-person invite. Not secret. */
335 sharedLabel: string | null;
336 /** The email domains a shared invite link is limited to; empty for any address. */
337 sharedDomains: string[];
Merge invite emails that confirm the address: the emailed link carries a proof only that email has, so signing up from it needs no code; shared links and typed codes still confirm338 /**
339 * Whether the page was opened from this pending invite's own email (its
340 * `proof` checked out): the account made with it starts with `address`
341 * confirmed. False without a proof, with a wrong one, or for an invite
342 * bound to no address.
343 */
344 emailProven: boolean;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look345};
346
347export type WaitlistStatus = "waiting" | "invited" | "dismissed";
348
349export type WaitlistEntry = {
350 id: string;
351 email: string;
352 about: string | null;
353 status: WaitlistStatus;
354 inviteId: string | null;
355 decidedBy: string | null;
356 decidedAt: string | null;
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas357 /** What staff wrote when approving; it went in the invite email. */
358 note: string | null;
359 /** The account made with the invite, once it was used. */
360 joinedAs: string | null;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look361 /** When they first asked. */
362 createdAt: string;
363 /** When they last asked. */
364 updatedAt: string;
365};
366
367export type InviteGrant = { amount: number; note: string | null; grantedBy: string; createdAt: string };
368export type InviteTreeNode = { username: string; joinedAt: string; invited: InviteTreeNode[] };
369
370/** Where a person came from and whom they brought. For a workspace, `username` is its slug. */
371export type InviteTree = {
372 username: string;
373 /** Who invited them, then who invited that person, and so on. */
374 invitedBy: string[];
375 /** The staff member who minted their invite, when staff did. */
376 staff: string | null;
377 allowance: Allowance;
378 grants: InviteGrant[];
379 invites: Invite[];
380 /** Whom they invited, three levels down. */
381 invited: InviteTreeNode[];
Merge shared invite links: label, uses, expiry, domains; joined through recorded (identity 0038)382 /** The shared invite link the account was made with, if it was. */
383 shared: SharedInviteSource | null;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look384};
385
Merge shared invite links: label, uses, expiry, domains; joined through recorded (identity 0038)386// --- Shared invite links, staff only ---------------------------------------------------
387//
388// One link for a group (a conference's judges, a post, a community): up to
389// `maxUses` new accounts, until it expires or staff revoke it, optionally only
390// for addresses at some domains. Each use makes a new account, which makes its
391// own workspace; it never joins an existing one and uses nobody's allowance.
392// The link is `https://g1t.sh/register?invite=<code>`. Mirrors the shared
393// invite types in `crates/contracts/src/identity.rs`.
394
395/** How long a shared invite link works when staff give no date. */
396export const SHARED_INVITE_TTL_DAYS = 14;
397/** The furthest ahead a shared invite link's last day may be set. */
398export const SHARED_INVITE_MAX_DAYS = 365;
399/** The most accounts one shared invite link makes. */
400export const MAX_SHARED_INVITE_USES = 1000;
401/** The most characters a shared invite link's label keeps. */
402export const MAX_SHARED_INVITE_LABEL = 80;
403/** The most email domains one shared invite link may be limited to. */
404export const MAX_SHARED_INVITE_DOMAINS = 10;
405
406/** Only a live link makes accounts; `used_up`: every use is taken. */
407export type SharedInviteStatus = "live" | "used_up" | "expired" | "revoked";
408
409/** The shared invite link an account was made with. */
410export type SharedInviteSource = { id: string; label: string };
411
412/** One shared invite link, as staff see it. */
413export type SharedInvite = {
414 /** `sinv_…`. */
415 id: string;
416 /** Whom it is for, such as `Cloudflare judges`. */
417 label: string;
418 /** The code, while it is live. */
419 code: string | null;
420 /** The code's first group, such as `g1t-k7m2`. */
421 hint: string;
422 maxUses: number;
423 /** Accounts made with it so far. */
424 uses: number;
425 /** Only addresses at these domains may use it; empty for any. */
426 domains: string[];
427 status: SharedInviteStatus;
428 /** The staff member who made it, by email. */
429 staff: string;
430 createdAt: string;
431 expiresAt: string;
432 revokedAt: string | null;
433 revokedBy: string | null;
434 /** The accounts made with it, oldest first; `username` is null once one is purged. */
435 accounts: { username: string | null; joinedAt: string }[];
436};
437
438/** What staff make a shared invite link from. */
439export type NewSharedInvite = {
440 label: string;
441 /** 1 to 1000. */
442 maxUses: number;
443 /** The last day it works, `YYYY-MM-DD` (UTC); null for 14 days from now. */
444 expiresOn: string | null;
445 /** Email domains it is limited to, such as `cloudflare.com`; empty for any address. */
446 domains: string[];
447};
448
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look449/** The most rows one staff listing of invites or the waitlist returns. */
450export const ADMIN_INVITES_LIMIT = 500;
451
452/**
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace453 * Staff-only identity, for sudo.g1t.sh. It takes no viewer and checks no
454 * membership: only sudo calls it, over its service binding, once Cloudflare
455 * Access and its staff list have let someone in. Never call it on behalf of
456 * a customer.
457 */
458export interface IdentityAdminApi {
459 /** Every workspace, newest first, at most 500; `query` matches slug, name, or an owner's username or email. */
460 workspaces(query?: string): Promise<AdminWorkspace[]>;
461 /** One workspace with all its members, or null. */
462 workspace(slug: string): Promise<AdminWorkspaceDetail | null>;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look463
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas464 /** The waitlist, newest first; `query` matches the address or what they said. */
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look465 waitlist(query?: string | null, status?: WaitlistStatus | null): Promise<WaitlistEntry[]>;
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas466 /** How many requests are waiting, for the navigation's badge. */
467 waitlistPending(): Promise<number>;
468 /**
469 * Approving mints an invite bound to the address and emails it, with
470 * `note` (up to 500 characters) if given; dismissing only marks it.
471 */
472 decideWaitlist(id: string, approve: boolean, staff: string, note?: string | null): Promise<Result<WaitlistEntry>>;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look473 /** Invites, newest first; `query` is a code's start, or part of an email, inviter or redeemer. */
474 invites(query?: string | null): Promise<Invite[]>;
475 revokeInvite(id: string, staff: string): Promise<Result<Invite>>;
476 /** An invite that uses nobody's allowance, optionally bound to (and emailed to) `email`. */
477 mintInvite(email: string | null, staff: string): Promise<Result<Invite>>;
478 /** More invites (or fewer, with a negative amount) for a person or a workspace. */
479 grantInvites(
480 target: "user" | "workspace",
481 name: string,
482 amount: number,
483 note: string,
484 staff: string,
485 ): Promise<Result<Allowance>>;
486 /** Where a person came from and whom they brought, or null. */
487 inviteTree(username: string): Promise<InviteTree | null>;
488 /** A workspace's granted invites and the invites made for it, or null. */
489 workspaceInvites(slug: string): Promise<InviteTree | null>;
Merge shared invite links: label, uses, expiry, domains; joined through recorded (identity 0038)490 /** Shared invite links, newest first, each with the accounts it made. */
491 sharedInvites(): Promise<SharedInvite[]>;
492 /** Makes a shared invite link; the result carries its code. Recorded in the audit log. */
493 createSharedInvite(link: NewSharedInvite, staff: string): Promise<Result<SharedInvite>>;
494 /** Stops a shared invite link making more accounts; those it made stay. Recorded in the audit log. */
495 revokeSharedInvite(id: string, staff: string): Promise<Result<SharedInvite>>;
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member496
497 /** Workspaces owners deleted that are not purged yet, newest first. */
498 deletedWorkspaces(): Promise<DeletedWorkspace[]>;
499 /**
500 * Brings a deleted workspace back, with its members, tokens and what went
501 * with it, while it is still restorable. Publishes `workspace.restored`.
502 */
503 restoreWorkspace(workspaceId: string, staff: string): Promise<Result<boolean>>;
504 /**
505 * Purges a deleted workspace now rather than at `purgeAfter`. `confirm` is
506 * its slug, typed out. Refused for a protected workspace. Publishes
507 * `workspace.deleted`.
508 */
509 purgeWorkspace(workspaceId: string, staff: string, confirm: string): Promise<Result<boolean>>;
Merge branch 'worktree-agent-a8385d293d42c913a'510
511 /** Every workspace alias, by name. */
512 aliases(): Promise<WorkspaceAlias[]>;
513 /**
514 * Points `alias` at the workspace whose slug is `workspace`. Refused for
515 * one of the site's routes, anyone's username, a workspace's slug (deleted
516 * or held after a rename) and an existing alias. `note` says why.
517 */
518 setAlias(alias: string, workspace: string, note: string, staff: string): Promise<Result<WorkspaceAlias>>;
519 /** Removes an alias; `reason` goes in sudo's audit log. */
520 removeAlias(alias: string, reason: string, staff: string): Promise<Result<boolean>>;
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace521}
522
Merge branch 'worktree-agent-a8385d293d42c913a'523/**
524 * A name g1t's staff point at a workspace, so its addresses lead there under
525 * the workspace's own name: `g1t`, the product, leads to `flagon-io`, Flagon,
526 * Inc. Staff-managed only; it follows the workspace through renames.
527 */
528export type WorkspaceAlias = {
529 alias: string;
530 workspaceId: string;
531 /** The workspace's slug and name now. */
532 workspace: string;
533 workspaceName: string;
534 /** Why it exists. */
535 note: string;
536 /** The staff member who set it, or `migration`. */
537 createdBy: string;
538 /** RFC 3339. */
539 createdAt: string;
540};
541
Billing accounts, terms and enterprises; g1t is no longer free542/** Who is asking. Every read and write in every service takes one. */
543export type Viewer = User | null;
544
Merge email confirmation gate: a code and a link, nothing until confirmed (identity 0036)545/**
546 * Whether this is a person whose account has not confirmed its email
547 * address. Such an account can only confirm it, change it, or sign out.
548 */
549export function awaitsConfirmation(user: Pick<User, "kind" | "verified"> | null | undefined): boolean {
550 return !!user && (user.kind ?? "user") === "user" && !user.verified;
551}
552
Billing accounts, terms and enterprises; g1t is no longer free553export type SshKey = {
554 id: string;
555 title: string;
556 fingerprint: string;
557 /** RFC 3339. */
558 createdAt: string;
Merge branch 'worktree-agent-a5a5de74d8863d554' into worktree-agent-a16631325aecf58ca559 /** When it last signed in over SSH, RFC 3339, to within 5 minutes; null when it never has. */
560 lastUsedAt: string | null;
Billing accounts, terms and enterprises; g1t is no longer free561};
562
563export type AccessToken = {
564 id: string;
565 name: string;
566 /** RFC 3339. */
567 createdAt: string;
568 /** RFC 3339, to within a few minutes. Null until it is first used. */
569 lastUsedAt: string | null;
570 /**
571 * For a workspace's token, the username of the member who made it. Null
572 * once that account is gone, and on personal tokens.
573 */
574 createdBy: string | null;
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers575 /** Its scopes, as `resource:level`, the highest of each resource. Null: full access. */
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step576 scopes: string[] | null;
577 /** Made before tokens had scopes: full access until someone narrows it. */
578 legacy: boolean;
579 /** RFC 3339. Null: it does not expire. */
580 expiresAt: string | null;
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers581 /** Its scopes as permissions: each resource it may use, at the highest level. */
582 permissions?: Permissions;
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules583 /** What it is for, as its owner wrote it. */
584 description?: string | null;
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers585 /**
586 * A personal token's reach: the workspace it is made for; null for every
587 * workspace you belong to (or, with `repositorySelection` public, none).
588 * Null on a workspace's own token, which reaches its workspace.
589 */
590 workspace?: string | null;
591 /** Which repositories of that workspace it reaches. */
592 repositorySelection?: RepositorySelection;
593 /** With `selected`: the repositories, as `owner/name`, that you can see. */
594 repositories?: string[];
595 /** Whether a token made for a workspace that approves tokens may be used there yet. */
596 status?: TokenStatus;
597 /** Why an owner denied or revoked it. */
598 reviewReason?: string | null;
599 /** A workspace's own token, acting as the workspace. */
600 workspaceOwned?: boolean;
601 /** A workspace's own token with Repositories: admin, an admin of its repositories. */
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules602 admin?: boolean;
Merge main into Artifacts Phase 2603 /** A personal token its owner let use the website as them. */
604 website?: boolean;
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules605};
606
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers607/** Which repositories a token reaches in its workspace: all, the selected ones, or public ones only. */
608export type RepositorySelection = "all" | "selected" | "public";
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules609
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers610/** Whether a token made for a workspace may be used there yet. */
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules611export type TokenStatus = "active" | "pending" | "denied" | "revoked";
612
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers613/** A new access token: a person's, or (with `owner`) a workspace's. */
614export type TokenInput = {
615 /** A workspace's slug to make that workspace's token; null for your own. */
616 owner?: string | null;
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules617 name: string;
618 description?: string | null;
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers619 /** 1 to 366 days; null for no expiry, where the workspaces it reaches allow that. */
620 ttlSeconds: number | null;
621 /**
622 * A personal token's reach: a workspace's slug, or null for every
623 * workspace you belong to (with `repositorySelection` public: none).
624 */
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules625 workspace: string | null;
626 repositorySelection: RepositorySelection;
627 /** With `selected`: `owner/name` or names in the workspace. */
628 repositories: string[];
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers629 /** Each resource's level; left out is no access. */
630 permissions: Partial<Record<ScopeResource, ScopeLevel>>;
Merge main into Artifacts Phase 2631 /** A personal token: whether it may use the website as you. Off unless set. */
632 website?: boolean;
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules633};
634
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers635/** A change to a token; what is left out stays. */
Merge main into Artifacts Phase 2636export type TokenChange = Partial<Pick<TokenInput, "name" | "description" | "repositorySelection" | "repositories" | "permissions" | "website">>;
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers637
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules638/** A workspace's rules for personal access tokens. */
639export type TokenPolicy = {
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers640 /** A token made for every workspace of its owner reaches this one. */
641 allowTokensForAllWorkspaces: boolean;
642 /** A token may be made for this workspace alone. */
643 allowTokensForThisWorkspace: boolean;
644 /** A token made for this workspace waits for an owner's approval. */
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules645 requireApproval: boolean;
646 /** Null: no limit. */
647 maxLifetimeDays: number | null;
648 forbidNoExpiry: boolean;
649 updatedBy?: string | null;
650 updatedAt?: string | null;
651};
652
653/** A member's personal token that reaches a workspace, as its owners see it. */
654export type MemberToken = {
655 owner: string;
656 token: AccessToken;
657 /** Whether it reaches the workspace now. */
658 reaches: boolean;
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers659 /** Why not: pending approval, denied, revoked, tokens for all workspaces not allowed, tokens made for this workspace not allowed, lasts too long, never expires. */
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules660 blockedBy?: string | null;
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step661};
662
663/** What a new or changed token may do. */
664export type TokenGrant = {
665 /** Null: full access. */
666 scopes: string[] | null;
Billing accounts, terms and enterprises; g1t is no longer free667};
668
669export type DeviceStart = {
670 /** Secret held by the tool and exchanged for a token once approved. */
671 deviceCode: string;
672 /** Short code shown to the person, e.g. `WDJB-MJHT`. */
673 userCode: string;
674 /** Seconds until both codes stop working. */
675 expiresIn: number;
676 /** Seconds the tool should wait between polls. */
677 interval: number;
678};
679
680export type DeviceRequest = { userCode: string; clientName: string };
681
682export type DeviceClaim =
683 | { status: "pending" | "denied" | "expired" }
684 | { status: "approved"; token: string; user: User };
685
686/** What the site passes on once a person has approved an application. */
687export type OAuthApproval = {
688 clientId: string;
689 /** Shown wherever the application's access is listed. */
690 clientName: string;
691 redirectUri: string;
692 /** PKCE challenge, method S256. */
693 codeChallenge: string;
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step694 /** What the person granted. Null: full access. */
695 scopes: string[] | null;
Billing accounts, terms and enterprises; g1t is no longer free696};
697
698export type OAuthTokens = {
699 accessToken: string;
700 /** Works once; using it returns the next one. */
701 refreshToken: string;
702 /** Seconds until the access token stops working. */
703 expiresIn: number;
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step704 /** The scopes granted, space-separated, or `*` for full access. */
705 scope?: string | null;
Billing accounts, terms and enterprises; g1t is no longer free706};
707
708/** An application a person has signed in to. */
709export type OAuthGrant = {
710 id: string;
711 clientName: string;
712 /** RFC 3339. */
713 createdAt: string;
714 /** RFC 3339. */
715 lastUsedAt: string;
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step716 /** What the person granted. Null: full access. */
717 scopes: string[] | null;
718 /** Signed in before applications had scopes: full access until narrowed. */
719 legacy: boolean;
Billing accounts, terms and enterprises; g1t is no longer free720};
721
722/** Accounts, credentials and sessions. */
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member723/**
724 * What deleting a workspace takes with it, and what stands in the way:
725 * nothing does while `billing` is null and it is not `protected`.
726 */
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look727export type WorkspaceDeletion = {
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member728 /** Its live repositories, deleted with it. */
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look729 repositories: number;
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member730 /** Its projects, hidden with it. */
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look731 projects: number;
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member732 members: number;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look733 /** Why billing cannot close it yet, in words for its owner. */
734 billing: string | null;
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member735 /** It can never be deleted, by anyone. */
736 protected: boolean;
737};
738
739/** How long a deleted workspace is kept, for g1t's staff to restore, before it is purged. */
740export const WORKSPACE_RESTORE_DAYS = 30;
741
742/**
743 * Workspaces nobody can delete, whatever identity's `PROTECTED_WORKSPACES`
744 * says: Flagon's, which runs g1t. Services that act on `workspace.deleting`
745 * check it too, so one published for it by mistake changes nothing.
746 */
747export const ALWAYS_PROTECTED_WORKSPACES: readonly string[] = ["flagon-io"];
748
749/** Whether `slug` is one of `ALWAYS_PROTECTED_WORKSPACES`, in any case. */
750export function isProtectedWorkspace(slug: string): boolean {
751 return ALWAYS_PROTECTED_WORKSPACES.includes(slug.trim().toLowerCase());
752}
753
754/** A workspace an owner deleted, kept until `purgeAfter` for staff to restore. */
755export type DeletedWorkspace = {
756 workspaceId: string;
757 slug: string;
758 name: string;
759 /** RFC 3339. */
760 deletedAt: string;
Merge sudo: delete an account with the workspaces it alone owns, purge each761 /** The username of the owner who deleted it, or the staff member who deleted it with the account that alone owned it. */
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member762 deletedBy: string;
763 /** RFC 3339: when it is purged unless restored first. */
764 purgeAfter: string;
765 /** What went with it, counted when it was deleted. */
766 went: WorkspaceDeletion;
767 /** Whether staff can still restore it. */
768 restorable: boolean;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look769};
770
People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how.771export interface IdentityApi extends AccessClient, TeamsClient, PeopleClient, DeployKeysClient {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look772 /**
773 * Creates an account and signs it in. While registration is invite-only,
774 * `inviteCode` must be an unused, unexpired invite (and, when it names an
775 * email, that address); it is ignored while registration is open.
776 */
777 register(
778 username: string,
779 email: string,
780 password: string,
781 inviteCode?: string | null,
782 /** Who is asking, such as the visitor's IP address, for rate limits. */
783 client?: string | null,
Merge invite emails that confirm the address: the emailed link carries a proof only that email has, so signing up from it needs no code; shared links and typed codes still confirm784 /**
785 * The `proof` from the invite email's link. When it is the invite's own
786 * and `email` is the address it was sent to, the account starts with that
787 * address confirmed; otherwise it is ignored.
788 */
789 emailProof?: string | null,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look790 ): Promise<Result<{ user: User; sessionToken: string }>>;
Billing accounts, terms and enterprises; g1t is no longer free791 /** Verifies a username and password for website sign-in. */
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look792 /**
793 * Verifies a username, or any confirmed address of the account, and its
794 * password. Wrong passwords are counted against the account and `client`
795 * (the visitor's IP address); past a limit nothing is checked for a while.
796 */
Merge main (membership, two-factor, GitHub repo roles) into tokens797 signIn(
798 username: string,
799 password: string,
800 client?: string | null,
801 ): Promise<Result<{ user: User; sessionToken: string; twoFactorChallenge?: string | null }>>;
802 /**
803 * The second step of signing in, for an account with two-factor
804 * authentication: the challenge `signIn` returned, and a code from the
805 * app or a recovery code.
806 */
807 twoFactorSignIn(challenge: string, code: string, client?: string | null): Promise<Result<{ user: User; sessionToken: string }>>;
Billing accounts, terms and enterprises; g1t is no longer free808 signOut(sessionToken: string): Promise<void>;
809
Merge email confirmation gate: a code and a link, nothing until confirmed (identity 0036)810 /**
811 * Sends a new confirmation code and link to the primary of an account
812 * that has not confirmed it, at most once a minute.
813 */
Billing accounts, terms and enterprises; g1t is no longer free814 resendVerification(user: User): Promise<Result<boolean>>;
Merge email confirmation gate: a code and a link, nothing until confirmed (identity 0036)815 /** Confirms the address the emailed link was sent to, signed in or not; ends the code sent with it. */
816 verifyEmail(token: string): Promise<Result<EmailConfirmed>>;
Billing accounts, terms and enterprises; g1t is no longer free817 /** Emails a reset link if the address has an account. Always resolves. */
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look818 /**
819 * Any confirmed address of an account works; the link goes to it, and the
820 * primary and backup are told. A few an hour per address and per `client`.
821 */
822 requestPasswordReset(email: string, client?: string | null): Promise<boolean>;
Billing accounts, terms and enterprises; g1t is no longer free823 /** Sets a new password from an emailed token and ends every session. */
824 resetPassword(token: string, password: string): Promise<Result<User>>;
825
826 /**
827 * Device sign-in (RFC 8628). A tool starts a request, a person approves
828 * its short code in a browser, and the tool claims an access token.
829 */
830 deviceStart(clientName: string): Promise<DeviceStart>;
831 /** What a user code is asking for, or null if it is not valid. */
832 deviceLookup(userCode: string): Promise<DeviceRequest | null>;
833 deviceResolve(userCode: string, user: User, approve: boolean): Promise<Result<boolean>>;
834 deviceClaim(deviceCode: string): Promise<DeviceClaim>;
835
836 /**
837 * OAuth 2.1 for applications that sign a person in through the browser.
838 * The caller has checked the client and its redirect address; this
839 * returns the one-time code the application exchanges for tokens.
840 */
841 oauthAuthorize(user: User, approval: OAuthApproval): Promise<{ code: string }>;
842 /** Redeems a code. It works once, for that client, with the PKCE verifier. */
843 oauthExchange(code: string, codeVerifier: string, clientId: string, redirectUri: string): Promise<Result<OAuthTokens>>;
844 /** Trades a refresh token for new tokens; the old ones stop working. */
845 oauthRefresh(refreshToken: string, clientId: string): Promise<Result<OAuthTokens>>;
846 /** Applications the user has signed in to, most recently used first. */
847 listOAuthGrants(user: User): Promise<OAuthGrant[]>;
848 /** Signs an application out. */
849 revokeOAuthGrant(user: User, id: string): Promise<void>;
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step850 /** Changes what an application may do, at once and when it refreshes. */
851 updateOAuthGrant(user: User, id: string, grant: TokenGrant): Promise<Result<OAuthGrant>>;
Billing accounts, terms and enterprises; g1t is no longer free852
853 createWorkspace(user: User, slug: string, name: string): Promise<Result<Workspace>>;
854 /** Public details of a workspace, or null. */
855 getWorkspace(slug: string): Promise<Workspace | null>;
Merge branch 'worktree-agent-a2013627e5ea4ab13'856 /** Where a workspace keeps its repositories' git data; null when there is no such workspace. */
857 workspaceResidency(slug: string): Promise<DataResidency | null>;
858 /**
859 * Owners only. Applies to repositories made from then on. Offer `eu`
860 * only when the repos service's `storageOptions()` says it is available.
861 */
862 setWorkspaceResidency(actor: User, slug: string, residency: DataResidency): Promise<Result<DataResidency>>;
Billing accounts, terms and enterprises; g1t is no longer free863 /** Members only. */
864 listMembers(slug: string, viewer: Viewer): Promise<Result<Member[]>>;
865 /** Owners only. */
866 addMember(actor: User, slug: string, username: string): Promise<Result<boolean>>;
Merge main (membership, two-factor, GitHub repo roles) into tokens867 /** Owners only; your own username is leaving. Never the last owner. */
Billing accounts, terms and enterprises; g1t is no longer free868 removeMember(actor: User, slug: string, username: string): Promise<Result<boolean>>;
Merge main (membership, two-factor, GitHub repo roles) into tokens869 /** Owners only: owner or member, and the roles held besides it. Never leaves no owner. */
870 updateMember(actor: User, slug: string, username: string, change: { role?: Role; org_roles?: OrgRole[] }): Promise<Result<Member>>;
871 /** Owners only: `username` becomes an owner, and you a member. */
872 transferOwnership(actor: User, slug: string, username: string): Promise<Result<boolean>>;
873 /** You leave the workspace. Never the last owner. */
874 leaveWorkspace(user: User, slug: string): Promise<Result<boolean>>;
875 /** Owners only: change some member privileges; returns all of them. */
876 setMemberPrivileges(actor: User, slug: string, change: Partial<MemberPrivileges>): Promise<Result<MemberPrivileges>>;
877 /** Owners only, with two-factor on themselves: require it of everyone. */
878 setTwoFactorRequirement(actor: User, slug: string, required: boolean): Promise<Result<boolean>>;
Billing accounts, terms and enterprises; g1t is no longer free879 /** Owners only. An empty name falls back to the slug. */
880 updateWorkspace(actor: User, slug: string, details: { name: string; description: string }): Promise<Result<Workspace>>;
Workspace names and icons, and a component kit for every control881 /**
Agents and memory, checks and conflicts, profiles, slug renames, custom domains882 * Owners only. Changes the slug, the first segment of the workspace's
883 * URLs; the display name is untouched. The old slug redirects to the new
884 * one, and stays reserved for this workspace, for `SLUG_HOLD_DAYS`.
885 * Publishes `workspace.renamed`.
886 */
887 renameWorkspace(actor: User, slug: string, newSlug: string): Promise<Result<Workspace>>;
888 /** Whether `renameWorkspace` would be allowed, changing nothing. */
889 checkWorkspaceRename(actor: User, slug: string, newSlug: string): Promise<Result<boolean>>;
890 /**
891 * The workspace's current slug when `slug` is one it was renamed from
Merge branch 'worktree-agent-a8385d293d42c913a'892 * within `SLUG_HOLD_DAYS`, or when `slug` is an alias staff set for it
893 * (`WorkspaceAlias`); null otherwise, including for a slug in use.
Agents and memory, checks and conflicts, profiles, slug renames, custom domains894 */
895 resolveSlug(slug: string): Promise<string | null>;
896 /**
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look897 * Owners only, a person only. `confirm` is the slug, typed out. Refused
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member898 * for a protected workspace, and while billing cannot settle it. Its
899 * repositories, projects and apps go with it; it is kept for
900 * `WORKSPACE_RESTORE_DAYS`, when g1t's staff can restore it, then purged.
901 * Its slug is never given to anyone else; the person whose username it is
902 * may make it again once it is purged. Publishes `workspace.deleting`.
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look903 */
904 deleteWorkspace(actor: User, slug: string, confirm: string): Promise<Result<boolean>>;
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member905 /** What `deleteWorkspace` would take with it, and what stands in its way, changing nothing. */
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look906 checkWorkspaceDeletion(actor: User, slug: string): Promise<Result<WorkspaceDeletion>>;
907 /**
Workspace names and icons, and a component kit for every control908 * Owners only. `image` is the file in base64: PNG, JPEG, WebP or GIF, at
909 * most `MAX_AVATAR_BYTES`, checked by its bytes. Null removes the icon.
910 */
911 setWorkspaceAvatar(actor: User, slug: string, image: string | null): Promise<Result<Workspace>>;
912 /** A person's own avatar, as `setWorkspaceAvatar`: the new one, or null. */
913 setUserAvatar(user: User, image: string | null): Promise<Result<string | null>>;
Billing accounts, terms and enterprises; g1t is no longer free914
915 /**
916 * A workspace's own access tokens. They belong to the workspace, act as
917 * it, and keep working when the member who made one leaves. Members only.
918 */
919 listWorkspaceTokens(slug: string, viewer: Viewer): Promise<Result<AccessToken[]>>;
920 /** Owners only. */
921 removeWorkspaceToken(actor: User, slug: string, id: string): Promise<Result<boolean>>;
922
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules923 /**
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers924 * An access token: yours, or (with `input.owner`, owners only) a
925 * workspace's. People only, signed in. The plaintext token is returned
926 * once and never stored. A personal token made for a workspace that asks
927 * for approval starts pending unless you are an owner there.
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules928 */
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers929 createToken(actor: User, input: TokenInput): Promise<Result<{ token: string; info: AccessToken }>>;
930 /**
931 * Changes a token of yours, or (with `owner`, owners only) a
932 * workspace's; what is left out stays. Widening a token made for a
933 * workspace that approves tokens asks for approval again.
934 */
935 updateToken(actor: User, id: string, change: TokenChange, owner?: string | null): Promise<Result<AccessToken>>;
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules936 /** A workspace's rules for personal access tokens. Members only. */
937 getTokenPolicy(slug: string, viewer: Viewer): Promise<Result<TokenPolicy>>;
938 /** Owners only, as people. `maxLifetimeDays` of 0 removes the limit. */
939 setTokenPolicy(
940 actor: User,
941 slug: string,
942 change: Partial<Omit<TokenPolicy, "updatedBy" | "updatedAt">>,
943 ): Promise<Result<TokenPolicy>>;
944 /** The members' tokens that can reach a workspace. Owners only. */
945 listMemberTokens(
946 actor: User,
947 slug: string,
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers948 filter?: { status?: TokenStatus },
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules949 ): Promise<Result<MemberToken[]>>;
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers950 /** Approve or deny a token waiting for approval. Owners only. */
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules951 reviewTokenRequest(actor: User, slug: string, id: string, approve: boolean, reason?: string | null): Promise<Result<MemberToken>>;
952 /** Take a member's token out of the workspace. Owners only. */
953 revokeMemberToken(actor: User, slug: string, id: string, reason?: string | null): Promise<Result<boolean>>;
954
Billing accounts, terms and enterprises; g1t is no longer free955 userForSession(sessionToken: string): Promise<Viewer>;
956
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look957 /** Whether registration is invite-only. */
958 registration(): Promise<RegistrationMode>;
959 /** A person's invites and what they have left. */
960 listInvites(user: User): Promise<InvitesOverview>;
961 /**
962 * A person makes an invite, optionally for one address (emailed to it),
963 * using one of theirs or, with `workspace`, one the workspace was granted.
964 * People only: never an agent or a workspace's token.
965 */
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)966 createInvite(
967 user: User,
Merge two kinds of invite, kept apart: an invite to g1t (Settings, invite-only only, no workspace unless asked) and an invitation to a workspace (its People page)968 options?: { email?: string | null; workspace?: string | null; join?: string | null; joinRole?: "owner" | "member" | null },
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)969 ): Promise<Result<Invite>>;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look970 /** Its maker, or an owner of its workspace, revokes a pending invite; the invite comes back. */
971 revokeInvite(user: User, id: string): Promise<Result<Invite>>;
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas972 /**
973 * What a code is for. Unknown, used, revoked and expired codes all get the
974 * same answer, unless `anyStatus`: then a real code that is spent is
Merge invite emails that confirm the address: the emailed link carries a proof only that email has, so signing up from it needs no code; shared links and typed codes still confirm975 * described, with its `status`. `viewer` sets `forViewer`; `emailProof`,
976 * the `proof` from the invite email's link, sets `emailProven`.
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas977 */
978 checkInvite(
979 code: string,
980 client?: string | null,
Merge invite emails that confirm the address: the emailed link carries a proof only that email has, so signing up from it needs no code; shared links and typed codes still confirm981 options?: { viewer?: User | null; anyStatus?: boolean; emailProof?: string | null },
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas982 ): Promise<Result<InvitePreview>>;
983 /**
984 * A signed-in person uses a workspace invite sent to their address, or one
985 * sent with a repository invitation; returns the workspace's slug, or
986 * `workspace/repo`.
987 */
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look988 acceptInvite(user: User, code: string): Promise<Result<string>>;
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)989 /**
990 * Owners only. Invites someone into a workspace by address (always with an
991 * invite bound to it) or by `username`: a workspace invitation they accept
992 * or decline. Nobody joins without saying yes. `role` is what they join as.
993 */
994 inviteMember(
995 actor: User,
996 slug: string,
Workspace is the workspace's settings, in one place. Its sidebar is grouped, General, Access, Money, Compute, Code, Agents, Chat, Artifacts, Security and Integrations, every page one click away with no settings inside settings, the groups folding and the owner-only pages hidden from members; what is not here yet is marked Soon with a hint. Members is a list with search and filters for role, two-factor and team, and Invite opens a dialog: who, by username, name or email, their role and a note that goes into the invitation; nothing is filled in inline any more. Invitations is its own page with All, Pending, Accepted, Declined, Expired and Revoked filters that say how many, and each row's menu can copy the link, send it again or revoke it; identity learned to send an invitation again and to carry the note. Permissions gathers every rule about who may do what, by part: Code's base permission and member privileges, who creates teams, who creates channels, and, marked Soon with what applies today, forking private repositories, adding agents to conversations, messaging agents directly, creating spaces and default sharing, creating workspace agents and raising budgets, deploying to production and publishing packages. General lost what moved. The permissions guide is new, and the workspaces, people and teams, access, chat, teams, authentication and billing guides say where things are now.997 who: { email?: string | null; username?: string | null; role?: Role | null; message?: string | null },
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)998 ): Promise<Result<Invite>>;
999 /** The workspace invitations waiting for the person's answer, newest first. */
1000 listInvitations(user: User): Promise<WorkspaceInvitation[]>;
1001 /** Joins the invitation's workspace with its role; returns the workspace's slug. */
1002 acceptInvitation(user: User, id: string): Promise<Result<string>>;
1003 /** Declines it; whoever sent it is told in their inbox. */
1004 declineInvitation(user: User, id: string): Promise<Result<boolean>>;
1005 /** People to invite, by username prefix or name: a username, a name and an avatar each. */
1006 findPeople(query: string, limit?: number): Promise<PersonMatch[]>;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1007 /** Owners only: the workspace's invites, newest first. */
1008 workspaceInvites(slug: string, viewer: Viewer): Promise<Result<Invite[]>>;
1009 /** Owners only. */
1010 revokeWorkspaceInvite(actor: User, slug: string, id: string): Promise<Result<Invite>>;
Workspace is the workspace's settings, in one place. Its sidebar is grouped, General, Access, Money, Compute, Code, Agents, Chat, Artifacts, Security and Integrations, every page one click away with no settings inside settings, the groups folding and the owner-only pages hidden from members; what is not here yet is marked Soon with a hint. Members is a list with search and filters for role, two-factor and team, and Invite opens a dialog: who, by username, name or email, their role and a note that goes into the invitation; nothing is filled in inline any more. Invitations is its own page with All, Pending, Accepted, Declined, Expired and Revoked filters that say how many, and each row's menu can copy the link, send it again or revoke it; identity learned to send an invitation again and to carry the note. Permissions gathers every rule about who may do what, by part: Code's base permission and member privileges, who creates teams, who creates channels, and, marked Soon with what applies today, forking private repositories, adding agents to conversations, messaging agents directly, creating spaces and default sharing, creating workspace agents and raising budgets, deploying to production and publishing packages. General lost what moved. The permissions guide is new, and the workspaces, people and teams, access, chat, teams, authentication and billing guides say where things are now.1011 /** Owners only: sends a pending invitation again, by email and in the invited account's inbox. Unchanged otherwise. */
1012 resendWorkspaceInvite(actor: User, slug: string, id: string): Promise<Result<Invite>>;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1013 /** Someone without an invite asks for one. Always the same answer for a valid address. */
1014 requestAccess(email: string, about: string, client?: string | null): Promise<Result<boolean>>;
1015
Billing accounts, terms and enterprises; g1t is no longer free1016 /** Verifies git credentials: the account password or an access token. */
1017 userForGitCredentials(username: string, secret: string): Promise<Viewer>;
1018 /** Resolves a `g1t_…` access token, as sent to the API and MCP server. */
1019 userForAccessToken(token: string): Promise<Viewer>;
1020 userForSshKey(fingerprint: string): Promise<Viewer>;
1021 userByUsername(username: string): Promise<Viewer>;
1022 /** The names behind account and workspace ids; unknown ids are left out. */
1023 usernames(ids: string[]): Promise<Record<string, string>>;
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)1024 /**
1025 * Internal: the people behind these ids (at most 50) with their
1026 * workspaces, roles and repository grants, as a signed-in viewer has
1027 * them. For the agents service's audience checks only. Ids of no live
1028 * account are left out.
1029 */
1030 usersForAudience(ids: string[]): Promise<User[]>;
Billing accounts, terms and enterprises; g1t is no longer free1031
Agents and memory, checks and conflicts, profiles, slug renames, custom domains1032 /** A person's public profile, or null if there is no such account. Never an email address. */
1033 profile(username: string): Promise<Profile | null>;
1034 /** A person changes their own profile. Every field is replaced; an empty one is cleared. */
1035 updateProfile(actor: User, fields: ProfileFields): Promise<Result<Profile>>;
1036 /**
1037 * The workspaces a profile shows `viewer`: those the viewer belongs to
1038 * as well, and those of `publicIn` (where the person made a public
1039 * project) that the person really belongs to. Nothing else.
1040 */
1041 profileWorkspaces(username: string, viewer: Viewer, publicIn: string[]): Promise<ProfileWorkspace[]>;
1042
The apps you pin to your dock are kept with your account, per workspace and in your order, so the dock is the same on every device: identity keeps them in dock_pins and answers dock_pins and set_dock_pins, the dock reads them with the rest of the page, pins kept only on this device carry over with your first change, this device's copy still draws the dock when identity can't be reached, a pin that can't be saved says so, and they go when you or the workspace do; the workspaces guide says how.1043 /**
1044 * The apps `user` pinned to their dock in the workspace `workspace` (a
1045 * slug), in the order they set; null when they never saved any there or
1046 * are not one of its members. Kept with the account, so every device
1047 * shows the same dock.
1048 */
1049 dockPins(user: User, workspace: string): Promise<string[] | null>;
1050 /**
1051 * Replaces `user`'s dock pins in `workspace` with `apps`, in that order:
1052 * keys of lowercase letters, digits and hyphens, repeats dropped, at
1053 * most `MAX_DOCK_PINS`. Which keys are real apps is the caller's to check.
1054 */
1055 setDockPins(user: User, workspace: string, apps: string[]): Promise<Result<string[]>>;
1056
Chat has sounds. A short warm cue plays for a message in a conversation you have open but aren't looking at, a direct message, a mention of you and an agent finishing something for you, with a soft tick for sending that is off until you turn it on; never for your own messages, a muted conversation, a conversation you are looking at in a window that has the front, or while you have paused notifications, and never twice for one message or more than once in a second and a half. The cues are made by the browser itself in two sets, Soft and Bright, so nothing is downloaded; the Sounds and notifications section of your notification settings turns them on and off, picks the set and the volume, plays each one, and offers desktop notifications for when the window is behind; Do not disturb is the one pause everyone sees, set from the chat sidebar's bell, the settings, or your avatar menu, now with a two-hour choice, and the top bar's bell shows a dot while it's on. Your choices are kept with your account. The sound layer sits behind one small interface so the desktop app can play them natively later. The chat and notifications guides say the rules.1057 /**
1058 * How chat sounds and desktop notifications reach `user`, kept with the
1059 * account so every device and the desktop app behave the same
1060 * (`SoundSettings`). The defaults when they never changed them.
1061 */
1062 soundSettings(user: User): Promise<SoundSettings>;
1063 /**
1064 * Changes some of them: only the fields sent change, and `sound_cues`
1065 * merges one cue at a time. Returns the settings as kept.
1066 */
1067 setSoundSettings(user: User, change: SoundSettingsChange): Promise<Result<SoundSettings>>;
1068
Billing accounts, terms and enterprises; g1t is no longer free1069 listSshKeys(user: User): Promise<SshKey[]>;
1070 /** Takes one line in OpenSSH public key format. */
1071 addSshKey(user: User, title: string, publicKey: string): Promise<Result<SshKey>>;
1072 removeSshKey(user: User, id: string): Promise<void>;
1073
1074 listAccessTokens(user: User): Promise<AccessToken[]>;
1075 /**
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers1076 * A credential minted for a person or workspace by another service. The
1077 * plaintext token is returned once and never stored. With `ttlSeconds`
1078 * the token expires and is left out of token lists; that form is used
1079 * for hosted agents. Tokens people make use `createToken`.
Billing accounts, terms and enterprises; g1t is no longer free1080 */
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step1081 createAccessToken(
1082 user: User,
1083 name: string,
1084 ttlSeconds?: number,
1085 grant?: TokenGrant & { listed?: boolean },
1086 ): Promise<{ token: string; info: AccessToken }>;
Billing accounts, terms and enterprises; g1t is no longer free1087 /**
1088 * A token for a g1t agent working for `onBehalfOf`: it acts as
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent1089 * `g1t`, in `scope.repo` only, and only for `scope.operations`.
Billing accounts, terms and enterprises; g1t is no longer free1090 */
1091 createAgentToken(
1092 onBehalfOf: User,
1093 scope: AgentScope,
1094 ttlSeconds: number,
1095 ): Promise<{ token: string; info: AccessToken }>;
1096 removeAccessToken(user: User, id: string): Promise<void>;
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1097 /**
1098 * A token for one sandbox run: it acts as the agent on behalf of
1099 * `onBehalfOf`, can do only what the run's kind needs in `repo`, and
1100 * expires after `ttlSeconds`. See `audit.ts`.
1101 */
1102 createRunCredential(input: CreateRunCredentialInput): Promise<{ token: string; info: AccessToken }>;
1103 /** Ties tokens, by the SHA-256 of their text in hex, to the agent run their sandbox recorded. */
1104 bindRunCredentials(tokenHashes: string[], runId: string): Promise<boolean>;
1105 /** Ends a sandbox's run credentials, by hash or by run. Never touches another token. */
1106 revokeRunCredentials(target: { tokenHashes?: string[]; runId?: string | null }): Promise<boolean>;
Billing accounts, terms and enterprises; g1t is no longer free1107}
1108
1109
1110/** What an agent's token may do: these operations, in this repository. */
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1111export type AgentScope = { repo: RepoPath; operations: string[]; run?: RunBinding };
Agents and memory, checks and conflicts, profiles, slug renames, custom domains1112
The apps you pin to your dock are kept with your account, per workspace and in your order, so the dock is the same on every device: identity keeps them in dock_pins and answers dock_pins and set_dock_pins, the dock reads them with the rest of the page, pins kept only on this device carry over with your first change, this device's copy still draws the dock when identity can't be reached, a pin that can't be saved says so, and they go when you or the workspace do; the workspaces guide says how.1113/** The most apps a person pins in one workspace. Mirrors `MAX_DOCK_PINS` in `crates/contracts/src/identity.rs`. */
1114export const MAX_DOCK_PINS = 24;
1115
Agents and memory, checks and conflicts, profiles, slug renames, custom domains1116/** The most characters each profile field takes. Mirrors `crates/contracts/src/identity.rs`. */
Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039)1117export const PROFILE_LIMITS = { name: 80, bio: 160, location: 80, website: 200, pronouns: 40, timezone: 64 } as const;
Agents and memory, checks and conflicts, profiles, slug renames, custom domains1118
1119/** What anyone may see about a person, at `g1t.sh/u/<username>`. */
1120export type Profile = {
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers1121 /** Lowercased: what the profile is found and linked by. */
Agents and memory, checks and conflicts, profiles, slug renames, custom domains1122 username: string;
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers1123 /** The username as its owner wrote it (`Ana`), when that differs: what the page shows. */
1124 displayUsername?: string | null;
Agents and memory, checks and conflicts, profiles, slug renames, custom domains1125 /** The name they go by, if they gave one. */
1126 name: string | null;
1127 bio: string | null;
1128 location: string | null;
1129 /** Always an `https://` address. */
1130 website: string | null;
1131 pronouns: string | null;
Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039)1132 /** The time zone they are in, an IANA name such as `America/Denver`. */
1133 timezone: string | null;
Agents and memory, checks and conflicts, profiles, slug renames, custom domains1134 /** The uploaded avatar's hash, served at `/avatars/<avatar>`. */
1135 avatar: string | null;
1136 /** When the account was made. RFC 3339. */
1137 createdAt: string;
1138};
1139
1140/** What a person may change on their profile. Empty clears a field. */
1141export type ProfileFields = {
1142 name: string;
1143 bio: string;
1144 location: string;
1145 /** `https://…`; a bare `example.com` is taken as `https://example.com`. */
1146 website: string;
1147 pronouns: string;
Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039)1148 /** An IANA time zone name, such as `America/Denver`; empty clears it. */
1149 timezone: string;
Agents and memory, checks and conflicts, profiles, slug renames, custom domains1150};
1151
1152/** A workspace on a person's profile. */
1153export type ProfileWorkspace = { slug: string; name: string; avatar: string | null };

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