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.
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 1 | /** |
| 2 | * Teams: groups of a workspace's members, given roles on repositories | |
| 3 | * together, mentioned together and asked to review together. Kept by the | |
| 4 | * identity service; mirrors `crates/contracts/src/teams.rs`. | |
| 5 | */ | |
| 6 | import type { RepoRole } from "./access"; | |
| 7 | import type { User } from "./identity"; | |
| 8 | import type { Result } from "./result"; | |
| 9 | ||
| 10 | export type TeamVisibility = "visible" | "secret"; | |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 11 | |
| 12 | /** | |
| 13 | * Who may create a workspace's teams: any member (the default) or its | |
| 14 | * owners only. Owners change it in the workspace's settings. | |
| 15 | */ | |
| 16 | export type TeamCreation = "members" | "owners"; | |
| 17 | ||
| 18 | /** Whether someone with `role` in a workspace may create its teams. */ | |
| 19 | export function mayCreateTeams(setting: TeamCreation | null | undefined, role: "owner" | "member" | null | undefined): boolean { | |
| 20 | if (!role) return false; | |
| 21 | return (setting ?? "members") === "members" || role === "owner"; | |
| 22 | } | |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 23 | export type TeamRole = "member" | "maintainer"; |
| 24 | export type ReviewAlgorithm = "round_robin" | "load_balance"; | |
| 25 | ||
| 26 | export const TEAM_VISIBILITY_LABELS: Record<TeamVisibility, string> = { | |
| 27 | visible: "Visible", | |
| 28 | secret: "Secret", | |
| 29 | }; | |
| 30 | ||
| 31 | export const TEAM_VISIBILITY_SUMMARIES: Record<TeamVisibility, string> = { | |
| 32 | visible: "Every member of the workspace can see it and mention it.", | |
| 33 | secret: "Only its own people and the workspace's owners can see it. Secret teams cannot be nested.", | |
| 34 | }; | |
| 35 | ||
| 36 | export const REVIEW_ALGORITHM_LABELS: Record<ReviewAlgorithm, string> = { | |
| 37 | round_robin: "Round robin", | |
| 38 | load_balance: "Load balance", | |
| 39 | }; | |
| 40 | ||
| 41 | export const REVIEW_ALGORITHM_SUMMARIES: Record<ReviewAlgorithm, string> = { | |
| 42 | round_robin: "Whoever this team asked least recently goes first.", | |
| 43 | load_balance: "Whoever has the fewest pull requests waiting on their review goes first.", | |
| 44 | }; | |
| 45 | ||
| 46 | /** The most people review assignment picks for one request. */ | |
| 47 | export const MAX_ASSIGNED = 10; | |
| 48 | ||
| 49 | export type ReviewAssignment = { | |
| 50 | enabled: boolean; | |
| 51 | algorithm: ReviewAlgorithm; | |
| 52 | count: number; | |
| 53 | skip_busy: boolean; | |
| 54 | busy_at: number; | |
| 55 | include_child_teams: boolean; | |
| 56 | excluded: string[]; | |
| 57 | notify_team: boolean; | |
| 58 | }; | |
| 59 | ||
| 60 | export const DEFAULT_REVIEW_ASSIGNMENT: ReviewAssignment = { | |
| 61 | enabled: false, | |
| 62 | algorithm: "round_robin", | |
| 63 | count: 1, | |
| 64 | skip_busy: false, | |
| 65 | busy_at: 5, | |
| 66 | include_child_teams: false, | |
| 67 | excluded: [], | |
| 68 | notify_team: false, | |
| 69 | }; | |
| 70 | ||
| 71 | export type TeamRef = { slug: string; name: string }; | |
| 72 | ||
| The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens. | 73 | /** |
| 74 | * Who leads a team: a person on it. `agent` is a lead from g1t's retired | |
| 75 | * hosted agents, kept only so old data parses; identity never answers with | |
| 76 | * one and no team can be given one. | |
| 77 | */ | |
| 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. | 78 | export type TeamLead = |
| 79 | | { kind: "user"; username: string; name: string | null; avatar: string | null } | |
| 80 | | { kind: "agent"; agent_id: string }; | |
| 81 | ||
| 82 | /** A team's chat channel: its id, and its name (without `#`) when it was chosen. */ | |
| 83 | export type TeamChannel = { id: string; name: string }; | |
| 84 | ||
| The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens. | 85 | /** How `update_team` takes a lead: `@username`; `""` for none. */ |
| 86 | export function leadInput(lead: { username: string } | null): string { | |
| 87 | return lead ? `@${lead.username}` : ""; | |
| 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. | 88 | } |
| 89 | ||
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 90 | export type Team = { |
| 91 | id: string; | |
| 92 | workspace: string; | |
| 93 | slug: string; | |
| 94 | name: string; | |
| 95 | description: string | null; | |
| 96 | visibility: TeamVisibility; | |
| 97 | parent: TeamRef | null; | |
| 98 | notify: boolean; | |
| 99 | review_assignment: ReviewAssignment; | |
| 100 | members_count: number; | |
| 101 | repos_count: number; | |
| 102 | child_teams_count: number; | |
| 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. | 103 | lead: TeamLead | null; |
| 104 | channel: TeamChannel | null; | |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 105 | viewer_role: TeamRole | null; |
| 106 | can_manage: boolean; | |
| 107 | created_at: string; | |
| 108 | updated_at: string; | |
| 109 | }; | |
| 110 | ||
| 111 | export type TeamMember = { | |
| 112 | username: string; | |
| 113 | name: string | null; | |
| 114 | avatar: string | null; | |
| 115 | role: TeamRole; | |
| 116 | /** The child team they are in, when listed through one. */ | |
| 117 | via: string | null; | |
| 118 | }; | |
| 119 | ||
| 120 | export type TeamRepo = { | |
| 121 | /** `workspace/name`. */ | |
| 122 | repo: string; | |
| 123 | repo_id: string; | |
| 124 | role: RepoRole; | |
| 125 | /** The parent team it comes from, when inherited. */ | |
| 126 | inherited_from: string | null; | |
| 127 | }; | |
| 128 | ||
| 129 | /** A team with a role on a repository, as its Access settings list it. */ | |
| 130 | export type RepoTeam = { | |
| 131 | slug: string; | |
| 132 | name: string; | |
| 133 | role: RepoRole; | |
| 134 | members_count: number; | |
| 135 | visibility: TeamVisibility; | |
| 136 | }; | |
| 137 | ||
| 138 | export type NewTeam = { | |
| 139 | name: string; | |
| 140 | slug?: string | null; | |
| 141 | description?: string | null; | |
| 142 | visibility?: TeamVisibility | null; | |
| 143 | parent?: string | null; | |
| 144 | notify?: boolean | null; | |
| 145 | members?: string[]; | |
| 146 | }; | |
| 147 | ||
| 148 | /** What changes; the rest stays. `parent: ""` takes the team out from under its parent. */ | |
| 149 | export type TeamChanges = { | |
| 150 | name?: string; | |
| 151 | slug?: string; | |
| 152 | description?: string; | |
| 153 | visibility?: TeamVisibility; | |
| 154 | parent?: string; | |
| 155 | notify?: boolean; | |
| 156 | review_assignment?: ReviewAssignment; | |
| The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens. | 157 | /** `@username`, someone on the team (see `leadInput`); `""` for no lead. */ |
| 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. | 158 | lead?: string; |
| 159 | /** The chat channel's id; `""` for none. Given with `channel_name`. */ | |
| 160 | channel_id?: string; | |
| 161 | channel_name?: string; | |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 162 | }; |
| 163 | ||
| 164 | /** `@acme/backend`, the way a team is mentioned. */ | |
| 165 | export function teamHandle(team: { workspace: string; slug: string }): string { | |
| 166 | return `@${team.workspace}/${team.slug}`; | |
| 167 | } | |
| 168 | ||
| 169 | /** A team's slug from its name, as identity makes it. Null when nothing is left. */ | |
| 170 | export function teamSlug(name: string): string | null { | |
| 171 | let slug = ""; | |
| 172 | for (const c of name.trim()) { | |
| 173 | if (/[A-Za-z0-9]/.test(c)) slug += c.toLowerCase(); | |
| 174 | else if (slug && !slug.endsWith("-")) slug += "-"; | |
| 175 | } | |
| 176 | slug = slug.replace(/-+$/, "").slice(0, 60).replace(/-+$/, ""); | |
| 177 | return slug ? slug : null; | |
| 178 | } | |
| 179 | ||
| 180 | /** One person's teams in a workspace, for the Members page. */ | |
| 181 | export type MemberTeams = { username: string; teams: TeamRef[] }; | |
| 182 | ||
| 183 | /** Identity's team methods. */ | |
| 184 | export interface TeamsClient { | |
| 185 | listTeams(viewer: User | null, workspace: string, query?: string | null): Promise<Result<Team[]>>; | |
| 186 | getTeam(viewer: User | null, workspace: string, team: string): Promise<Result<Team>>; | |
| 187 | createTeam(actor: User, workspace: string, team: NewTeam): Promise<Result<Team>>; | |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 188 | /** Who may create the workspace's teams. Owners only. */ |
| 189 | setTeamCreation(actor: User, slug: string, setting: TeamCreation): Promise<Result<TeamCreation>>; | |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 190 | updateTeam(actor: User, workspace: string, team: string, changes: TeamChanges): Promise<Result<Team>>; |
| 191 | deleteTeam(actor: User, workspace: string, team: string): Promise<Result<boolean>>; | |
| 192 | teamMembers(viewer: User | null, workspace: string, team: string, includeChildTeams?: boolean): Promise<Result<TeamMember[]>>; | |
| 193 | setTeamMember(actor: User, workspace: string, team: string, username: string, role: TeamRole): Promise<Result<TeamMember>>; | |
| 194 | removeTeamMember(actor: User, workspace: string, team: string, username: string): Promise<Result<boolean>>; | |
| 195 | childTeams(viewer: User | null, workspace: string, team: string): Promise<Result<Team[]>>; | |
| 196 | teamRepos(viewer: User | null, workspace: string, team: string): Promise<Result<TeamRepo[]>>; | |
| 197 | setTeamRepo(actor: User, workspace: string, team: string, owner: string, name: string, role: RepoRole): Promise<Result<TeamRepo>>; | |
| 198 | removeTeamRepo(actor: User, workspace: string, team: string, owner: string, name: string): Promise<Result<boolean>>; | |
| 199 | userTeams(viewer: User | null, workspace: string, username: string): Promise<Result<Team[]>>; | |
| 200 | /** Each member's teams the viewer can see. Members only. */ | |
| 201 | teamMemberships(viewer: User | null, workspace: string): Promise<Result<MemberTeams[]>>; | |
| 202 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.