Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
273 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.

status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas1/**
2 * Agents at work and what they remember, kept by the work service. Mirrors
3 * `g1t_contracts::agents`.
4 */
5import type { ServiceBinding } from "./clients";
6import type { User, Viewer } from "./identity";
7import type { RepoPath } from "./repos";
8import type { Result } from "./result";
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step9import type { Confidence, Pull, PullStatus, SessionEntry } from "./work";
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas10
11/** The work a run does. checks, queue and mergecheck run commands, not a model. */
12export type RunKind =
13 | "implement"
14 | "revise"
15 | "review"
16 | "answer"
17 | "update"
18 | "plan"
19 | "checks"
20 | "queue"
21 | "mergecheck";
22
23export const RUN_KINDS: RunKind[] = ["implement", "revise", "review", "answer", "update", "plan", "checks", "queue", "mergecheck"];
24
25/** How a kind of run reads in a sentence: "is implementing", "a review run". */
26export const RUN_KIND_LABEL: Record<RunKind, string> = {
27 implement: "Implementing",
28 revise: "Revising",
29 review: "Reviewing",
30 answer: "Answering",
31 update: "Catching up",
32 plan: "Planning",
33 checks: "Checks",
34 queue: "Merge queue",
35 mergecheck: "Merge check",
36};
37
38/** Whether a message reaches the agent while it runs, at its next step. */
39export function takesMessages(kind: RunKind): boolean {
40 return kind === "implement" || kind === "revise" || kind === "answer";
41}
42
43export function isAgentKind(kind: RunKind): boolean {
44 return kind !== "checks" && kind !== "queue" && kind !== "mergecheck";
45}
46
47export type AgentRunStatus = "queued" | "running" | "succeeded" | "failed" | "stopped";
48
49export function isActiveRun(status: AgentRunStatus): boolean {
50 return status === "queued" || status === "running";
51}
52
53export type RunStep = { at: string; text: string };
54
55export type AgentRun = {
56 id: string;
57 repo: RepoPath;
58 number: number | null;
59 title: string | null;
60 kind: RunKind;
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent61 /** `g1t`, for its own agent and for a sandbox that runs commands. */
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas62 agent: string;
63 /** Members only. */
64 model: string | null;
65 status: AgentRunStatus;
66 step: string | null;
67 /** Oldest first; empty in lists. */
68 steps: RunStep[];
69 stepCount: number;
70 startedBy: string | null;
71 error: string | null;
72 /** Members only. */
73 costUsd: number | null;
74 turns: number | null;
75 /** The most it may cost, from its guardrails. Null: no cap, or not a member. */
76 budgetUsd?: number | null;
77 /** The longest it may take, in minutes, from its guardrails. */
78 timeCapMinutes?: number | null;
79 /** `budget` or `time` when g1t stopped it for reaching that cap. */
80 halted?: "budget" | "time" | "abuse" | null;
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step81 /** How sure g1t was of the change as this run left it, for a run that made or revised one. */
82 confidence?: Confidence | null;
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas83 createdAt: string;
84 startedAt: string | null;
85 finishedAt: string | null;
86 updatedAt: string;
87};
88
89export type AgentRunTicket = { runId: string; token: string };
90
91export type OpenRunInput = {
92 actor: User;
93 repo: RepoPath;
94 number?: number | null;
95 pullId?: string | null;
96 title?: string | null;
97 kind: RunKind;
98 agent?: string | null;
99 model?: string | null;
100 sandbox: string;
101 startedBy?: string | null;
102 /** Its caps, from its guardrails. */
103 budgetUsd?: number | null;
104 timeCapMinutes?: number | null;
105};
106
107export type RunFilter = {
108 repo?: RepoPath;
109 workspace?: string;
110 active?: boolean;
111 kind?: RunKind;
112 status?: AgentRunStatus;
113 number?: number;
114 limit?: number;
115};
116
117export type SessionSummary = {
118 number: number;
119 title: string;
120 status: PullStatus;
121 agent: string;
122 entries: number;
123 tools: number;
124 prompt: string | null;
125 kinds: RunKind[];
126 runs: number;
127 costUsd: number | null;
128 active: boolean;
129 startedAt: string;
130 lastAt: string;
131};
132
133export type SessionFilter = { kind?: RunKind; outcome?: PullStatus; number?: number };
134
135export type SessionView = {
136 pull: Pull;
137 entries: SessionEntry[];
138 runs: AgentRun[];
139 costUsd: number | null;
140};
141
142export type MemoryScope = "project" | "workspace";
143export type MemoryKind = "fact" | "convention" | "decision" | "gotcha";
144export const MEMORY_KINDS: MemoryKind[] = ["fact", "convention", "decision", "gotcha"];
145
146export type MemorySource = {
147 /** `person`, `agent` or `run`; for captured memory `review`, `pr` or `doc` too (see `./context`). */
148 kind: string;
149 runId: string | null;
150 repo: RepoPath | null;
151 number: number | null;
152 /** What it was captured from: `run:<id>`, `comment:<id>`, `pull:<repo id>#<n>`, `doc:<repo id>:<path>`. */
153 reference?: string | null;
154 /** What it was learned from, quoted. */
155 evidence?: string | null;
156};
157
158/** Kept memories are given to agents; candidates wait for review; dismissed ones are never suggested again. */
159export type MemoryStatus = "candidate" | "kept" | "dismissed";
160
161export type Memory = {
162 id: string;
163 scope: MemoryScope;
164 workspace: string;
165 repo: RepoPath | null;
166 text: string;
167 kind: MemoryKind;
168 source: MemorySource;
169 createdBy: string;
170 pinned: boolean;
171 createdAt: string;
172 updatedAt: string;
173 lastUsedAt: string | null;
174 /** Absent from services that predate capture: `kept`. */
175 status?: MemoryStatus;
176 /** 0 to 1: how sure its source was. Null for what people wrote. */
177 confidence?: number | null;
178 /** How many independent sources said it. */
179 seen?: number;
180};
181
182export type Memories = { project: Memory[]; workspace: Memory[] };
183
184export type NewMemory = {
185 scope: MemoryScope;
186 repo?: RepoPath | null;
187 text: string;
188 kind?: MemoryKind;
189 pinned?: boolean;
190};
191
192export type MemoryChange = { text?: string; kind?: MemoryKind; pinned?: boolean };
193
194export interface AgentsApi {
195 /** For the runner: records a sandbox it is starting. */
196 openRun(input: OpenRunInput): Promise<Result<AgentRunTicket>>;
197 /** For the runner: ends a run when its sandbox stops. Refused once it has ended. */
198 closeRun(runId: string, token: string, outcome: "succeeded" | "failed", error?: string): Promise<Result<AgentRunStatus>>;
199 /** Marks a run stopped and says where its sandbox is. Members only. */
200 stopRun(actor: User, repo: RepoPath, id: string): Promise<Result<{ run: AgentRun; sandbox: string }>>;
201 listRuns(viewer: Viewer, filter: RunFilter): Promise<Result<AgentRun[]>>;
202 getRun(viewer: Viewer, repo: RepoPath, id: string): Promise<Result<AgentRun>>;
203 listSessions(viewer: Viewer, repo: RepoPath, filter?: SessionFilter): Promise<Result<SessionSummary[]>>;
204 getSession(viewer: Viewer, repo: RepoPath, number: number): Promise<Result<SessionView>>;
205 listMemories(viewer: Viewer, workspace: string, repo?: RepoPath | null): Promise<Result<Memories>>;
206 addMemory(actor: User, workspace: string, memory: NewMemory): Promise<Result<Memory>>;
207 updateMemory(actor: User, workspace: string, id: string, change: MemoryChange): Promise<Result<Memory>>;
208 deleteMemory(actor: User, workspace: string, id: string): Promise<Result<boolean>>;
209 /**
210 * For the runner: what to tell an agent starting in `repo`, marked used.
211 * `requester` is the person the run acts for: one who is not a member of
212 * the workspace (an outside collaborator) is given the project's memory
213 * only, never the workspace's.
214 */
215 memoryContext(repo: RepoPath, budget?: number, requester?: User | null): Promise<{ text: string | null; count: number }>;
216 /** For the runner: agent runs the workspace has queued or running, for its plan's cap. */
217 activeAgents(workspace: string): Promise<number>;
218 /** For the runner: what an issue's agents have spent; `number` may be one of its pull requests. */
219 issueSpend(repo: RepoPath, number: number): Promise<Result<IssueSpend>>;
220 /** For the runner: gives back a lifecycle step it could not start for want of a slot. */
221 waitForSlot(pullId: string, reason: string): Promise<boolean>;
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent222 /** For the runner: a comment from g1t on an issue or pull request. */
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas223 agentComment(repo: RepoPath, number: number, body: string): Promise<boolean>;
224 /** For the runner: a run a person asked for, waiting for a slot. */
225 addWait(workspace: string, kind: string, payload: unknown): Promise<Result<boolean>>;
226 waitingWorkspaces(): Promise<string[]>;
227 /** The oldest run waiting in a workspace, taken out of the queue. */
228 takeWait(workspace: string): Promise<AgentWait | null>;
229 /** What a run's model cost, in dollars, with the run's token. */
230 runCost(runId: string, token: string): Promise<number | null>;
231}
232
233/** What an issue's agents have spent so far. Mirrors `IssueSpend` in agents.rs. */
234export type IssueSpend = { issue: number; spentMicros: number };
235
236/** A run waiting for one of its workspace's agent slots. */
237export type AgentWait = { id: string; workspace: string; kind: string; payload: unknown; createdAt: string };
238
239/** The agents and memory methods of the work service. */
240export function agentsClient(service: ServiceBinding): AgentsApi {
241 const call = async <T>(method: string, args: object): Promise<T> => {
242 const response = await service.fetch(`https://service/rpc/${method}`, {
243 method: "POST",
244 headers: { "content-type": "application/json" },
245 body: JSON.stringify(args),
246 });
247 if (!response.ok) throw new Error(`${method} failed with status ${response.status}`);
248 return (await response.json()) as T;
249 };
250 return {
251 openRun: (input) => call("open_run", input),
252 closeRun: (runId, token, outcome, error) => call("report_run", { runId, token, outcome, error }),
253 stopRun: (actor, repo, id) => call("stop_run", { actor, repo, id }),
254 listRuns: (viewer, filter) => call("list_runs", { viewer, ...filter }),
255 getRun: (viewer, repo, id) => call("get_run", { viewer, repo, id }),
256 listSessions: (viewer, repo, filter = {}) => call("list_sessions", { viewer, repo, ...filter }),
257 getSession: (viewer, repo, number) => call("get_session", { viewer, repo, number }),
258 listMemories: (viewer, workspace, repo) => call("list_memories", { viewer, workspace, repo: repo ?? null }),
259 addMemory: (actor, workspace, memory) =>
260 call("add_memory", { actor, workspace, repo: memory.repo ?? null, scope: memory.scope, text: memory.text, kind: memory.kind ?? "fact", pinned: memory.pinned ?? false }),
261 updateMemory: (actor, workspace, id, change) => call("update_memory", { actor, workspace, id, ...change }),
262 deleteMemory: (actor, workspace, id) => call("delete_memory", { actor, workspace, id }),
263 memoryContext: (repo, budget, requester) => call("memory_context", { repo, budget, requester: requester ?? null }),
264 activeAgents: (workspace) => call("active_agents", { workspace }),
265 issueSpend: (repo, number) => call("issue_spend", { repo, number }),
266 waitForSlot: (pullId, reason) => call("wait_for_slot", { pullId, reason }),
267 agentComment: (repo, number, body) => call("agent_comment", { repo, number, body }),
268 addWait: (workspace, kind, payload) => call("add_wait", { workspace, kind, payload }),
269 waitingWorkspaces: () => call("waiting_workspaces", {}),
270 takeWait: (workspace) => call("take_wait", { workspace }),
271 runCost: (runId, token) => call("run_cost", { runId, token }),
272 };
273}

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