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, integrations, security and identity no longer serve g1t's own agents. Billing stops metering agent runs, model tokens and AI Gateway requests, drops its model catalogue and price-keeper model prices, and stops selling AI credit, which only ever paid for those; credit bought earlier still pays toward what is owed. A usage report, statement or invoice that holds an old agent or model meter shows it as "Other usage" rather than failing, and the get_ai_credit and buy_ai_credit API operations and MCP billing actions go. Integrations no longer connects model providers or routes models, and no longer assigns imported issues or alerts to g1t; a model provider already connected can still be disconnected. Security drops "Fix with g1t". Identity no longer creates agent tokens or puts agents on teams, a team's lead is a person, and run credentials are issued only for checks, the merge queue, merge checks, deployments and bumps; tokens and credentials already issued keep working until they expire. The reserved names g1t and g1t-agent stay reserved. No tables are dropped. | 1 | //! Run credentials: a token per sandbox run (checks, the merge queue, a |
| 2 | //! merge check, a deployment or a security update's bump), bound to the | |
| 3 | //! run, its repository and what its kind of work needs, acting as g1t on | |
| 4 | //! behalf of the person who started the work, or of the workspace for | |
| 5 | //! g1t's own. See `g1t_contracts::credentials` for the policy. | |
| 6 | //! | |
| 7 | //! Credentials made for agent runs before g1t stopped running agents keep | |
| 8 | //! resolving until they expire (at most six hours), so nothing in flight | |
| 9 | //! breaks; no new ones are made. | |
| README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise | 10 | |
| 11 | use g1t_contracts::credentials::{ | |
| 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. | 12 | Acting, BindRunCredentialsArgs, CreateRunCredentialArgs, CredentialUse, Principal, RevokeRunCredentialsArgs, |
| Billing, integrations, security and identity no longer serve g1t's own agents. Billing stops metering agent runs, model tokens and AI Gateway requests, drops its model catalogue and price-keeper model prices, and stops selling AI credit, which only ever paid for those; credit bought earlier still pays toward what is owed. A usage report, statement or invoice that holds an old agent or model meter shows it as "Other usage" rather than failing, and the get_ai_credit and buy_ai_credit API operations and MCP billing actions go. Integrations no longer connects model providers or routes models, and no longer assigns imported issues or alerts to g1t; a model provider already connected can still be disconnected. Security drops "Fix with g1t". Identity no longer creates agent tokens or puts agents on teams, a team's lead is a person, and run credentials are issued only for checks, the merge queue, merge checks, deployments and bumps; tokens and credentials already issued keep working until they expire. The reserved names g1t and g1t-agent stay reserved. No tables are dropped. | 13 | RunBinding, RunCredentialKind, intersect, intersect_grants, operations_for, |
| README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise | 14 | }; |
| 15 | use g1t_contracts::identity::{ | |
| 16 | AGENT_ID, AGENT_NAME, AgentScope, CreateAccessTokenArgs, CreatedAccessToken, | |
| 17 | }; | |
| 18 | use g1t_contracts::time::SQL_NOW; | |
| 19 | use g1t_contracts::{PrincipalKind, User, Viewer}; | |
| 20 | use worker::Result; | |
| 21 | use worker::wasm_bindgen::JsValue; | |
| 22 | ||
| 23 | use crate::Identity; | |
| 24 | ||
| 25 | /// No run outlives this, whatever its caller asks for. | |
| 26 | const MAX_RUN_TTL_SECONDS: u64 = 6 * 60 * 60; | |
| 27 | const MIN_RUN_TTL_SECONDS: u64 = 60; | |
| 28 | /// More hashes than one sandbox ever holds. | |
| 29 | const MAX_HASHES: usize = 8; | |
| 30 | ||
| 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. | 31 | /// Whether a credential is still made for this kind of run and use: a |
| 32 | /// runner's, for sandbox work that is not one of the retired hosted | |
| 33 | /// agents' (`RunCredentialKind::retired`). | |
| 34 | fn issued_for(kind: RunCredentialKind, usage: CredentialUse) -> bool { | |
| 35 | !kind.retired() && usage == CredentialUse::Runner | |
| Billing, integrations, security and identity no longer serve g1t's own agents. Billing stops metering agent runs, model tokens and AI Gateway requests, drops its model catalogue and price-keeper model prices, and stops selling AI credit, which only ever paid for those; credit bought earlier still pays toward what is owed. A usage report, statement or invoice that holds an old agent or model meter shows it as "Other usage" rather than failing, and the get_ai_credit and buy_ai_credit API operations and MCP billing actions go. Integrations no longer connects model providers or routes models, and no longer assigns imported issues or alerts to g1t; a model provider already connected can still be disconnected. Security drops "Fix with g1t". Identity no longer creates agent tokens or puts agents on teams, a team's lead is a person, and run credentials are issued only for checks, the merge queue, merge checks, deployments and bumps; tokens and credentials already issued keep working until they expire. The reserved names g1t and g1t-agent stay reserved. No tables are dropped. | 36 | } |
| 37 | ||
| README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise | 38 | /// A SHA-256 in lowercase hex, as tokens are stored. |
| 39 | fn is_hash(value: &str) -> bool { | |
| 40 | value.len() == 64 | |
| 41 | && value | |
| 42 | .bytes() | |
| 43 | .all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b)) | |
| 44 | } | |
| 45 | ||
| 46 | impl Identity { | |
| 47 | /// The composite identity behind an agent's token: the agent, acting | |
| 48 | /// for the person (or workspace) the token was made for, in the run's | |
| 49 | /// workspace only, and only while that person still belongs to it. | |
| 50 | pub(crate) async fn agent_principal( | |
| 51 | &self, | |
| 52 | credential_id: &str, | |
| 53 | user_id: Option<&str>, | |
| 54 | workspace_id: Option<&str>, | |
| 55 | scope: AgentScope, | |
| 56 | ) -> Result<Viewer> { | |
| 57 | let person = match (user_id, workspace_id) { | |
| 58 | (Some(id), _) => { | |
| 59 | self.find_user( | |
| 60 | "SELECT id, username, email_verified_at IS NOT NULL AS verified | |
| Merge account deletion: soft delete for 30 days, staff restore and purge, ghost for what remains (identity 0037) | 61 | FROM users WHERE id = ? AND deleted_at IS NULL", |
| README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise | 62 | id, |
| 63 | ) | |
| 64 | .await? | |
| 65 | } | |
| 66 | (None, Some(id)) => self.workspace_principal(id).await?, | |
| 67 | (None, None) => None, | |
| 68 | }; | |
| 69 | // The person is gone: so is everything that acted for them. | |
| 70 | let Some(person) = person else { | |
| 71 | return Ok(None); | |
| 72 | }; | |
| 73 | let agent = scope | |
| 74 | .run | |
| 75 | .as_ref() | |
| 76 | .map(|run| run.agent.clone()) | |
| 77 | .unwrap_or_else(|| AGENT_NAME.to_owned()); | |
| 78 | // g1t's own run: the workspace's credential, acting as g1t, so the | |
| 79 | // pull request and working copy g1t opened are its own (run.system). | |
| 80 | let on_behalf_of = if scope.run.as_ref().is_some_and(|run| run.system) { | |
| 81 | Principal { | |
| 82 | id: g1t_contracts::system::ID.to_owned(), | |
| 83 | username: g1t_contracts::system::USERNAME.to_owned(), | |
| 84 | } | |
| 85 | } else { | |
| 86 | Principal { | |
| 87 | id: person.id, | |
| 88 | username: person.username, | |
| 89 | } | |
| 90 | }; | |
| 91 | Ok(Some(User { | |
| 92 | id: AGENT_ID.to_owned(), | |
| 93 | username: AGENT_NAME.to_owned(), | |
| 94 | kind: PrincipalKind::Agent, | |
| 95 | verified: person.verified, | |
| 96 | workspaces: intersect(&person.workspaces, &scope.repo.namespace), | |
| 97 | // The person's roles on the workspace's repositories, so an | |
| 98 | // outside collaborator's agent works where they may, and never | |
| 99 | // beyond Write (credentials::AGENT_CEILING). | |
| 100 | grants: intersect_grants(&person.grants, &scope.repo.namespace), | |
| 101 | acting: Some(Box::new(Acting { | |
| 102 | credential_id: credential_id.to_owned(), | |
| 103 | agent, | |
| 104 | on_behalf_of, | |
| 105 | scope, | |
| 106 | })), | |
| 107 | ..User::default() | |
| 108 | })) | |
| 109 | } | |
| 110 | ||
| 111 | pub async fn create_run_credential( | |
| 112 | &self, | |
| 113 | a: CreateRunCredentialArgs, | |
| 114 | ) -> Result<CreatedAccessToken> { | |
| 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. | 115 | if !issued_for(a.kind, a.usage) { |
| Billing, integrations, security and identity no longer serve g1t's own agents. Billing stops metering agent runs, model tokens and AI Gateway requests, drops its model catalogue and price-keeper model prices, and stops selling AI credit, which only ever paid for those; credit bought earlier still pays toward what is owed. A usage report, statement or invoice that holds an old agent or model meter shows it as "Other usage" rather than failing, and the get_ai_credit and buy_ai_credit API operations and MCP billing actions go. Integrations no longer connects model providers or routes models, and no longer assigns imported issues or alerts to g1t; a model provider already connected can still be disconnected. Security drops "Fix with g1t". Identity no longer creates agent tokens or puts agents on teams, a team's lead is a person, and run credentials are issued only for checks, the merge queue, merge checks, deployments and bumps; tokens and credentials already issued keep working until they expire. The reserved names g1t and g1t-agent stay reserved. No tables are dropped. | 116 | return Err(worker::Error::RustError(format!( |
| 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. | 117 | "no run credential is made for {} runs' {}", |
| 118 | a.kind.as_str(), | |
| 119 | a.usage.as_str() | |
| Billing, integrations, security and identity no longer serve g1t's own agents. Billing stops metering agent runs, model tokens and AI Gateway requests, drops its model catalogue and price-keeper model prices, and stops selling AI credit, which only ever paid for those; credit bought earlier still pays toward what is owed. A usage report, statement or invoice that holds an old agent or model meter shows it as "Other usage" rather than failing, and the get_ai_credit and buy_ai_credit API operations and MCP billing actions go. Integrations no longer connects model providers or routes models, and no longer assigns imported issues or alerts to g1t; a model provider already connected can still be disconnected. Security drops "Fix with g1t". Identity no longer creates agent tokens or puts agents on teams, a team's lead is a person, and run credentials are issued only for checks, the merge queue, merge checks, deployments and bumps; tokens and credentials already issued keep working until they expire. The reserved names g1t and g1t-agent stay reserved. No tables are dropped. | 120 | ))); |
| 121 | } | |
| 122 | let agent = AGENT_NAME.to_owned(); | |
| README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise | 123 | let scope = AgentScope { |
| 124 | repo: a.repo.clone(), | |
| 125 | operations: operations_for(a.kind, a.usage) | |
| 126 | .into_iter() | |
| 127 | .map(str::to_owned) | |
| 128 | .collect(), | |
| 129 | run: Some(RunBinding { | |
| 130 | kind: a.kind, | |
| 131 | usage: a.usage, | |
| 132 | run_id: None, | |
| 133 | number: a.number, | |
| 134 | agent: agent.clone(), | |
| 135 | system: a.on_behalf_of.kind == PrincipalKind::System, | |
| 136 | read: a.read, | |
| 137 | push: a.push, | |
| 138 | }), | |
| 139 | }; | |
| Billing, integrations, security and identity no longer serve g1t's own agents. Billing stops metering agent runs, model tokens and AI Gateway requests, drops its model catalogue and price-keeper model prices, and stops selling AI credit, which only ever paid for those; credit bought earlier still pays toward what is owed. A usage report, statement or invoice that holds an old agent or model meter shows it as "Other usage" rather than failing, and the get_ai_credit and buy_ai_credit API operations and MCP billing actions go. Integrations no longer connects model providers or routes models, and no longer assigns imported issues or alerts to g1t; a model provider already connected can still be disconnected. Security drops "Fix with g1t". Identity no longer creates agent tokens or puts agents on teams, a team's lead is a person, and run credentials are issued only for checks, the merge queue, merge checks, deployments and bumps; tokens and credentials already issued keep working until they expire. The reserved names g1t and g1t-agent stay reserved. No tables are dropped. | 140 | // g1t's own work (a security update's bump) has no person behind it: its credential acts for the workspace, |
| README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise | 141 | // as a workspace's own token would. |
| 142 | let on_behalf_of = if a.on_behalf_of.kind == PrincipalKind::System { | |
| 143 | match self.workspace_id_of(&a.repo.namespace).await? { | |
| 144 | Some(id) => User { | |
| 145 | id, | |
| 146 | username: a.repo.namespace.to_lowercase(), | |
| 147 | kind: PrincipalKind::Workspace, | |
| 148 | ..User::default() | |
| 149 | }, | |
| 150 | None => return Err(worker::Error::RustError(format!("no workspace {}", a.repo.namespace))), | |
| 151 | } | |
| 152 | } else { | |
| 153 | a.on_behalf_of | |
| 154 | }; | |
| 155 | let created = self | |
| 156 | .create_access_token(CreateAccessTokenArgs { | |
| 157 | user: on_behalf_of, | |
| 158 | name: format!( | |
| 159 | "{agent}: {} run in {}/{}{}", | |
| 160 | a.kind.as_str(), | |
| 161 | a.repo.namespace, | |
| 162 | a.repo.name, | |
| 163 | a.number.map(|n| format!("#{n}")).unwrap_or_default() | |
| 164 | ), | |
| 165 | ttl_seconds: Some( | |
| 166 | a.ttl_seconds | |
| 167 | .clamp(MIN_RUN_TTL_SECONDS, MAX_RUN_TTL_SECONDS), | |
| 168 | ), | |
| 169 | // A run credential's scope is its run's; see agent_scope. | |
| 170 | scopes: None, | |
| 171 | listed: false, | |
| g1t coordinates the work and the person's own sessions do it: Ask g1t reads g1t with the same tools any agent gets, answers in one conversation shared with the @g1t direct message in Chat, and hands work to sessions in the person's own harness, whose subagents g1t records, ties to issues and pull requests, and lets see each other. | 172 | repo: None, |
| README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise | 173 | }) |
| 174 | .await?; | |
| 175 | self.db | |
| 176 | .prepare("UPDATE access_tokens SET agent_scope = ? WHERE id = ?") | |
| 177 | .bind(&[ | |
| 178 | serde_json::to_string(&scope)?.into(), | |
| 179 | created.info.id.as_str().into(), | |
| 180 | ])? | |
| 181 | .run() | |
| 182 | .await?; | |
| 183 | Ok(created) | |
| 184 | } | |
| 185 | ||
| Billing, integrations, security and identity no longer serve g1t's own agents. Billing stops metering agent runs, model tokens and AI Gateway requests, drops its model catalogue and price-keeper model prices, and stops selling AI credit, which only ever paid for those; credit bought earlier still pays toward what is owed. A usage report, statement or invoice that holds an old agent or model meter shows it as "Other usage" rather than failing, and the get_ai_credit and buy_ai_credit API operations and MCP billing actions go. Integrations no longer connects model providers or routes models, and no longer assigns imported issues or alerts to g1t; a model provider already connected can still be disconnected. Security drops "Fix with g1t". Identity no longer creates agent tokens or puts agents on teams, a team's lead is a person, and run credentials are issued only for checks, the merge queue, merge checks, deployments and bumps; tokens and credentials already issued keep working until they expire. The reserved names g1t and g1t-agent stay reserved. No tables are dropped. | 186 | /// Ties a sandbox's credentials to the run it recorded. A token |
| README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise | 187 | /// already bound keeps its run. |
| 188 | pub async fn bind_run_credentials(&self, a: BindRunCredentialsArgs) -> Result<bool> { | |
| 189 | let hashes: Vec<&String> = a | |
| 190 | .token_hashes | |
| 191 | .iter() | |
| 192 | .filter(|hash| is_hash(hash)) | |
| 193 | .take(MAX_HASHES) | |
| 194 | .collect(); | |
| 195 | if hashes.is_empty() || a.run_id.trim().is_empty() { | |
| 196 | return Ok(false); | |
| 197 | } | |
| 198 | let marks = vec!["?"; hashes.len()].join(", "); | |
| 199 | let mut values: Vec<JsValue> = vec![a.run_id.as_str().into(), a.run_id.as_str().into()]; | |
| 200 | values.extend(hashes.iter().map(|hash| JsValue::from(hash.as_str()))); | |
| 201 | self.db | |
| 202 | .prepare(format!( | |
| 203 | "UPDATE access_tokens | |
| 204 | SET run_id = ?, agent_scope = json_set(agent_scope, '$.run.runId', ?) | |
| 205 | WHERE token_hash IN ({marks}) AND run_id IS NULL | |
| 206 | AND json_extract(agent_scope, '$.run') IS NOT NULL" | |
| 207 | )) | |
| 208 | .bind(&values)? | |
| 209 | .run() | |
| 210 | .await?; | |
| 211 | Ok(true) | |
| 212 | } | |
| 213 | ||
| 214 | /// Ends a sandbox's credentials: they stop working at once. Only run | |
| 215 | /// credentials are touched. | |
| 216 | pub async fn revoke_run_credentials(&self, a: RevokeRunCredentialsArgs) -> Result<bool> { | |
| 217 | let hashes: Vec<&String> = a | |
| 218 | .token_hashes | |
| 219 | .iter() | |
| 220 | .filter(|hash| is_hash(hash)) | |
| 221 | .take(MAX_HASHES) | |
| 222 | .collect(); | |
| 223 | let mut conditions = Vec::new(); | |
| 224 | let mut values: Vec<JsValue> = Vec::new(); | |
| 225 | if !hashes.is_empty() { | |
| 226 | conditions.push(format!( | |
| 227 | "token_hash IN ({})", | |
| 228 | vec!["?"; hashes.len()].join(", ") | |
| 229 | )); | |
| 230 | values.extend(hashes.iter().map(|hash| JsValue::from(hash.as_str()))); | |
| 231 | } | |
| 232 | if let Some(run_id) = a.run_id.as_deref().filter(|id| !id.trim().is_empty()) { | |
| 233 | conditions.push("run_id = ?".to_owned()); | |
| 234 | values.push(run_id.into()); | |
| 235 | } | |
| 236 | if conditions.is_empty() { | |
| 237 | return Ok(false); | |
| 238 | } | |
| 239 | self.db | |
| 240 | .prepare(format!( | |
| 241 | "UPDATE access_tokens SET expires_at = {SQL_NOW} | |
| 242 | WHERE ({}) AND json_extract(agent_scope, '$.run') IS NOT NULL | |
| 243 | AND (expires_at IS NULL OR expires_at > {SQL_NOW})", | |
| 244 | conditions.join(" OR ") | |
| 245 | )) | |
| 246 | .bind(&values)? | |
| 247 | .run() | |
| 248 | .await?; | |
| 249 | Ok(true) | |
| 250 | } | |
| 251 | } | |
| 252 | ||
| 253 | #[cfg(test)] | |
| 254 | mod tests { | |
| 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. | 255 | use super::{CredentialUse, RunCredentialKind, is_hash, issued_for}; |
| Billing, integrations, security and identity no longer serve g1t's own agents. Billing stops metering agent runs, model tokens and AI Gateway requests, drops its model catalogue and price-keeper model prices, and stops selling AI credit, which only ever paid for those; credit bought earlier still pays toward what is owed. A usage report, statement or invoice that holds an old agent or model meter shows it as "Other usage" rather than failing, and the get_ai_credit and buy_ai_credit API operations and MCP billing actions go. Integrations no longer connects model providers or routes models, and no longer assigns imported issues or alerts to g1t; a model provider already connected can still be disconnected. Security drops "Fix with g1t". Identity no longer creates agent tokens or puts agents on teams, a team's lead is a person, and run credentials are issued only for checks, the merge queue, merge checks, deployments and bumps; tokens and credentials already issued keep working until they expire. The reserved names g1t and g1t-agent stay reserved. No tables are dropped. | 256 | |
| 257 | #[test] | |
| 258 | fn credentials_are_made_only_for_sandbox_work_that_is_not_an_agents() { | |
| 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. | 259 | let issued: Vec<&str> = RunCredentialKind::ALL |
| 260 | .iter() | |
| 261 | .filter(|kind| issued_for(**kind, CredentialUse::Runner)) | |
| 262 | .map(|kind| kind.as_str()) | |
| 263 | .collect(); | |
| Billing, integrations, security and identity no longer serve g1t's own agents. Billing stops metering agent runs, model tokens and AI Gateway requests, drops its model catalogue and price-keeper model prices, and stops selling AI credit, which only ever paid for those; credit bought earlier still pays toward what is owed. A usage report, statement or invoice that holds an old agent or model meter shows it as "Other usage" rather than failing, and the get_ai_credit and buy_ai_credit API operations and MCP billing actions go. Integrations no longer connects model providers or routes models, and no longer assigns imported issues or alerts to g1t; a model provider already connected can still be disconnected. Security drops "Fix with g1t". Identity no longer creates agent tokens or puts agents on teams, a team's lead is a person, and run credentials are issued only for checks, the merge queue, merge checks, deployments and bumps; tokens and credentials already issued keep working until they expire. The reserved names g1t and g1t-agent stay reserved. No tables are dropped. | 264 | assert_eq!(issued, ["checks", "queue", "mergecheck", "deploy", "bump"]); |
| 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. | 265 | // A retired hosted agent's tools token is never made. |
| 266 | assert!(RunCredentialKind::ALL.iter().all(|kind| !issued_for(*kind, CredentialUse::Tools))); | |
| Billing, integrations, security and identity no longer serve g1t's own agents. Billing stops metering agent runs, model tokens and AI Gateway requests, drops its model catalogue and price-keeper model prices, and stops selling AI credit, which only ever paid for those; credit bought earlier still pays toward what is owed. A usage report, statement or invoice that holds an old agent or model meter shows it as "Other usage" rather than failing, and the get_ai_credit and buy_ai_credit API operations and MCP billing actions go. Integrations no longer connects model providers or routes models, and no longer assigns imported issues or alerts to g1t; a model provider already connected can still be disconnected. Security drops "Fix with g1t". Identity no longer creates agent tokens or puts agents on teams, a team's lead is a person, and run credentials are issued only for checks, the merge queue, merge checks, deployments and bumps; tokens and credentials already issued keep working until they expire. The reserved names g1t and g1t-agent stay reserved. No tables are dropped. | 267 | } |
| README: run the core forge locally with Docker Compose; forks described as copies, not copy-on-write, until Cloudflare says otherwise | 268 | |
| 269 | #[test] | |
| 270 | fn only_hashes_are_taken() { | |
| 271 | assert!(is_hash(&"a1".repeat(32))); | |
| 272 | assert!(!is_hash(&"A1".repeat(32))); | |
| 273 | assert!(!is_hash("g1t_0123")); | |
| 274 | assert!(!is_hash(&"0".repeat(63))); | |
| 275 | } | |
| 276 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.