Commit

A free allowance on g1t's models, so anyone can try its agents

Until now g1t's hosted models were open only to listed workspaces, so anyone else, judges included, saw an empty forge until they pasted in a model key of their own. Every workspace now gets $1 of model cost on g1t's own models, free, out of one $40 pool, until the end of finalist judging (2026-10-22, 11:59 PM Pacific). Billing works it out from the runs it already records (`trial`: TRIAL_WORKSPACE_MICROS, TRIAL_TOTAL_MICROS, TRIAL_UNTIL; empty turns it off), and the runner counts an open allowance as hosted access, so agents, checks, the merge queue and workflow runners all start. Listed workspaces are not counted against the pool. When a workspace's share, or the pool, is spent, or the date passes, it closes, and every page that starts work says why and links to Integrations. - Mission control's checklist shows "Try g1t's agents free"; once the allowance is gone, "Connect a model" says it was used up or ended. - Integrations says how much is left and until when. - The landing page gains "Watch agents at work" (hello's merged pull requests, no sign-up) and says every new workspace gets $1 free until October 22; that line goes away on its own after. - The quickstart, usage guide (new "The free allowance" section), models, agents, Actions and overview pages and llms.txt say so. Verified as g1t-reviewer, a workspace with no provider: assigned an issue, the agent opened a pull request, its checks and its GitHub Actions workflow passed on g1t's runners, it was merged, and Integrations shows $0.80 of $1.00 left.

syntaqxcommitted Parent371a7c1Browse files
17 files+267−500/17 viewed
+6−5
140140 member of the workspace chooses to merge anyway.
141141
142142 Checks run in repositories of workspaces that can use g1t's agents: those
143−with [their own model provider](/guides/models/), and those g1t's hosted
144−models are open to. See [the preview](/guides/usage-and-billing/#the-preview).
143+with [their own model provider](/guides/models/), and those on
144+[the free allowance](/guides/usage-and-billing/#the-free-allowance) of
145+g1t's hosted models.
145146
146147 ## Review
147148
231232 - **Milestones.**
232233 - **g1t agents for everyone.** g1t can put its own agents on an issue, each
233234 in a sandbox. A workspace that connects its own model provider can use
234− them today; g1t's hosted models are open to selected workspaces until
235− payments go live.
236− See [the preview](/guides/usage-and-billing/#the-preview).
235+ them today, and until October 22 every workspace gets a free $1 of agent
236+ time on g1t's own models, no key needed.
237+ See [the free allowance](/guides/usage-and-billing/#the-free-allowance).
+6−4
118118
119119 ## Who may run workflows
120120
121−Workflows run in every workspace that uses g1t's agents: one with its own
122−[model provider](/guides/models/) connected, or one g1t has opened its
123−hosted models to. They are free while g1t is being built out. Elsewhere a
124−run is recorded with its jobs failed and the reason.
121+Workflows run in every workspace that can use g1t's agents: one with its
122+own [model provider](/guides/models/) connected, or one on
123+[the free allowance](/guides/usage-and-billing/#the-free-allowance) while
124+it lasts. They are free while g1t is being built out. Elsewhere a run is
125+recorded with its jobs failed and the reason, and the Actions page says so
126+before the first run.
125127
126128 ## From the API
127129
+3−3
224224
225225 - g1t's agents, and the sandboxes that run acceptance checks and the merge
226226 queue, work in any workspace that has
227− [its own model provider](/guides/models/). g1t's hosted models are open to
228− selected workspaces until payments go live. See
229− [the preview](/guides/usage-and-billing/#the-preview).
227+ [its own model provider](/guides/models/), and, until October 22, in any
228+ workspace on its free $1 of g1t's own models. See
229+ [the free allowance](/guides/usage-and-billing/#the-free-allowance).
230230 - An agent is given one fork and the issue. Its credential, though, is your
231231 account's for the length of the run; credentials limited to the pull
232232 request are planned.
+3−1
77
88 - **g1t's hosted models.** g1t chooses the model for each kind of work, pays
99 the provider, and charges your workspace's credit what it cost plus a
10− margin. Open to selected workspaces until payments go live, then to all.
10+ margin. Until October 22 every workspace gets $1 of it free (see
11+ [the free allowance](/guides/usage-and-billing/#the-free-allowance));
12+ once payments go live, open to all.
1113 - **Your own providers.** Connect as many as you use, then choose, for each
1214 kind of work, which provider and model it runs on. Each provider bills
1315 you directly. Open to every workspace now.
+21−4
1515 model, and a workspace pays for the runs on its repositories from credit it
1616 buys in advance. There is no subscription and no seat price.
1717
18+## The free allowance
19+
20+So anyone can try g1t's agents without a key of their own, every workspace
21+gets **$1 of model cost on g1t's own models**, free, until **October 22,
22+2026** (11:59 PM Pacific). That is roughly 10 to 25 agent runs: changes,
23+reviews and revisions. The allowance covers the GitHub Actions runners
24+too, while it lasts.
25+
26+- Mission control and **Settings → Integrations** show what is left.
27+- When it is used up, agents and workflow runs stop starting, and the
28+ pages that start them say so and link to Integrations. Connect your own
29+ model provider there and everything carries on at once.
30+- The allowance draws on one shared pool. If the pool runs out before
31+ October 22, the allowance ends for everyone early.
32+- Workspaces on their own provider never use it.
33+
1834 ## What is charged
1935
2036 | | Charged |
124140 workspace's agents, acceptance checks and merge queue work at once. Your
125141 provider bills you for the models; g1t charges nothing for now, and $0.10
126142 a run once pricing starts.
127−- **g1t's hosted models, for selected workspaces:** while payments are in
128− test mode, g1t's own models are open only to workspaces it has opened
129− them to. When payments go live, every workspace can use them, paid from
130− its credit.
143+- **g1t's hosted models:** while payments are in test mode, every
144+ workspace can use g1t's own models on
145+ [the free allowance](#the-free-allowance) ($1 each, until October 22),
146+ and a few g1t has opened them to without limit. When payments go live,
147+ every workspace can use them, paid from its credit.
131148
132149 Each workspace decides where its model spend goes. A workspace that can use
133150 neither sees a message saying so, with the way to connect its own provider.
+10−6
5757 Add **acceptance checks**, commands that must pass, to hold the work to
5858 them.
5959
60−5. **Connect a model**
60+5. **Use the free allowance, or connect a model**
6161
6262 g1t's agents, and the runners your workflows use, need a model to work
63− with. In your workspace's **Settings → Integrations**, connect your
64− Anthropic or OpenAI key, or any compatible endpoint. Your provider bills
65− you for the model; g1t itself costs nothing while it is being built out.
66− Mission control and the issue page link there until it is done. See
67− [model providers](/guides/models/).
63+ with. Until October 22, every new workspace gets **$1 of agent time on
64+ g1t's own models**, no key needed: enough for a good handful of issues.
65+ Mission control shows what is left. See
66+ [the free allowance](/guides/usage-and-billing/#the-free-allowance).
67+
68+ For more, or to choose models, connect your Anthropic or OpenAI key, or
69+ any compatible endpoint, in your workspace's **Settings →
70+ Integrations**. Your provider bills you for the model; g1t itself costs
71+ nothing while it is being built out. See [model providers](/guides/models/).
6872
6973 6. **Put an agent on it**
7074
+11−2
99 import { RepoList } from "./repo-list";
1010 import { ButtonLink, CopyLine } from "./ui";
1111
12+/** When the free allowance on g1t's models ends (billing's TRIAL_UNTIL). */
13+const TRIAL_ENDS = Date.parse("2026-10-23T06:59:59Z");
14+
1215 /** A small label above a heading, in the mono face. */
1316 function Eyebrow({ children }: { children: ReactNode }) {
1417 return <p className="font-mono text-xs tracking-[0.2em] text-merged uppercase">{children}</p>;
156159 Get started
157160 <ArrowRight size={15} />
158161 </ButtonLink>
159− <ButtonLink to="https://docs.g1t.sh/quickstart/" variant="quiet" large>
160− Read the docs
162+ <ButtonLink to="/syntaqx/hello/pulls?state=closed" variant="quiet" large>
163+ Watch agents at work
161164 </ButtonLink>
162165 </div>
166+ {Date.now() < TRIAL_ENDS && (
167+ <p className="mx-auto mt-5 max-w-xl animate-fade-up text-sm text-fg-soft/70 text-balance">
168+ Free to try: every new workspace gets $1 of agent time on g1t's models, no key needed, until
169+ October 22. Bring your own model for more.
170+ </p>
171+ )}
163172 </div>
164173 <div className="relative mx-auto max-w-5xl px-4 pb-4">
165174 <ConvergeArt className="w-full" />
+21−4
2323
2424 const REFRESH_MS = 5000;
2525
26+const dollars = (micros: number) => `$${(Math.max(0, micros) / 1_000_000).toFixed(2)}`;
27+
2628 export function meta({}: Route.MetaArgs) {
2729 return [
2830 { title: "g1t — Git for AI scale" },
7072 viewer,
7173 repos,
7274 canRunAgents: models == null || models.hosted || models.own != null,
75+ // Its free allowance on g1t's models, when that is what it runs on.
76+ trial: models?.own == null ? (models?.trial ?? null) : null,
7377 assigned: assigned.flatMap((issue) => {
7478 const repo = known.get(issue.repoId);
7579 return repo ? [{ issue, repo }] : [];
247251 }
248252
249253 export default function Home({ loaderData }: Route.ComponentProps) {
250− const { viewer, repos, active, assigned, canRunAgents } = loaderData;
254+ const { viewer, repos, active, assigned, canRunAgents, trial } = loaderData;
251255
252256 const needsYou = active.filter((item) => item.lifecycle?.stage === "needs_you");
253257 const ready = active.filter((item) => item.lifecycle?.stage === "ready");
286290 to: workspace ? `/new?workspace=${workspace}` : "/new",
287291 action: "Add",
288292 },
289− canRunAgents
293+ trial?.open
290294 ? {
295+ done: true,
296+ title: "Try g1t's agents free",
297+ about: `This workspace has ${dollars(trial.limitMicros - trial.usedMicros)} of its free ${dollars(trial.limitMicros)} left on g1t's models${trial.endsAt ? `, until ${new Date(trial.endsAt).toLocaleDateString("en-US", { month: "long", day: "numeric", timeZone: "America/Los_Angeles" })}` : ""}: assign issues and watch agents land them. Connect your own model under Integrations any time for more.`,
298+ to: workspace ? `/${workspace}/-/integrations` : null,
299+ action: "Connect",
300+ }
301+ : canRunAgents
302+ ? {
291303 // Nothing to pay while g1t is being built out.
292304 done: Boolean(shell?.free) || shell?.creditMicros == null || shell.creditMicros > 0,
293305 title: "Add agent credit",
298310 : {
299311 done: false,
300312 title: "Connect a model",
301− about:
302− "Agents need a model to think with. Connect your Anthropic or OpenAI key, or any compatible endpoint; g1t itself costs nothing while it is being built out.",
313+ about: `${
314+ trial?.reason === "used"
315+ ? "This workspace has used its free allowance on g1t's models. "
316+ : trial?.reason === "pool" || trial?.reason === "ended"
317+ ? "The free allowance on g1t's models has ended. "
318+ : ""
319+ }Agents need a model to think with. Connect your Anthropic or OpenAI key, or any compatible endpoint; g1t itself costs nothing while it is being built out.`,
303320 to: workspace ? `/${workspace}/-/integrations` : null,
304321 action: "Connect",
305322 },
+14−6
6666 repos: listed.map((repo) => `${repo.namespace}/${repo.name}`),
6767 adding: isProvider(adding) ? adding : null,
6868 hostedOpen: access.hosted,
69+ trial: access.trial,
6970 routes: routes.ok ? routes.value : [],
7071 feeMicros: account.ok ? account.value.orchestrationFeeMicros : 100_000,
7172 free: account.ok ? Boolean(account.value.status.free) : false,
175176 }
176177
177178 export default function WorkspaceIntegrations({ loaderData, actionData }: Route.ComponentProps) {
178− const { slug, role, connections, deliveries, repos: repoNames, adding, feeMicros, free, marginPercent, hostedOpen, routes } =
179+ const { slug, role, connections, deliveries, repos: repoNames, adding, feeMicros, free, marginPercent, hostedOpen, trial, routes } =
179180 loaderData;
180181 const owner = role === "owner";
181182 const busy = useNavigation().state === "submitting";
199200 <AlertTriangle size={16} />
200201 </span>
201202 <p className="text-sm text-muted">
202− <span className="font-medium text-fg">Choose how your agents reach a model.</span> g1t's hosted models
203− are not open to {slug} yet. Connect a provider of your own below and your agents start at once,
204− billed by that provider.
203+ <span className="font-medium text-fg">Choose how your agents reach a model.</span>{" "}
204+ {trial?.reason === "used"
205+ ? `${slug} has used its free allowance on g1t's models.`
206+ : trial?.reason === "pool" || trial?.reason === "ended"
207+ ? "The free allowance on g1t's models has ended."
208+ : `g1t's hosted models are not open to ${slug}.`}{" "}
209+ Connect a provider of your own below and your agents start at once, billed by that provider.
205210 </p>
206211 </div>
207212 )}
209214 {modelConnections.length === 0 && hostedOpen && (
210215 <p className="flex items-center gap-2 rounded-xl border border-line bg-surface px-4 py-3 text-sm text-muted">
211216 <CheckCircle2 size={15} className="shrink-0 text-merged" />
212− All work runs on g1t's models, charged to your credit at cost plus {marginPercent}%. Connect a provider of
213− your own to choose models and pay for them there.
217+ {trial?.open
218+ ? `All work runs on g1t's models, free: ${slug} has ${dollars(Math.max(0, trial.limitMicros - trial.usedMicros))} of its ${dollars(trial.limitMicros)} allowance left${trial.endsAt ? `, until ${new Date(trial.endsAt).toLocaleDateString("en-US", { month: "long", day: "numeric", timeZone: "America/Los_Angeles" })}` : ""}. Connect a provider of your own for more, or to choose models.`
219+ : free
220+ ? "All work runs on g1t's models, free while g1t is being built out. Connect a provider of your own to choose models and pay for them there."
221+ : `All work runs on g1t's models, charged to your credit at cost plus ${marginPercent}%. Connect a provider of your own to choose models and pay for them there.`}
214222 </p>
215223 )}
216224 {modelConnections.length > 0 && (
+3−3
154154
155155 Each workspace decides how its agents reach a model: its own provider
156156 (connected under Integrations, billed by the provider) works for any
157−workspace; g1t's hosted models are open to selected workspaces until
158−payments go live. Without either, these calls answer with a message saying
159−so.
157+workspace; until October 22, 2026, every workspace also gets $1 of free
158+agent time on g1t's hosted models, no key needed. Without either, these
159+calls answer with a message saying so.
160160
161161 - **Hand off an outcome:** `POST {repo}/plans` with `brief`: what should be
162162 true when the work is done. A planner reads the repository and proposes
+29−0
3434 pub free: bool,
3535 }
3636
37+/// `trial`: the free allowance on g1t's hosted models for a workspace that
38+/// is not otherwise open to them, so people can try g1t's agents without a
39+/// key of their own. Each workspace gets a few dollars of model cost, out
40+/// of one pool, until an end date. Returns `Trial`.
41+#[derive(Debug, Serialize, Deserialize)]
42+#[serde(rename_all = "camelCase")]
43+pub struct TrialArgs {
44+ pub workspace: String,
45+ /// Workspaces open to hosted models anyway, whose use is not counted
46+ /// against the pool.
47+ #[serde(default)]
48+ pub exempt: Vec<String>,
49+}
50+
51+#[derive(Clone, Debug, Serialize, Deserialize)]
52+#[serde(rename_all = "camelCase")]
53+pub struct Trial {
54+ /// Whether its agents may use g1t's hosted models on the allowance now.
55+ pub open: bool,
56+ /// What its runs on g1t's models have cost, in millionths of a dollar.
57+ pub used_micros: i64,
58+ pub limit_micros: i64,
59+ /// RFC 3339; when the allowance ends for everyone.
60+ pub ends_at: Option<String>,
61+ /// Why it is closed: `off` (no allowance), `ended`, `used` (this
62+ /// workspace's is spent) or `pool` (everyone's is).
63+ pub reason: Option<String>,
64+}
65+
3766 /// A workspace's standing.
3867 #[derive(Clone, Debug, Serialize, Deserialize)]
3968 #[serde(rename_all = "camelCase")]
+16−0
6767 * buys credit; each run deducts its cost plus g1t's margin; with no credit,
6868 * no agent starts.
6969 */
70+/**
71+ * The free allowance on g1t's hosted models for a workspace not otherwise
72+ * open to them: a few dollars of model cost each, out of one pool, until a
73+ * date. Mirrors `Trial` in `crates/contracts/src/billing.rs`.
74+ */
75+export type Trial = {
76+ open: boolean;
77+ usedMicros: number;
78+ limitMicros: number;
79+ endsAt: string | null;
80+ /** Why it is closed: `off`, `ended`, `used` (this workspace's) or `pool` (everyone's). */
81+ reason: "off" | "ended" | "used" | "pool" | null;
82+};
83+
7084 export interface BillingApi {
7185 status(): Promise<BillingStatus>;
7286 /** Members of the workspace only. */
88102 * opened for it. A failure, with the reason to show, when it has no credit.
89103 */
90104 canStart(workspace: string): Promise<Result<boolean>>;
105+ /** A workspace's free allowance on g1t's hosted models; `exempt` are open to them anyway. */
106+ trial(workspace: string, exempt: string[]): Promise<Trial>;
91107 /**
92108 * Asks whether a workspace may start an agent and opens the run it will be
93109 * charged for. Null when billing is off; a failure when there is no credit.
+1−0
186186 call("checkout", { actor, workspace, amountCents, returnUrl }),
187187 confirm: (workspace, viewer, session) => call("confirm", { workspace, viewer, session }),
188188 canStart: (workspace) => call("can_start", { workspace }),
189+ trial: (workspace, exempt) => call("trial", { workspace, exempt }),
189190 startRun: (run) => call("start_run", run),
190191 };
191192 }
+4−1
1+import type { Trial } from "./billing";
12 import type { User, Viewer } from "./identity";
23 import type { Result } from "./result";
34 import type { RepoPath } from "./repos";
2122 export type ModelAccess = {
2223 /** The workspace's own model connection, by name, if it has one. */
2324 own: string | null;
24− /** Whether g1t's hosted models are open to it. */
25+ /** Whether g1t's hosted models are open to it, on its free allowance or otherwise. */
2526 hosted: boolean;
27+ /** Its free allowance, when that is how it reaches g1t's hosted models; null when it needs none. */
28+ trial: Trial | null;
2629 };
2730
2831 export interface RunnerApi {
+95−0
126126 orchestration_fee_micros: i64,
127127 /// While g1t is being built out, nothing is charged (`FREE_WHILE_BUILDING`).
128128 free: bool,
129+ /// The free allowance on g1t's hosted models, when there is one.
130+ trial: Option<TrialConfig>,
131+}
132+
133+/// `TRIAL_WORKSPACE_MICROS`, `TRIAL_TOTAL_MICROS` and `TRIAL_UNTIL`.
134+struct TrialConfig {
135+ per_workspace_micros: i64,
136+ total_micros: i64,
137+ /// RFC 3339, in UTC.
138+ until: String,
139+}
140+
141+#[derive(serde::Deserialize)]
142+struct Sum {
143+ micros: Option<i64>,
129144 }
130145
131146 impl Billing {
468483 }))
469484 }
470485
486+ /// A workspace's free allowance on g1t's hosted models: what its runs
487+ /// there have cost against its share, and the pool everyone draws on.
488+ async fn trial(&self, a: TrialArgs) -> Result<Trial> {
489+ let workspace = a.workspace.to_lowercase();
490+ let Some(config) = &self.trial else {
491+ return Ok(Trial {
492+ open: false,
493+ used_micros: 0,
494+ limit_micros: 0,
495+ ends_at: None,
496+ reason: Some("off".to_owned()),
497+ });
498+ };
499+ let used = self
500+ .db
501+ .prepare(
502+ "SELECT SUM(cost_micros) AS micros FROM ledger
503+ WHERE kind = 'usage' AND COALESCE(billed_to, 'g1t') = 'g1t' AND workspace = ?",
504+ )
505+ .bind(&[workspace.as_str().into()])?
506+ .first::<Sum>(None)
507+ .await?
508+ .and_then(|sum| sum.micros)
509+ .unwrap_or_default();
510+ // Everyone's, but for the workspaces open to hosted models anyway.
511+ let exempt: Vec<String> = a.exempt.iter().map(|name| name.trim().to_lowercase()).collect();
512+ let marks = vec!["?"; exempt.len().max(1)].join(", ");
513+ let mut values: Vec<JsValue> = exempt.iter().map(|name| JsValue::from(name.as_str())).collect();
514+ if values.is_empty() {
515+ values.push(JsValue::from(""));
516+ }
517+ let pooled = self
518+ .db
519+ .prepare(format!(
520+ "SELECT SUM(cost_micros) AS micros FROM ledger
521+ WHERE kind = 'usage' AND COALESCE(billed_to, 'g1t') = 'g1t' AND workspace NOT IN ({marks})"
522+ ))
523+ .bind(&values)?
524+ .first::<Sum>(None)
525+ .await?
526+ .and_then(|sum| sum.micros)
527+ .unwrap_or_default();
528+ let reason = if rfc3339(now_ms()) >= config.until {
529+ Some("ended")
530+ } else if used >= config.per_workspace_micros {
531+ Some("used")
532+ } else if pooled >= config.total_micros {
533+ Some("pool")
534+ } else {
535+ None
536+ };
537+ Ok(Trial {
538+ open: reason.is_none(),
539+ used_micros: used,
540+ limit_micros: config.per_workspace_micros,
541+ ends_at: Some(config.until.clone()),
542+ reason: reason.map(str::to_owned),
543+ })
544+ }
545+
471546 async fn can_start(&self, a: CanStartArgs) -> Result<Outcome<bool>> {
472547 if self.stripe.is_none() {
473548 return Ok(Outcome::Ok(true));
610685 .and_then(|fee| fee.to_string().parse().ok())
611686 .unwrap_or(100_000),
612687 free: env.var("FREE_WHILE_BUILDING").is_ok_and(|v| v.to_string() == "true"),
688+ trial: {
689+ let number = |name: &str| env.var(name).ok().and_then(|v| v.to_string().parse::<i64>().ok());
690+ match (
691+ number("TRIAL_WORKSPACE_MICROS"),
692+ number("TRIAL_TOTAL_MICROS"),
693+ env.var("TRIAL_UNTIL").ok().map(|v| v.to_string()),
694+ ) {
695+ (Some(per_workspace_micros), Some(total_micros), Some(until))
696+ if per_workspace_micros > 0 && !until.is_empty() =>
697+ {
698+ Some(TrialConfig {
699+ per_workspace_micros,
700+ total_micros,
701+ until,
702+ })
703+ }
704+ _ => None,
705+ }
706+ },
613707 };
614708 match method.as_str() {
615709 "status" => reply(&billing.status()),
619713 "checkout" => reply(&billing.checkout(args(body)?).await?),
620714 "confirm" => reply(&billing.confirm(args(body)?).await?),
621715 "can_start" => reply(&billing.can_start(args(body)?).await?),
716+ "trial" => reply(&billing.trial(args(body)?).await?),
622717 "start_run" => reply(&billing.start_run(args(body)?).await?),
623718 "finish_run" => reply(&billing.finish_run(args(body)?).await?),
624719 _ => Response::error("Unknown method", 404),
+9−1
2929 // While g1t is being built out, workspaces pay nothing: runs are
3030 // recorded with what they cost, and nothing is charged. Set to
3131 // "false" when pricing starts.
32− "FREE_WHILE_BUILDING": "true"
32+ "FREE_WHILE_BUILDING": "true",
33+ // A free allowance on g1t's hosted models, so people can try g1t's
34+ // agents without a key of their own: each workspace may use this
35+ // much model cost ($1), out of one pool for everyone ($40), until
36+ // the end of finalist judging (2026-10-22, 11:59 PM Pacific). Any
37+ // of them empty turns it off.
38+ "TRIAL_WORKSPACE_MICROS": "1000000",
39+ "TRIAL_TOTAL_MICROS": "40000000",
40+ "TRIAL_UNTIL": "2026-10-23T06:59:59Z"
3341 },
3442 // Secret: STRIPE_SECRET_KEY. Without it nothing is charged and the
3543 // runner decides who may start agents some other way.
+15−10
562562 * How a workspace's agents reach a model, as the workspace decided: its
563563 * own provider, which it pays, or g1t's hosted models, which its credit
564564 * pays for. Hosted models are open to every workspace once billing takes
565− * real money, and before that to those listed. Null when it can use
566− * neither yet.
565+ * real money; before that to those listed, and to any other on its free
566+ * allowance while that lasts. Null when it can use neither yet.
567567 */
568568 async modelAccess(namespace: string): Promise<ModelAccess> {
569− if (!this.modelsReachable()) return { own: null, hosted: false };
569+ if (!this.modelsReachable()) return { own: null, hosted: false, trial: null };
570+ const billing = billingClient(this.env.BILLING);
570571 const [own, status] = await Promise.all([
571572 integrationsClient(this.env.INTEGRATIONS)
572573 .modelProvider(namespace)
573574 .catch(() => null),
574− billingClient(this.env.BILLING).status(),
575+ billing.status(),
575576 ]);
576− return {
577− own: own?.name ?? null,
578− hosted: this.previewListed(namespace) || (status.enabled && status.live),
579− };
577+ if (this.previewListed(namespace) || (status.enabled && status.live)) {
578+ return { own: own?.name ?? null, hosted: true, trial: null };
579+ }
580+ const exempt = this.env.HOSTED_AGENT_WORKSPACES.split(",")
581+ .map((name) => name.trim().toLowerCase())
582+ .filter((name) => name && name !== "*");
583+ const trial = await billing.trial(namespace, exempt).catch(() => null);
584+ return { own: own?.name ?? null, hosted: Boolean(trial?.open), trial };
580585 }
581586
582587 /**
598603 error: {
599604 code: "forbidden",
600605 message:
601− "Workflows run on g1t's runners for workspaces that use g1t's agents: connect your own model provider under Integrations, free while g1t is being built out.",
606+ "Workflows run on g1t's runners for workspaces that can use g1t's agents, and this one has no model to use: its free allowance on g1t's models is used up or over. Connect your own model provider under Integrations; g1t is free while it is being built out.",
602607 },
603608 };
604609 }
10331038 if (!(await this.workspaceAllowed(repo.namespace))) {
10341039 return fail(
10351040 "forbidden",
1036− `g1t's hosted models are not open to the ${repo.namespace} workspace yet. An owner can connect the workspace's own model provider under Integrations, and its agents start at once.`,
1041+ `The ${repo.namespace} workspace has no model for its agents: its free allowance on g1t's models is used up or over. An owner can connect the workspace's own model provider under Integrations, and its agents start at once.`,
10371042 );
10381043 }
10391044 if (!(await this.allowed(actor, repo))) {