g1t/services/runner/src/delegate.ts

71 lines2,766 bytesCodeBlame
1/**
2 * Putting an agent on something in one step (`RunnerService.delegate`):
3 * what is said about the agent once its issue is open, whatever became of
4 * it. Pure, so it is tested on its own.
5 */
6import type { AgentStart, Delegated, Issue, Pull } from "@g1t/contracts";
7
8const SITE = "https://g1t.sh";
9
10/** What the workspace's agents have no model is said as. */
11export function noModelMessage(workspace: string): string {
12 return `The ${workspace} 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.`;
13}
14
15/**
16 * Where the fix for a refusal is: the workspace's billing page (its limit
17 * and per-issue caps have anchors there), its model settings, or support,
18 * which has no page to fix it on.
19 */
20export function fixUrlFor(code: string, workspace: string): string | null {
21 const slug = workspace.toLowerCase();
22 switch (code) {
23 case "not_paid":
24 case "trial_used":
25 case "oss_pool_empty":
26 return `${SITE}/${slug}/-/billing`;
27 case "limit":
28 return `${SITE}/${slug}/-/billing#limit`;
29 case "issue_cap":
30 return `${SITE}/${slug}/-/billing#caps`;
31 case "no_model":
32 return `${SITE}/${slug}/-/integrations`;
33 default:
34 return null;
35 }
36}
37
38/** The agent at work on `pull`. */
39export function started(issue: Issue, pull: Pull): Delegated {
40 return { issue, pull, agent: { status: "started", code: null, message: null, fixUrl: null } };
41}
42
43/** Every agent slot is busy: it starts by itself when one frees up. */
44export function queued(issue: Issue, message: string): Delegated {
45 return { issue, pull: null, agent: { status: "queued", code: "waiting", message, fixUrl: null } };
46}
47
48/** It did not start: why, and where to fix it. */
49export function notStarted(issue: Issue, code: string, message: string, workspace: string): Delegated {
50 const agent: AgentStart = { status: "not_started", code, message, fixUrl: fixUrlFor(code, workspace) };
51 return { issue, pull: null, agent };
52}
53
54/** The issue's title and body, tidied, and the checks with blank ones left out. */
55export function delegateInput(input: { title?: unknown; body?: unknown; checks?: unknown; labels?: unknown }): {
56 title: string;
57 body: string;
58 checks: string[];
59 labels: string[];
60} {
61 const list = (value: unknown) =>
62 (Array.isArray(value) ? value : typeof value === "string" ? value.split("\n") : [])
63 .map((item) => String(item).trim())
64 .filter(Boolean);
65 return {
66 title: typeof input.title === "string" ? input.title.trim() : "",
67 body: typeof input.body === "string" ? input.body.trim() : "",
68 checks: list(input.checks),
69 labels: list(input.labels),
70 };
71}