Skip to content
999 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.

Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow1import type { ActionsApi } from "./actions";
2import type { BillingAdminApi, BillingApi } from "./billing";
3import type { DeploymentsApi } from "./deployments";
4import type { ProjectsApi } from "./projects";
5import type { EventsApi } from "./events";
6import type { IdentityAdminApi, IdentityApi } from "./identity";
7import type { IntegrationsApi } from "./integrations";
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member8import type { PackagesApi } from "./packages";
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow9import type { WebhooksApi } from "./webhooks";
10import type { ReposApi } from "./repos";
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar11import type { PullDetail, WorkApi } from "./work";
12import type { Result } from "./result";
Every agent can have its own computer. A session that needs one wakes it: a home of its own on g1t cloud, one per agent and never shared, where it runs commands, reads and writes files and keeps what it made, with each session working in its own folder under a shared home; after ten idle minutes it sleeps, its home kept as a snapshot and restored when it wakes, and Reset wipes the home while memory and artifacts stay. Its shell and files are abilities with the usual choices, Alone, Alone when asked, Ask first or Never, offered only inside sessions and never to a chat reply; every command shows on the session with its output, and the agent's new Computer tab shows the state, the disk used of the five gigabytes included, the recent commands, and Wake, Put to sleep and Reset. Machine time counts only while it is awake, on the sandbox lines of the ledger that name the agent and who asked, held to the same spend caps as the session; the disk itself costs nothing in this version. The runner gained a long-lived supervisor that answers the computer's requests inside the container, and the runner service a computer per agent that keeps its snapshot in the agent homes bucket when one is attached, and says so when none is. The REST API and the agent tool can read a computer, wake it, put it to sleep and reset it. The agents, abilities, sessions, runners, billing and deploy guides say how it works and what an operator sets up; pinning a computer to your own runner, its browser and take-over come next.13import type { RunnerComputerApi } from "./runner";
Fast pages, required checks on the branch, self-hosted runners, honest incidents14import type { RunnersApi } from "./runners";
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow15
16/** A service binding, as far as these clients need it. */
17export type ServiceBinding = {
18 fetch(input: string, init?: RequestInit): Promise<Response>;
19};
20
21/**
22 * Calls a method on a service that speaks the JSON protocol used by the
23 * Rust services: `POST /rpc/<method>` with the arguments as the body.
24 */
25async function rpc<T>(
26 service: ServiceBinding,
27 method: string,
28 args: object,
29): Promise<T> {
30 // The hostname is ignored; a service binding always reaches its service.
31 const response = await service.fetch(`https://service/rpc/${method}`, {
32 method: "POST",
33 headers: { "content-type": "application/json" },
34 body: JSON.stringify(args),
35 });
36 if (!response.ok) {
37 throw new Error(`${method} failed with status ${response.status}`);
38 }
39 return (await response.json()) as T;
40}
41
42export function identityClient(service: ServiceBinding): IdentityApi {
43 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
44 return {
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 confirm45 register: (username, email, password, inviteCode, client, emailProof) =>
46 call("register", {
47 username,
48 email,
49 password,
50 invite_code: inviteCode ?? null,
51 email_proof: emailProof ?? null,
52 client: client ?? null,
53 }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow54 signIn: (username, password, client) => call("sign_in", { username, password, client: client ?? null }),
Merge main (membership, two-factor, GitHub repo roles) into tokens55 twoFactorSignIn: (challenge, code, client) => call("two_factor_sign_in", { challenge, code, client: client ?? null }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow56 signOut: (sessionToken) => call("sign_out", { sessionToken }),
57 resendVerification: (user) => call("resend_verification", { user }),
58 verifyEmail: (token) => call("verify_email", { token }),
59 requestPasswordReset: (email, client) => call("request_password_reset", { email, client: client ?? null }),
60 resetPassword: (token, password) =>
61 call("reset_password", { token, password }),
62 deviceStart: (clientName) => call("device_start", { clientName }),
63 deviceLookup: (userCode) => call("device_lookup", { userCode }),
64 deviceResolve: (userCode, user, approve) =>
65 call("device_resolve", { userCode, user, approve }),
66 deviceClaim: (deviceCode) => call("device_claim", { deviceCode }),
67 oauthAuthorize: (user, approval) => call("oauth_authorize", { user, ...approval }),
68 oauthExchange: (code, codeVerifier, clientId, redirectUri) =>
69 call("oauth_exchange", { code, codeVerifier, clientId, redirectUri }),
70 oauthRefresh: (refreshToken, clientId) =>
71 call("oauth_refresh", { refreshToken, clientId }),
72 listOAuthGrants: (user) => call("list_oauth_grants", { user }),
73 revokeOAuthGrant: (user, id) => call("revoke_oauth_grant", { user, id }),
74 updateOAuthGrant: (user, id, grant) =>
75 call("update_oauth_grant", { user, id, scopes: grant.scopes }),
76 createWorkspace: (user, slug, name) => call("create_workspace", { user, slug, name }),
77 getWorkspace: (slug) => call("get_workspace", { slug }),
Merge branch 'worktree-agent-a2013627e5ea4ab13'78 workspaceResidency: (slug) => call("workspace_residency", { slug }),
79 setWorkspaceResidency: (actor, slug, residency) => call("set_workspace_residency", { actor, slug, residency }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow80 listMembers: (slug, viewer) => call("list_members", { slug, viewer }),
81 addMember: (actor, slug, username) => call("add_member", { actor, slug, username }),
82 removeMember: (actor, slug, username) =>
Merge main (membership, two-factor, GitHub repo roles) into tokens83 call("remove_member", { actor, slug, username, surface: "web" }),
84 updateMember: (actor, slug, username, change) =>
85 call("update_member", { actor, slug, username, role: change.role ?? null, org_roles: change.org_roles ?? null, surface: "web" }),
86 transferOwnership: (actor, slug, username) => call("transfer_ownership", { actor, slug, username, surface: "web" }),
87 leaveWorkspace: (user, slug) => call("leave_workspace", { user, slug, surface: "web" }),
88 setMemberPrivileges: (actor, slug, change) => call("set_member_privileges", { actor, slug, privileges: change, surface: "web" }),
89 setTwoFactorRequirement: (actor, slug, required) =>
90 call("set_two_factor_requirement", { actor, slug, required, surface: "web" }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow91 updateWorkspace: (actor, slug, details) =>
92 call("update_workspace", { actor, slug, ...details }),
93 renameWorkspace: (actor, slug, newSlug) => call("rename_workspace", { actor, slug, newSlug }),
94 checkWorkspaceRename: (actor, slug, newSlug) =>
95 call("check_workspace_rename", { actor, slug, newSlug }),
96 resolveSlug: (slug) => call("resolve_slug", { slug }),
97 deleteWorkspace: (actor, slug, confirm) => call("delete_workspace", { actor, slug, confirm }),
98 checkWorkspaceDeletion: (actor, slug) => call("check_workspace_deletion", { actor, slug }),
99 setWorkspaceAvatar: (actor, slug, image) => call("set_workspace_avatar", { actor, slug, image }),
100 setUserAvatar: (user, image) => call("set_user_avatar", { user, image }),
101 listWorkspaceTokens: (slug, viewer) => call("list_workspace_tokens", { slug, viewer }),
102 removeWorkspaceToken: (actor, slug, id) =>
103 call("remove_workspace_token", { actor, slug, id }),
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers104 createToken: (actor, input) =>
105 call("create_token", {
106 actor,
107 owner: input.owner ?? null,
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules108 name: input.name,
109 description: input.description ?? null,
110 ttl_seconds: input.ttlSeconds,
111 workspace: input.workspace,
112 repository_selection: input.repositorySelection,
113 repositories: input.repositories,
114 permissions: input.permissions,
Merge main into Artifacts Phase 2115 website: input.website ?? false,
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules116 }),
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers117 updateToken: (actor, id, change, owner) =>
118 call("update_token", {
119 actor,
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules120 id,
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers121 owner: owner ?? null,
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules122 name: change.name ?? null,
123 description: change.description ?? null,
124 repository_selection: change.repositorySelection ?? null,
125 repositories: change.repositories ?? null,
126 permissions: change.permissions ?? null,
Merge main into Artifacts Phase 2127 website: change.website ?? null,
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules128 }),
129 getTokenPolicy: (slug, viewer) => call("get_token_policy", { slug, viewer }),
130 setTokenPolicy: (actor, slug, change) =>
131 call("set_token_policy", {
132 actor,
133 slug,
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers134 allow_tokens_for_all_workspaces: change.allowTokensForAllWorkspaces ?? null,
135 allow_tokens_for_this_workspace: change.allowTokensForThisWorkspace ?? null,
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules136 require_approval: change.requireApproval ?? null,
137 max_lifetime_days: change.maxLifetimeDays ?? null,
138 forbid_no_expiry: change.forbidNoExpiry ?? null,
139 surface: "web",
140 }),
141 listMemberTokens: (actor, slug, filter = {}) =>
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers142 call("list_member_tokens", { actor, slug, status: filter.status ?? null }),
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules143 reviewTokenRequest: (actor, slug, id, approve, reason) =>
144 call("review_token_request", { actor, slug, id, approve, reason: reason ?? null, surface: "web" }),
145 revokeMemberToken: (actor, slug, id, reason) =>
146 call("revoke_member_token", { actor, slug, id, reason: reason ?? null, surface: "web" }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow147 userForSession: (sessionToken) => call("user_for_session", { sessionToken }),
148 registration: () => call("registration", {}),
149 listInvites: (user) => call("list_invites", { user }),
150 createInvite: (user, options = {}) =>
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)151 call("create_invite", { user, email: options.email ?? null, workspace: options.workspace ?? null, join: options.join ?? null, join_role: options.join ? (options.joinRole ?? null) : null }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow152 revokeInvite: (user, id) => call("revoke_invite", { user, id }),
153 checkInvite: (code, client, options = {}) =>
154 call("check_invite", {
155 code,
156 client: client ?? null,
157 viewer: options.viewer ?? null,
158 any_status: options.anyStatus ?? false,
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 confirm159 email_proof: options.emailProof ?? null,
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow160 }),
161 acceptInvite: (user, code) => call("accept_invite", { user, code }),
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)162 inviteMember: (actor, slug, who) =>
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.163 call("invite_member", { actor, slug, email: who.email ?? "", username: who.username ?? null, role: who.role ?? null, message: who.message ?? 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)164 listInvitations: (user) => call("list_invitations", { user }),
165 acceptInvitation: (user, id) => call("accept_invitation", { user, id, surface: "web" }),
166 declineInvitation: (user, id) => call("decline_invitation", { user, id, surface: "web" }),
167 findPeople: (query, limit) => call("find_people", { query, limit: limit ?? null }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow168 workspaceInvites: (slug, viewer) => call("workspace_invites", { slug, viewer }),
169 revokeWorkspaceInvite: (actor, slug, id) => call("revoke_workspace_invite", { actor, slug, id }),
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.170 resendWorkspaceInvite: (actor, slug, id) => call("resend_workspace_invite", { actor, slug, id }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow171 requestAccess: (email, about, client) => call("request_access", { email, about, client: client ?? null }),
172 userForGitCredentials: (username, secret) =>
173 call("user_for_git_credentials", { username, secret }),
174 userForAccessToken: (token) => call("user_for_access_token", { token }),
175 userForSshKey: (fingerprint) => call("user_for_ssh_key", { fingerprint }),
176 userByUsername: (username) => call("user_by_username", { username }),
177 usernames: (ids) => call("usernames", { ids }),
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)178 usersForAudience: (ids) => call("users_for_audience", { ids }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow179 profile: (username) => call("profile", { username }),
180 updateProfile: (actor, fields) => call("update_profile", { actor, ...fields }),
181 profileWorkspaces: (username, viewer, publicIn) =>
182 call("profile_workspaces", { username, viewer, public: publicIn }),
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.183 dockPins: (user, workspace) => call("dock_pins", { user, workspace }),
184 setDockPins: (user, workspace, apps) => call("set_dock_pins", { user, workspace, apps }),
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.185 soundSettings: (user) => call("sound_settings", { user }),
186 setSoundSettings: (user, change) => call("set_sound_settings", { user, change }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow187 listSshKeys: (user) => call("list_ssh_keys", { user }),
188 addSshKey: (user, title, publicKey) =>
189 call("add_ssh_key", { user, title, publicKey }),
190 removeSshKey: (user, id) => call("remove_ssh_key", { user, id }),
191 listAccessTokens: (user) => call("list_access_tokens", { user }),
192 createAccessToken: (user, name, ttlSeconds, grant) =>
193 call("create_access_token", {
194 user,
195 name,
196 ttlSeconds,
197 scopes: grant?.scopes ?? null,
198 listed: grant?.listed ?? false,
199 }),
200 createAgentToken: (onBehalfOf, scope, ttlSeconds) =>
201 call("create_agent_token", { onBehalfOf, scope, ttlSeconds }),
202 removeAccessToken: (user, id) => call("remove_access_token", { user, id }),
203 createRunCredential: (input) => call("create_run_credential", input),
204 bindRunCredentials: (tokenHashes, runId) => call("bind_run_credentials", { tokenHashes, runId }),
205 revokeRunCredentials: (target) => call("revoke_run_credentials", target),
206 // Who has access to a repository; see access.ts.
207 repoAccess: (owner, name, viewer) => call("repo_access", { viewer, path: { namespace: owner, name } }),
208 addCollaborator: (actor, owner, name, invitee, role) =>
209 call("add_collaborator", { actor, path: { namespace: owner, name }, invitee, role }),
210 setCollaboratorRole: (actor, owner, name, username, role) =>
211 call("set_collaborator_role", { actor, path: { namespace: owner, name }, username, role }),
212 removeCollaborator: (actor, owner, name, username) =>
213 call("remove_collaborator", { actor, path: { namespace: owner, name }, username }),
214 collaboratorPermission: (viewer, owner, name, username) =>
215 call("collaborator_permission", { viewer, path: { namespace: owner, name }, username }),
216 myRepoInvitations: (user) => call("my_repo_invitations", { user }),
217 respondRepoInvitation: (user, id, accept) => call("respond_repo_invitation", { user, id, accept }),
218 revokeRepoInvitation: (actor, owner, name, id) =>
219 call("revoke_repo_invitation", { actor, path: { namespace: owner, name }, id }),
220 setBasePermission: (actor, slug, base) => call("set_base_permission", { actor, slug, base_permission: base }),
221 outsideCollaborators: (viewer, slug) => call("outside_collaborators", { viewer, slug }),
Merge branch 'worktree-agent-a5a5de74d8863d554' into worktree-agent-a16631325aecf58ca222 // A repository's deploy keys; see deploy-keys.ts.
223 listDeployKeys: (viewer, owner, name) => call("list_deploy_keys", { viewer, path: { namespace: owner, name } }),
224 addDeployKey: (actor, owner, name, key) =>
225 call("add_deploy_key", { actor, path: { namespace: owner, name }, title: key.title, key: key.key, read_only: key.readOnly }),
226 removeDeployKey: (actor, owner, name, id) => call("remove_deploy_key", { actor, path: { namespace: owner, name }, id }),
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar227 // Teams; see teams.ts.
228 listTeams: (viewer, workspace, query) => call("list_teams", { viewer, workspace, query: query ?? null }),
229 getTeam: (viewer, workspace, team) => call("get_team", { viewer, workspace, team }),
230 createTeam: (actor, workspace, team) => call("create_team", { actor, workspace, ...team }),
Merge branch 'worktree-agent-ad7c6d88d93adc817'231 setTeamCreation: (actor, slug, setting) => call("set_team_creation", { actor, slug, team_creation: setting }),
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar232 updateTeam: (actor, workspace, team, changes) => call("update_team", { actor, workspace, team, ...changes }),
233 deleteTeam: (actor, workspace, team) => call("delete_team", { actor, workspace, team }),
234 teamMembers: (viewer, workspace, team, includeChildTeams) =>
235 call("team_members", { viewer, workspace, team, include_child_teams: includeChildTeams ?? false }),
236 setTeamMember: (actor, workspace, team, username, role) =>
237 call("set_team_member", { actor, workspace, team, username, role }),
238 removeTeamMember: (actor, workspace, team, username) =>
239 call("remove_team_member", { actor, workspace, team, username }),
240 childTeams: (viewer, workspace, team) => call("child_teams", { viewer, workspace, team }),
241 teamRepos: (viewer, workspace, team) => call("team_repos", { viewer, workspace, team }),
242 setTeamRepo: (actor, workspace, team, owner, name, role) =>
243 call("set_team_repo", { actor, workspace, team, repo: { namespace: owner, name }, role }),
244 removeTeamRepo: (actor, workspace, team, owner, name) =>
245 call("remove_team_repo", { actor, workspace, team, repo: { namespace: owner, name } }),
246 userTeams: (viewer, workspace, username) => call("user_teams", { viewer, workspace, username }),
247 teamMemberships: (viewer, workspace) => call("team_memberships", { viewer, workspace }),
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.248 teamAgents: (viewer, workspace, team) => call("team_agents", { viewer, workspace, team }),
249 setTeamAgent: (actor, workspace, team, agentId) => call("set_team_agent", { actor, workspace, team, agent_id: agentId }),
250 removeTeamAgent: (actor, workspace, team, agentId) => call("remove_team_agent", { actor, workspace, team, agent_id: agentId }),
An agent is on teams the way a person is: through team membership, with no team or department of its own. Add it to teams when you create it, from its profile or from the team's page; the Agents sidebar groups agents by the teams they're on, with those on none last; what an agent is told about its teammates, team budgets, skills reach, Spend's By team and the People pages all read memberships. Agents with an old team name matching a team are moved onto it once; the rest are left off a team. Templates name a role only. The agents, teams and people guides say how.251 agentTeams: (workspace, agentId) => call("agent_teams", { workspace, agent_id: agentId }),
252 teamAgentIndex: (workspace) => call("team_agent_index", { workspace }),
253 adoptAgentTeams: (workspaceId, agents) => call("adopt_agent_teams", { workspace_id: workspaceId, agents }),
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.254 // People; see people.ts.
255 peopleDirectory: (viewer, workspace) => call("people_directory", { viewer, workspace }),
256 setMemberProfile: (actor, workspace, username, changes) => call("set_member_profile", { actor, workspace, username, ...changes }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow257 };
258}
259
260/**
261 * The slug a renamed workspace has now, for a `workspace.renamed` handler:
262 * asked of identity by the workspace's id, so that renames delivered twice
263 * or out of order converge. Falls back to the event's `to`.
264 */
265export async function currentWorkspaceSlug(
266 identity: ServiceBinding,
267 renamed: { workspaceId: string; to: string },
268): Promise<string> {
269 const names = await identityClient(identity).usernames([renamed.workspaceId]);
270 return names[renamed.workspaceId] ?? renamed.to;
271}
272
273/** The slugs whose rows move to `current`: the two a rename names, less `current`. */
274export function staleSlugs(renamed: { from: string; to: string }, current: string): string[] {
275 return [...new Set([renamed.from, renamed.to])].filter((slug) => slug !== current);
276}
277
278/**
279 * Where a transferred repository is now, as `namespace/name`, for a
280 * `repo.transferred` handler: asked of repos by id, so transfers delivered
281 * twice or out of order converge. Falls back to the event's destination.
282 */
283export async function currentRepoPath(
284 repos: ServiceBinding,
285 transferred: { repoId: string; name: string; to: string },
286): Promise<string> {
287 const path = await rpc<{ namespace: string; name: string } | null>(repos, "path_by_id", { id: transferred.repoId });
288 return path ? `${path.namespace}/${path.name}` : `${transferred.to}/${transferred.name}`;
289}
290
291/** The paths whose rows move to `current`: the two a transfer names, less `current`. */
292export function stalePaths(transferred: { name: string; from: string; to: string }, current: string): string[] {
293 return [...new Set([transferred.from, transferred.to].map((ns) => `${ns}/${transferred.name}`))].filter(
294 (path) => path !== current,
295 );
296}
297
298/**
299 * A repository's path change, from `repo.transferred` or `repo.renamed`,
300 * read the same way: the two paths (`namespace/name`) the event names, old
301 * then new. Null for any other event.
302 */
303export type RepoMove = { repoId: string; paths: [string, string] };
304
305export function repoMove(event: { type: string; data: unknown }): RepoMove | null {
306 const data = event.data as Record<string, string>;
307 if (event.type === "repo.transferred") {
308 return { repoId: data.repoId!, paths: [`${data.from}/${data.name}`, `${data.to}/${data.name}`] };
309 }
310 if (event.type === "repo.renamed") {
311 return { repoId: data.repoId!, paths: [`${data.namespace}/${data.from}`, `${data.namespace}/${data.to}`] };
312 }
313 return null;
314}
315
316/**
317 * Where a moved repository is now, as `namespace/name`: asked of repos by
318 * id, so moves delivered twice or out of order converge. Falls back to the
319 * event's new path.
320 */
321export async function currentMovedPath(repos: ServiceBinding, move: RepoMove): Promise<string> {
322 const path = await rpc<{ namespace: string; name: string } | null>(repos, "path_by_id", { id: move.repoId });
323 return path ? `${path.namespace}/${path.name}` : move.paths[1];
324}
325
326/** The paths whose rows move to `current`: the two a move names, less `current`. */
327export function staleMovedPaths(move: RepoMove, current: string): string[] {
328 return [...new Set(move.paths)].filter((path) => path !== current);
329}
330
331/** Staff-only identity. Only sudo binds to it; see `IdentityAdminApi`. */
332export function identityAdminClient(service: ServiceBinding): IdentityAdminApi {
333 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
334 return {
335 workspaces: (query) => call("admin_workspaces", { query: query ?? null }),
336 workspace: (slug) => call("admin_workspace", { slug }),
337 waitlist: (query, status) => call("admin_waitlist", { query: query ?? null, status: status ?? null }),
338 waitlistPending: () => call("admin_waitlist_pending", {}),
339 decideWaitlist: (id, approve, staff, note) => call("admin_decide_waitlist", { id, approve, staff, note: note ?? null }),
340 invites: (query) => call("admin_invites", { query: query ?? null }),
341 revokeInvite: (id, staff) => call("admin_revoke_invite", { id, staff }),
342 mintInvite: (email, staff) => call("admin_mint_invite", { email, staff }),
343 grantInvites: (target, name, amount, note, staff) =>
344 call("admin_grant_invites", { target, name, amount, note, staff }),
345 inviteTree: (username) => call("admin_invite_tree", { username }),
346 workspaceInvites: (slug) => call("admin_workspace_invites", { slug }),
Merge shared invite links: label, uses, expiry, domains; joined through recorded (identity 0038)347 sharedInvites: () => call("admin_shared_invites", {}),
348 createSharedInvite: (link, staff) =>
349 call("admin_create_shared_invite", {
350 label: link.label,
351 max_uses: link.maxUses,
352 expires_on: link.expiresOn,
353 domains: link.domains,
354 staff,
355 }),
356 revokeSharedInvite: (id, staff) => call("admin_revoke_shared_invite", { id, staff }),
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member357 deletedWorkspaces: () => call("admin_deleted_workspaces", {}),
358 restoreWorkspace: (workspaceId, staff) => call("admin_restore_workspace", { workspaceId, staff }),
359 purgeWorkspace: (workspaceId, staff, confirm) => call("admin_purge_workspace", { workspaceId, staff, confirm }),
Merge branch 'worktree-agent-a8385d293d42c913a'360 aliases: () => call("admin_aliases", {}),
361 setAlias: (alias, workspace, note, staff) => call("admin_set_alias", { alias, workspace, note, staff }),
362 removeAlias: (alias, reason, staff) => call("admin_remove_alias", { alias, reason, staff }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow363 };
364}
365
366export function reposClient(service: ServiceBinding): ReposApi {
367 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
368 return {
369 get: (path, viewer) => call("get", { path, viewer }),
370 getById: (id, viewer) => call("get_by_id", { id, viewer }),
Fast pages, required checks on the branch, self-hosted runners, honest incidents371 readable: (ids, viewer) => call("readable", { ids, viewer }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow372 publicNamespaces: (ownerId) => call("public_namespaces", { ownerId }),
373 list: (viewer, options = {}) => call("list", { viewer, ...options }),
374 create: (owner, input) => call("create", { owner, ...input }),
375 update: (actor, path, changes) => call("update", { actor, path, ...changes }),
376 transfer: (actor, path, to) => call("transfer", { actor, path, to }),
377 delete: (actor, path, confirm) => call("delete", { actor, path, confirm }),
378 deleted: (viewer, namespace) => call("deleted", { viewer, namespace }),
379 restore: (actor, path) => call("restore", { actor, path }),
380 purge: (actor, path, confirm) => call("purge", { actor, path, confirm }),
381 rename: (actor, path, name) => call("rename", { actor, path, name }),
382 archive: (actor, path, archived) => call("archive", { actor, path, archived }),
383 setVisibility: (actor, path, isPrivate, confirm) => call("set_visibility", { actor, path, isPrivate, confirm }),
384 setDefaultBranch: (actor, path, branch) => call("set_default_branch", { actor, path, branch }),
385 renameBranch: (actor, path, from, to) => call("rename_branch", { actor, path, from, to }),
386 resolveBranch: (repoId, branch) => call("resolve_branch", { repoId, branch }),
387 statusById: (id) => call("status_by_id", { id }),
Merge branch 'worktree-agent-a2013627e5ea4ab13'388 storageOptions: () => call("storage_options", {}),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow389 resolvePath: (path) => call("resolve_path", { path }),
390 tree: (path, viewer, ref, treePath) =>
391 call("tree", { path, viewer, ref, treePath }),
392 blob: (path, viewer, ref, filePath) =>
393 call("blob", { path, viewer, ref, filePath }),
394 log: (path, viewer, ref, limit) => call("log", { path, viewer, ref, limit }),
395 blame: (path, viewer, ref, filePath) => call("blame", { path, viewer, ref, filePath }),
396 forkForPull: (sourceId, pullId, actor) =>
397 call("fork_for_pull", { sourceId, pullId, actor }),
398 gitAccess: (path, viewer, service) =>
399 call("git_access", { path, viewer, service }),
400 branches: (path, viewer) => call("branches", { path, viewer }),
Branches and Tags pages, each file's last commit, and the branch menu on files401 lastCommits: (path, viewer, ref, treePath) => call("last_commits", { path, viewer, ref, treePath }),
Merge project overview: one branch_drift call, spliced histories, cached tags, 6 repos calls instead of 25402 branchDrift: (path, viewer, base, heads) => call("branch_drift", { path, viewer, base, heads }),
Branches and Tags pages, each file's last commit, and the branch menu on files403 tags: (path, viewer) => call("tags", { path, viewer }),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97404 about: (path, viewer) => call("about", { path, viewer }),
405 languages: (path, viewer) => call("languages", { path, viewer }),
406 contributors: (path, viewer) => call("contributors", { path, viewer }),
407 license: (path, viewer) => call("license", { path, viewer }),
408 stars: (path, viewer) => call("stars", { path, viewer }),
409 star: (actor, path, starred) => call("star", { path, actor, starred }),
410 stargazers: (path, viewer, page) => call("stargazers", { path, viewer, page: page ?? null }),
411 starred: (username, viewer) => call("starred", { username, viewer }),
412 releases: (path, viewer) => call("releases", { path, viewer }),
413 release: (path, viewer, which) => call("release", { path, viewer, id: which.id ?? null, tag: which.tag ?? null, latest: which.latest ?? false }),
414 createRelease: (actor, path, release) => call("create_release", { path, actor, ...release }),
415 updateRelease: (actor, path, id, change) => call("update_release", { path, actor, id, ...change }),
416 deleteRelease: (actor, path, id) => call("delete_release", { path, actor, id }),
Merge branch 'worktree-agent-a19b06d959c105bf7' into claude/cool-newton-7nm8r9417 listFiles: (repoId, ref, limit, options) => call("list_files", { repoId, ref, skipDirs: [], limit, links: options?.links ?? false }),
418 openArchiveToken: (path, token) => call("open_archive_token", { path, token }),
Download ZIP from the Code button; a slimmer lifecycle panel; agent steps say what was done, not sandbox paths419 rawBlobs: (repoId, hashes, maxBytes) => call("raw_blobs", { repoId, hashes, maxBytes }),
Merge g1tusercontent.com: registry answers run nothing in a browser, the site's pages run only their own scripts, repository files and avatars on their own origin, raw files rate limited per address420 rawFile: (repoId, ref, path, maxBytes) => call("raw_file", { repoId, ref, path, maxBytes }),
Fast pages, required checks on the branch, self-hosted runners, honest incidents421 commitFile: (repo, actor, file) => call("commit_file", { repo, actor, ...file }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow422 land: (sourceId, actor, branch) => call("land", { sourceId, actor, branch }),
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar423 compare: (repoId, viewer, base, head, baseBranch) => call("compare", { repoId, viewer, base, head, baseBranch }),
Merge branch 'worktree-agent-ac5b181a013e54348'424 claimBackups: (limit, maxRunning) => call("claim_backups", { limit, maxRunning }),
425 // The sandbox's own calls are snake_case (they come through the API).
426 failBackup: (jobId, token, error) => call("backup_fail", { job_id: jobId, token, error }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow427 };
428}
429
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar430/**
431 * `PullDetail`'s own fields that the work service writes in snake_case:
432 * `g1t_contracts::work::PullDetail` has no camelCase renaming, though what
433 * it holds (`Pull`, `CheckRun` and the rest) does. Each with its name here.
434 */
435const PULL_DETAIL_FIELDS = [
436 ["review_pending", "reviewPending"],
437 ["earlier_checks", "earlierChecks"],
438 ["required_checks", "requiredChecks"],
439 ["code_owners", "codeOwners"],
440] as const;
441
442/**
443 * A pull request's detail as the work service sent it, with its own fields
444 * in the camelCase this package uses: read at the edge, by `workClient`'s
445 * `getPull`. Either spelling is taken, so a service that already sends
446 * camelCase reads the same.
447 */
448export function pullDetailFromWire(raw: Record<string, unknown>): PullDetail {
449 const detail: Record<string, unknown> = { ...raw };
450 for (const [snake, camel] of PULL_DETAIL_FIELDS) {
451 if (snake in detail) {
452 if (!(camel in detail)) detail[camel] = detail[snake];
453 delete detail[snake];
454 }
455 }
456 if (typeof detail.reviewPending !== "boolean") detail.reviewPending = false;
457 return detail as PullDetail;
458}
459
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow460export function workClient(service: ServiceBinding): WorkApi {
461 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
462 return {
463 openIssue: (actor, repo, input) => call("open_issue", { actor, repo, ...input }),
464 delegateIssue: (actor, repo, input) => call("delegate_issue", { actor, repo, ...input }),
465 listIssues: (repo, viewer, filter = {}) => call("list_issues", { repo, viewer, ...filter }),
466 getIssue: (repo, number, viewer) => call("get_issue", { repo, number, viewer }),
467 updateIssue: (actor, repo, number, input) =>
468 call("update_issue", { actor, repo, number, ...input }),
469 closeIssue: (actor, repo, number, reason) =>
470 call("close_issue", { actor, repo, number, reason }),
471 reopenIssue: (actor, repo, number) => call("reopen_issue", { actor, repo, number }),
472 listLabels: (repo, viewer) => call("list_labels", { repo, viewer }),
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar473 saveLabel: (actor, repo, label) => call("save_label", { actor, repo, ...label }),
474 deleteLabel: (actor, repo, name) => call("delete_label", { actor, repo, name }),
475 addDefaultLabels: (actor, repo) => call("add_default_labels", { actor, repo }),
476 setLabels: (actor, repo, number, labels, change = "set") =>
477 call("set_labels", { actor, repo, number, labels, change }),
478 listMilestones: (repo, viewer, state) => call("list_milestones", { repo, viewer, state }),
479 getMilestone: (repo, number, viewer) => call("get_milestone", { repo, number, viewer }),
480 saveMilestone: (actor, repo, milestone) => call("save_milestone", { actor, repo, ...milestone }),
481 deleteMilestone: (actor, repo, number) => call("delete_milestone", { actor, repo, number }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow482 counts: (repo, viewer) => call("counts", { repo, viewer }),
483 addComment: (actor, repo, number, comment) =>
484 call("add_comment", { actor, repo, number, ...comment }),
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts485 editComment: (actor, repo, commentId, body) => call("edit_comment", { actor, repo, commentId, body }),
486 deleteComment: (actor, repo, commentId) => call("delete_comment", { actor, repo, commentId }),
Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar487 workspaceAgentComment: (repo, number, agent, actingFor, body) =>
488 call("workspace_agent_comment", { repo, number, agent, acting_for: actingFor, body }),
489 workspaceAgentReview: (repo, number, agent, actingFor, verdict, body) =>
490 call("workspace_agent_review", { repo, number, agent, acting_for: actingFor, verdict, body }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow491 startChecks: (pullId) => call("start_checks", { pullId }),
492 reportChecks: (runId, token, report) =>
493 call("report_checks", { runId, token, ...report }),
494 startReview: (pullId) => call("start_review", { pullId }),
495 failReview: (runId, token, error) => call("report_review", { runId, token, error }),
496 startMergecheck: (pullId) => call("start_mergecheck", { pullId }),
497 failMergecheck: (pullId, token, error) => call("report_mergecheck", { pullId, token, error }),
498 advance: (pullId) => call("advance", { pullId }),
499 stall: (pullId, reason) => call("stall", { pullId, reason }),
500 managedPulls: (repoId) => call("managed_pulls", { repoId }),
501 queue: (repo, viewer) => call("queue", { repo, viewer }),
502 queueBuild: (repoId) => call("queue_build", { repoId }),
503 failQueue: (entryId, token, error) => call("report_queue", { entryId, token, error }),
504 removeFromQueue: (actor, repo, number) => call("remove_from_queue", { actor, repo, number }),
505 messageAgent: (actor, repo, number, body) => call("message_agent", { actor, repo, number, body }),
506 catchUpJob: (pullId) => call("catch_up_job", { pullId }),
507 wakeForMessages: (pullId) => call("wake_for_messages", { pullId }),
508 getSettings: (repo, viewer) => call("get_settings", { repo, viewer }),
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge509 // Rulesets travel in snake_case (rules.ts).
510 listRulesets: (owner, viewer, includeParents = false) =>
511 call("list_rulesets", { viewer, ...owner, include_parents: includeParents }),
512 getRuleset: (owner, id, viewer) => call("get_ruleset", { viewer, ...owner, id }),
513 saveRuleset: (actor, owner, ruleset, id) => call("save_ruleset", { actor, ...owner, id: id ?? null, ruleset }),
514 deleteRuleset: (actor, owner, id) => call("delete_ruleset", { actor, ...owner, id }),
515 effectiveRules: (repo, name, viewer, target = "branch") => call("effective_rules", { viewer, repo, name, target }),
516 ruleEvaluations: (owner, viewer, filter = {}) => call("rule_evaluations", { viewer, ...owner, ...filter }),
Fast pages, required checks on the branch, self-hosted runners, honest incidents517 seenChecks: (repo, viewer) => call("seen_checks", { repo, viewer }),
Merge checks: statuses and check runs on every commit518 commitChecks: (repo, viewer, shas) => call("commit_checks", { repo, viewer, shas }),
519 getCheckRun: (repo, id, viewer) => call("get_check_run", { repo, id, viewer }),
520 checkRunAnnotations: (repo, id, viewer) => call("check_run_annotations", { repo, id, viewer }),
521 requestCheckAction: (actor, repo, id, identifier) => call("request_check_action", { actor, repo, id, identifier }),
522 rerequestCheckRun: (actor, repo, id) => call("rerequest_check_run", { actor, repo, id }),
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar523 codeownersErrors: (repo, viewer, ref) => call("codeowners_errors", { repo, viewer, ref: ref ?? null }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow524 updateSettings: (actor, repo, settings) =>
525 call("update_settings", { actor, repo, settings }),
526 openPull: (actor, repo, input) => call("open_pull", { actor, repo, ...input }),
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar527 listPulls: (repo, viewer, state, filter = {}) => call("list_pulls", { repo, viewer, state, ...filter }),
Fast pages, required checks on the branch, self-hosted runners, honest incidents528 pullsForRepos: (repoIds, viewer, limit) => call("pulls_for_repos", { repoIds, viewer, limit }),
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar529 // Its own fields arrive in snake_case: read into camelCase here.
530 getPull: (repo, number, viewer) =>
531 call<Result<Record<string, unknown>>>("get_pull", { repo, number, viewer }).then(
532 (found): Result<PullDetail> => (found.ok ? { ok: true, value: pullDetailFromWire(found.value) } : found),
533 ),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow534 updatePull: (actor, repo, number, changes) =>
535 call("update_pull", { actor, repo, number, ...changes }),
536 catchUpPull: (actor, repo, number) => call("catch_up_pull", { actor, repo, number }),
537 readyPull: (actor, repo, number, summary) =>
538 call("ready_pull", { actor, repo, number, summary }),
539 closePull: (actor, repo, number) => call("close_pull", { actor, repo, number }),
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts540 reopenPull: (actor, repo, number) => call("reopen_pull", { actor, repo, number }),
541 convertPullToDraft: (actor, repo, number) => call("convert_pull_to_draft", { actor, repo, number }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow542 mergePull: (actor, repo, number, options = {}) =>
543 call("merge_pull", { actor, repo, number, ...options }),
544 listActivePulls: (viewer) => call("list_active_pulls", { viewer }),
545 byAuthor: (username, viewer, filter = {}) => call("by_author", { username, viewer, ...filter }),
Chat controls, public profiles, shadcn selects, and no Docs tab in a project546 contributions: (username, viewer) => call("contributions", { username, viewer }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow547 startPlan: (actor, repo, brief) => call("start_plan", { actor, repo, brief }),
548 failPlan: (planId, token, error) => call("report_plan", { planId, token, error }),
549 getPlan: (repo, viewer, id) => call("get_plan", { repo, viewer, id }),
550 listPlans: (repo, viewer) => call("list_plans", { repo, viewer }),
551 applyPlan: (actor, repo, id, options = {}) =>
552 call("apply_plan", { actor, repo, id, ...options }),
553 queueIssue: (actor, repo, number, queued) =>
554 call("queue_issue", { actor, repo, number, queued }),
555 readyIssues: (repoId) => call("ready_issues", { repoId }),
556 listAssignedIssues: (viewer) => call("list_assigned_issues", { viewer }),
557 appendSession: (actor, repo, number, entries) =>
558 call("append_session", { actor, repo, number, entries }),
559 readSession: (repo, number, viewer, afterSeq = 0) =>
560 call("read_session", { repo, number, viewer, afterSeq }),
561 };
562}
563
564export function billingClient(service: ServiceBinding): BillingApi {
565 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
566 return {
567 status: () => call("status", {}),
568 account: (workspace, viewer) => call("account", { workspace, viewer }),
569 ledger: (workspace, viewer) => call("ledger", { workspace, viewer }),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging570 credits: (workspace, viewer) => call("credits", { workspace, viewer }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow571 statement: (workspace, viewer, month = null, group = "day") => call("statement", { workspace, viewer, month, group }),
572 statementEntries: (workspace, viewer, filter) =>
573 call("statement_entries", {
574 workspace,
575 viewer,
576 month: filter.month,
577 kind: filter.kind,
578 day: filter.day ?? null,
579 project: filter.project ?? null,
580 before: filter.before ?? null,
581 }),
582 usage: (workspace, viewer, since) => call("usage", { workspace, viewer, since }),
Mission control shows model usage, yours and the workspace's: tokens, cost, active days, cache share, each day, and the mix583 tokenUsage: (workspace, viewer, options = {}) =>
584 call("token_usage", { workspace, viewer, person: options.person ?? null, days: options.days ?? null }),
585 recordTokens: (usage) => call("record_tokens", usage),
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens586 gatewayModels: () => call("gateway_models", {}),
Merge branch 'main' into actions-toolkit-oidc-artifacts587 modelDefaults: () => call("model_defaults", {}),
588 recordDiscovery: (provider, models, by, error = null) => call("record_discovery", { provider, models, by, error }),
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens589 gatewayAdmit: (workspace) => call("gateway_admit", { workspace }),
590 recordGateway: (record) => call("record_gateway", record),
591 gatewayRequests: (workspace, viewer, options = {}) =>
592 call("gateway_requests", { workspace, viewer, limit: options.limit ?? null, before: options.before ?? null }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow593 checkout: (actor, workspace, amountCents, returnUrl, method = "card") =>
594 call("checkout", { actor, workspace, amountCents, returnUrl, method }),
595 confirm: (workspace, viewer, session) => call("confirm", { workspace, viewer, session }),
596 canStart: (workspace) => call("can_start", { workspace }),
597 trial: (workspace, exempt) => call("trial", { workspace, exempt }),
598 startRun: (run) => call("start_run", run),
599 features: (workspace, viewer) => call("features", { workspace, viewer }),
600 subscribe: (actor, workspace, feature, returnUrl) =>
601 call("subscribe", { actor, workspace, feature, returnUrl }),
602 confirmSubscription: (workspace, viewer, session) =>
603 call("confirm_subscription", { workspace, viewer, session }),
604 cancelSubscription: (actor, workspace, feature, resume = false) =>
605 call("cancel_subscription", { actor, workspace, feature, resume }),
606 hasFeature: (workspace, feature) => call("has_feature", { workspace, feature }),
Merge Stripe Tax, the card fee on card payments, and one free workspace per person607 freeWorkspaces: (workspaces) => call("free_workspaces", { workspaces }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow608 chargeFeature: (charge) => call("charge_feature", charge),
609 billingPortal: (actor, workspace, returnUrl) => call("billing_portal", { actor, workspace, return_url: returnUrl }),
610 recordSandbox: (usage) => call("record_sandbox", usage),
611 limit: (workspace, viewer) => call("limit", { workspace, viewer }),
612 checkLimit: (workspace) => call("check_limit", { workspace }),
613 prices: () => call("prices", {}),
614 notePending: (workspace, source, costMicros, detail = null) => call("note_pending", { workspace, source, costMicros, detail }),
615 usageMeters: (workspace, viewer) => call("usage_meters", { workspace, viewer }),
616 setSpendLimit: (actor, workspace, spendLimitMicros, useFullLimit = false, raiseOnce = false) =>
617 call("set_spend_limit", { actor, workspace, spendLimitMicros, use_full_limit: useFullLimit, raise_once: raiseOnce }),
618 invoices: (workspace, viewer) => call("invoices", { workspace, viewer }),
619 entitlements: (workspace) => call("entitlements", { workspace }),
620 reserve: (reservation) => call("reserve", reservation),
621 settle: (reservationId, actualMicros) => call("settle", { reservationId, actualMicros }),
622 cardCheck: (actor, workspace, returnUrl) => call("card_check", { actor, workspace, returnUrl }),
623 confirmCardCheck: (workspace, viewer, session) => call("confirm_card_check", { workspace, viewer, session }),
624 requestLimit: (actor, workspace, request) => call("request_limit", { actor, workspace, ...request }),
625 limitRequests: (workspace, viewer) => call("limit_requests", { workspace, viewer }),
626 confirmSpike: (actor, workspace, keepGoing) => call("confirm_spike", { actor, workspace, keepGoing }),
627 setCaps: (actor, workspace, caps) => call("set_caps", { actor, workspace, ...caps }),
Usage, Billing settings and prepaid AI credit; fixes from the UX audit628 usageReport: (workspace, viewer, range) =>
629 call("usage_report", { workspace, viewer, from: range.from, until: range.until, products: range.products ?? [], projects: range.projects ?? [] }),
630 aiCredit: (workspace, viewer) => call("ai_credit", { workspace, viewer }),
631 buyAiCredit: (actor, workspace, amountCents, returnUrl) => call("buy_ai_credit", { actor, workspace, amountCents, returnUrl }),
632 confirmAiCredit: (workspace, viewer, session) => call("confirm_ai_credit", { workspace, viewer, session }),
633 setAiReload: (actor, workspace, reload) => call("set_ai_reload", { actor, workspace, ...reload }),
634 setBudget: (actor, workspace, budget) => call("set_budget", { actor, workspace, ...budget }),
635 billingDetails: (workspace, viewer) => call("billing_details", { workspace, viewer }),
636 setBillingDetails: (actor, workspace, details) => call("set_billing_details", { actor, workspace, ...details }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow637 };
638}
639
640export function billingAdminClient(service: ServiceBinding): BillingAdminApi {
641 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
642 return {
643 accounts: (query) => call("admin_accounts", { query: query ?? null }),
644 account: (id) => call("admin_account", { id }),
645 setTerms: (id, terms, by) => call("admin_set_terms", { id, terms, by }),
646 setAllowances: (id, allowances, note, by) => call("admin_set_allowances", { id, allowances, note, by }),
647 createEnterprise: (name, workspaces, by) => call("admin_create_enterprise", { name, workspaces, by }),
648 attach: (workspace, account, by) => call("admin_attach", { workspace, account, by }),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging649 credit: (workspace, amountMicros, note, by, options = { kind: "goodwill" }) =>
650 call("admin_credit", {
651 workspace,
652 amount_micros: amountMicros,
653 note,
654 by,
655 kind: options.kind,
656 expires_at: options.expiresAt ?? null,
657 refund_for: options.refundFor ?? null,
658 refund_day: options.refundDay ?? null,
659 }),
660 credits: (filter = {}) =>
661 call("admin_credits", {
662 workspace: filter.workspace ?? null,
663 kind: filter.kind ?? null,
664 month: filter.month ?? null,
665 by: filter.by ?? null,
666 }),
667 revokeCredit: (id, note, by) => call("admin_revoke_credit", { id, note, by }),
sudo: reset a test workspace's billing so it starts again as a new customer; refused on a live Stripe key, for comped workspaces and for an enterprise's668 resetBilling: (workspace, confirm, note, by) => call("admin_reset_billing", { workspace, confirm, note, by }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow669 billingLink: (workspace, by) => call("admin_billing_link", { workspace, by }),
Stripe's webhook secret is a Worker secret, STRIPE_WEBHOOK_SECRET, from a destination made in Stripe's dashboard670 stripe: (fix = false, by) => call("admin_stripe", { fix, by: by ?? null }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow671 enterpriseBilling: (id, email, by) => call("admin_enterprise_billing", { id, email, by }),
Merge Stripe Tax, the card fee on card payments, and one free workspace per person672 enterpriseAddress: (id, address, taxIdType, taxId, by) => call("admin_enterprise_address", { id, address, taxIdType, taxId, by }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow673 invoiceEnterprise: (id, by) => call("admin_invoice_enterprise", { id, by }),
674 accountsFor: (workspaces) => call("admin_accounts", { query: null, workspaces }),
675 signals: () => call("admin_signals", {}),
676 overview: () => call("admin_overview", {}),
677 sales: (workspace) => call("admin_sales", { workspace }),
678 setSales: (workspace, record, by) =>
679 call("admin_set_sales", {
680 workspace,
681 stage: record.stage,
682 owner: record.owner ?? null,
683 next_step: record.nextStep ?? null,
684 next_at: record.nextAt ?? null,
685 by,
686 }),
687 addNote: (workspace, text, by) => call("admin_add_note", { workspace, text, by }),
688 workspaceInvoices: (workspace) => call("admin_workspace_invoices", { workspace }),
689 allInvoices: (filter = {}) => call("admin_invoices", { status: filter.status ?? null, month: filter.month ?? null }),
690 audit: (filter = {}) =>
691 call("admin_audit", { by: filter.by ?? null, action: filter.action ?? null, before: filter.before ?? null }),
692 limitRequests: (status = "open") => call("admin_limit_requests", { status }),
693 decideLimitRequest: (id, decision, amountMicros, note, by) =>
694 call("admin_decide_limit_request", { id, decision, amount_micros: amountMicros, note, by }),
695 overages: () => call("admin_overages", {}),
696 goodwill: (workspace, amountMicros, reason, by, day = null) =>
697 call("admin_goodwill", { workspace, amount_micros: amountMicros, reason, by, day }),
698 velocity: () => call("admin_velocity", {}),
699 recordPayment: (workspace, amountMicros, reference, note, by) =>
700 call("admin_record_payment", { workspace, amount_micros: amountMicros, reference, note, by }),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily701 costs: (days) => call("admin_costs", { days: days ?? null }),
702 costAlerts: () => call("admin_cost_alerts", {}),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays703 spendCaps: () => call("admin_spend_caps", {}),
704 liftBreaker: (note, by) => call("admin_lift_breaker", { note, by }),
Merge platform pause and the hourly usage watcher: staff can pause compute, schedules, indexing or renders for everyone, the watcher emails on a breach and is never blind quietly, and the models proxy holds each run to its cap (billing 0051, integrations 0006)705 platformGuard: () => call("admin_platform_guard", {}),
706 setPause: (level, paused, note, by) => call("admin_set_pause", { level, paused, note, by }),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily707 decideProposal: (id, decision, note, by) => call("admin_decide_proposal", { id, decision, note, by }),
708 setCostSettings: (settings, by) => call("admin_set_cost_settings", { settings, by }),
709 setCostMapping: (mapping, by) =>
710 call("admin_set_cost_mapping", {
711 product: mapping.product,
712 meter: mapping.meter,
713 bucket: mapping.bucket ?? "",
714 price_meter: mapping.priceMeter ?? null,
715 own_meter: mapping.ownMeter ?? null,
716 scale_to_own: mapping.scaleToOwn ?? false,
717 drift_percent: mapping.driftPercent ?? null,
718 note: mapping.note ?? "",
719 remove: mapping.remove ?? false,
720 by,
721 }),
722 runCosts: (by) => call("admin_run_costs", { by }),
Merge branch 'main' into actions-toolkit-oidc-artifacts723 models: () => call("admin_models", {}),
724 decideModel: (model, decision, details, reason, by) =>
725 call("admin_decide_model", {
726 model,
727 decision,
728 name: details.name ?? null,
729 tier_hint: details.tierHint ?? null,
730 prices: details.prices
731 ? {
732 input_micros: details.prices.inputMicros,
733 output_micros: details.prices.outputMicros,
734 cache_read_micros: details.prices.cacheReadMicros,
735 cache_write_micros: details.prices.cacheWriteMicros,
736 cache_write_1h_micros: details.prices.cacheWrite1hMicros,
737 threshold: details.prices.threshold,
738 over_input_micros: details.prices.overInputMicros,
739 over_output_micros: details.prices.overOutputMicros,
740 over_cache_read_micros: details.prices.overCacheReadMicros,
741 over_cache_write_micros: details.prices.overCacheWriteMicros,
742 over_cache_write_1h_micros: details.prices.overCacheWrite1hMicros,
743 }
744 : null,
745 reason,
746 by,
747 }),
748 setModelDefault: (purpose, value, reason, by) =>
749 call("admin_set_model_default", {
750 purpose,
751 model: value.model ?? null,
752 tier: value.tier ?? null,
753 effort: value.effort ?? null,
754 reason,
755 by,
756 }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow757 };
758}
759
760export function eventsClient(service: ServiceBinding): EventsApi {
761 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
762 return {
763 publish: (events) => call("publish", { events }),
764 list: (query) => call("list", query),
Home says what people did as well as what agents did. Since you were last here now has a People column and an Agents column: who pushed how many commits to which projects, pull requests opened, merged and reviewed, issues opened and closed, docs edited, messages sent and deploys that went out, each line a link to where those are listed, with the agents' acceptance (first time, after review, didn't finish) kept as a row of their column; the sentence under the heading sums it up honestly, and says when a part of g1t could not be read, or that the span was quiet. Landed counts merged pull requests, commits pushed straight to a default branch, production deploys that went live, releases and packages, newest first; Running now adds workflow runs. Behind it, the events service answers an activity digest over a span in one round trip from its existing indexes, every push now records how many commits it carried, and chat counts the messages sent in the conversations you can read. The Home guide defines every line, and says how this scales.765 activityDigest: (args) => call("activity_digest", args),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow766 };
767}
768
769export function integrationsClient(service: ServiceBinding): IntegrationsApi {
770 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
771 return {
772 list: (workspace, viewer) => call("list", { workspace, viewer }),
773 connect: (actor, workspace, input) => call("connect", { actor, workspace, ...input }),
774 update: (actor, workspace, id, input) => call("update", { actor, workspace, id, ...input }),
775 disconnect: (actor, workspace, id) => call("disconnect", { actor, workspace, id }),
776 test: (actor, workspace, id) => call("test", { actor, workspace, id }),
777 deliveries: (workspace, viewer, id) => call("deliveries", { workspace, viewer, id }),
778 resolve: (workspace, viewer, reference) => call("resolve", { workspace, viewer, reference }),
779 references: (workspace, text, limit) => call("references", { workspace, text, limit }),
780 import: (actor, repo, reference, assign) => call("import", { actor, repo, reference, assign }),
Each agent has an Abilities tab: g1t's built-ins, always on within the asker's access; its computer, coming; each connected integration's actions one row each, read, import, comment or resolve, with a level for each, alone, alone when the person asked for it, ask first or never, and whose connection it runs on; and MCP servers an owner adds. Reading is alone, writing inside g1t follows today's choices, anything that leaves g1t asks first, and production deploys can't go above ask. The agents service enforces every level: ask first posts a card to allow or deny and parks a session until it's answered, a refusal names its rule in the transcript and the audit log, and a missing ability posts a request to owners. The agent abilities guide says how.781 comment: (actor, workspace, reference, text, link) => call("comment", { actor, workspace, reference, text, link }),
782 close: (actor, workspace, reference, text, link) => call("close", { actor, workspace, reference, text, link }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow783 links: (repo, number) => call("links", { repo, number }),
784 modelProvider: (workspace) => call("model_provider", { workspace }),
785 openModelSession: (run) => call("open_model_session", run),
786 modelUpstream: (token) => call("model_upstream", { token }),
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens787 gatewayUpstream: (workspace) => call("gateway_upstream", { workspace }),
AI Gateway: OpenAI's format, open models, and your own providers788 gatewayProviders: (workspace) => call("gateway_providers", { workspace }),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily789 closeModelSessions: (tokenHashes) => call("close_model_sessions", { token_hashes: tokenHashes }),
Merge platform pause and the hourly usage watcher: staff can pause compute, schedules, indexing or renders for everyone, the watcher emails on a breach and is never blind quietly, and the models proxy holds each run to its cap (billing 0051, integrations 0006)790 capModelSessions: (tokenHashes, capMicros) => call("cap_model_sessions", { token_hashes: tokenHashes, cap_micros: capMicros }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow791 routes: (workspace, viewer) => call("routes", { workspace, viewer }),
792 setRoutes: (actor, workspace, routes) => call("set_routes", { actor, workspace, routes }),
793 };
794}
795
796export function webhooksClient(service: ServiceBinding): WebhooksApi {
797 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
798 return {
799 list: (viewer, owner) => call("list", { viewer, ...owner }),
800 create: (actor, owner, input) => call("create", { actor, ...owner, ...input }),
801 update: (actor, owner, id, input) => call("update", { actor, ...owner, id, ...input }),
802 delete: (actor, owner, id) => call("delete", { actor, ...owner, id }),
803 ping: (actor, owner, id) => call("ping", { actor, ...owner, id }),
804 deliveries: (viewer, owner, id) => call("deliveries", { viewer, ...owner, id }),
805 redeliver: (actor, owner, deliveryId) => call("redeliver", { actor, ...owner, deliveryId }),
806 };
807}
808
809export function actionsClient(service: ServiceBinding): ActionsApi {
810 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
811 return {
812 workflows: (repo, viewer) => call("workflows", { repo, viewer }),
813 runs: (repo, viewer, filter = {}) => call("runs", { repo, viewer, ...filter }),
Merge Actions runs: summaries, attempts and re-runs, graceful cancel, log downloads, badges (actions 0009)814 run: (repo, viewer, id, attempt) => call("run", { repo, viewer, id, attempt: attempt ?? null }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow815 logs: (repo, viewer, job, after = 0) => call("logs", { repo, viewer, job, after }),
Merge Actions runs: summaries, attempts and re-runs, graceful cancel, log downloads, badges (actions 0009)816 summaries: (repo, viewer, id, attempt) => call("summaries", { repo, viewer, id, attempt: attempt ?? null }),
817 jobLogText: (repo, viewer, job) => call("job_log_text", { repo, viewer, job }),
818 runLogs: (repo, viewer, id, attempt) => call("run_logs", { repo, viewer, id, attempt: attempt ?? null }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow819 dispatch: (actor, repo, workflow, ref, inputs) => call("dispatch", { actor, repo, workflow, ref, inputs }),
Merge Actions runs: summaries, attempts and re-runs, graceful cancel, log downloads, badges (actions 0009)820 cancel: (actor, repo, id, force = false) => call("cancel", { actor, repo, id, force }),
821 rerun: (actor, repo, id, failedOnly = false, options = {}) =>
822 call("rerun", { actor, repo, id, failed_only: failedOnly, job: options.job ?? null, debug: options.debug ?? false }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow823 setWorkflowEnabled: (actor, repo, workflow, enabled) =>
824 call("set_workflow_enabled", { actor, repo, workflow, enabled }),
825 settings: (actor, owner, kind) => call("settings", { actor, ...owner, kind }),
826 setSetting: (actor, owner, kind, name, value, options = {}) =>
827 call("set_setting", { actor, ...owner, kind, name, value, ...options }),
828 deleteSetting: (actor, owner, kind, name, id) => call("delete_setting", { actor, ...owner, kind, name, id }),
Merge branch 'worktree-agent-a3abfcce648e87dca'829 approveRun: (actor, repo, id) => call("approve_run", { actor, repo, id }),
830 pendingDeployments: (repo, viewer, id) => call("pending_deployments", { repo, viewer, id }),
831 reviewDeployments: (actor, repo, id, state, environments = [], comment) =>
832 call("review_deployments", { actor, repo, id, state, environments, comment: comment ?? null }),
833 actionsSettings: (repo, viewer) => call("actions_settings", { repo, viewer }),
834 setActionsSettings: (actor, repo, change) => call("set_actions_settings", { actor, repo, ...change }),
835 workspaceActionsSettings: (workspace, viewer) => call("workspace_actions_settings", { workspace, viewer }),
836 setWorkspaceActionsSettings: (actor, workspace, change) =>
837 call("set_workspace_actions_settings", { actor, workspace, ...change }),
Merge branch 'worktree-agent-a209db8188a251e77' into claude/cool-newton-7nm8r9838 workspaceManagedWorkflows: (workspace, viewer) => call("workspace_managed_workflows", { workspace, viewer }),
839 setWorkspaceManagedWorkflow: (actor, workspace, name, change, create = false) =>
840 call("set_workspace_managed_workflow", { actor, workspace, name, create, ...change }),
841 removeWorkspaceManagedWorkflow: (actor, workspace, name) =>
842 call("remove_workspace_managed_workflow", { actor, workspace, name }),
Merge branch 'worktree-agent-a3abfcce648e87dca'843 environments: (repo, viewer) => call("environments", { repo, viewer }),
844 setEnvironment: (actor, repo, name, change) => call("set_environment", { actor, repo, name, ...change }),
845 deleteEnvironment: (actor, repo, name) => call("delete_environment", { actor, repo, name }),
Actions: OIDC tokens, the toolkit's cache and artifact services, and artifacts in R2846 artifacts: (repo, viewer, filter = {}) => call("artifacts", { repo, viewer, ...filter }),
847 artifactDownload: (repo, viewer, by) => call("artifact_download", { repo, viewer, ...by }),
848 deleteArtifact: (actor, repo, id) => call("delete_artifact", { actor, repo, id }),
849 artifactRetention: (repo, viewer, days) => call("artifact_retention", { repo, viewer, days }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow850 };
851}
852
853
Fast pages, required checks on the branch, self-hosted runners, honest incidents854/** Self-hosted runners, kept by the actions service. */
855export function runnersClient(service: ServiceBinding): RunnersApi {
856 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
857 return {
858 stuck: (viewer) => call("stuck_jobs", { viewer }),
The Runners page shows the machines a workspace's agents and workflow jobs run on, in Workspace under Compute: g1t's cloud beside your own runners, what each is running now with a link to it, what's waiting, this month's machine time and its cost (your own runners' free), where agent work and workflow jobs run, adding a runner, and groups; runner_activity in Actions says what runs where, work handed to your runners keeps its agent run, and the self-hosted runners guide says how.859 activity: (actor, workspace) => call("runner_activity", { actor, workspace }),
Fast pages, required checks on the branch, self-hosted runners, honest incidents860 list: (actor, owner) => call("runners", { actor, ...owner }),
861 createToken: (actor, owner, group) => call("create_registration_token", { actor, ...owner, group }),
862 remove: (actor, owner, id) => call("remove_runner", { actor, ...owner, id }),
863 groups: (actor, workspace) => call("runner_groups", { actor, workspace }),
864 setGroup: (actor, workspace, group) => call("set_runner_group", { actor, workspace, ...group }),
865 deleteGroup: (actor, workspace, id) => call("delete_runner_group", { actor, workspace, id }),
866 settings: (actor, owner) => call("runner_settings", { actor, ...owner }),
867 setSettings: (actor, owner, change) => call("set_runner_settings", { actor, ...owner, ...change }),
868 };
869}
870
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow871export function deploymentsClient(service: ServiceBinding): DeploymentsApi {
872 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
873 return {
874 settings: (project, viewer) => call("settings", { project, viewer }),
875 updateSettings: (actor, project, changes) => call("update_settings", { actor, project, changes }),
876 list: (project, viewer) => call("list", { project, viewer }),
877 get: (project, id, viewer) => call("get", { project, id, viewer }),
878 redeploy: (actor, project, branch) => call("redeploy", { actor, project, branch }),
879 takeDown: (actor, project, branch) => call("take_down", { actor, project, branch }),
880 overview: (workspace, viewer) => call("overview", { workspace, viewer }),
881 usage: (workspace, viewer) => call("usage", { workspace, viewer }),
882 domains: (project, viewer) => call("domains", { project, viewer }),
883 addDomain: (actor, project, hostname, options = {}) =>
884 call("add_domain", { actor, project, hostname, twin: !!options.twin }),
885 removeDomain: (actor, project, id) => call("remove_domain", { actor, project, id }),
886 refreshDomain: (actor, project, id) => call("refresh_domain", { actor, project, id }),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97887 repoDeployments: (repo, viewer, filter = {}) => call("list_deployments", { repo, viewer, ...filter }),
888 repoDeployment: (repo, id, viewer) => call("get_deployment", { repo, id, viewer }),
889 environments: (repo, viewer) => call("list_environments", { repo, viewer }),
890 createDeployment: (actor, repo, input) => call("create_deployment", { repo, actor, ...input }),
891 createDeploymentStatus: (actor, repo, id, input) => call("create_deployment_status", { repo, actor, id, ...input }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow892 };
893}
894
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member895/** Packages: the registries beside the code (services/packages). */
896export function packagesClient(service: ServiceBinding): PackagesApi {
897 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
898 return {
899 list: (workspace, viewer, filter = {}) =>
900 call("list_packages", {
901 workspace,
902 viewer,
903 ecosystem: filter.ecosystem ?? null,
904 repo_id: filter.repoId ?? null,
905 query: filter.query ?? null,
906 }),
907 get: (workspace, ecosystem, name, viewer) => call("get_package", { workspace, ecosystem, name, viewer }),
908 deleteVersion: (actor, workspace, ecosystem, name, version, surface) =>
909 call("delete_version", { actor, workspace, ecosystem, name, version, surface: surface ?? null }),
910 deletePackage: (actor, workspace, ecosystem, name, surface) =>
911 call("delete_package", { actor, workspace, ecosystem, name, surface: surface ?? null }),
912 set: (actor, workspace, ecosystem, name, change, surface) =>
913 call("set_package", {
914 actor,
915 workspace,
916 ecosystem,
917 name,
918 visibility: change.visibility ?? null,
919 link: change.link ?? null,
920 unlink: change.unlink ?? false,
Merge packages: roles, Actions access, source label, soft delete, API921 inherit_access: change.inheritAccess ?? null,
922 surface: surface ?? null,
923 }),
924 settings: (workspace, ecosystem, name, viewer) => call("package_settings", { workspace, ecosystem, name, viewer }),
925 deleted: (workspace, viewer) => call("deleted_packages", { workspace, viewer }),
926 restorePackage: (actor, workspace, ecosystem, name, surface) =>
927 call("restore_package", { actor, workspace, ecosystem, name, surface: surface ?? null }),
928 restoreVersion: (actor, workspace, ecosystem, name, version, surface) =>
929 call("restore_version", { actor, workspace, ecosystem, name, version, surface: surface ?? null }),
930 setAccess: (actor, workspace, ecosystem, name, who, role, surface) =>
931 call("set_package_access", {
932 actor,
933 workspace,
934 ecosystem,
935 name,
936 user: "user" in who ? who.user : null,
937 team: "team" in who ? who.team : null,
938 role,
939 surface: surface ?? null,
940 }),
941 removeAccess: (actor, workspace, ecosystem, name, who, surface) =>
942 call("remove_package_access", {
943 actor,
944 workspace,
945 ecosystem,
946 name,
947 user: "user" in who ? who.user : null,
948 team: "team" in who ? who.team : null,
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member949 surface: surface ?? null,
950 }),
Merge packages: roles, Actions access, source label, soft delete, API951 setActionsAccess: (actor, workspace, ecosystem, name, repo, role, surface) =>
952 call("set_actions_access", { actor, workspace, ecosystem, name, repo, role, surface: surface ?? null }),
953 removeActionsAccess: (actor, workspace, ecosystem, name, repo, surface) =>
954 call("remove_actions_access", { actor, workspace, ecosystem, name, repo, surface: surface ?? null }),
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member955 storage: (workspace) => call("storage", { workspace }),
956 storageAll: () => call("storage_all", {}),
Composer from the workspace's own repositories, and go get from g1t.sh957 syncComposer: (repoId) => call("sync_composer", { repo_id: repoId }),
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member958 };
959}
960
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow961export function projectsClient(service: ServiceBinding): ProjectsApi {
962 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
963 return {
964 list: (workspace, viewer) => call("list", { workspace, viewer }),
965 get: (workspace, slug, viewer) => call("get", { workspace, slug, viewer }),
966 byRepo: (repoId) => call("by_repo", { repoId }),
967 create: (actor, workspace, input) => call("create", { actor, workspace, input }),
968 update: (actor, workspace, slug, changes) => call("update", { actor, workspace, slug, changes }),
A project is an app or a library: libraries show their package and how to ship a release, not production969 deploymentsChanged: (projectId, enabled) => call("deployments_changed", { projectId, enabled }),
Projects: pinned and recent projects, per person per workspace970 shortcuts: (workspace, viewer) => call("shortcuts", { workspace, viewer }),
971 pin: (actor, workspace, slug, position) => call("pin", { actor, workspace, slug, position: position ?? null }),
972 unpin: (actor, workspace, slug) => call("unpin", { actor, workspace, slug }),
973 reorderPins: (actor, workspace, slugs) => call("reorder_pins", { actor, workspace, slugs }),
974 visited: (actor, projectId) => call("visited", { actor, projectId }),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97975 publicLinks: (repos) => call("public_links", { repos }),
Deploys as code: a manifest of every Worker, a deploy tool that ships only what changed in parallel stages, and a g1t Actions workflow976 };
977}
Every agent can have its own computer. A session that needs one wakes it: a home of its own on g1t cloud, one per agent and never shared, where it runs commands, reads and writes files and keeps what it made, with each session working in its own folder under a shared home; after ten idle minutes it sleeps, its home kept as a snapshot and restored when it wakes, and Reset wipes the home while memory and artifacts stay. Its shell and files are abilities with the usual choices, Alone, Alone when asked, Ask first or Never, offered only inside sessions and never to a chat reply; every command shows on the session with its output, and the agent's new Computer tab shows the state, the disk used of the five gigabytes included, the recent commands, and Wake, Put to sleep and Reset. Machine time counts only while it is awake, on the sandbox lines of the ledger that name the agent and who asked, held to the same spend caps as the session; the disk itself costs nothing in this version. The runner gained a long-lived supervisor that answers the computer's requests inside the container, and the runner service a computer per agent that keeps its snapshot in the agent homes bucket when one is attached, and says so when none is. The REST API and the agent tool can read a computer, wake it, put it to sleep and reset it. The agents, abilities, sessions, runners, billing and deploy guides say how it works and what an operator sets up; pinning a computer to your own runner, its browser and take-over come next.978
979/**
980 * An agent's own computer, on the runner (services/runner `AgentComputer`):
981 * what the agents service calls for a session's `run_command` and file
982 * tools, and for the Computer tab. Arguments and answers are snake_case.
983 */
984export function runnerComputerClient(service: ServiceBinding): RunnerComputerApi {
985 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
986 return {
987 computerStatus: (agentId) => call("computer_status", { agent_id: agentId }),
988 computerWake: (args) => call("computer_wake", args),
989 computerExec: (args) => call("computer_exec", args),
990 computerReadFile: (args) => call("computer_read_file", args),
991 computerWriteFile: (args) => call("computer_write_file", args),
992 computerSleep: (agentId) => call("computer_sleep", { agent_id: agentId }),
993 computerReset: (agentId) => call("computer_reset", { agent_id: agentId }),
994 computerForget: (agentId) => call("computer_forget", { agent_id: agentId }),
An archived agent can be found, brought back, or deleted for good. The Agents page ends with Archived, folded, where owners see every archived agent and members their own personal ones, each with when and by whom it was archived and what deleting it would take; Restore brings it back, under a new handle when a live agent has taken its old one, and Delete permanently asks for the handle to be typed and then removes its sessions, replies, memories, routines, versions, drafts, skills and its computer's home, leaving its messages in chat under its saved name, the workspace's spend totals and the audit log. In Chat an archived agent is deactivated the way people expect: never offered as someone to message or mention, never woken, a conversation with it kept in the list, dimmed and marked Archived, open to read with a note in place of the composer and a way to restore it for owners; after deletion the conversation still reads, its messages marked as a deleted agent's. The agents, chat and agent budgets guides say so.995 computerKeep: (agentId) => call("computer_keep", { agent_id: agentId }),
996 computerErase: (agentId) => call("computer_erase", { agent_id: agentId }),
Every agent can have its own computer. A session that needs one wakes it: a home of its own on g1t cloud, one per agent and never shared, where it runs commands, reads and writes files and keeps what it made, with each session working in its own folder under a shared home; after ten idle minutes it sleeps, its home kept as a snapshot and restored when it wakes, and Reset wipes the home while memory and artifacts stay. Its shell and files are abilities with the usual choices, Alone, Alone when asked, Ask first or Never, offered only inside sessions and never to a chat reply; every command shows on the session with its output, and the agent's new Computer tab shows the state, the disk used of the five gigabytes included, the recent commands, and Wake, Put to sleep and Reset. Machine time counts only while it is awake, on the sandbox lines of the ledger that name the agent and who asked, held to the same spend caps as the session; the disk itself costs nothing in this version. The runner gained a long-lived supervisor that answers the computer's requests inside the container, and the runner service a computer per agent that keeps its snapshot in the agent homes bucket when one is attached, and says so when none is. The REST API and the agent tool can read a computer, wake it, put it to sleep and reset it. The agents, abilities, sessions, runners, billing and deploy guides say how it works and what an operator sets up; pinning a computer to your own runner, its browser and take-over come next.997 computerCommands: (agentId, sessionId) => call("computer_commands", { agent_id: agentId, session_id: sessionId ?? null }),
998 };
999}

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