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.
| Billing and Usage reconcile: own-provider runs leave Billing's at-price total, and Usage's not-charged part is at price less charged | 1 | /** |
| 2 | * Run credentials and the audit log. Mirrors `crates/contracts/src/credentials.rs` | |
| 3 | * and `crates/contracts/src/audit.rs`. | |
| 4 | * | |
| 5 | * Every sandbox run gets its own tokens, bound to the run, its repository | |
| 6 | * and what its kind of work needs, and acting as an agent on behalf of the | |
| 7 | * person who started the work. Everything done with them is recorded in | |
| 8 | * the workspace's audit log, with the rule that allowed or refused it. | |
| 9 | */ | |
| 10 | ||
| 11 | import type { ServiceBinding } from "./clients"; | |
| 12 | import type { RepoPath } from "./repos"; | |
| 13 | import type { User } from "./identity"; | |
| 14 | ||
| 15 | /** What a run does, as far as its credentials are concerned. */ | |
| 16 | export type RunCredentialKind = | |
| 17 | | "implement" | |
| 18 | | "revise" | |
| 19 | | "review" | |
| 20 | | "answer" | |
| 21 | | "update" | |
| 22 | | "plan" | |
| 23 | | "checks" | |
| 24 | | "queue" | |
| 25 | | "mergecheck" | |
| 26 | | "deploy" | |
| 27 | | "bump"; | |
| 28 | ||
| 29 | /** | |
| 30 | * `runner`: g1t's runner in the sandbox, which clones, pushes and records | |
| 31 | * the session, acting downstream as the person. `tools`: the agent's own | |
| 32 | * MCP tools, acting as the agent. | |
| 33 | */ | |
| 34 | export type CredentialUse = "runner" | "tools"; | |
| 35 | ||
| 36 | /** A repository a run may push to; `branch` null means any branch of it. */ | |
| 37 | export type GitGrant = { repo: RepoPath; branch?: string | null }; | |
| 38 | ||
| The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens. | 39 | /** What binds a run's token to one run. */ |
| Billing and Usage reconcile: own-provider runs leave Billing's at-price total, and Usage's not-charged part is at price less charged | 40 | export type RunBinding = { |
| 41 | kind: RunCredentialKind; | |
| 42 | use: CredentialUse; | |
| 43 | runId?: string | null; | |
| 44 | number?: number | null; | |
| 45 | agent: string; | |
| 46 | read: RepoPath[]; | |
| 47 | push: GitGrant[]; | |
| 48 | /** g1t's own run: the workspace's credential, acting on behalf of g1t. */ | |
| 49 | system?: boolean; | |
| 50 | }; | |
| 51 | ||
| The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens. | 52 | /** Set on a run's token (or a historical `@g1t` agent's) resolved from its token: the composite identity. */ |
| Billing and Usage reconcile: own-provider runs leave Billing's at-price total, and Usage's not-charged part is at price less charged | 53 | export type Acting = { |
| 54 | credentialId: string; | |
| 55 | agent: string; | |
| 56 | onBehalfOf: { id: string; username: string }; | |
| 57 | scope: { repo: RepoPath; operations: string[]; run?: RunBinding }; | |
| 58 | }; | |
| 59 | ||
| 60 | export type CreateRunCredentialInput = { | |
| 61 | onBehalfOf: User; | |
| 62 | repo: RepoPath; | |
| 63 | kind: RunCredentialKind; | |
| 64 | use: CredentialUse; | |
| 65 | /** The pull request the run works on. */ | |
| 66 | number?: number | null; | |
| 67 | /** Repositories it may clone besides those it may push to. */ | |
| 68 | read?: RepoPath[]; | |
| 69 | push?: GitGrant[]; | |
| 70 | /** The run's timeout. */ | |
| 71 | ttlSeconds: number; | |
| 72 | }; | |
| 73 | ||
| 74 | export type ActorKind = "person" | "agent" | "workspace" | "runner" | "system"; | |
| 75 | export type AuditOutcome = "allowed" | "denied"; | |
| 76 | /** Where it came in: the API, MCP, git, or g1t.sh's own pages. */ | |
| 77 | export type AuditSurface = "rest" | "mcp" | "git" | "web" | "registry"; | |
| 78 | ||
| 79 | export type AuditEntry = { | |
| 80 | id: string; | |
| 81 | /** RFC 3339. */ | |
| 82 | time: string; | |
| 83 | actorKind: ActorKind; | |
| 84 | /** A person's username, the agent's name, or a workspace's slug. */ | |
| 85 | actor: string; | |
| 86 | actorId: string; | |
| 87 | agent: string | null; | |
| 88 | /** The person an agent acted for. */ | |
| 89 | onBehalfOf: string | null; | |
| 90 | runId: string | null; | |
| 91 | runKind: string | null; | |
| 92 | credentialId: string | null; | |
| 93 | /** An operation such as `create_issue`, or `git.push` and `git.fetch`. */ | |
| 94 | action: string; | |
| 95 | surface: AuditSurface; | |
| 96 | workspace: string; | |
| 97 | /** `owner/name`. */ | |
| 98 | repo: string | null; | |
| 99 | number: number | null; | |
| 100 | gitRef: string | null; | |
| 101 | path: string | null; | |
| 102 | outcome: AuditOutcome; | |
| 103 | /** The rule that allowed or refused it, such as `run:implement/tools` or `scope:repository`. */ | |
| 104 | rule: string; | |
| 105 | /** `ok`, or the failure's code. */ | |
| 106 | result: string | null; | |
| 107 | message: string | null; | |
| 108 | requestId: string; | |
| 109 | }; | |
| 110 | ||
| 111 | /** | |
| 112 | * Which of a workspace's entries the viewer may see: an owner everything; | |
| 113 | * a member what was done to its projects, and what they did or had done | |
| 114 | * on their behalf. | |
| 115 | */ | |
| 116 | export type AuditVisibility = { kind: "all" } | { kind: "projects"; username: string }; | |
| 117 | ||
| 118 | export type AuditQuery = { | |
| 119 | workspace: string; | |
| 120 | visibility: AuditVisibility; | |
| 121 | actor?: string | null; | |
| 122 | agent?: string | null; | |
| 123 | action?: string | null; | |
| 124 | repo?: string | null; | |
| 125 | number?: number | null; | |
| 126 | outcome?: AuditOutcome | null; | |
| 127 | actorKind?: ActorKind | null; | |
| 128 | runIds?: string[]; | |
| 129 | /** RFC 3339, inclusive. */ | |
| 130 | since?: string | null; | |
| 131 | /** RFC 3339, exclusive. */ | |
| 132 | until?: string | null; | |
| 133 | before?: string | null; | |
| 134 | /** At most 500. */ | |
| 135 | limit?: number | null; | |
| 136 | }; | |
| 137 | ||
| 138 | export type AuditPage = { entries: AuditEntry[]; next: string | null }; | |
| 139 | ||
| 140 | export interface AuditApi { | |
| 141 | /** Newest first. The caller has checked who may see the workspace's log. */ | |
| 142 | list(query: AuditQuery): Promise<AuditPage>; | |
| 143 | } | |
| 144 | ||
| 145 | /** The audit log, which the events service keeps. */ | |
| 146 | export function auditClient(events: ServiceBinding): AuditApi { | |
| 147 | return { | |
| 148 | list: async (query) => { | |
| 149 | const response = await events.fetch("https://service/rpc/audit_list", { | |
| 150 | method: "POST", | |
| 151 | headers: { "content-type": "application/json" }, | |
| 152 | body: JSON.stringify(query), | |
| 153 | }); | |
| 154 | if (!response.ok) throw new Error(`audit_list failed with status ${response.status}`); | |
| 155 | return (await response.json()) as AuditPage; | |
| 156 | }, | |
| 157 | }; | |
| 158 | } | |
| 159 | ||
| 160 | /** How an actor is shown: "g1t on behalf of syntaqx". */ | |
| 161 | export function describeActor(entry: Pick<AuditEntry, "actor" | "agent" | "onBehalfOf">): string { | |
| 162 | return entry.onBehalfOf ? `${entry.agent ?? entry.actor} on behalf of ${entry.onBehalfOf}` : entry.actor; | |
| 163 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.