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

Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays1import 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";
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays9import 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";
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays14import type { RunnersApi } from "./runners";
15
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 }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays54 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 }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays56 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 }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays80 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" }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays91 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" }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays147 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 }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays152 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,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays160 }),
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 }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays168 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 }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays171 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 }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays179 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 }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays187 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 }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays257 };
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 }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays363 };
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 }),
371 readable: (ids, viewer) => call("readable", { ids, viewer }),
372 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 }),
The Branches page does things to branches now. It has tabs (Overview, Yours, Active, Stale, All, kept in the address as ?tab=) and search; Overview shows the default branch, then the first five of your branches, the active ones and the stale ones, each linking to the rest. Each branch is a row with its name in a code chip with a copy button, who made its latest commit and when, that commit's checks, behind and ahead counts with bars drawn against the largest shown, and its pull request (the open one, else the newest merged or closed) with its state; on a phone the columns fold under the name. Yours is branches whose latest commit is the viewer's; stale is no commit in 90 days.386 createBranch: (actor, path, name, from = "") => call("create_branch", { actor, path, name, from }),
387 removeBranch: (actor, path, branch, head) => call("remove_branch", { actor, path, branch, head }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays388 resolveBranch: (repoId, branch) => call("resolve_branch", { repoId, branch }),
389 statusById: (id) => call("status_by_id", { id }),
Merge branch 'worktree-agent-a2013627e5ea4ab13'390 storageOptions: () => call("storage_options", {}),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays391 resolvePath: (path) => call("resolve_path", { path }),
392 tree: (path, viewer, ref, treePath) =>
393 call("tree", { path, viewer, ref, treePath }),
394 blob: (path, viewer, ref, filePath) =>
395 call("blob", { path, viewer, ref, filePath }),
396 log: (path, viewer, ref, limit) => call("log", { path, viewer, ref, limit }),
397 blame: (path, viewer, ref, filePath) => call("blame", { path, viewer, ref, filePath }),
398 forkForPull: (sourceId, pullId, actor) =>
399 call("fork_for_pull", { sourceId, pullId, actor }),
400 gitAccess: (path, viewer, service) =>
401 call("git_access", { path, viewer, service }),
402 branches: (path, viewer) => call("branches", { path, viewer }),
Branches and Tags pages, each file's last commit, and the branch menu on files403 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 25404 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 files405 tags: (path, viewer) => call("tags", { path, viewer }),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97406 about: (path, viewer) => call("about", { path, viewer }),
407 languages: (path, viewer) => call("languages", { path, viewer }),
408 contributors: (path, viewer) => call("contributors", { path, viewer }),
409 license: (path, viewer) => call("license", { path, viewer }),
410 stars: (path, viewer) => call("stars", { path, viewer }),
411 star: (actor, path, starred) => call("star", { path, actor, starred }),
412 stargazers: (path, viewer, page) => call("stargazers", { path, viewer, page: page ?? null }),
413 starred: (username, viewer) => call("starred", { username, viewer }),
414 releases: (path, viewer) => call("releases", { path, viewer }),
415 release: (path, viewer, which) => call("release", { path, viewer, id: which.id ?? null, tag: which.tag ?? null, latest: which.latest ?? false }),
416 createRelease: (actor, path, release) => call("create_release", { path, actor, ...release }),
417 updateRelease: (actor, path, id, change) => call("update_release", { path, actor, id, ...change }),
418 deleteRelease: (actor, path, id) => call("delete_release", { path, actor, id }),
Merge GitHub pull request #8 (claude/cool-newton-7nm8r9): Docker deployments on g1t.page behind a switch that stays off until Cloudflare's container APIs are checked live, workflows a workspace manages in its repositories, tarball and zipball downloads, refs/pull/* kept to g1t alone, framework detection for g1t.page builds, and a workflow's event payload matching GitHub's.419 listFiles: (repoId, ref, limit, options) => call("list_files", { repoId, ref, skipDirs: [], limit, links: options?.links ?? false }),
420 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 paths421 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 address422 rawFile: (repoId, ref, path, maxBytes) => call("raw_file", { repoId, ref, path, maxBytes }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays423 commitFile: (repo, actor, file) => call("commit_file", { repo, actor, ...file }),
424 land: (sourceId, actor, branch) => call("land", { sourceId, actor, branch }),
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar425 compare: (repoId, viewer, base, head, baseBranch) => call("compare", { repoId, viewer, base, head, baseBranch }),
Merge branch 'worktree-agent-ac5b181a013e54348'426 claimBackups: (limit, maxRunning) => call("claim_backups", { limit, maxRunning }),
427 // The sandbox's own calls are snake_case (they come through the API).
428 failBackup: (jobId, token, error) => call("backup_fail", { job_id: jobId, token, error }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays429 };
430}
431
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar432/**
433 * `PullDetail`'s own fields that the work service writes in snake_case:
434 * `g1t_contracts::work::PullDetail` has no camelCase renaming, though what
435 * it holds (`Pull`, `CheckRun` and the rest) does. Each with its name here.
436 */
437const PULL_DETAIL_FIELDS = [
438 ["review_pending", "reviewPending"],
439 ["earlier_checks", "earlierChecks"],
440 ["required_checks", "requiredChecks"],
441 ["code_owners", "codeOwners"],
442] as const;
443
444/**
445 * A pull request's detail as the work service sent it, with its own fields
446 * in the camelCase this package uses: read at the edge, by `workClient`'s
447 * `getPull`. Either spelling is taken, so a service that already sends
448 * camelCase reads the same.
449 */
450export function pullDetailFromWire(raw: Record<string, unknown>): PullDetail {
451 const detail: Record<string, unknown> = { ...raw };
452 for (const [snake, camel] of PULL_DETAIL_FIELDS) {
453 if (snake in detail) {
454 if (!(camel in detail)) detail[camel] = detail[snake];
455 delete detail[snake];
456 }
457 }
458 if (typeof detail.reviewPending !== "boolean") detail.reviewPending = false;
459 return detail as PullDetail;
460}
461
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays462export function workClient(service: ServiceBinding): WorkApi {
463 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
464 return {
465 openIssue: (actor, repo, input) => call("open_issue", { actor, repo, ...input }),
466 delegateIssue: (actor, repo, input) => call("delegate_issue", { actor, repo, ...input }),
467 listIssues: (repo, viewer, filter = {}) => call("list_issues", { repo, viewer, ...filter }),
468 getIssue: (repo, number, viewer) => call("get_issue", { repo, number, viewer }),
469 updateIssue: (actor, repo, number, input) =>
470 call("update_issue", { actor, repo, number, ...input }),
471 closeIssue: (actor, repo, number, reason) =>
472 call("close_issue", { actor, repo, number, reason }),
473 reopenIssue: (actor, repo, number) => call("reopen_issue", { actor, repo, number }),
474 listLabels: (repo, viewer) => call("list_labels", { repo, viewer }),
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar475 saveLabel: (actor, repo, label) => call("save_label", { actor, repo, ...label }),
476 deleteLabel: (actor, repo, name) => call("delete_label", { actor, repo, name }),
477 addDefaultLabels: (actor, repo) => call("add_default_labels", { actor, repo }),
478 setLabels: (actor, repo, number, labels, change = "set") =>
479 call("set_labels", { actor, repo, number, labels, change }),
480 listMilestones: (repo, viewer, state) => call("list_milestones", { repo, viewer, state }),
481 getMilestone: (repo, number, viewer) => call("get_milestone", { repo, number, viewer }),
482 saveMilestone: (actor, repo, milestone) => call("save_milestone", { actor, repo, ...milestone }),
483 deleteMilestone: (actor, repo, number) => call("delete_milestone", { actor, repo, number }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays484 counts: (repo, viewer) => call("counts", { repo, viewer }),
485 addComment: (actor, repo, number, comment) =>
486 call("add_comment", { actor, repo, number, ...comment }),
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts487 editComment: (actor, repo, commentId, body) => call("edit_comment", { actor, repo, commentId, body }),
488 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 calendar489 workspaceAgentComment: (repo, number, agent, actingFor, body) =>
490 call("workspace_agent_comment", { repo, number, agent, acting_for: actingFor, body }),
491 workspaceAgentReview: (repo, number, agent, actingFor, verdict, body) =>
492 call("workspace_agent_review", { repo, number, agent, acting_for: actingFor, verdict, body }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays493 startChecks: (pullId) => call("start_checks", { pullId }),
494 reportChecks: (runId, token, report) =>
495 call("report_checks", { runId, token, ...report }),
496 startReview: (pullId) => call("start_review", { pullId }),
497 failReview: (runId, token, error) => call("report_review", { runId, token, error }),
498 startMergecheck: (pullId) => call("start_mergecheck", { pullId }),
499 failMergecheck: (pullId, token, error) => call("report_mergecheck", { pullId, token, error }),
500 advance: (pullId) => call("advance", { pullId }),
501 stall: (pullId, reason) => call("stall", { pullId, reason }),
502 managedPulls: (repoId) => call("managed_pulls", { repoId }),
503 queue: (repo, viewer) => call("queue", { repo, viewer }),
504 queueBuild: (repoId) => call("queue_build", { repoId }),
505 failQueue: (entryId, token, error) => call("report_queue", { entryId, token, error }),
506 removeFromQueue: (actor, repo, number) => call("remove_from_queue", { actor, repo, number }),
507 messageAgent: (actor, repo, number, body) => call("message_agent", { actor, repo, number, body }),
508 catchUpJob: (pullId) => call("catch_up_job", { pullId }),
509 wakeForMessages: (pullId) => call("wake_for_messages", { pullId }),
510 getSettings: (repo, viewer) => call("get_settings", { repo, viewer }),
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge511 // Rulesets travel in snake_case (rules.ts).
512 listRulesets: (owner, viewer, includeParents = false) =>
513 call("list_rulesets", { viewer, ...owner, include_parents: includeParents }),
514 getRuleset: (owner, id, viewer) => call("get_ruleset", { viewer, ...owner, id }),
515 saveRuleset: (actor, owner, ruleset, id) => call("save_ruleset", { actor, ...owner, id: id ?? null, ruleset }),
516 deleteRuleset: (actor, owner, id) => call("delete_ruleset", { actor, ...owner, id }),
517 effectiveRules: (repo, name, viewer, target = "branch") => call("effective_rules", { viewer, repo, name, target }),
518 ruleEvaluations: (owner, viewer, filter = {}) => call("rule_evaluations", { viewer, ...owner, ...filter }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays519 seenChecks: (repo, viewer) => call("seen_checks", { repo, viewer }),
Merge checks: statuses and check runs on every commit520 commitChecks: (repo, viewer, shas) => call("commit_checks", { repo, viewer, shas }),
521 getCheckRun: (repo, id, viewer) => call("get_check_run", { repo, id, viewer }),
522 checkRunAnnotations: (repo, id, viewer) => call("check_run_annotations", { repo, id, viewer }),
523 requestCheckAction: (actor, repo, id, identifier) => call("request_check_action", { actor, repo, id, identifier }),
524 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 bar525 codeownersErrors: (repo, viewer, ref) => call("codeowners_errors", { repo, viewer, ref: ref ?? null }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays526 updateSettings: (actor, repo, settings) =>
527 call("update_settings", { actor, repo, settings }),
528 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 bar529 listPulls: (repo, viewer, state, filter = {}) => call("list_pulls", { repo, viewer, state, ...filter }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays530 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 bar531 // Its own fields arrive in snake_case: read into camelCase here.
532 getPull: (repo, number, viewer) =>
533 call<Result<Record<string, unknown>>>("get_pull", { repo, number, viewer }).then(
534 (found): Result<PullDetail> => (found.ok ? { ok: true, value: pullDetailFromWire(found.value) } : found),
535 ),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays536 updatePull: (actor, repo, number, changes) =>
537 call("update_pull", { actor, repo, number, ...changes }),
538 catchUpPull: (actor, repo, number) => call("catch_up_pull", { actor, repo, number }),
539 readyPull: (actor, repo, number, summary) =>
540 call("ready_pull", { actor, repo, number, summary }),
541 closePull: (actor, repo, number) => call("close_pull", { actor, repo, number }),
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts542 reopenPull: (actor, repo, number) => call("reopen_pull", { actor, repo, number }),
543 convertPullToDraft: (actor, repo, number) => call("convert_pull_to_draft", { actor, repo, number }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays544 mergePull: (actor, repo, number, options = {}) =>
545 call("merge_pull", { actor, repo, number, ...options }),
546 listActivePulls: (viewer) => call("list_active_pulls", { viewer }),
547 byAuthor: (username, viewer, filter = {}) => call("by_author", { username, viewer, ...filter }),
Chat controls, public profiles, shadcn selects, and no Docs tab in a project548 contributions: (username, viewer) => call("contributions", { username, viewer }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays549 startPlan: (actor, repo, brief) => call("start_plan", { actor, repo, brief }),
550 failPlan: (planId, token, error) => call("report_plan", { planId, token, error }),
551 getPlan: (repo, viewer, id) => call("get_plan", { repo, viewer, id }),
552 listPlans: (repo, viewer) => call("list_plans", { repo, viewer }),
553 applyPlan: (actor, repo, id, options = {}) =>
554 call("apply_plan", { actor, repo, id, ...options }),
555 queueIssue: (actor, repo, number, queued) =>
556 call("queue_issue", { actor, repo, number, queued }),
557 readyIssues: (repoId) => call("ready_issues", { repoId }),
558 listAssignedIssues: (viewer) => call("list_assigned_issues", { viewer }),
559 appendSession: (actor, repo, number, entries) =>
560 call("append_session", { actor, repo, number, entries }),
561 readSession: (repo, number, viewer, afterSeq = 0) =>
562 call("read_session", { repo, number, viewer, afterSeq }),
563 };
564}
565
566export function billingClient(service: ServiceBinding): BillingApi {
567 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
568 return {
569 status: () => call("status", {}),
570 account: (workspace, viewer) => call("account", { workspace, viewer }),
571 ledger: (workspace, viewer) => call("ledger", { workspace, viewer }),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging572 credits: (workspace, viewer) => call("credits", { workspace, viewer }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays573 statement: (workspace, viewer, month = null, group = "day") => call("statement", { workspace, viewer, month, group }),
574 statementEntries: (workspace, viewer, filter) =>
575 call("statement_entries", {
576 workspace,
577 viewer,
578 month: filter.month,
579 kind: filter.kind,
580 day: filter.day ?? null,
581 project: filter.project ?? null,
582 before: filter.before ?? null,
583 }),
584 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 mix585 tokenUsage: (workspace, viewer, options = {}) =>
586 call("token_usage", { workspace, viewer, person: options.person ?? null, days: options.days ?? null }),
587 recordTokens: (usage) => call("record_tokens", usage),
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens588 gatewayModels: () => call("gateway_models", {}),
Merge branch 'main' into actions-toolkit-oidc-artifacts589 modelDefaults: () => call("model_defaults", {}),
590 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 tokens591 gatewayAdmit: (workspace) => call("gateway_admit", { workspace }),
592 recordGateway: (record) => call("record_gateway", record),
593 gatewayRequests: (workspace, viewer, options = {}) =>
594 call("gateway_requests", { workspace, viewer, limit: options.limit ?? null, before: options.before ?? null }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays595 checkout: (actor, workspace, amountCents, returnUrl, method = "card") =>
596 call("checkout", { actor, workspace, amountCents, returnUrl, method }),
597 confirm: (workspace, viewer, session) => call("confirm", { workspace, viewer, session }),
598 canStart: (workspace) => call("can_start", { workspace }),
599 trial: (workspace, exempt) => call("trial", { workspace, exempt }),
600 startRun: (run) => call("start_run", run),
601 features: (workspace, viewer) => call("features", { workspace, viewer }),
602 subscribe: (actor, workspace, feature, returnUrl) =>
603 call("subscribe", { actor, workspace, feature, returnUrl }),
604 confirmSubscription: (workspace, viewer, session) =>
605 call("confirm_subscription", { workspace, viewer, session }),
606 cancelSubscription: (actor, workspace, feature, resume = false) =>
607 call("cancel_subscription", { actor, workspace, feature, resume }),
608 hasFeature: (workspace, feature) => call("has_feature", { workspace, feature }),
Merge Stripe Tax, the card fee on card payments, and one free workspace per person609 freeWorkspaces: (workspaces) => call("free_workspaces", { workspaces }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays610 chargeFeature: (charge) => call("charge_feature", charge),
611 billingPortal: (actor, workspace, returnUrl) => call("billing_portal", { actor, workspace, return_url: returnUrl }),
612 recordSandbox: (usage) => call("record_sandbox", usage),
613 limit: (workspace, viewer) => call("limit", { workspace, viewer }),
614 checkLimit: (workspace) => call("check_limit", { workspace }),
615 prices: () => call("prices", {}),
616 notePending: (workspace, source, costMicros, detail = null) => call("note_pending", { workspace, source, costMicros, detail }),
617 usageMeters: (workspace, viewer) => call("usage_meters", { workspace, viewer }),
618 setSpendLimit: (actor, workspace, spendLimitMicros, useFullLimit = false, raiseOnce = false) =>
619 call("set_spend_limit", { actor, workspace, spendLimitMicros, use_full_limit: useFullLimit, raise_once: raiseOnce }),
620 invoices: (workspace, viewer) => call("invoices", { workspace, viewer }),
621 entitlements: (workspace) => call("entitlements", { workspace }),
622 reserve: (reservation) => call("reserve", reservation),
623 settle: (reservationId, actualMicros) => call("settle", { reservationId, actualMicros }),
624 cardCheck: (actor, workspace, returnUrl) => call("card_check", { actor, workspace, returnUrl }),
625 confirmCardCheck: (workspace, viewer, session) => call("confirm_card_check", { workspace, viewer, session }),
626 requestLimit: (actor, workspace, request) => call("request_limit", { actor, workspace, ...request }),
627 limitRequests: (workspace, viewer) => call("limit_requests", { workspace, viewer }),
628 confirmSpike: (actor, workspace, keepGoing) => call("confirm_spike", { actor, workspace, keepGoing }),
629 setCaps: (actor, workspace, caps) => call("set_caps", { actor, workspace, ...caps }),
Usage, Billing settings and prepaid AI credit; fixes from the UX audit630 usageReport: (workspace, viewer, range) =>
631 call("usage_report", { workspace, viewer, from: range.from, until: range.until, products: range.products ?? [], projects: range.projects ?? [] }),
632 aiCredit: (workspace, viewer) => call("ai_credit", { workspace, viewer }),
633 buyAiCredit: (actor, workspace, amountCents, returnUrl) => call("buy_ai_credit", { actor, workspace, amountCents, returnUrl }),
634 confirmAiCredit: (workspace, viewer, session) => call("confirm_ai_credit", { workspace, viewer, session }),
635 setAiReload: (actor, workspace, reload) => call("set_ai_reload", { actor, workspace, ...reload }),
636 setBudget: (actor, workspace, budget) => call("set_budget", { actor, workspace, ...budget }),
637 billingDetails: (workspace, viewer) => call("billing_details", { workspace, viewer }),
638 setBillingDetails: (actor, workspace, details) => call("set_billing_details", { actor, workspace, ...details }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays639 };
640}
641
642export function billingAdminClient(service: ServiceBinding): BillingAdminApi {
643 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
644 return {
645 accounts: (query) => call("admin_accounts", { query: query ?? null }),
646 account: (id) => call("admin_account", { id }),
647 setTerms: (id, terms, by) => call("admin_set_terms", { id, terms, by }),
648 setAllowances: (id, allowances, note, by) => call("admin_set_allowances", { id, allowances, note, by }),
649 createEnterprise: (name, workspaces, by) => call("admin_create_enterprise", { name, workspaces, by }),
650 attach: (workspace, account, by) => call("admin_attach", { workspace, account, by }),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging651 credit: (workspace, amountMicros, note, by, options = { kind: "goodwill" }) =>
652 call("admin_credit", {
653 workspace,
654 amount_micros: amountMicros,
655 note,
656 by,
657 kind: options.kind,
658 expires_at: options.expiresAt ?? null,
659 refund_for: options.refundFor ?? null,
660 refund_day: options.refundDay ?? null,
661 }),
662 credits: (filter = {}) =>
663 call("admin_credits", {
664 workspace: filter.workspace ?? null,
665 kind: filter.kind ?? null,
666 month: filter.month ?? null,
667 by: filter.by ?? null,
668 }),
669 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's670 resetBilling: (workspace, confirm, note, by) => call("admin_reset_billing", { workspace, confirm, note, by }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays671 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 dashboard672 stripe: (fix = false, by) => call("admin_stripe", { fix, by: by ?? null }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays673 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 person674 enterpriseAddress: (id, address, taxIdType, taxId, by) => call("admin_enterprise_address", { id, address, taxIdType, taxId, by }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays675 invoiceEnterprise: (id, by) => call("admin_invoice_enterprise", { id, by }),
676 accountsFor: (workspaces) => call("admin_accounts", { query: null, workspaces }),
677 signals: () => call("admin_signals", {}),
678 overview: () => call("admin_overview", {}),
679 sales: (workspace) => call("admin_sales", { workspace }),
680 setSales: (workspace, record, by) =>
681 call("admin_set_sales", {
682 workspace,
683 stage: record.stage,
684 owner: record.owner ?? null,
685 next_step: record.nextStep ?? null,
686 next_at: record.nextAt ?? null,
687 by,
688 }),
689 addNote: (workspace, text, by) => call("admin_add_note", { workspace, text, by }),
690 workspaceInvoices: (workspace) => call("admin_workspace_invoices", { workspace }),
691 allInvoices: (filter = {}) => call("admin_invoices", { status: filter.status ?? null, month: filter.month ?? null }),
692 audit: (filter = {}) =>
693 call("admin_audit", { by: filter.by ?? null, action: filter.action ?? null, before: filter.before ?? null }),
694 limitRequests: (status = "open") => call("admin_limit_requests", { status }),
695 decideLimitRequest: (id, decision, amountMicros, note, by) =>
696 call("admin_decide_limit_request", { id, decision, amount_micros: amountMicros, note, by }),
697 overages: () => call("admin_overages", {}),
698 goodwill: (workspace, amountMicros, reason, by, day = null) =>
699 call("admin_goodwill", { workspace, amount_micros: amountMicros, reason, by, day }),
700 velocity: () => call("admin_velocity", {}),
701 recordPayment: (workspace, amountMicros, reference, note, by) =>
702 call("admin_record_payment", { workspace, amount_micros: amountMicros, reference, note, by }),
703 costs: (days) => call("admin_costs", { days: days ?? null }),
704 costAlerts: () => call("admin_cost_alerts", {}),
705 spendCaps: () => call("admin_spend_caps", {}),
706 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)707 platformGuard: () => call("admin_platform_guard", {}),
708 setPause: (level, paused, note, by) => call("admin_set_pause", { level, paused, note, by }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays709 decideProposal: (id, decision, note, by) => call("admin_decide_proposal", { id, decision, note, by }),
710 setCostSettings: (settings, by) => call("admin_set_cost_settings", { settings, by }),
711 setCostMapping: (mapping, by) =>
712 call("admin_set_cost_mapping", {
713 product: mapping.product,
714 meter: mapping.meter,
715 bucket: mapping.bucket ?? "",
716 price_meter: mapping.priceMeter ?? null,
717 own_meter: mapping.ownMeter ?? null,
718 scale_to_own: mapping.scaleToOwn ?? false,
719 drift_percent: mapping.driftPercent ?? null,
720 note: mapping.note ?? "",
721 remove: mapping.remove ?? false,
722 by,
723 }),
724 runCosts: (by) => call("admin_run_costs", { by }),
Merge branch 'main' into actions-toolkit-oidc-artifacts725 models: () => call("admin_models", {}),
726 decideModel: (model, decision, details, reason, by) =>
727 call("admin_decide_model", {
728 model,
729 decision,
730 name: details.name ?? null,
731 tier_hint: details.tierHint ?? null,
732 prices: details.prices
733 ? {
734 input_micros: details.prices.inputMicros,
735 output_micros: details.prices.outputMicros,
736 cache_read_micros: details.prices.cacheReadMicros,
737 cache_write_micros: details.prices.cacheWriteMicros,
738 cache_write_1h_micros: details.prices.cacheWrite1hMicros,
739 threshold: details.prices.threshold,
740 over_input_micros: details.prices.overInputMicros,
741 over_output_micros: details.prices.overOutputMicros,
742 over_cache_read_micros: details.prices.overCacheReadMicros,
743 over_cache_write_micros: details.prices.overCacheWriteMicros,
744 over_cache_write_1h_micros: details.prices.overCacheWrite1hMicros,
745 }
746 : null,
747 reason,
748 by,
749 }),
750 setModelDefault: (purpose, value, reason, by) =>
751 call("admin_set_model_default", {
752 purpose,
753 model: value.model ?? null,
754 tier: value.tier ?? null,
755 effort: value.effort ?? null,
756 reason,
757 by,
758 }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays759 };
760}
761
762export function eventsClient(service: ServiceBinding): EventsApi {
763 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
764 return {
765 publish: (events) => call("publish", { events }),
766 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.767 activityDigest: (args) => call("activity_digest", args),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays768 };
769}
770
771export function integrationsClient(service: ServiceBinding): IntegrationsApi {
772 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
773 return {
774 list: (workspace, viewer) => call("list", { workspace, viewer }),
775 connect: (actor, workspace, input) => call("connect", { actor, workspace, ...input }),
776 update: (actor, workspace, id, input) => call("update", { actor, workspace, id, ...input }),
777 disconnect: (actor, workspace, id) => call("disconnect", { actor, workspace, id }),
778 test: (actor, workspace, id) => call("test", { actor, workspace, id }),
779 deliveries: (workspace, viewer, id) => call("deliveries", { workspace, viewer, id }),
780 resolve: (workspace, viewer, reference) => call("resolve", { workspace, viewer, reference }),
781 references: (workspace, text, limit) => call("references", { workspace, text, limit }),
782 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.783 comment: (actor, workspace, reference, text, link) => call("comment", { actor, workspace, reference, text, link }),
784 close: (actor, workspace, reference, text, link) => call("close", { actor, workspace, reference, text, link }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays785 links: (repo, number) => call("links", { repo, number }),
786 modelProvider: (workspace) => call("model_provider", { workspace }),
787 openModelSession: (run) => call("open_model_session", run),
788 modelUpstream: (token) => call("model_upstream", { token }),
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens789 gatewayUpstream: (workspace) => call("gateway_upstream", { workspace }),
AI Gateway: OpenAI's format, open models, and your own providers790 gatewayProviders: (workspace) => call("gateway_providers", { workspace }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays791 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)792 capModelSessions: (tokenHashes, capMicros) => call("cap_model_sessions", { token_hashes: tokenHashes, cap_micros: capMicros }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays793 routes: (workspace, viewer) => call("routes", { workspace, viewer }),
794 setRoutes: (actor, workspace, routes) => call("set_routes", { actor, workspace, routes }),
795 };
796}
797
798export function webhooksClient(service: ServiceBinding): WebhooksApi {
799 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
800 return {
801 list: (viewer, owner) => call("list", { viewer, ...owner }),
802 create: (actor, owner, input) => call("create", { actor, ...owner, ...input }),
803 update: (actor, owner, id, input) => call("update", { actor, ...owner, id, ...input }),
804 delete: (actor, owner, id) => call("delete", { actor, ...owner, id }),
805 ping: (actor, owner, id) => call("ping", { actor, ...owner, id }),
806 deliveries: (viewer, owner, id) => call("deliveries", { viewer, ...owner, id }),
807 redeliver: (actor, owner, deliveryId) => call("redeliver", { actor, ...owner, deliveryId }),
808 };
809}
810
811export function actionsClient(service: ServiceBinding): ActionsApi {
812 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
813 return {
814 workflows: (repo, viewer) => call("workflows", { repo, viewer }),
815 runs: (repo, viewer, filter = {}) => call("runs", { repo, viewer, ...filter }),
Merge Actions runs: summaries, attempts and re-runs, graceful cancel, log downloads, badges (actions 0009)816 run: (repo, viewer, id, attempt) => call("run", { repo, viewer, id, attempt: attempt ?? null }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays817 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)818 summaries: (repo, viewer, id, attempt) => call("summaries", { repo, viewer, id, attempt: attempt ?? null }),
819 jobLogText: (repo, viewer, job) => call("job_log_text", { repo, viewer, job }),
820 runLogs: (repo, viewer, id, attempt) => call("run_logs", { repo, viewer, id, attempt: attempt ?? null }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays821 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)822 cancel: (actor, repo, id, force = false) => call("cancel", { actor, repo, id, force }),
823 rerun: (actor, repo, id, failedOnly = false, options = {}) =>
824 call("rerun", { actor, repo, id, failed_only: failedOnly, job: options.job ?? null, debug: options.debug ?? false }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays825 setWorkflowEnabled: (actor, repo, workflow, enabled) =>
826 call("set_workflow_enabled", { actor, repo, workflow, enabled }),
827 settings: (actor, owner, kind) => call("settings", { actor, ...owner, kind }),
828 setSetting: (actor, owner, kind, name, value, options = {}) =>
829 call("set_setting", { actor, ...owner, kind, name, value, ...options }),
830 deleteSetting: (actor, owner, kind, name, id) => call("delete_setting", { actor, ...owner, kind, name, id }),
Merge branch 'worktree-agent-a3abfcce648e87dca'831 approveRun: (actor, repo, id) => call("approve_run", { actor, repo, id }),
832 pendingDeployments: (repo, viewer, id) => call("pending_deployments", { repo, viewer, id }),
833 reviewDeployments: (actor, repo, id, state, environments = [], comment) =>
834 call("review_deployments", { actor, repo, id, state, environments, comment: comment ?? null }),
835 actionsSettings: (repo, viewer) => call("actions_settings", { repo, viewer }),
836 setActionsSettings: (actor, repo, change) => call("set_actions_settings", { actor, repo, ...change }),
837 workspaceActionsSettings: (workspace, viewer) => call("workspace_actions_settings", { workspace, viewer }),
838 setWorkspaceActionsSettings: (actor, workspace, change) =>
839 call("set_workspace_actions_settings", { actor, workspace, ...change }),
Merge GitHub pull request #8 (claude/cool-newton-7nm8r9): Docker deployments on g1t.page behind a switch that stays off until Cloudflare's container APIs are checked live, workflows a workspace manages in its repositories, tarball and zipball downloads, refs/pull/* kept to g1t alone, framework detection for g1t.page builds, and a workflow's event payload matching GitHub's.840 workspaceManagedWorkflows: (workspace, viewer) => call("workspace_managed_workflows", { workspace, viewer }),
841 setWorkspaceManagedWorkflow: (actor, workspace, name, change, create = false) =>
842 call("set_workspace_managed_workflow", { actor, workspace, name, create, ...change }),
843 removeWorkspaceManagedWorkflow: (actor, workspace, name) =>
844 call("remove_workspace_managed_workflow", { actor, workspace, name }),
Merge branch 'worktree-agent-a3abfcce648e87dca'845 environments: (repo, viewer) => call("environments", { repo, viewer }),
846 setEnvironment: (actor, repo, name, change) => call("set_environment", { actor, repo, name, ...change }),
847 deleteEnvironment: (actor, repo, name) => call("delete_environment", { actor, repo, name }),
Actions: OIDC tokens, the toolkit's cache and artifact services, and artifacts in R2848 artifacts: (repo, viewer, filter = {}) => call("artifacts", { repo, viewer, ...filter }),
849 artifactDownload: (repo, viewer, by) => call("artifact_download", { repo, viewer, ...by }),
850 deleteArtifact: (actor, repo, id) => call("delete_artifact", { actor, repo, id }),
851 artifactRetention: (repo, viewer, days) => call("artifact_retention", { repo, viewer, days }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays852 };
853}
854
855
856/** Self-hosted runners, kept by the actions service. */
857export function runnersClient(service: ServiceBinding): RunnersApi {
858 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
859 return {
860 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.861 activity: (actor, workspace) => call("runner_activity", { actor, workspace }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays862 list: (actor, owner) => call("runners", { actor, ...owner }),
863 createToken: (actor, owner, group) => call("create_registration_token", { actor, ...owner, group }),
864 remove: (actor, owner, id) => call("remove_runner", { actor, ...owner, id }),
865 groups: (actor, workspace) => call("runner_groups", { actor, workspace }),
866 setGroup: (actor, workspace, group) => call("set_runner_group", { actor, workspace, ...group }),
867 deleteGroup: (actor, workspace, id) => call("delete_runner_group", { actor, workspace, id }),
868 settings: (actor, owner) => call("runner_settings", { actor, ...owner }),
869 setSettings: (actor, owner, change) => call("set_runner_settings", { actor, ...owner, ...change }),
Merge the desktop app's runner: it starts with the app for the signed-in person, sandboxed by default and running nothing where no sandbox is available yet, registers through the site, keeps itself up to date, and says everywhere what it is doing, why it is not, and how to fix it.870 reportDesktop: (actor, report) => call("report_desktop", { actor, ...report }),
871 desktops: (actor, workspace) => call("desktop_runners", { actor, workspace }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays872 };
873}
874
875export function deploymentsClient(service: ServiceBinding): DeploymentsApi {
876 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
877 return {
878 settings: (project, viewer) => call("settings", { project, viewer }),
879 updateSettings: (actor, project, changes) => call("update_settings", { actor, project, changes }),
880 list: (project, viewer) => call("list", { project, viewer }),
881 get: (project, id, viewer) => call("get", { project, id, viewer }),
882 redeploy: (actor, project, branch) => call("redeploy", { actor, project, branch }),
883 takeDown: (actor, project, branch) => call("take_down", { actor, project, branch }),
884 overview: (workspace, viewer) => call("overview", { workspace, viewer }),
885 usage: (workspace, viewer) => call("usage", { workspace, viewer }),
886 domains: (project, viewer) => call("domains", { project, viewer }),
887 addDomain: (actor, project, hostname, options = {}) =>
888 call("add_domain", { actor, project, hostname, twin: !!options.twin }),
889 removeDomain: (actor, project, id) => call("remove_domain", { actor, project, id }),
890 refreshDomain: (actor, project, id) => call("refresh_domain", { actor, project, id }),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97891 repoDeployments: (repo, viewer, filter = {}) => call("list_deployments", { repo, viewer, ...filter }),
892 repoDeployment: (repo, id, viewer) => call("get_deployment", { repo, id, viewer }),
893 environments: (repo, viewer) => call("list_environments", { repo, viewer }),
894 createDeployment: (actor, repo, input) => call("create_deployment", { repo, actor, ...input }),
895 createDeploymentStatus: (actor, repo, id, input) => call("create_deployment_status", { repo, actor, id, ...input }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays896 };
897}
898
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member899/** Packages: the registries beside the code (services/packages). */
900export function packagesClient(service: ServiceBinding): PackagesApi {
901 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
902 return {
903 list: (workspace, viewer, filter = {}) =>
904 call("list_packages", {
905 workspace,
906 viewer,
907 ecosystem: filter.ecosystem ?? null,
908 repo_id: filter.repoId ?? null,
909 query: filter.query ?? null,
910 }),
911 get: (workspace, ecosystem, name, viewer) => call("get_package", { workspace, ecosystem, name, viewer }),
912 deleteVersion: (actor, workspace, ecosystem, name, version, surface) =>
913 call("delete_version", { actor, workspace, ecosystem, name, version, surface: surface ?? null }),
914 deletePackage: (actor, workspace, ecosystem, name, surface) =>
915 call("delete_package", { actor, workspace, ecosystem, name, surface: surface ?? null }),
916 set: (actor, workspace, ecosystem, name, change, surface) =>
917 call("set_package", {
918 actor,
919 workspace,
920 ecosystem,
921 name,
922 visibility: change.visibility ?? null,
923 link: change.link ?? null,
924 unlink: change.unlink ?? false,
Merge packages: roles, Actions access, source label, soft delete, API925 inherit_access: change.inheritAccess ?? null,
926 surface: surface ?? null,
927 }),
928 settings: (workspace, ecosystem, name, viewer) => call("package_settings", { workspace, ecosystem, name, viewer }),
929 deleted: (workspace, viewer) => call("deleted_packages", { workspace, viewer }),
930 restorePackage: (actor, workspace, ecosystem, name, surface) =>
931 call("restore_package", { actor, workspace, ecosystem, name, surface: surface ?? null }),
932 restoreVersion: (actor, workspace, ecosystem, name, version, surface) =>
933 call("restore_version", { actor, workspace, ecosystem, name, version, surface: surface ?? null }),
934 setAccess: (actor, workspace, ecosystem, name, who, role, surface) =>
935 call("set_package_access", {
936 actor,
937 workspace,
938 ecosystem,
939 name,
940 user: "user" in who ? who.user : null,
941 team: "team" in who ? who.team : null,
942 role,
943 surface: surface ?? null,
944 }),
945 removeAccess: (actor, workspace, ecosystem, name, who, surface) =>
946 call("remove_package_access", {
947 actor,
948 workspace,
949 ecosystem,
950 name,
951 user: "user" in who ? who.user : null,
952 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 member953 surface: surface ?? null,
954 }),
Merge packages: roles, Actions access, source label, soft delete, API955 setActionsAccess: (actor, workspace, ecosystem, name, repo, role, surface) =>
956 call("set_actions_access", { actor, workspace, ecosystem, name, repo, role, surface: surface ?? null }),
957 removeActionsAccess: (actor, workspace, ecosystem, name, repo, surface) =>
958 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 member959 storage: (workspace) => call("storage", { workspace }),
960 storageAll: () => call("storage_all", {}),
Composer from the workspace's own repositories, and go get from g1t.sh961 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 member962 };
963}
964
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays965export function projectsClient(service: ServiceBinding): ProjectsApi {
966 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
967 return {
968 list: (workspace, viewer) => call("list", { workspace, viewer }),
969 get: (workspace, slug, viewer) => call("get", { workspace, slug, viewer }),
970 byRepo: (repoId) => call("by_repo", { repoId }),
971 create: (actor, workspace, input) => call("create", { actor, workspace, input }),
972 update: (actor, workspace, slug, changes) => call("update", { actor, workspace, slug, changes }),
Merge branch 'worktree-agent-a5b0d9e2733e9e382'973 setProductionImage: (actor, workspace, slug, change) => call("set_production_image", { actor, workspace, slug, change }),
974 externalProductions: () => call("external_productions", {}),
A project is an app or a library: libraries show their package and how to ship a release, not production975 deploymentsChanged: (projectId, enabled) => call("deployments_changed", { projectId, enabled }),
Projects: pinned and recent projects, per person per workspace976 shortcuts: (workspace, viewer) => call("shortcuts", { workspace, viewer }),
977 pin: (actor, workspace, slug, position) => call("pin", { actor, workspace, slug, position: position ?? null }),
978 unpin: (actor, workspace, slug) => call("unpin", { actor, workspace, slug }),
979 reorderPins: (actor, workspace, slugs) => call("reorder_pins", { actor, workspace, slugs }),
980 visited: (actor, projectId) => call("visited", { actor, projectId }),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97981 publicLinks: (repos) => call("public_links", { repos }),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays982 };
983}
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.984
985/**
986 * An agent's own computer, on the runner (services/runner `AgentComputer`):
987 * what the agents service calls for a session's `run_command` and file
988 * tools, and for the Computer tab. Arguments and answers are snake_case.
989 */
990export function runnerComputerClient(service: ServiceBinding): RunnerComputerApi {
991 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
992 return {
993 computerStatus: (agentId) => call("computer_status", { agent_id: agentId }),
994 computerWake: (args) => call("computer_wake", args),
995 computerExec: (args) => call("computer_exec", args),
996 computerReadFile: (args) => call("computer_read_file", args),
997 computerWriteFile: (args) => call("computer_write_file", args),
998 computerSleep: (agentId) => call("computer_sleep", { agent_id: agentId }),
999 computerReset: (agentId) => call("computer_reset", { agent_id: agentId }),
1000 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.1001 computerKeep: (agentId) => call("computer_keep", { agent_id: agentId }),
1002 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.1003 computerCommands: (agentId, sessionId) => call("computer_commands", { agent_id: agentId, session_id: sessionId ?? null }),
1004 };
1005}

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