Skip to content
258 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Mission control: a place to ask g1t, shown and not on yet, with the actions under it1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import {
5 type ComputeEntitlements,
6 type ComputeKind,
7 type PlanMemory,
8 ComputeGate,
9 actualMicros,
10 alwaysPasses,
11 entitlementsKeptSeconds,
12 localRefusal,
13 onBillingError,
14 readEntitlements,
15 refusalCode,
16 refusalMessage,
17 sandboxEstimateMicros,
18} from "../../../packages/contracts/src/compute.ts";
Merge branch 'main' into ask-integration19import { dockerFor, throughWorker, withPlanLimits } from "./egress.ts";
Mission control: a place to ask g1t, shown and not on yet, with the actions under it20
21const repo = { namespace: "acme", name: "web" };
22
23function ent(change: Partial<ComputeEntitlements> = {}): ComputeEntitlements {
24 return {
25 plan: "paid",
26 compute: true,
27 trialMicrosLeft: 0,
28 trialVerified: true,
29 firstMonth: false,
30 maxRunMinutes: 0,
31 ceilingMicros: 100_000_000,
32 exposureMicros: 0,
33 paused: null,
34 ...change,
35 };
36}
37
38/** A billing service that answers as told, and records what it was asked. */
39function billing(answers: {
40 entitlements?: unknown;
41 reserve?: unknown;
42 fail?: Set<string>;
43}) {
44 const asked: { method: string; body: Record<string, unknown> }[] = [];
45 const binding = {
46 async fetch(url: string, init?: RequestInit): Promise<Response> {
47 const method = url.split("/rpc/")[1];
48 asked.push({ method, body: JSON.parse(String(init?.body ?? "{}")) });
49 if (answers.fail?.has(method)) return new Response("down", { status: 503 });
50 if (method === "entitlements") return Response.json(answers.entitlements ?? null);
51 if (method === "reserve") return Response.json(answers.reserve ?? { ok: true, value: { id: "rsv_1", paidBy: "credit" } });
52 if (method === "settle") return Response.json({ ok: true, value: true });
53 if (method === "prices") return Response.json({ prices: [{ meter: "sandbox_second", costMicros: 20 }] });
54 return new Response("not found", { status: 404 });
55 },
56 };
57 return { binding, asked };
58}
59
60/** A plan memory that remembers nothing unless told. */
61function memory(plan: ComputeEntitlements["plan"] | null = null): PlanMemory {
62 return { get: async () => plan, put: async () => undefined };
63}
64
65const quiet = () => undefined;
66
67function wire(e: ComputeEntitlements) {
68 // As billing serialises it: camelCase.
69 return { workspace: "acme", ...e };
70}
71
72const request = (kind: ComputeKind, isPublic = false) => ({
73 workspace: "acme",
74 repo,
75 public: isPublic,
76 kind,
77 estimateMicros: 100_000,
78});
79
80// ---- Gating decisions ---------------------------------------------------------------
81
82test("a paid workspace's start is reserved and goes ahead", async () => {
83 const { binding, asked } = billing({ entitlements: wire(ent()) });
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.84 const admitted = await new ComputeGate(binding, memory(), quiet).admit(request("deploy"));
Mission control: a place to ask g1t, shown and not on yet, with the actions under it85 assert.equal(admitted.ok, true);
86 assert.deepEqual(admitted.ok && admitted.reservation, { id: "rsv_1", paidBy: "credit" });
87 const reserve = asked.find((call) => call.method === "reserve")!;
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.88 assert.deepEqual(reserve.body, { workspace: "acme", repo, public: false, kind: "deploy", estimateMicros: 100_000 });
Mission control: a place to ask g1t, shown and not on yet, with the actions under it89});
90
91test("billing's refusal is shown in its own words", async () => {
92 for (const code of ["not_paid", "trial_used", "limit", "oss_pool_empty"] as const) {
93 const { binding } = billing({
94 entitlements: wire(ent({ plan: "free", compute: false })),
95 reserve: { ok: false, error: { code, message: `Refused: ${code}. /acme/-/billing` } },
96 });
97 const admitted = await new ComputeGate(binding, memory(), quiet).admit(request("check", true));
98 assert.equal(admitted.ok, false, code);
99 assert.equal(!admitted.ok && admitted.code, code);
100 assert.equal(!admitted.ok && admitted.message, `Refused: ${code}. /acme/-/billing`);
101 }
102});
103
104test("a refusal without words gets g1t's, with what to do and where", async () => {
105 const { binding } = billing({
106 entitlements: wire(ent({ plan: "free", compute: false })),
107 reserve: { ok: false, error: { code: "not_paid", message: "" } },
108 });
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.109 const admitted = await new ComputeGate(binding, memory(), quiet).admit(request("deploy"));
110 assert.equal(!admitted.ok && admitted.message, refusalMessage("not_paid", "acme", "deploy"));
Billing is ready to take real money, and stays on Stripe's test key until the switch. The g1t plan is $21 a month, all-in, with $10 of usage included: the card processing fee is part of the price, and no payment carries a fee line any more (old fee rows still read as they were). Card, billing address, tax IDs, invoices and cancelling are on Stripe's billing page (Manage billing), whose configuration billing keeps in step; Checkout asks for the billing address and a tax ID; g1t's Billing page keeps usage, the spend limit, budget alerts and prepaying. Members other than owners and billing managers no longer see billing details or invoices, from the page or the API.111 assert.match(refusalMessage("not_paid", "acme", "deploy"), /^Deployments need a paid workspace\. Start the \$21 plan or try it with \$5 of free usage after a card check: \/acme\/-\/billing$/);
Mission control: a place to ask g1t, shown and not on yet, with the actions under it112});
113
114test("a paused workspace starts nothing, and billing is not asked to reserve", async () => {
115 const { binding, asked } = billing({ entitlements: wire(ent({ paused: "A spend spike is waiting for an owner." })) });
116 const admitted = await new ComputeGate(binding, memory(), quiet).admit(request("workflow"));
117 assert.equal(!admitted.ok && admitted.code, "paused");
118 assert.match(!admitted.ok ? admitted.message : "", /A spend spike is waiting for an owner/);
119 assert.equal(asked.some((call) => call.method === "reserve"), false);
120});
121
122test("an answer that holds compute back is kept seconds, one that lets it through half a minute", () => {
123 assert.equal(entitlementsKeptSeconds(ent()), 30);
124 assert.equal(entitlementsKeptSeconds(ent({ paused: "A spend spike is waiting for an owner." })), 3);
125 assert.equal(entitlementsKeptSeconds(ent({ plan: "free", compute: false })), 3);
126 // A free workspace with compute (its trial) runs, so it is kept as long.
127 assert.equal(entitlementsKeptSeconds(ent({ plan: "free", compute: true })), 30);
128});
129
130test("internal and enterprise plans pass even when billing refuses", async () => {
131 for (const plan of ["internal", "enterprise"] as const) {
132 const { binding } = billing({
133 entitlements: wire(ent({ plan })),
134 reserve: { ok: false, error: { code: "limit", message: "Over." } },
135 });
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.136 const admitted = await new ComputeGate(binding, memory(), quiet).admit(request("deploy"));
Mission control: a place to ask g1t, shown and not on yet, with the actions under it137 assert.equal(admitted.ok, true, plan);
138 }
139 assert.equal(alwaysPasses("internal") && alwaysPasses("enterprise") && !alwaysPasses("paid"), true);
140});
141
142test("a pause from billing holds for every plan: g1t's own budget and its daily breaker", async () => {
143 for (const plan of ["internal", "enterprise", "paid"] as const) {
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.144 const message = "flagon-io's monthly budget is used up ($150.00 of $150.00 this month at cost), so new runs wait.";
145 const { binding } = billing({ entitlements: wire(ent({ plan })), reserve: { ok: false, error: { code: "paused", message } } });
146 const admitted = await new ComputeGate(binding, memory(), quiet).admit(request("workflow"));
Mission control: a place to ask g1t, shown and not on yet, with the actions under it147 assert.equal(!admitted.ok && admitted.code, "paused", plan);
148 assert.equal(!admitted.ok && admitted.message, message);
149 }
150 // Billing's pause reason reads cleanly inside g1t's sentence.
151 assert.equal(
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.152 refusalMessage("paused", "acme", "workflow", "Its budget is used up."),
Mission control: a place to ask g1t, shown and not on yet, with the actions under it153 "g1t paused compute for this workspace: Its budget is used up. Contact support@g1t.sh to have it looked at.",
154 );
155});
156
157test("billing down: free workspaces fail closed, paying ones go on", async () => {
158 const down = new Set(["entitlements", "reserve"]);
159 // Nothing known about the workspace: treated as free.
160 let gate = new ComputeGate(billing({ fail: down }).binding, memory(null), quiet);
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.161 let admitted = await gate.admit(request("deploy"));
Mission control: a place to ask g1t, shown and not on yet, with the actions under it162 assert.equal(!admitted.ok && admitted.code, "billing_unavailable");
163 // Last seen paid: goes ahead, without a reservation.
164 gate = new ComputeGate(billing({ fail: down }).binding, memory("paid"), quiet);
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.165 admitted = await gate.admit(request("deploy"));
Mission control: a place to ask g1t, shown and not on yet, with the actions under it166 assert.deepEqual(admitted, { ok: true, reservation: null, entitlements: null });
167 // Entitlements answer, reserve errors: the plan decides.
168 gate = new ComputeGate(billing({ entitlements: wire(ent({ plan: "free", compute: false, trialMicrosLeft: 5_000_000 })), fail: new Set(["reserve"]) }).binding, memory(), quiet);
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.169 admitted = await gate.admit(request("deploy"));
Mission control: a place to ask g1t, shown and not on yet, with the actions under it170 assert.equal(admitted.ok, false);
171 gate = new ComputeGate(billing({ entitlements: wire(ent()), fail: new Set(["reserve"]) }).binding, memory(), quiet);
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.172 assert.equal((await gate.admit(request("deploy"))).ok, true);
Mission control: a place to ask g1t, shown and not on yet, with the actions under it173 assert.equal(onBillingError("free"), "refuse");
174 assert.equal(onBillingError(null), "refuse");
175 assert.equal(onBillingError("paid"), "allow");
176});
177
178test("an answer that is not a refusal counts as billing being down", async () => {
179 const { binding } = billing({
180 entitlements: wire(ent({ plan: "free", compute: false })),
181 reserve: { ok: false, error: { code: "invalid", message: "Unknown method" } },
182 });
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.183 const admitted = await new ComputeGate(binding, memory(), quiet).admit(request("deploy"));
Mission control: a place to ask g1t, shown and not on yet, with the actions under it184 assert.equal(!admitted.ok && admitted.code, "billing_unavailable");
185 assert.equal(refusalCode({ code: "payment_required" }), "not_paid");
186 assert.equal(refusalCode({ code: "conflict", reason: "trial_used" }), "trial_used");
187 assert.equal(refusalCode({ code: "invalid" }), null);
188});
189
190test("entitlements read in either case, and not at all without a plan", () => {
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.191 const snake = readEntitlements({ plan: "free", trial_micros_left: 5, trial_verified: true, max_run_minutes: 30, paused: "" });
Mission control: a place to ask g1t, shown and not on yet, with the actions under it192 assert.equal(snake?.trialMicrosLeft, 5);
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.193 assert.equal(snake?.maxRunMinutes, 30);
Mission control: a place to ask g1t, shown and not on yet, with the actions under it194 assert.equal(snake?.paused, null);
195 assert.equal(readEntitlements({ team: true })?.plan, undefined);
196 assert.equal(readEntitlements({ ok: true, value: { plan: "paid" } })?.plan, "paid");
197});
198
199test("before they try: what a free workspace can start, by kind and repository", () => {
200 const free = ent({ plan: "free", compute: false, trialVerified: false });
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.201 assert.equal(localRefusal(free, "deploy", false), "not_paid");
Mission control: a place to ask g1t, shown and not on yet, with the actions under it202 assert.equal(localRefusal(free, "check", true), "not_paid", "the pool needs a card check");
203 const verified = ent({ plan: "free", compute: false, trialVerified: true, trialMicrosLeft: 0 });
204 // The open-source path: public repositories' checks, workflows and queue.
205 for (const kind of ["check", "workflow", "queue"] as const) assert.equal(localRefusal(verified, kind, true), null, kind);
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.206 assert.equal(localRefusal(verified, "deploy", true), "trial_used");
Mission control: a place to ask g1t, shown and not on yet, with the actions under it207 assert.equal(localRefusal(verified, "check", false), "trial_used");
208 const trial = ent({ plan: "free", compute: true, trialVerified: true, trialMicrosLeft: 4_000_000 });
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.209 assert.equal(localRefusal(trial, "deploy", false), null);
Mission control: a place to ask g1t, shown and not on yet, with the actions under it210 assert.equal(localRefusal(ent(), "deploy", false), null);
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.211 assert.equal(localRefusal(ent({ paused: "Held." }), "deploy", false), "paused");
Mission control: a place to ask g1t, shown and not on yet, with the actions under it212});
213
214// ---- Estimates ------------------------------------------------------------------------
215
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.216test("an estimate is the sandbox for its whole time cap", () => {
Mission control: a place to ask g1t, shown and not on yet, with the actions under it217 assert.equal(sandboxEstimateMicros(60, 25), 90_000);
218 assert.equal(sandboxEstimateMicros(-5, 25), 0);
219});
220
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.221test("what work cost: its seconds at the sandbox price", () => {
Mission control: a place to ask g1t, shown and not on yet, with the actions under it222 assert.equal(actualMicros(600, 20), 12_000);
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.223 assert.equal(actualMicros(-10, 20), 0);
Mission control: a place to ask g1t, shown and not on yet, with the actions under it224});
225
226test("the gate reads the sandbox price from the price book", async () => {
227 const gate = new ComputeGate(billing({}).binding, memory(), quiet);
228 assert.equal(await gate.microsPerSecond(), 20);
229 const down = new ComputeGate(billing({ fail: new Set(["prices"]) }).binding, memory(), quiet);
Merge branch 'worktree-agent-a56293a6f836cd343'230 assert.equal(await down.microsPerSecond(), 63);
Mission control: a place to ask g1t, shown and not on yet, with the actions under it231});
232
233// ---- Caps -------------------------------------------------------------------------------
234
Merge branch 'worktree-agent-ac950b9c891220ad3'235test("the time cap is the lower of the guardrails' and the plan's", () => {
236 const guard = { policy: { minutes: {} }, minutes: 90 };
237 assert.equal(withPlanLimits(guard, { minutes: 60 }).minutes, 60);
238 assert.equal(withPlanLimits(guard, { minutes: null }).minutes, 90);
Mission control: a place to ask g1t, shown and not on yet, with the actions under it239});
240
Merge branch 'main' into ask-integration241// ---- Sandboxes' network ------------------------------------------------------------------
Mission control: a place to ask g1t, shown and not on yet, with the actions under it242
Merge branch 'main' into ask-integration243test("only a job that may be given brokered secrets passes the Worker", () => {
244 assert.equal(throughWorker({ brokered: true }, undefined), true);
245 assert.equal(throughWorker({ brokered: true }, "on"), true);
246 // Everything else goes straight out.
247 assert.equal(throughWorker({ brokered: false }, undefined), false);
248 assert.equal(throughWorker({}, undefined), false);
249 assert.equal(throughWorker(null, undefined), false);
250 // The operator's switch.
251 assert.equal(throughWorker({ brokered: true }, " OFF "), false);
Mission control: a place to ask g1t, shown and not on yet, with the actions under it252});
253
Merge branch 'main' into actions-toolkit-oidc-artifacts254test("workflow jobs get Docker unless the operator turns it off", () => {
255 assert.equal(dockerFor(undefined), "on");
256 assert.equal(dockerFor("on"), "on");
257 assert.equal(dockerFor(" OFF "), "off");
Mission control: a place to ask g1t, shown and not on yet, with the actions under it258});

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