Skip to content

Commit

Merge branch 'worktree-agent-ac950b9c891220ad3'

syntaqxcommitted Parents0a5d1f6e2f0a5eBrowse files
42 files+104−6840/42 viewed
+8−10
1414 | --- | --- |
1515 | Something a person does on g1t.sh | The guide for it in `apps/docs/src/content/docs/guides/` |
1616 | An API route, a field, or an MCP tool | `apps/api/src/operations.rs` descriptions (they feed the OpenAPI document and the API reference), the example and notes for it in `apps/api/src/reference.json`, `reference/mcp.md`, and any guide that shows the call. Then refresh the docs' copy of the OpenAPI document with `G1T_WRITE_OPENAPI=1 cargo test -p g1t-api openapi`; `cargo test` fails until you do. |
17−| How agents behave | `guides/working-with-g1t.md`, and `apps/web/public/llms.txt` |
17+| What coding agents are told about g1t | `guides/bring-your-own-agent.mdx`, `guides/claude-code-plugin.md`, the plugin's skills in `plugins/claude-code/`, and `apps/web/public/llms.txt` |
1818 | Settings, limits or prices | The page that names them, and the table it is in |
1919 | A new feature | A section in the guide that owns it, linked from the docs home if it is a new task |
2020 | How g1t is put together: a service, a binding, a store, an event route, the path of a request | Its page in the docs' Architecture section (`apps/docs/src/content/docs/architecture/`), and its diagram in `apps/docs/src/diagrams/architecture.ts`. [How g1t ships](https://docs.g1t.sh/architecture/shipping/#keeping-this-section-true) says which page covers what. |
6161 - Hover hints use the `Tooltip` component, never `title=`. Native
6262 `<select>`, file inputs, checkboxes and radios use their `ui`
6363 components.
64−- **Every avatar is `Avatar`** (`ui/avatar.tsx`), and every agent's is
65− `Avatar` with `agent` set, or `AgentAvatar` (`components/agent-avatar.tsx`),
66− which wraps it. That draws the agent's bot face (`AgentFace`,
67− `components/agent-face.tsx`) from its `look` or its `avatar_seed`, puts
68− the agent marker in its corner and names it "…, agent" to a screen
69− reader. Never hand-draw an agent's face, a sparkle, a letter on a tile or
70− the marker at a call site: a place that shows an agent differently is a
71− bug.
64+- **Every avatar is `Avatar`** (`ui/avatar.tsx`), and @g1t's is `Avatar`
65+ with `agent` set, or `AgentAvatar` (`components/agent-avatar.tsx`), which
66+ wraps it: g1t's pixel 1 with the agent marker in its corner, named
67+ "…, agent" to a screen reader. Never hand-draw a sparkle, a letter on a
68+ tile or the marker at a call site: a place that shows @g1t differently is
69+ a bug.
7270 - Look at every page you change, in light and dark, on a desktop and a
7371 phone, before calling it done.
7472
337335 Worker's secret `GIT_FALLBACK_NAMESPACES`) and open an incident. To
338336 switch back, `reconcile` anything the fallback took, then delete the
339337 secret. The script's header has every step.
340−- **Costs and margin**, model prices, credits and the platform pause are
338+- **Costs and margin**, credits and the platform pause are
341339 in sudo, under **Costs & margin**. The code is in `services/billing/src`
342340 (`costs.rs`, `margin.rs`, `pricing.rs`, `budget.rs`, `platform.rs`).
+1−1
234234 pub kind: String,
235235 pub name: String,
236236 pub repo: String,
237− /// The repository's Actions (or Agents) page, where its run is.
237+ /// The repository's Actions page, where its run is.
238238 pub link: Option<String>,
239239 }
240240
+2−2
7070 ## Coming soon
7171
7272 Every category has integrations on the way, among them Slack, Microsoft
73−Teams, Notion, Confluence, PagerDuty, Cloudflare, AWS, Figma, Okta and
74−Google Drive for workspaces. For you: **Google Calendar** and **Outlook
73+Teams, Google Docs, PagerDuty, AWS, Miro, Okta and Google Drive for
74+workspaces. For you: **Google Calendar** and **Outlook
7575 Calendar** will set your [status](/guides/chat/#presence-and-status) to *In a meeting* while
7676 you are in one; **Zoom**
7777 and **Google Meet** will set it to *On a call*. A status you set by hand
+2−3
186186
187187 /**
188188 * A person's or an agent's picture: the uploaded one when there is one,
189− * else their initial. Agents carry a marker, as everywhere on g1t. The
190− * site draws an agent's own bot face (components/agent-face.tsx); bringing
191− * that here is to come.
189+ * else their initial. Agents (@g1t, and authors of old messages from
190+ * retired agents) carry a marker, as everywhere on g1t.
192191 */
193192 export function Avatar({ name, uri, agent, size = 32 }: { name: string; uri?: string | null; agent?: boolean; size?: number }) {
194193 const colors = useColors();
+2−14
1919 DOCS_URL: string;
2020 /** Where deployed apps live: `https://g1t.page`. */
2121 PAGES_URL: string;
22− MODELS_URL: string;
2322 /** A public repository that always exists, `owner/name`, to fetch refs of. */
2423 PROBE_REPO: string;
2524 /** The site as browsers reach it, when the checks reach it another way. */
9190 const mcp = trim(vars.MCP_URL);
9291 const docs = trim(vars.DOCS_URL);
9392 const pages = trim(vars.PAGES_URL);
94− const models = trim(vars.MODELS_URL);
9593 const repo = (vars.PROBE_REPO ?? "").trim();
9694 // What the page shows as the site's address: where people reach it.
9795 const shown = host(trim(vars.PUBLIC_SITE_URL) || site);
186184 check: { kind: "http", steps: [{ url: `${pages}/` }] },
187185 }
188186 : null,
189− models
190− ? {
191− key: "agents",
192− name: "Agents' model proxy",
193− address: host(models),
194− checks: "The proxy agents reach their model through. The model providers behind it are not checked.",
195− core: false,
196− check: { kind: "http", steps: [{ url: `${models}/` }] },
197− }
198− : null,
199− models
187+ pages
200188 ? {
201189 key: "sandboxes",
202190 name: "Sandboxes",
203− address: "Agents, checks, workflows and builds",
191+ address: "Checks, workflows and builds",
204192 checks: "Starting a sandbox costs money and takes seconds, so it is not checked from here yet.",
205193 core: false,
206194 check: { kind: "none" },
+1−2
2525 MCP_URL: "https://mcp.g1t.sh",
2626 DOCS_URL: "https://docs.g1t.sh",
2727 PAGES_URL: "https://g1t.page",
28− MODELS_URL: "https://models.g1t.sh",
2928 PROBE_REPO: "flagon-io/g1t",
3029 };
3130
188187 const all = components(VARS, true);
189188 assert.deepEqual(
190189 all.map((c) => c.key),
191− ["site", "api", "git", "speed", "mcp", "docs", "deployments", "agents", "sandboxes", "billing"],
190+ ["site", "api", "git", "speed", "mcp", "docs", "deployments", "sandboxes", "billing"],
192191 );
193192 const speed = all.find((c) => c.key === "speed")!;
194193 assert.deepEqual(speed.check, { kind: "http", steps: [{ url: "https://g1t.sh/flagon-io/g1t", browser: true }, { url: "https://g1t.sh/explore", browser: true }] });
+3−3
2828 SITE_URL: "https://g1t.sh",
2929 API_URL: "https://api.g1t.sh",
3030 DOCS_URL: "https://docs.g1t.sh",
31− MODELS_URL: "https://models.g1t.sh",
31+ PAGES_URL: "https://g1t.page",
3232 PROBE_REPO: "flagon-io/g1t",
3333 },
3434 false,
7575 });
7676
7777 test("the page: banner, every part with its bar, and no incidents", () => {
78− const html = renderPage(page({ site: "up", api: "up", git: "up", docs: "up", agents: "up" }), OPTIONS);
78+ const html = renderPage(page({ site: "up", api: "up", git: "up", docs: "up", deployments: "up" }), OPTIONS);
7979 assert.match(html, /<h1>All systems normal<\/h1>/);
8080 for (const name of ["Website and sign-in", "API", "Git and repositories", "Documentation", "Sandboxes"]) assert.ok(html.includes(name), name);
8181 assert.equal((html.match(/<ol class="bar"/g) ?? []).length, 5, "Sandboxes has no bar");
8989
9090 test("an outage and an open incident are said at the top, escaped", () => {
9191 const html = renderPage(
92− page({ site: "down", api: "up", git: "up", docs: "up", agents: "up" }, [
92+ page({ site: "down", api: "up", git: "up", docs: "up" }, [
9393 INCIDENT,
9494 ]),
9595 OPTIONS,
+1−1
353353 const past = incidents.filter((i) => i.resolved_at != null && Date.parse(i.resolved_at) >= recentSince);
354354 const inProgress = maintenance.filter((m) => maintenanceState(m, options.now) === "in_progress");
355355 const upcoming = maintenance.filter((m) => maintenanceState(m, options.now) === "scheduled");
356− const description = `${overall.title}. Live status of g1t: the website, API, git, MCP, docs, deployments, agents and billing, with 90 days of history.`;
356+ const description = `${overall.title}. Live status of g1t: the website, API, git, MCP, docs, deployments, sandboxes and billing, with 90 days of history.`;
357357 const checkedLine = checked_at
358358 ? stale
359359 ? `The checks have not run since ${time(checked_at, true)}, so what follows may be out of date.`
+0−1
5353 "MCP_URL": "https://mcp.g1t.sh",
5454 "DOCS_URL": "https://docs.g1t.sh",
5555 "PAGES_URL": "https://g1t.page",
56− "MODELS_URL": "https://models.g1t.sh",
5756 // A public repository that always exists, whose branches are listed
5857 // as a clone would.
5958 "PROBE_REPO": "flagon-io/g1t",
+2−3
179179 register or rename a workspace to while it exists. **Remove**
180180 (`admin_remove_alias`) needs a reason. Both go in sudo's audit log
181181 (`alias_added`, `alias_removed`), naming the staff member. `@g1t` in
182− text still means g1t's agent: it links to how the agent works, never to
183− `/g1t`.
182+ text still means g1t itself (Ask g1t), never the alias's `/g1t`.
184183 - **Enterprises**: customers that pay for several workspaces with one
185184 bill, one limit and one set of terms. Each has its workspaces (add or
186185 remove them), combined usage, terms, credits, ledger and audit log, and
295294
296295 ```sh
297296 npm run typecheck -w @g1t/sudo
298−npm test -w @g1t/sudo # JWT verification, forms, money, the workspace join, paging, nav, charts, signals, models
297+npm test -w @g1t/sudo # JWT verification, forms, money, the workspace join, paging, nav, charts, signals
299298 npm run build -w @g1t/sudo
300299 ```
301300
+2−2
5959 assert.equal(accountPath("ws_acme"), "/workspaces/acme");
6060 assert.equal(accountPath("ent_bigco"), "/enterprises/ent_bigco");
6161 assert.equal(accountPath("stripe"), null);
62− // Shared invite links are filed under `ws_invites`: a reserved name, never a workspace.
63− assert.equal(accountPath("ws_invites"), "/invites?tab=shared");
62+ // Old shared invite links were filed under `ws_invites`: a reserved name, never a workspace.
63+ assert.equal(accountPath("ws_invites"), "/invites");
6464 assert.equal(accountName("ws_invites"), "Shared invite links");
6565 assert.equal(accountPath("acme"), null);
6666 assert.equal(accountPath("ws_-bad"), null);
+2−2
6767 */
6868 export function accountPath(account: string | null | undefined): string | null {
6969 const id = (account ?? "").trim().toLowerCase();
70− // Shared invite links (identity's shared_invites.rs): `invites` is a reserved name, never a workspace's.
71− if (id === SHARED_INVITES_ACCOUNT) return "/invites?tab=shared";
70+ // Shared invite links, retired with invite-only registration: `invites` is a reserved name, never a workspace's.
71+ if (id === SHARED_INVITES_ACCOUNT) return "/invites";
7272 const status = /^(incident|maintenance):([a-z0-9-]{1,64})$/.exec(id);
7373 if (status) return status[1] === "incident" ? `/incidents/${status[2]}` : `/incidents/maintenance/${status[2]}`;
7474 if (ENTERPRISE.test(id)) return `/enterprises/${encodeURIComponent(id)}`;
+2−2
190190 label: "Usage explorer",
191191 to: "/usage",
192192 icon: "usage",
193− about: "What customers used, sliced by meter, model, repository and day.",
193+ about: "What customers used, sliced by meter, repository and day.",
194194 soon: {
195195 summary: [
196196 "What customers used, across every workspace, sliced any way: by meter, repository and day. It answers the questions that come up in a sales call or a cost review: who uses the most sandbox time, and what a customer spends per pull request.",
198198 ],
199199 plans: [
200200 "Charged, cost and margin over any range, for everyone or one customer",
201− "Group by meter, model, task, repository or workspace; compare two periods",
201+ "Group by meter, repository or workspace; compare two periods",
202202 "The heaviest workspaces this month, and how fast each is growing",
203203 "Export to CSV",
204204 ],
+1−1
8484 /**
8585 * What g1t absorbs of real cost on a goodwill credit: whatever is past its
8686 * margin on the overage. The margin is money g1t never had; the rest it
87− * paid Cloudflare and model providers for.
87+ * paid Cloudflare for.
8888 */
8989 export function absorbedMicros(amountMicros: number, marginMicros: number): number {
9090 return Math.max(0, amountMicros - Math.max(0, marginMicros));
+1−1
143143 </h2>
144144 <p className="mt-2 text-sm text-fg-soft">
145145 A {usd(review.amountMicros)} credit to <span className="font-mono">{review.workspace}</span> is past the {usd(cap)} a goodwill credit
146− absorbs in one click. That is money g1t already paid Cloudflare and model providers. Say why, for whoever looks next; it goes in the
146+ absorbs in one click. That is money g1t already paid Cloudflare. Say why, for whoever looks next; it goes in the
147147 audit log with your email.
148148 </p>
149149 <form method="post" action={`${pathname}#review`} className="mt-4 space-y-3">
+1−3
9595 {(prices || pricesError) && (
9696 <Section
9797 title="The price book today"
98− description={
99− prices ? `Read-only. Models are charged at cost plus ${prices.modelMarginPercent}%.` : undefined
100− }
98+ description={prices ? "Read-only." : undefined}
10199 className="mt-8"
102100 >
103101 {pricesError ? (
+4−4
1−This policy explains what information g1t collects, why, where it goes, how long we keep it, and what you can do about it. It covers g1t.sh, api.g1t.sh, mcp.g1t.sh, docs.g1t.sh, g1t.page and the agents and sandboxes g1t runs. g1t is run by Flagon, Inc. ("Flagon", "we"), which is responsible for your information.
1+This policy explains what information g1t collects, why, where it goes, how long we keep it, and what you can do about it. It covers g1t.sh, api.g1t.sh, mcp.g1t.sh, docs.g1t.sh, g1t.page and the sandboxes g1t runs. g1t is run by Flagon, Inc. ("Flagon", "we"), which is responsible for your information.
22
33 The short version: we collect what we need to run a git platform for you and your agents, we don't sell it, we don't advertise, we don't use your private content to train AI models, and the only analytics on g1t.sh counts how pages are used without reading what is in your workspaces.
44
2525
2626 ### What agents do
2727
28−When an agent runs, we keep a **session**: what it was asked, the steps it took, the tools it called, what it wrote, which model it used, what it cost, and how the run ended. Run credentials and model keys are removed from what is recorded. A session is as visible as its project: **on a public project, anyone can read it**. Workflow and check logs are kept the same way.
28+When a coding agent you connect records its work on g1t, we keep a **session**: what it was asked, the steps and tools it reports, what it wrote, which model it says it used, and how it ended. Credentials are removed from what is recorded. When you ask g1t a question, the conversation is kept with your direct message with @g1t. A session is as visible as its project: **on a public project, anyone can read it**. Workflow and check logs are kept the same way.
2929
3030 To catch cryptocurrency mining, every sandbox samples its own CPU use, disk and network activity, and the command lines of the processes it runs. These measurements are used only for abuse detection.
3131
8484
8585 We use your information to:
8686
87−- run g1t: sign you in, store and show your content to the people allowed to see it, run the agents, checks and deployments you ask for;
87+- run g1t: sign you in, store and show your content to the people allowed to see it, run the checks, workflows and deployments you ask for;
8888 - keep g1t secure and fair: detect abuse, mining and fraud, enforce limits, and investigate problems;
8989 - bill workspaces and keep the records the law requires;
9090 - send you the email g1t needs to: confirming your address, resetting your password, billing alerts and receipts, answers to requests you made, and important changes to g1t or these policies. **We don't send marketing email**;
100100 We share information only to run g1t, at your direction, or when the law requires it.
101101
102102 - **Service providers ("subprocessors")** that run parts of g1t for us, under contracts that limit them to doing so: Cloudflare (hosting, storage, databases, sandboxes, email and search), Stripe (payments) and HeyCatch (site analytics). The [subprocessors](/policies/subprocessors) page lists them and what each receives.
103−- **Services you connect.** When you connect an issue tracker, error tracking or a webhook, or let your sandboxes reach a host, we send them what that connection needs, because you asked us to.
103+- **Services you connect.** When you connect an AI provider, an issue tracker, error tracking or a webhook, or let your sandboxes reach a host, we send them what that connection needs, because you asked us to.
104104 - **Other people on g1t**, as your settings allow: your workspace's members, and everyone for public projects and your public profile.
105105 - **Vulnerability databases.** To find vulnerable dependencies, we send the names and versions of packages in your lockfiles to OSV.dev, an open database run by Google. Nothing else about you or your repository is sent.
106106 - **The law.** We disclose information when we must by law, such as a valid court order, and when we need to protect the rights, property or safety of our users, the public or Flagon. Where we can, we tell you first.
+2−2
22
33 ## What is charged, and why
44
5−The forge is free. What costs money is compute (agents, sandboxes, checks, workflows, the merge queue, deployments, and storage and git traffic past what is included) at what it costs us plus 20%, and the g1t plan, $20 a month per workspace with $10 of usage included. Because usage is charged at close to what it costs us, most of it can't be given back once it has run: we've already paid for it.
5+The forge is free. What costs money is compute (sandboxes, checks, workflows, the merge queue, deployments, and storage and git traffic past what is included) at what it costs us plus 20%, and the g1t plan, $20 a month per workspace with $10 of usage included. Because usage is charged at close to what it costs us, most of it can't be given back once it has run: we've already paid for it.
66
77 That's why g1t works hard to keep spending from surprising you: a $5 trial that never turns into a charge, a spend limit you set, caps per run and per issue, alerts at 50%, 75%, 90% and 100%, and a pause when spending suddenly spikes until an owner says to keep going.
88
99 ## Spent more than you meant to
1010
11−If a month went well past what you meant to spend (a runaway job, an agent that kept going, a loop you didn't notice), tell us. On the workspace's **Billing** page, an owner chooses **Spent more than you meant to? Tell us.**, says what happened, and sends it.
11+If a month went well past what you meant to spend (a runaway job, a workflow that kept going, a loop you didn't notice), tell us. On the workspace's **Billing** page, an owner chooses **Spent more than you meant to? Tell us.**, says what happened, and sends it.
1212
1313 - **Once every 12 months per workspace**, g1t can credit the overage above your usual month.
1414 - **The 20% we added to that overage always comes back to you.** We don't keep a margin on a mistake.
+2−2
44
55 | Company | What they do for g1t | What they receive | Where |
66 | --- | --- | --- | --- |
7−| **Cloudflare, Inc.** | Hosting and the network g1t runs on (Workers), databases (D1), storage for repositories, avatars and screenshots (Artifacts, KV and R2), queues, the sandboxes agents and checks run in (Containers), sending email (Email Service), search embeddings (Workers AI and Vectorize), and screenshots of deployed apps (Browser Rendering) | Everything stored on g1t, and every request to it | Global |
7+| **Cloudflare, Inc.** | Hosting and the network g1t runs on (Workers), databases (D1), storage for repositories, avatars and screenshots (Artifacts, KV and R2), queues, the sandboxes checks, workflows and builds run in (Containers), sending email (Email Service), search embeddings (Workers AI and Vectorize), and screenshots of deployed apps (Browser Rendering) | Everything stored on g1t, and every request to it | Global |
88 | **Stripe, Inc.** | Payments, cards, invoices and receipts | Workspace owners' billing details, card details (entered on Stripe's page, never on g1t), and what each invoice charges | United States |
99 | **HeyCatch, Inc.** | Product analytics on g1t.sh: page views and clicks. Visitors in the EU, EEA, UK and Switzerland are asked first | The page's address (names replaced outside the public pages), the text of what is clicked on the public pages, browser and device type, IP address, the referring site, an analytics id, and a signed-in person's internal account id. Never repository content, chat, page titles inside the app, what you type, or recordings of your session | United States |
1010
1818
1919 When you connect these, your information goes to them because you asked, under your own agreement with them. They are not g1t's subprocessors:
2020
21−- **Integrations**, such as GitHub, Linear, Jira, Sentry and Datadog.
21+- **Integrations**, such as your workspace's AI provider, GitHub, Linear, Jira, Sentry and Datadog.
2222 - **Webhooks** you add, which send events to the addresses you choose.
2323 - **Hosts your sandboxes reach**, such as package registries and anything your jobs and builds fetch from or send to.
2424 - **Custom domains** you point at your deployments.
+10−12
1−These terms are the agreement between you and Flagon, Inc. ("Flagon", "we", "us") when you use g1t: the website at g1t.sh, its API at api.g1t.sh, its MCP server at mcp.g1t.sh, git over g1t.sh, the documentation, apps deployed to g1t.page, and the agents and sandboxes g1t runs for you (together, "g1t" or "the service").
1+These terms are the agreement between you and Flagon, Inc. ("Flagon", "we", "us") when you use g1t: the website at g1t.sh, its API at api.g1t.sh, its MCP server at mcp.g1t.sh, git over g1t.sh, the documentation, apps deployed to g1t.page, and the sandboxes g1t runs for you (together, "g1t" or "the service").
22
33 We've tried to write them the way we'd want to read them: plainly. If anything here is unclear, write to [hey@flagon.io](mailto:hey@flagon.io) and we'll explain it.
44
1010
1111 - **You need an account to do most things.** Give us a real email address and keep it current: it's how we reach you about your account, security and billing, and how you get back in if you lose your password.
1212 - **You must be at least 13 years old**, and old enough where you live to agree to these terms yourself. If you're under 18, a parent or guardian must agree to them for you.
13−- **An account is for one person.** Automation is welcome: use access tokens, workspace tokens and agents for it. Don't share a password, and don't create accounts to get around a limit, a trial, or a suspension.
13+- **An account is for one person.** Automation is welcome: use access tokens, workspace tokens and coding agents for it. Don't share a password, and don't create accounts to get around a limit, a trial, or a suspension.
1414 - **Keep your credentials safe.** You're responsible for what happens under your account, your tokens and your SSH keys. If you think someone else has them, revoke them in your settings and tell us at [hey@flagon.io](mailto:hey@flagon.io).
1515
1616 ## 2. Workspaces
1717
18−Everything on g1t lives in a workspace: its projects, repositories, issues, pull requests, agents, settings and bill. A workspace's **owners** control it. They decide who is a member, what members and agents may do, and they're responsible for paying for it. If you create a workspace for an organization, the organization is the customer, and its owners act for it.
18+Everything on g1t lives in a workspace: its projects, repositories, issues, pull requests, settings and bill. A workspace's **owners** control it. They decide who is a member, what members and agents may do, and they're responsible for paying for it. If you create a workspace for an organization, the organization is the customer, and its owners act for it.
1919
2020 ## 3. Acceptable use
2121
2828 To run g1t, we need your permission to handle your content. You give Flagon a worldwide, non-exclusive, royalty-free license to host, store, copy, process, transmit and display your content **only as needed to provide, secure and improve the service for you**. That includes, for example:
2929
3030 - storing your repositories and showing them to the people you let see them;
31−- sending the relevant parts to a model provider when you ask an agent to work (see section 6);
31+- sending the relevant parts to your workspace's AI provider when you ask g1t a question (see section 6);
3232 - indexing it so you can search it;
3333 - scanning it for secrets and vulnerable dependencies, so we can warn you;
3434 - making backups.
3535
3636 This license ends when your content is deleted from g1t, except for copies in backups (which expire on their own schedule) and content others have a right to keep under section 5.
3737
38−**We don't sell your content, and we don't use your private content to train AI models.** We don't let the model providers we use for hosted agents train on it either.
38+**We don't sell your content, and we don't use your private content to train AI models.** g1t itself doesn't send your content to any model provider: Ask g1t uses the AI provider your workspace connects, under your agreement with them.
3939
4040 You're responsible for your content: that you have the right to put it on g1t, and that it doesn't break the law or someone else's rights.
4141
4949
5050 ## 6. Agents act on your behalf
5151
52−g1t's agents ("g1t agents") write code, review changes, answer questions, plan work and land changes, when you or another member of your workspace asks them to: by assigning an issue, mentioning an agent, starting a plan, or turning on something that runs on its own, such as upkeep or automatic merges.
52+The coding agents you connect to g1t (through the MCP server, the API, the g1t CLI or a plugin) work with your access: they read and change what you let them, and open issues and pull requests, record their sessions and claim work as you.
5353
54−- **An agent acts on behalf of the person who started its work**, within the limits of that person's access and the workspace's guardrails. Its actions are recorded as such in the audit log.
54+- **An agent acts on behalf of the person whose credentials it uses**, within the limits of that person's access, the token's scopes and the workspace's rules. Its actions are recorded as such in the audit log.
5555 - **You're responsible for what you ask agents to do**, and for checking what they produce before you rely on it, merge it or deploy it, as you would for a teammate's work. Agents make mistakes. Their output can be wrong, insecure, or similar to code that exists elsewhere, and you're responsible for making sure your use of it complies with the licenses and laws that apply.
56−- **Guardrails** (time caps on runs) reduce risk. They're a safety net, not a guarantee that an agent won't do something you didn't intend.
5756 - **What agents produce for you is your content.** As between you and us, Flagon claims no rights in it.
58−- **Agents cost money.** Runs, sandbox time and model use are charged to the workspace that owns the repository, as described on [pricing](/pricing).
5957
60−To run hosted agents, we send the relevant content (such as the issue, the code it needs and the conversation so far) to a model provider. The [subprocessors](/policies/subprocessors) page lists who. If your workspace connects its own model provider, your content goes to that provider instead, under your agreement with them.
58+**Ask g1t** answers questions about your workspace with the AI provider your workspace connects. To answer, we send that provider the question and the content it needs (such as the issue, pull request or files it reads), under your agreement with them. Its answers can be wrong too.
6159
6260 ## 7. Services you connect
6361
6664 ## 8. Paying for g1t
6765
6866 - **The forge is free.** Repositories, git, issues, pull requests and review cost nothing, within the free limits described on [pricing](/pricing).
69−- **Compute is paid.** Agents, sandboxes, checks, workflows, the merge queue, deployments and the other metered items on [pricing](/pricing) are charged to the workspace, at what they cost us plus the markup shown there. **The g1t plan** is a monthly subscription per workspace, with some usage included.
67+- **Compute is paid.** Sandboxes, checks, workflows, the merge queue, deployments and the other metered items on [pricing](/pricing) are charged to the workspace, at what they cost us plus the markup shown there. **The g1t plan** is a monthly subscription per workspace, with some usage included.
7068 - **Usage prices follow our costs.** They move when what we pay our providers moves, and every change is listed on the pricing page with its reason. We'll give owners at least 30 days' notice by email before we raise the plan's own monthly price.
7169 - **Payment.** Payments are processed by Stripe. You authorize us to charge the workspace's card, or another payment method on file, for the plan, for usage as it is billed, and when the workspace nears its limit. Invoices are available from the Billing page. Card numbers never reach g1t.
7270 - **Taxes.** Prices don't include taxes unless we say so. You're responsible for the taxes that apply to your purchases, other than taxes on our income.
106104
107105 ## 12. Disclaimers
108106
109−We work hard to make g1t reliable, but **g1t is provided "as is" and "as available."** To the extent the law allows, Flagon disclaims all warranties, express or implied, including merchantability, fitness for a particular purpose, title and non-infringement. We don't promise that g1t will be uninterrupted or error-free, that agents' output will be correct or fit for your purpose, or that content will never be lost. Keep your own copies of what matters to you: git makes this easy.
107+We work hard to make g1t reliable, but **g1t is provided "as is" and "as available."** To the extent the law allows, Flagon disclaims all warranties, express or implied, including merchantability, fitness for a particular purpose, title and non-infringement. We don't promise that g1t will be uninterrupted or error-free, that answers or agents' output will be correct or fit for your purpose, or that content will never be lost. Keep your own copies of what matters to you: git makes this easy.
110108
111109 ## 13. Limitation of liability
112110
+3−3
4343 });
4444
4545 test("search finds by name, description, tag and keyword, across every category", () => {
46− const found = arrange(workspace, { category: "design", query: "sentry", connected: none });
46+ const found = arrange(workspace, { category: "design", query: "resolve", connected: none });
4747 assert.deepEqual(found.available.map((view) => view.id), ["sentry"], "a search is not held to the category");
48− assert.deepEqual(found.soon.map((view) => view.id), ["sentry-mcp"], "what is coming sorts after what works");
48+ assert.deepEqual(found.soon.map((view) => view.id), ["pagerduty"], "what is coming sorts after what works");
4949 assert.ok(matchesQuery(personal.find((view) => view.id === "google-calendar")!, "in a meeting"));
5050 assert.ok(matchesQuery(workspace.find((view) => view.id === "anthropic")!, "claude"));
5151 assert.ok(matchesQuery(workspace.find((view) => view.id === "incident-io")!, "incidentio"));
5252 assert.ok(matchesQuery(workspace.find((view) => view.id === "pagerduty")!, "opens ISSUES"));
53− assert.ok(!matchesQuery(workspace.find((view) => view.id === "figma")!, "kubernetes"));
53+ assert.ok(!matchesQuery(workspace.find((view) => view.id === "miro")!, "kubernetes"));
5454 });
5555
5656 test("the navigation counts each category and leaves out empty ones", () => {
+6−1
9797 export const POLICIES_DRAFT = true;
9898
9999 /** When any policy last changed, `YYYY-MM-DD`. */
100−export const POLICIES_UPDATED = "2026-10-09";
100+export const POLICIES_UPDATED = "2026-10-13";
101101
102102 /** Every change to the policies, newest first. */
103103 export const POLICY_HISTORY: { date: string; change: string }[] = [
104104 {
105+ date: "2026-10-13",
106+ change:
107+ "Terms of Service and Privacy Policy: g1t no longer runs agents of its own. Section 6 of the terms covers the coding agents you connect and Ask g1t, which answers with your workspace's own AI provider; agents are no longer billed compute. Subprocessors: your workspace's AI provider listed among the integrations you choose.",
108+ },
109+ {
105110 date: "2026-10-09",
106111 change:
107112 "Privacy Policy: g1t.sh uses HeyCatch for site analytics, with names removed outside the public pages, and one analytics cookie, set in the EU, EEA, UK and Switzerland only after you allow it. Subprocessors: HeyCatch, Inc. added.",
+12−13
108108 */
109109 const READS = new Set(
110110 (
111− "account active_agents all_ids get list queue pulls_for_repos blame blob branches by_author by_repo contributions catalog check_invite check_limit " +
112− "check_workspace_deletion check_workspace_rename collaborator_permission compare counts deleted deliveries " +
113− "domains entitlements entity explore features git_access has_feature invoices ledger limit " +
114− "limit_requests links log logs managed_pulls my_repo_invitations " +
115− "outside_collaborators overview path_by_id prices profile profile_workspaces public_namespaces read_session " +
116− "readable ready_issues references registration repo_access resolve resolve_branch resolve_path resolve_slug " +
117− "routes run run_context run_cost runner_activity runner_groups runner_settings runners runs scorecards search usage_report " +
118− "settings statement statement_entries status status_by_id suggest tree usage usage_meters user_by_username " +
119− "user_for_session user_for_access_token usernames waiting_workspaces workflows workspace workspace_invites github_enabled " +
120− "stars about public_links branch_drift tags last_commits languages contributors license releases release " +
121− "stargazers starred commit_checks shortcuts spend person_budgets usage_report templates install_requests " +
122− "marketplaces discover plugin plugin_installs plugin_agents connectors connector agent_connectors customize_bundle " +
123− "people_directory team_agents team_context team_members github_visible_installations skill_library skill agent_skills last_visit team_agent_index " +
111+ "account all_ids get list queue pulls_for_repos blame blob branches by_author by_repo contributions " +
112+ "check_invite check_limit check_workspace_deletion check_workspace_rename collaborator_permission " +
113+ "compare counts deleted deliveries domains entitlements explore features git_access has_feature " +
114+ "invoices ledger limit limit_requests links log logs managed_pulls my_repo_invitations " +
115+ "outside_collaborators overview path_by_id prices profile profile_workspaces public_namespaces " +
116+ "read_session readable repo_access resolve resolve_branch resolve_path resolve_slug " +
117+ "routes run runner_activity runner_groups runner_settings runners runs search usage_report settings " +
118+ "statement statement_entries status status_by_id suggest tree usage usage_meters user_by_username " +
119+ "user_for_session user_for_access_token usernames workflows workspace workspace_invites " +
120+ "github_enabled stars about public_links branch_drift tags last_commits languages contributors " +
121+ "license releases release stargazers starred commit_checks shortcuts spend usage_report templates " +
122+ "people_directory team_members github_visible_installations last_visit " +
124123 "activity_digest activity"
125124 ).split(" "),
126125 );
+0−1
1−.cache/
+0−17
1−# syntax=docker/dockerfile:1.7
2−#
3−# g1t's reviewer as a ReviewBench agent image: the g1t sandbox's base image
4−# (the Claude Code CLI and toolchains production reviews run with) plus the
5−# contract adapter in agent/. Built by `node bench/reviewbench/run.mjs build`,
6−# which passes BASE from services/runner/base.json and regenerates
7−# agent/instructions.txt from crates/runner/src/review.rs first.
8−#
9−# linux/amd64, as the contract requires.
10−
11−ARG BASE
12−FROM --platform=linux/amd64 ${BASE}
13−COPY agent/ /bench/
14−# /work/change.diff and /work/review.json are where the production prompt
15−# tells the agent to read and write; the benchmark mounts the rest of /work.
16−RUN mkdir -p /work && chmod 0777 /work
17−ENTRYPOINT ["node", "/bench/agent.mjs"]
+0−122
1−// g1t's pull request reviewer behind the ReviewBench agent contract
2−// (https://github.com/review-bench/ReviewBench/blob/main/AGENT_CONTRACT.md).
3−//
4−// Runs inside the image built from bench/reviewbench/Dockerfile, which is
5−// the g1t sandbox's base image (same Claude Code CLI, same toolchains).
6−// It does what crates/runner/src/review.rs does after its clone: write the
7−// change to /work/change.diff, run Claude Code headless with the same
8−// flags as crates/runner/src/harness.rs, read /work/review.json, and apply
9−// the same filters services/work/src/reviews.rs applies when it records a
10−// review. Then it writes the review's line comments as findings.
11−//
12−// Configuration (all optional):
13−// ANTHROPIC_MODEL or RB_CONFIG_MODEL model id (production review route: claude-sonnet-5-5)
14−// ANTHROPIC_BASE_URL g1t's model proxy (MODELS_URL/anthropic) or a gateway
15−// ANTHROPIC_API_KEY provider key, or a model-proxy session token
16−// RB_CONFIG_BUDGET_USD per-PR cost cap (--max-budget-usd), default 5
17−// RB_CONFIG_VARIANT prompt variant: "production" (default) or a file in variants/
18−
19−import { spawn } from "node:child_process";
20−import { copyFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
21−
22−import { buildPrompt } from "./prompt-lib.mjs";
23−
24−const REPO = "/work/repo";
25−const DIFF_FILE = "/work/change.diff";
26−const REVIEW_FILE = "/work/review.json";
27−const MAX_TURNS = "80"; // harness.rs
28−const MAX_REVIEW_COMMENTS = 30; // reviews.rs
29−const MAX_REVIEW_CHARS = 20_000; // reviews.rs
30−
31−const env = (name, fallback) => process.env[name] ?? fallback;
32−const out = env("RB_OUT", "/work/out/findings.json");
33−const agent = env("RB_AGENT", "g1t");
34−const pr = JSON.parse(readFileSync(env("RB_PR_JSON", "/work/pr/pr.json"), "utf8"));
35−const model = env("RB_CONFIG_MODEL", env("ANTHROPIC_MODEL", "claude-sonnet-5-5"));
36−const budget = Number(env("RB_CONFIG_BUDGET_USD", "5"));
37−const variant = env("RB_CONFIG_VARIANT", "production");
38−
39−// The run's settings, printed so ReviewBench can see each label took effect.
40−console.log(`model=${model} budget_usd=${budget} variant=${variant}`);
41−
42−function write(findings, usage) {
43− const body = {
44− pr: { repo: pr.repo ?? `https://github.com/${env("RB_NWO")}`, pr_number: Number(env("RB_PR_NUMBER", pr.pr_number)), base: env("RB_BASE", pr.base), head: env("RB_HEAD", pr.head) },
45− agent,
46− findings,
47− usage,
48− };
49− writeFileSync(out, JSON.stringify(body, null, 2));
50−}
51−
52−/** Paths the change touches, as the work service knows them (pull.files). */
53−function changedPaths(diff) {
54− const paths = new Set();
55− for (const line of diff.split("\n")) {
56− const m = line.match(/^\+\+\+ b\/(.+)$/) ?? line.match(/^--- a\/(.+)$/);
57− if (m && m[1] !== "/dev/null") paths.add(m[1]);
58− }
59− return paths;
60−}
61−
62−function runClaude(prompt) {
63− return new Promise((resolve, reject) => {
64− const args = ["--print", prompt, "--output-format", "stream-json", "--verbose", "--max-turns", MAX_TURNS, "--dangerously-skip-permissions", "--model", model];
65− if (budget > 0) args.push("--max-budget-usd", budget.toFixed(2));
66− const child = spawn("claude", args, { cwd: REPO, stdio: ["ignore", "pipe", "inherit"], env: { ...process.env, ANTHROPIC_MODEL: model } });
67− let result = null;
68− let buffer = "";
69− child.stdout.on("data", (chunk) => {
70− buffer += chunk;
71− let at;
72− while ((at = buffer.indexOf("\n")) >= 0) {
73− const line = buffer.slice(0, at);
74− buffer = buffer.slice(at + 1);
75− try {
76− const event = JSON.parse(line);
77− if (event.type === "result") result = event;
78− } catch {}
79− }
80− });
81− child.on("error", reject);
82− child.on("close", (code) => (result ? resolve(result) : reject(new Error(`claude exited ${code} without a result`))));
83− });
84−}
85−
86−const started = Date.now();
87−copyFileSync(env("RB_DIFF", "/work/pr/diff.patch"), DIFF_FILE);
88−const diff = readFileSync(DIFF_FILE, "utf8");
89−if (!diff.trim()) {
90− // review.rs bails on an empty change; the benchmark still wants a file.
91− write([], { time_in_ms: Date.now() - started });
92− process.exit(0);
93−}
94−
95−const instructionsFile = variant === "production" ? new URL("./instructions.txt", import.meta.url) : new URL(`./variants/${variant}.txt`, import.meta.url);
96−const prompt = buildPrompt(pr, readFileSync(instructionsFile, "utf8"));
97−const result = await runClaude(prompt);
98−
99−let review = null;
100−if (existsSync(REVIEW_FILE)) {
101− try {
102− review = JSON.parse(readFileSync(REVIEW_FILE, "utf8"));
103− } catch {}
104−}
105−// review.rs: no file means the agent's answer is the review, with no line comments.
106−const comments = Array.isArray(review?.comments) ? review.comments : [];
107−const known = changedPaths(diff);
108−const findings = comments
109− .filter((c) => typeof c?.body === "string" && c.body.trim() && typeof c.path === "string")
110− // reviews.rs: a line in a file the pull request does not change is dropped.
111− .filter((c) => known.size === 0 || known.has(c.path.replace(/^\.\//, "")))
112− .slice(0, MAX_REVIEW_COMMENTS)
113− .map((c) => {
114− const line = Number.isInteger(c.line) && c.line > 0 ? c.line : 1;
115− const end = Number.isInteger(c.end_line) && c.end_line >= line ? c.end_line : line;
116− return { file: c.path.replace(/^\.\//, ""), start_line: line, end_line: end, message: c.body.trim().slice(0, MAX_REVIEW_CHARS), producer: agent };
117− });
118−
119−// Kept beside the findings for analysis: what production would also have
120−// posted (verdict, summary) and what the run cost.
121−writeFileSync(out.replace(/\.json$/, ".g1t.json"), JSON.stringify({ verdict: review?.verdict ?? null, body: review?.body ?? result.result ?? "", dropped: comments.length - findings.length, cost_usd: result.total_cost_usd, turns: result.num_turns, model }, null, 2));
122−write(findings, { time_in_ms: Date.now() - started, ...(typeof result.total_cost_usd === "number" ? { cost_usd: result.total_cost_usd } : {}) });
+0−15
1−You are reviewing a pull request. The repository is checked out in the current directory at the pull request's head. The change under review is in /work/change.diff; read it first, then read the surrounding code as needed. You may run the project's tests. Do not modify the repository.
2−
3−Judge whether the change does what it is for, whether it is correct, and whether it would break anything. Be specific and brief. Comment only on real problems or things a maintainer would want to know; do not praise, and do not restate the diff.
4−
5−Write your review to /work/review.json as JSON with exactly this shape:
6−
7−{
8− "verdict": "approve" or "request_changes",
9− "body": "A summary in Markdown: what you checked and your conclusion.",
10− "comments": [
11− { "path": "path/in/the/repository", "line": 12, "body": "What is wrong on this line and what to do about it." }
12− ]
13−}
14−
15−`line` is the line number in the file as it is after the change. `comments` may be empty. Use "request_changes" only if something must be fixed before merging. Then finish.
+0−8
1−// The part of ../prompt.mjs the image needs, without the path to the Rust
2−// source (the image only carries instructions.txt, generated from it).
3−
4−/** What production says about the pull request, then the instructions. */
5−export function buildPrompt(pr, instructions) {
6− const about = [`Pull request #${pr.pr_number}: ${pr.title ?? ""}`, pr.body ?? ""].filter((part) => part.trim()).join("\n\n");
7− return `${about}\n\n${instructions}`;
8−}
+0−21
1−You are reviewing a pull request. The repository is checked out in the current directory at the pull request's head. The change under review is in /work/change.diff. Do not modify the repository.
2−
3−Work in three passes.
4−
5−1. Map the change. Read the diff and list every file it touches. For each changed function, type or config, find its callers and the code it calls (grep for the names) and read them. Read the tests that cover the change, and the tests that should and do not.
6−
7−2. Look for problems, file by file, in this order: correctness (wrong logic, off-by-one, null and error paths, broken invariants, concurrency, resource leaks), security (input reaching queries, shells, paths, HTML or regexes; secrets; authorization), reliability (missing error handling, retries, timeouts, migrations that cannot run twice), API and compatibility (callers this change breaks, changed behavior without a version bump), tests (missing or ineffective tests for new behavior), performance (work in loops, unbounded growth), documentation that the change makes wrong, and accessibility in UI code. Where you can, run the project's tests or a quick check to confirm a suspicion.
8−
9−3. Check each candidate before keeping it. Re-read the exact lines. Drop it if it is not true of the code at head, if it is pure style, or if the change does not make it worse. Keep every distinct problem that survives; do not stop at the first few.
10−
11−Write your review to /work/review.json as JSON with exactly this shape:
12−
13−{
14− "verdict": "approve" or "request_changes",
15− "body": "A summary in Markdown: what you checked and your conclusion.",
16− "comments": [
17− { "path": "path/in/the/repository", "line": 12, "end_line": 14, "severity": "high" | "medium" | "low", "body": "What is wrong, why it matters, and what to do about it." }
18− ]
19−}
20−
21−`line` and `end_line` are line numbers in the file as it is after the change, spanning the problem. One comment per problem. Every problem you mention in the summary must also be a comment on its lines. `comments` may be empty. Use "request_changes" only if something must be fixed before merging. Then finish.
+0−49
1−// The review prompt, taken from the runner's own source so the benchmark
2−// measures what production runs. crates/runner/src/review.rs holds the
3−// instructions as a Rust string constant; services/runner/src/index.ts
4−// (startReviewRun) puts "what the pull request is" in front of them.
5−//
6−// Production also appends repository instructions (AGENTS.md, CLAUDE.md,
7−// .g1t/review.md); Claude Code reads CLAUDE.md from the checkout by
8−// itself, as it does in a trusted production run.
9−
10−import { readFileSync } from "node:fs";
11−import { fileURLToPath } from "node:url";
12−
13−const REVIEW_RS = fileURLToPath(new URL("../../crates/runner/src/review.rs", import.meta.url));
14−
15−/** The `INSTRUCTIONS` constant of review.rs, unescaped as rustc would. */
16−export function instructionsFromRust(source = readFileSync(REVIEW_RS, "utf8")) {
17− const match = source.match(/const INSTRUCTIONS: &str = "([\s\S]*?)";\n/);
18− if (!match) throw new Error("INSTRUCTIONS not found in review.rs; update bench/reviewbench/prompt.mjs");
19− return match[1]
20− // A backslash at the end of a line continues the string and eats the
21− // next line's leading whitespace.
22− .replace(/\\\n\s*/g, "")
23− .replace(/\\"/g, '"')
24− .replace(/\\n/g, "\n")
25− .replace(/\\\\/g, "\\");
26−}
27−
28−export { buildPrompt } from "./agent/prompt-lib.mjs";
29−
30−// `node prompt.mjs` writes instructions.txt for the image build, and
31−// `node prompt.mjs --check` fails when it has drifted from review.rs.
32−if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
33− const out = fileURLToPath(new URL("./agent/instructions.txt", import.meta.url));
34− const text = instructionsFromRust();
35− if (process.argv.includes("--check")) {
36− let current = "";
37− try {
38− current = readFileSync(out, "utf8");
39− } catch {}
40− if (current !== text) {
41− console.error("bench/reviewbench/agent/instructions.txt is out of date; run node bench/reviewbench/prompt.mjs");
42− process.exit(1);
43− }
44− } else {
45− const { writeFileSync } = await import("node:fs");
46− writeFileSync(out, text);
47− console.log(`wrote ${out} (${text.length} chars)`);
48− }
49−}
+0−278
1−#!/usr/bin/env node
2−// Runs g1t's pull request reviewer over ReviewBench and scores it with the
3−// benchmark's own judge.
4−//
5−// node bench/reviewbench/run.mjs setup clone ReviewBench (pinned) and install its judge
6−// node bench/reviewbench/run.mjs build build the agent image from the runner base image
7−// node bench/reviewbench/run.mjs estimate --set sample:50
8−// node bench/reviewbench/run.mjs review --set sample:50 [--seed 1] [--variant production] [--model claude-sonnet-5-5] --yes
9−// node bench/reviewbench/run.mjs judge --run <id> [--judge-model claude-sonnet-5] --yes
10−// node bench/reviewbench/run.mjs report --run <id> [--json]
11−//
12−// --set is test (the benchmark's 25), full (all 219) or sample:N (N of the
13−// 219, stratified by change size, reproducible with --seed).
14−//
15−// Model access, from the environment, passed into each container:
16−// ANTHROPIC_API_KEY a provider key, or
17−// ANTHROPIC_BASE_URL + ANTHROPIC_API_KEY g1t's model proxy (MODELS_URL/anthropic and a session token)
18−// The judge reads ANTHROPIC_API_KEY itself (through ReviewBench's pi registry).
19−//
20−// Nothing that spends money runs without --yes. Each review is capped by
21−// --budget-usd (default 5), and a run stops once --max-total-usd is spent.
22−//
23−// Needs docker, git, jq and bash (Git Bash on Windows) and Node 20+.
24−
25−import { execFileSync, spawnSync } from "node:child_process";
26−import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
27−import { dirname, join } from "node:path";
28−import { fileURLToPath } from "node:url";
29−
30−const HERE = dirname(fileURLToPath(import.meta.url));
31−const ROOT = join(HERE, "..", "..");
32−const CACHE = join(HERE, ".cache");
33−const RB = join(CACHE, "ReviewBench");
34−const RUNS = join(CACHE, "runs");
35−// The ReviewBench commit these numbers are comparable against.
36−const RB_COMMIT = "ceb0794a3768da6ef4a56e5311dfb4afd29e5dee";
37−const IMAGE = "g1t-reviewbench:dev";
38−// The model the official leaderboard is judged with.
39−const DEFAULT_JUDGE = "claude-sonnet-5";
40−// services/runner/wrangler.jsonc AGENT_ROUTES.review
41−const DEFAULT_MODEL = "claude-sonnet-5-5";
42−
43−// $ per million tokens: input, output, cache read, cache write (5 minute).
44−const PRICES = {
45− "claude-sonnet-5-5": [2, 10, 0.2, 2.5],
46− "claude-sonnet-5": [2, 10, 0.2, 2.5],
47− "claude-opus-5-5": [4, 20, 0.2, 5],
48− "claude-haiku-4-5": [1, 5, 0.1, 1.25],
49−};
50−
51−function args() {
52− const [command, ...rest] = process.argv.slice(2);
53− const flags = {};
54− for (let i = 0; i < rest.length; i++) {
55− const name = rest[i].replace(/^--/, "");
56− const next = rest[i + 1];
57− if (next === undefined || next.startsWith("--")) flags[name] = true;
58− else flags[name] = rest[++i];
59− }
60− return { command, flags };
61−}
62−
63−const run = (cmd, argv, opts = {}) => {
64− const result = spawnSync(cmd, argv, { stdio: "inherit", ...opts });
65− if (result.status !== 0) throw new Error(`${cmd} ${argv.join(" ")} exited ${result.status}`);
66−};
67−
68−function manifest() {
69− return JSON.parse(readFileSync(join(RB, "corpus", "manifest.json"), "utf8"));
70−}
71−
72−/** A seeded shuffle, so a sample is the same sample next week. */
73−function shuffle(items, seed) {
74− let state = seed >>> 0 || 1;
75− const random = () => ((state = (state * 1664525 + 1013904223) >>> 0) / 2 ** 32);
76− const copy = [...items];
77− for (let i = copy.length - 1; i > 0; i--) {
78− const j = Math.floor(random() * (i + 1));
79− [copy[i], copy[j]] = [copy[j], copy[i]];
80− }
81− return copy;
82−}
83−
84−/** Indices into the full manifest for --set. */
85−function select(set, seed) {
86− const all = manifest();
87− if (set === "full") return all.map((_, i) => i);
88− if (set === "test") {
89− const test = JSON.parse(readFileSync(join(RB, "corpus", "test", "test.json"), "utf8"));
90− const keys = new Set(test.map((p) => `${p.nwo}#${p.pr_number}`));
91− return all.flatMap((p, i) => (keys.has(`${p.nwo}#${p.pr_number}`) ? [i] : []));
92− }
93− const m = /^sample:(\d+)$/.exec(set);
94− if (!m) throw new Error(`--set is test, full or sample:N, not ${set}`);
95− const n = Number(m[1]);
96− // Stratified by change size, in the corpus's proportions, so a sample is
97− // not all small or all huge.
98− const bucket = (p) => {
99− const lines = p.lines_added + p.lines_removed;
100− return lines <= 200 ? 0 : lines <= 1000 ? 1 : 2;
101− };
102− const strata = [[], [], []];
103− all.forEach((p, i) => strata[bucket(p)].push(i));
104− const picked = [];
105− for (const stratum of strata) {
106− const share = Math.round((stratum.length / all.length) * n);
107− picked.push(...shuffle(stratum, seed).slice(0, share));
108− }
109− return picked.slice(0, n).sort((a, b) => a - b);
110−}
111−
112−/**
113− * What one review is expected to cost. An agentic review re-reads its
114− * growing context every turn, mostly from cache: turns and context grow
115− * with the size of the change. Calibrate against real review runs (the
116− * billing ledger records each run's cost) before trusting the absolute
117− * numbers.
118− */
119−function estimateOne(p, model) {
120− const [, output, cacheRead, cacheWrite] = PRICES[model] ?? PRICES[DEFAULT_MODEL];
121− const lines = p.lines_added + p.lines_removed;
122− const turns = Math.min(80, 12 + Math.round(Math.sqrt(lines) * 0.8) + Math.min(p.files_changed, 40) * 0.4);
123− const startContext = 18_000 + Math.min(lines, 6_000) * 12; // prompt, tools, the diff when read
124− const endContext = Math.min(startContext + turns * 2_500, 180_000); // files read along the way
125− const meanContext = (startContext + endContext) / 2;
126− // Each turn re-reads the context from cache and writes only what it added.
127− const fresh = endContext;
128− const cachedReads = Math.max(0, turns * meanContext - fresh);
129− const outTokens = turns * 450 + 2_500;
130− return (cachedReads * cacheRead + fresh * cacheWrite + outTokens * output) / 1e6;
131−}
132−
133−/**
134− * The judge: matching per file chunk plus classifying every unmatched
135− * finding with read access to the repository (a few tool calls each).
136− */
137−function estimateJudge(p, model, findingsPerPr = 6) {
138− const [input, output, cacheRead] = PRICES[model] ?? PRICES[DEFAULT_JUDGE];
139− const matchCalls = Math.max(1, Math.ceil(findingsPerPr / 3));
140− const unmatched = findingsPerPr * 0.5;
141− const tokensIn = matchCalls * 6_000 + unmatched * 5 * 12_000;
142− const tokensOut = matchCalls * 1_500 + unmatched * 5 * 600;
143− return (tokensIn * 0.5 * input + tokensIn * 0.5 * cacheRead + tokensOut * output) / 1e6;
144−}
145−
146−function estimate(indices, model, judgeModel, rounds = 1) {
147− const all = manifest();
148− let review = 0;
149− let judge = 0;
150− for (const i of indices) {
151− review += estimateOne(all[i], model);
152− judge += estimateJudge(all[i], judgeModel);
153− }
154− return { prs: indices.length, rounds, review_usd: review * rounds, judge_usd: judge * rounds, total_usd: (review + judge) * rounds };
155−}
156−
157−function setup() {
158− mkdirSync(CACHE, { recursive: true });
159− if (!existsSync(join(RB, ".git"))) run("git", ["clone", "https://github.com/review-bench/ReviewBench.git", RB]);
160− run("git", ["-C", RB, "fetch", "-q", "origin", RB_COMMIT]);
161− run("git", ["-C", RB, "checkout", "-q", "--detach", RB_COMMIT]);
162− run("npm", ["ci", "--no-audit", "--no-fund"], { cwd: RB, shell: process.platform === "win32" });
163−}
164−
165−function build() {
166− run(process.execPath, [join(HERE, "prompt.mjs")]);
167− const base = JSON.parse(readFileSync(join(ROOT, "services", "runner", "base.json"), "utf8"));
168− run("docker", ["build", "--platform", "linux/amd64", "--build-arg", `BASE=${base.image}@${base.digest}`, "-t", IMAGE, HERE]);
169−}
170−
171−function review(flags) {
172− if (!flags.yes) throw new Error("review spends model credit; run estimate first, then pass --yes");
173− const set = flags.set ?? "sample:50";
174− const seed = Number(flags.seed ?? 1);
175− const model = flags.model ?? DEFAULT_MODEL;
176− const variant = flags.variant ?? "production";
177− const budget = Number(flags["budget-usd"] ?? 5);
178− const maxTotal = Number(flags["max-total-usd"] ?? 150);
179− const indices = select(set, seed);
180− const id = flags.run ?? `${new Date().toISOString().slice(0, 10)}-${set.replace(":", "")}-${variant}-${model}`;
181− const dir = join(RUNS, id);
182− mkdirSync(dir, { recursive: true });
183− writeFileSync(join(dir, "run.json"), JSON.stringify({ id, set, seed, model, variant, budget_usd: budget, indices, reviewbench: RB_COMMIT, g1t: execFileSync("git", ["-C", ROOT, "rev-parse", "HEAD"]).toString().trim(), started_at: new Date().toISOString() }, null, 2));
184− const env = { ...process.env, RB_CONFIG_MODEL: model, RB_CONFIG_VARIANT: variant, RB_CONFIG_BUDGET_USD: String(budget), TRY_AGENT_WORK: join(dir, ".work") };
185− const pass = ["-e", "RB_CONFIG_MODEL", "-e", "RB_CONFIG_VARIANT", "-e", "RB_CONFIG_BUDGET_USD", "-e", "ANTHROPIC_API_KEY"];
186− if (process.env.ANTHROPIC_BASE_URL) pass.push("-e", "ANTHROPIC_BASE_URL");
187− let spent = 0;
188− for (const i of indices) {
189− if (spent >= maxTotal) {
190− console.error(`stopped: spent $${spent.toFixed(2)} of --max-total-usd ${maxTotal}`);
191− break;
192− }
193− spawnSync("bash", [join(RB, "scripts", "try-agent.sh"), IMAGE, "--set", "full", "--pr", String(i), ...pass], { cwd: dir, env, stdio: "inherit" });
194− spent = sidecars(dir).reduce((sum, s) => sum + (s.cost_usd ?? 0), 0);
195− }
196− console.log(`run ${id}: ${readdirSync(join(dir, "findings")).length} of ${indices.length} reviewed, $${spent.toFixed(2)} spent`);
197−}
198−
199−/** What production would have posted beside the findings, and the cost. */
200−function sidecars(dir) {
201− const out = join(dir, ".work", "out");
202− if (!existsSync(out)) return [];
203− return readdirSync(out).flatMap((key) => {
204− const file = join(out, key, "findings.g1t.json");
205− return existsSync(file) ? [{ key, ...JSON.parse(readFileSync(file, "utf8")) }] : [];
206− });
207−}
208−
209−function judge(flags) {
210− if (!flags.yes) throw new Error("judging spends model credit; pass --yes");
211− const dir = join(RUNS, flags.run);
212− const judgeModel = flags["judge-model"] ?? DEFAULT_JUDGE;
213− run("npm", ["run", "judge", "--", "--candidate", join(dir, "findings"), "--golden", join(RB, "golden"), "--manifest", join(RB, "corpus", "manifest.json"), "--provider", "anthropic", "--model", judgeModel, "--output", join(dir, "scoring", "results.json"), "--repo-dir", join(CACHE, "judge-repos"), "--concurrency", String(flags.concurrency ?? 4)], { cwd: RB, shell: process.platform === "win32" });
214−}
215−
216−function report(flags) {
217− const dir = join(RUNS, flags.run);
218− const meta = JSON.parse(readFileSync(join(dir, "run.json"), "utf8"));
219− const results = JSON.parse(readFileSync(join(dir, "scoring", "results.json"), "utf8"));
220− const side = sidecars(dir);
221− const cost = side.reduce((sum, s) => sum + (s.cost_usd ?? 0), 0);
222− const summary = {
223− run: meta.id,
224− date: meta.started_at.slice(0, 10),
225− g1t: meta.g1t,
226− model: meta.model,
227− variant: meta.variant,
228− set: meta.set,
229− prs: side.length,
230− findings: readdirSync(join(dir, "findings")).reduce((sum, f) => sum + JSON.parse(readFileSync(join(dir, "findings", f), "utf8")).findings.length, 0),
231− review_cost_usd: Number(cost.toFixed(2)),
232− request_changes: side.filter((s) => s.verdict === "request_changes").length,
233− dropped_outside_change: side.reduce((sum, s) => sum + (s.dropped ?? 0), 0),
234− // The judge's own aggregate block, as ReviewBench writes it.
235− metrics: results.metrics ?? results.summary ?? results,
236− };
237− if (flags.json) {
238− console.log(JSON.stringify(summary));
239− return;
240− }
241− console.log(`## ReviewBench: ${summary.run}\n`);
242− console.log(`g1t ${summary.g1t.slice(0, 8)}, ${summary.model}, prompt ${summary.variant}, ${summary.set} (${summary.prs} PRs), review cost $${summary.review_cost_usd}\n`);
243− console.log("```json\n" + JSON.stringify(summary.metrics, null, 2) + "\n```");
244−}
245−
246−const { command, flags } = args();
247−try {
248− switch (command) {
249− case "setup":
250− setup();
251− break;
252− case "build":
253− build();
254− break;
255− case "estimate": {
256− const indices = select(flags.set ?? "sample:50", Number(flags.seed ?? 1));
257− const model = flags.model ?? DEFAULT_MODEL;
258− const judgeModel = flags["judge-model"] ?? DEFAULT_JUDGE;
259− console.log(JSON.stringify({ set: flags.set ?? "sample:50", model, judge: judgeModel, ...estimate(indices, model, judgeModel, Number(flags.rounds ?? 1)) }, (k, v) => (typeof v === "number" ? Number(v.toFixed(2)) : v), 2));
260− break;
261− }
262− case "review":
263− review(flags);
264− break;
265− case "judge":
266− judge(flags);
267− break;
268− case "report":
269− report(flags);
270− break;
271− default:
272− console.error(readFileSync(fileURLToPath(import.meta.url), "utf8").split("\n").slice(1, 22).join("\n"));
273− process.exit(2);
274− }
275−} catch (error) {
276− console.error(String(error.message ?? error));
277− process.exit(1);
278−}
+0−1
14541454 #[serde(default)]
14551455 pub labels: Option<Vec<String>>,
14561456 /// Usernames of the people it is assigned to; replaces the whole set.
1457− /// Assigning it to g1t is the runner's `run`, not this.
14581457 #[serde(default)]
14591458 pub assignees: Option<Vec<String>>,
14601459 /// The number of the milestone to put it in; 0 takes it out of its
+0−1
390390 MCP_URL: "",
391391 DOCS_URL: "",
392392 PAGES_URL: "",
393− MODELS_URL: "",
394393 PROBE_REPO: process.env.STATUS_PROBE_REPO ?? "",
395394 SUPPORT_URL: `${PUBLIC_URL}/support`,
396395 OG_IMAGE: "",
+7−0
2525 left in work's D1, no longer read or written; read the memory parts below
2626 as history.
2727
28+**Removed the same week:** GitHub mirroring (a repository comes across
29+from GitHub by importing it once; `services/repos/src/mirror.rs` is gone),
30+Boards and Roadmap, the org chart and reporting lines, get-started
31+checklists, preview stacks and project dependencies, and invite-only
32+registration. Wherever this review leans on mirroring as a live bridge to
33+GitHub, or on any of these, read it as history.
34+
2835 Labels used throughout:
2936
3037 | Label | Meaning |
+2−1
773773 export type PriceBook = {
774774 prices: Price[];
775775 changes: PriceChange[];
776+ /** Retired with the models g1t no longer runs; still sent. */
776777 modelMarginPercent: number;
777778 /** Every plan, as sold now. */
778779 plans?: FeaturePlan[];
14621463 todayMicros: number;
14631464 /** `PLATFORM_DAILY_SPEND_CAP_MICROS`; 0: no breaker. */
14641465 dailyCapMicros: number;
1465− /** New hosted-model agent runs g1t would pay for are paused. */
1466+ /** New workflows, checks and merge queue runs g1t would pay for are paused. */
14661467 tripped: boolean;
14671468 trippedAt: string | null;
14681469 liftedBy: string | null;
+0−3
4848 .map(([id]) => id.replace(/_/g, "-"));
4949 assert.deepEqual(available("workspace"), ["alerts-webhook", "datadog", "github", "jira", "linear", "sentry", "webhooks", ...models].sort());
5050 assert.deepEqual(available("personal"), ["github", "mcp"]);
51− for (const connector of CONNECTORS) {
52− if (connector.mcp) assert.equal(connector.status, "soon", `${connector.id}: nothing connects to MCP servers yet`);
53− }
5451 });
5552
5653 test("a connector shows its personal side to you", () => {
+9−67
4949 | "Sends events"
5050 | "Alerts into chat"
5151 | "Sets your status"
52− | "Finds a time"
5352 | "AI provider"
5453 | "Deploys"
5554 | "Syncs people";
7271 personal?: { description?: string; status?: ConnectorStatus; capabilities?: ConnectorCapability[] };
7372 /** More words search should find it by. */
7473 keywords?: string[];
75− /**
76− * The remote MCP server behind it, when the tool publishes one, and how
77− * it signs in (`oauth`: the browser; `header`: a key an owner pastes;
78− * `none`). Its connector is soon: see `mcp()` below.
79− */
80− mcp?: {
81− url: string;
82− auth: "oauth" | "header" | "none";
83− /** For `header`: the header the key goes in (Authorization when unset). */
84− header?: string;
85− /** What to know before connecting: where the key comes from, or that signing in needs an app g1t doesn't have yet. */
86− caveat?: string;
87− };
8874 };
8975
9076 /** A connector as one context (a workspace, or you) shows it. */
10086 keywords: string[];
10187 /** The other scope it can also be connected in. */
10288 alsoIn: ConnectorScope | null;
103− /** The MCP server behind it, when there is one. */
104− mcp: Connector["mcp"] | null;
10589 };
10690
10791 /** The categories, in the order the directory lists them. */
139123 provider,
140124 keywords: ["model", "llm", ...keywords],
141125 };
142−}
143−
144−/**
145− * A tool with a remote MCP server. Soon: Customize, where an owner
146− * connected one, was retired with g1t's hosted agents, so nothing in g1t
147− * connects to these servers today. The server is kept for when something
148− * does; until then each person's own harness connects to it directly.
149− */
150−function mcp(
151− id: string,
152− name: string,
153− category: ConnectorCategory,
154− description: string,
155− server: NonNullable<Connector["mcp"]>,
156− capabilities: ConnectorCapability[] = ["Agents can read", "Agents can act"],
157− keywords: string[] = [],
158−): Connector {
159− return { id, name, category, description, scopes: W, status: "soon", capabilities, keywords: ["mcp", ...keywords], mcp: server };
160126 }
161127
162128 /** A planned connector. */
189155 },
190156 keywords: ["git", "import", "sign in"],
191157 },
192− mcp("github-mcp", "GitHub (MCP)", "code", "Agents read and act on repositories, issues and pull requests on GitHub, through GitHub's MCP server.", { url: "https://api.githubcopilot.com/mcp/", auth: "header", header: "Authorization", caveat: "Paste a GitHub personal access token as Bearer <token>: GitHub's server takes one in place of a sign-in." }, ["Agents can read", "Agents can act"], ["issues", "pull requests"]),
193158 {
194159 id: "webhooks",
195160 name: "Webhooks",
231196 },
232197 keywords: ["tracker", "tickets"],
233198 },
234− mcp("linear-mcp", "Linear (MCP)", "issues", "Agents search, create and update issues, projects and cycles in Linear, through Linear's MCP server.", { url: "https://mcp.linear.app/mcp", auth: "oauth" }, ["Agents can read", "Agents can act"], ["tracker", "issues"]),
235199 {
236200 id: "jira",
237201 name: "Jira",
244208 provider: "jira",
245209 keywords: ["atlassian", "tracker", "tickets"],
246210 },
247− mcp("asana", "Asana", "issues", "Agents read, create and update tasks and projects in Asana, through Asana's MCP server.", { url: "https://mcp.asana.com/v2/mcp", auth: "oauth", caveat: "Asana signs in only apps registered with it, and g1t isn't one yet: signing in here fails until it is." }, ["Agents can read", "Agents can act"], ["tasks", "projects"]),
248211 soon("shortcut", "Shortcut", "issues", W, "Stories agents read when work mentions them, updated when the work lands.", ["Agents can read", "Writes back"]),
249212 soon("clickup", "ClickUp", "issues", W, "Tasks agents can read and import, kept in step with their pull requests.", ["Agents can read", "Writes back"]),
250213 soon("trello", "Trello", "issues", W, "Cards become issues, and move along their board as the work does.", ["Opens issues", "Writes back"]),
251214
252215 // Chat & meetings -------------------------------------------------------
253− soon("slack", "Slack", "chat", BOTH, "Alerts and agent updates in the channels your team already reads, and agents to talk to there.", ["Alerts into chat", "Agents can act"], {
216+ soon("slack", "Slack", "chat", BOTH, "Alerts and updates in the channels your team already reads.", ["Alerts into chat"], {
254217 description: "Your status in g1t follows the one you set in Slack, and the other way round.",
255218 capabilities: ["Sets your status"],
256219 }),
257− soon("microsoft-teams", "Microsoft Teams", "chat", BOTH, "Alerts and agent updates posted to Teams channels, with agents to talk to there.", ["Alerts into chat", "Agents can act"], {
220+ soon("microsoft-teams", "Microsoft Teams", "chat", BOTH, "Alerts and updates posted to Teams channels.", ["Alerts into chat"], {
258221 description: "Your status in g1t follows your Teams presence.",
259222 capabilities: ["Sets your status"],
260223 }),
261− soon("discord", "Discord", "chat", W, "Releases, alerts and agent updates posted to a Discord server.", ["Alerts into chat"]),
224+ soon("discord", "Discord", "chat", W, "Releases, alerts and updates posted to a Discord server.", ["Alerts into chat"]),
262225 soon("zoom", "Zoom", "chat", BOTH, "Meeting notes and recordings agents can read when work refers to them.", ["Agents can read"], {
263226 description: "Sets your status to On a call while you are in a Zoom meeting.",
264227 capabilities: ["Sets your status"],
266229 soon("google-meet", "Google Meet", "chat", P, "Sets your status to On a call while you are in a meeting, and starts a call from any channel.", ["Sets your status"]),
267230
268231 // Calendar & email ------------------------------------------------------
269− soon("google-calendar", "Google Calendar", "calendar", P, "Sets your status to In a meeting while you are in one; agents can find a time that suits everyone.", ["Sets your status", "Finds a time"]),
270− soon("outlook-calendar", "Outlook Calendar", "calendar", P, "Sets your status to In a meeting while you are in one; agents can find a time that suits everyone.", ["Sets your status", "Finds a time"]),
271− soon("gmail", "Gmail", "calendar", P, "Forward a thread to an agent, or turn an email into an issue, from your own inbox.", ["Opens issues", "Agents can read"]),
272− soon("outlook-mail", "Outlook Mail", "calendar", P, "Forward a thread to an agent, or turn an email into an issue, from your own inbox.", ["Opens issues", "Agents can read"]),
232+ soon("google-calendar", "Google Calendar", "calendar", P, "Sets your status to In a meeting while you are in one.", ["Sets your status"]),
233+ soon("outlook-calendar", "Outlook Calendar", "calendar", P, "Sets your status to In a meeting while you are in one.", ["Sets your status"]),
234+ soon("gmail", "Gmail", "calendar", P, "Turn an email into an issue, from your own inbox.", ["Opens issues"]),
235+ soon("outlook-mail", "Outlook Mail", "calendar", P, "Turn an email into an issue, from your own inbox.", ["Opens issues"]),
273236
274237 // Docs & knowledge ------------------------------------------------------
275− mcp("notion", "Notion", "docs", "Agents search, read and write Notion pages and databases, through Notion's MCP server.", { url: "https://mcp.notion.com/mcp", auth: "oauth" }, ["Agents can read", "Agents can act"], ["pages", "wiki", "databases"]),
276− mcp("atlassian", "Atlassian (Jira and Confluence)", "docs", "Agents search and update Jira issues and Confluence pages, through Atlassian's MCP server.", { url: "https://mcp.atlassian.com/v1/mcp", auth: "oauth" }, ["Agents can read", "Agents can act"], ["jira", "confluence", "tickets", "wiki"]),
277238 soon("google-docs", "Google Docs", "docs", BOTH, "Agents read the specs and plans linked from issues and chat.", ["Agents can read"], {
278239 description: "Docs you share in chat open for agents as well as people.",
279240 capabilities: ["Agents can read"],
280241 }),
281− mcp("zapier", "Zapier", "data", "Agents run the actions you expose from Zapier, across thousands of apps, through Zapier's MCP server.", { url: "https://mcp.zapier.com/api/mcp/mcp", auth: "header", header: "Authorization", caveat: "Paste the token from your Zapier MCP settings as Bearer <token>." }, ["Agents can act"], ["automation", "apps"]),
282242 soon("coda", "Coda", "docs", W, "Agents read the docs and tables linked from issues and chat.", ["Agents can read"]),
283243
284244 // Monitoring & incidents ------------------------------------------------
318278 provider: "webhook",
319279 keywords: ["incoming", "http", "alerts", "pagerduty", "grafana"],
320280 },
321− mcp("sentry-mcp", "Sentry (MCP)", "monitoring", "Agents search issues, read stack traces and look up errors in Sentry, through Sentry's MCP server.", { url: "https://mcp.sentry.dev/mcp", auth: "oauth" }, ["Agents can read", "Agents can act"], ["errors", "issues"]),
322− soon("pagerduty", "PagerDuty", "monitoring", BOTH, "Incidents open issues an agent can start on, and resolve when the fix lands.", ["Opens issues", "Writes back"], {
281+ soon("pagerduty", "PagerDuty", "monitoring", BOTH, "Incidents open issues, and resolve when the fix lands.", ["Opens issues", "Writes back"], {
323282 description: "Sets your status to On call while you are on call.",
324283 capabilities: ["Sets your status"],
325284 }),
326285 soon("opsgenie", "Opsgenie", "monitoring", W, "Alerts open issues, and close when the fix lands.", ["Opens issues", "Writes back"]),
327− soon("incident-io", "incident.io", "monitoring", W, "Incidents get a channel in g1t, with agents to dig through what changed.", ["Opens issues", "Alerts into chat"]),
286+ soon("incident-io", "incident.io", "monitoring", W, "Incidents get a channel in g1t, with what changed alongside.", ["Opens issues", "Alerts into chat"]),
328287 soon("grafana", "Grafana", "monitoring", W, "Alert rules open issues, and agents read the dashboards they link to.", ["Opens issues", "Agents can read"]),
329288 soon("new-relic", "New Relic", "monitoring", W, "Alerts open issues, with the traces agents need to find the cause.", ["Opens issues", "Agents can read"]),
330289 soon("honeycomb", "Honeycomb", "monitoring", W, "Triggers open issues, and agents query traces while they work.", ["Opens issues", "Agents can read"]),
331290 soon("better-stack", "Better Stack", "monitoring", W, "Uptime incidents and log alerts open issues.", ["Opens issues"]),
332291
333292 // Cloud & deploy --------------------------------------------------------
334− mcp("cloudflare", "Cloudflare", "cloud", "Agents read your Cloudflare account's Workers, storage and settings, and look things up in its docs, through Cloudflare's MCP servers.", { url: "https://bindings.mcp.cloudflare.com/mcp", auth: "oauth" }, ["Agents can read", "Agents can act"], ["workers", "r2", "d1", "dns"]),
335− mcp("cloudflare-docs", "Cloudflare docs", "docs", "Agents search Cloudflare's documentation while they work.", { url: "https://docs.mcp.cloudflare.com/mcp", auth: "none" }, ["Agents can read"], ["documentation"]),
336293 soon("aws", "Amazon Web Services", "cloud", W, "Workflows and deployments reach AWS with short-lived credentials, no stored keys.", ["Deploys"]),
337294 soon("google-cloud", "Google Cloud", "cloud", W, "Workflows and deployments reach Google Cloud with short-lived credentials.", ["Deploys"]),
338295 soon("azure", "Microsoft Azure", "cloud", W, "Workflows and deployments reach Azure with short-lived credentials.", ["Deploys"]),
339− mcp("vercel", "Vercel", "cloud", "Agents read your Vercel projects, deployments and logs, through Vercel's MCP server.", { url: "https://mcp.vercel.com", auth: "oauth" }, ["Agents can read", "Agents can act"], ["deployments", "previews"]),
340296 soon("netlify", "Netlify", "cloud", W, "Deploy previews as checks on pull requests.", ["Deploys", "Agents can read"]),
341297 soon("fly-io", "Fly.io", "cloud", W, "Deploy apps from workflows, and roll back from the deployment's page.", ["Deploys"]),
342298 soon("kubernetes", "Kubernetes", "cloud", W, "Roll out to your own clusters from workflows, with each rollout's status on g1t.", ["Deploys"]),
369325 },
370326 soon("amazon-bedrock", "Amazon Bedrock", "ai", W, "Models in your own AWS account, billed there.", ["AI provider"]),
371327 soon("vertex-ai", "Google Vertex AI", "ai", W, "Models in your own Google Cloud project, billed there.", ["AI provider"]),
372− mcp("hugging-face", "Hugging Face", "ai", "Agents search models, datasets, papers and Spaces on the Hub, through Hugging Face's MCP server.", { url: "https://huggingface.co/mcp", auth: "oauth" }, ["Agents can read"], ["models", "datasets", "hub"]),
373− mcp("context7", "Context7", "docs", "Agents read current documentation for the libraries a codebase uses, through Context7's MCP server.", { url: "https://mcp.context7.com/mcp", auth: "none" }, ["Agents can read"], ["documentation", "libraries"]),
374− mcp("microsoft-learn", "Microsoft Learn", "docs", "Agents search Microsoft's documentation for Azure, .NET and Windows, through Microsoft Learn's MCP server.", { url: "https://learn.microsoft.com/api/mcp", auth: "none" }, ["Agents can read"], ["azure", "dotnet", "documentation"]),
375− mcp("deepwiki", "DeepWiki", "docs", "Agents read generated documentation for public repositories, through DeepWiki's MCP server.", { url: "https://mcp.deepwiki.com/mcp", auth: "none" }, ["Agents can read"], ["repositories", "documentation"]),
376328
377329 // Data & analytics ------------------------------------------------------
378330 soon("postgresql", "PostgreSQL", "data", W, "Agents read your schema, and run read-only queries while they investigate.", ["Agents can read"]),
379331 soon("snowflake", "Snowflake", "data", W, "Agents answer questions from your warehouse with read-only queries.", ["Agents can read"]),
380332 soon("bigquery", "BigQuery", "data", W, "Agents answer questions from your datasets with read-only queries.", ["Agents can read"]),
381− mcp("posthog", "PostHog", "data", "Agents read product analytics, feature flags and errors in PostHog, through PostHog's MCP server.", { url: "https://mcp.posthog.com/mcp", auth: "oauth" }, ["Agents can read", "Agents can act"], ["analytics", "feature flags"]),
382− mcp("supabase", "Supabase", "data", "Agents read your Supabase projects, schemas and logs, and run queries, through Supabase's MCP server.", { url: "https://mcp.supabase.com/mcp", auth: "oauth" }, ["Agents can read", "Agents can act"], ["postgres", "database"]),
383− mcp("neon", "Neon", "data", "Agents read and manage Neon Postgres projects and branches, through Neon's MCP server.", { url: "https://mcp.neon.tech/mcp", auth: "oauth" }, ["Agents can read", "Agents can act"], ["postgres", "database"]),
384333 soon("amplitude", "Amplitude", "data", W, "Agents read the charts that work refers to.", ["Agents can read"]),
385334 soon("segment", "Segment", "data", W, "Agents read your tracking plan, and check new events against it.", ["Agents can read"]),
386335
387336 // CRM & support ---------------------------------------------------------
388337 soon("zendesk", "Zendesk", "crm", W, "Turn a ticket into an issue, and tell the customer when the fix ships.", ["Opens issues", "Writes back"]),
389− mcp("intercom", "Intercom", "crm", "Agents search conversations, contacts and companies in Intercom, through Intercom's MCP server.", { url: "https://mcp.intercom.com/mcp", auth: "oauth" }, ["Agents can read", "Agents can act"], ["support", "conversations", "customers"]),
390− mcp("hubspot", "HubSpot", "crm", "Agents read contacts, companies and deals in HubSpot, through HubSpot's MCP server.", { url: "https://mcp.hubspot.com/anthropic", auth: "oauth", caveat: "HubSpot signs in only apps registered with it, and g1t isn't one yet: signing in here fails until it is." }, ["Agents can read", "Agents can act"], ["crm", "contacts", "deals"]),
391338 soon("salesforce", "Salesforce", "crm", W, "Agents read the account and case behind a request.", ["Agents can read"]),
392− mcp("stripe", "Stripe", "crm", "Agents read customers, payments and subscriptions, and look things up in Stripe's docs, through Stripe's MCP server.", { url: "https://mcp.stripe.com", auth: "oauth" }, ["Agents can read", "Agents can act"], ["payments", "billing", "customers"]),
393− mcp("paypal", "PayPal", "crm", "Agents read and manage orders, invoices and subscriptions, through PayPal's MCP server.", { url: "https://mcp.paypal.com/mcp", auth: "oauth" }, ["Agents can read", "Agents can act"], ["payments", "invoices"]),
394339
395340 // Design ----------------------------------------------------------------
396− mcp("figma", "Figma", "design", "Agents read designs, components and variables from your Figma files, and build to them, through Figma's MCP server.", { url: "https://mcp.figma.com/mcp", auth: "oauth" }, ["Agents can read"], ["design", "frames", "components"]),
397− mcp("canva", "Canva", "design", "Agents search, create and export Canva designs, through Canva's MCP server.", { url: "https://mcp.canva.com/mcp", auth: "oauth" }, ["Agents can read", "Agents can act"], ["design", "slides"]),
398341 soon("miro", "Miro", "design", W, "Agents read the boards linked from issues and chat.", ["Agents can read"]),
399342 soon("storybook", "Storybook", "design", W, "Each pull request's stories as a check, with what changed.", ["Agents can read"]),
400343
446389 provider: connector.provider ?? null,
447390 keywords: connector.keywords ?? [],
448391 alsoIn: connector.scopes.includes(other) ? other : null,
449− mcp: connector.mcp ?? null,
450392 };
451393 }
452394
+1−1
597597 /** One file of a project's docs, for a viewer who can read the repository; not found otherwise. */
598598 repoPage(workspace: string, viewer: User, repo: string, path: string): Promise<Result<DocRepoPage>>;
599599 /**
600− * Indexes the workspace's pages and projects' docs for agents' recall
600+ * Indexes the workspace's pages and projects' docs for search
601601 * again, in batches in the background. Workspace owners. True when a run
602602 * started (false: one is already going).
603603 */
+10−4
1818 * push, a vulnerability report), shown like a mention; the rest of the
1919 * security items are ordinary `inbox` ones.
2020 */
21−export type NotificationKind = "dm" | "mention" | "thread_reply" | "inbox" | "approval" | "security";
21+export type NotificationKind =
22+ | "dm"
23+ | "mention"
24+ | "thread_reply"
25+ | "inbox"
26+ /** Retired with g1t's own agents (a session at its cap); kept so stored items and preferences still read. */
27+ | "approval"
28+ | "security";
2229
2330 export const NOTIFICATION_KINDS: readonly NotificationKind[] = ["dm", "mention", "thread_reply", "inbox", "approval", "security"];
2431
5158 /** The thread it is in, for a reply: quick replies go there. */
5259 thread_root?: string | null;
5360 /**
54− * The chat card it is about, when that card has something to press: a
55− * session at its cap (Approve more, Stop, Open), a draft issue (File
56− * issue, Discard). The toast and the notifications panel show these
61+ * The chat card it is about, when that card has something to press,
62+ * such as a draft issue (File issue, Discard). The toast and the notifications panel show these
5763 * actions, and pressing one goes to the site's `card_action`, exactly as
5864 * on the card itself.
5965 */
+2−2
650650 /** The branch to merge into: the default branch when left out. */
651651 base?: string;
652652 /**
653− * Opened from a branch as a draft, still being worked on: it can't merge,
654− * and agents' review routines wait, until it is marked ready.
653+ * Opened from a branch as a draft, still being worked on: it can't merge
654+ * until it is marked ready.
655655 */
656656 draft?: boolean;
657657 };
+1−2
4141
4242 /**
4343 * The Workers that read D1 or call services that do, by config: what
44− * `apply` sets the placement of. The rest (pages, models, og, runner,
44+ * `apply` sets the placement of. The rest (pages, og, runner,
4545 * status, docs) serve from the edge or have no data of their own.
4646 */
4747 export const PLACED = [
5050 "apps/sudo/wrangler.jsonc",
5151 "services/actions/wrangler.jsonc",
5252 "services/billing/wrangler.jsonc",
53− "services/context/wrangler.jsonc",
5453 "services/deployments/wrangler.jsonc",
5554 "services/events/wrangler.jsonc",
5655 "services/identity/wrangler.jsonc",
+2−3
27242724 /**
27252725 * Previews of a branch that is gone. A pull request's preview comes down
27262726 * when it closes or merges; a branch's preview (built by a project's
2727− * deploy workflow, or by the preview stacks projects' dependencies once
2728− * built) has no pull request, so it comes down once its branch is
2729− * deleted, here for any whose deletion was missed. A deleted
2727+ * deploy workflow) has no pull request, so it comes down once its branch
2728+ * is deleted, here for any whose deletion was missed. A deleted
27302729 * workspace's stay paused for a restore.
27312730 */
27322731 private async takeDownUnheld(): Promise<void> {