pr_01m47d24b0e6n91zwymwxg0vpx/packages/contracts/src/work.ts

102 lines3,941 bytesCodeBlame
1import type { EventSubscriber } from "./events";
2import type { User, Viewer } from "./identity";
3import type { RepoPath } from "./repos";
4import type { Result } from "./result";
5
6export type IntentStatus = "open" | "shipped" | "withdrawn";
7
8/** A goal stated against a repo. Replaces the issue and the pull request. */
9export type Intent = {
10 id: string;
11 repoId: string;
12 /** Sequential per repo, shown as `#12`. */
13 number: number;
14 title: string;
15 /** The goal in prose: what an agent is given to work from. */
16 brief: string;
17 /** Commands that must pass for an attempt to be accepted. */
18 checks: string[];
19 status: IntentStatus;
20 author: User;
21 createdAt: number;
22 attemptCount: number;
23};
24
25export type AttemptStatus = "working" | "submitted" | "shipped" | "abandoned";
26
27/** Where the agent runs: on g1t's sandboxes, or in someone's own session. */
28export type AttemptRuntime = "hosted" | "external";
29
30/** One agent's run at an intent, in its own fork. */
31export type Attempt = {
32 id: string;
33 intentId: string;
34 repoId: string;
35 /** Sequential per intent. */
36 number: number;
37 /** A label for the agent doing the work, e.g. `claude-code`. */
38 agent: string;
39 runtime: AttemptRuntime;
40 status: AttemptStatus;
41 /** The agent's own account of what it did, set on submit. */
42 summary: string | null;
43 fork: RepoPath;
44 /** The fork's repository id. */
45 forkRepoId: string;
46 headCommit: string | null;
47 /**
48 * For a shipped attempt, what the branch pointed to before it landed.
49 * Comparing against it shows what the attempt changed.
50 */
51 landedBase: string | null;
52 startedBy: User;
53 createdAt: number;
54 updatedAt: number;
55};
56
57export type SessionEntryKind = "prompt" | "message" | "tool_call" | "tool_result" | "note";
58
59/** One step of an agent's session: the "why" behind an attempt's commits. */
60export type SessionEntry = {
61 seq: number;
62 kind: SessionEntryKind;
63 text: string;
64 /** For tool calls and results. */
65 tool: string | null;
66 /** The fork's head commit when this entry was recorded, if known. */
67 commit: string | null;
68 at: number;
69};
70
71export type NewSessionEntry = Pick<SessionEntry, "kind" | "text"> &
72 Partial<Pick<SessionEntry, "tool" | "commit" | "at">>;
73
74export type IntentDetail = { intent: Intent; attempts: Attempt[] };
75
76export type OpenIntentInput = { title: string; brief: string; checks?: string[] };
77
78export type StartAttemptInput = { agent: string; runtime: AttemptRuntime };
79
80/** Intents, attempts and sessions. */
81export interface WorkApi extends EventSubscriber {
82 openIntent(actor: User, repo: RepoPath, input: OpenIntentInput): Promise<Result<Intent>>;
83 listIntents(repo: RepoPath, viewer: Viewer, status?: IntentStatus): Promise<Result<Intent[]>>;
84 getIntent(repo: RepoPath, number: number, viewer: Viewer): Promise<Result<IntentDetail>>;
85 withdrawIntent(actor: User, intentId: string): Promise<Result<Intent>>;
86
87 /** Forks the repo for the agent and returns the attempt to push to. */
88 startAttempt(actor: User, intentId: string, input: StartAttemptInput): Promise<Result<Attempt>>;
89 getAttempt(attemptId: string, viewer: Viewer): Promise<Result<{ attempt: Attempt; intent: Intent }>>;
90 submitAttempt(actor: User, attemptId: string, summary: string): Promise<Result<Attempt>>;
91 abandonAttempt(actor: User, attemptId: string): Promise<Result<Attempt>>;
92 /**
93 * Lands the attempt on the repository's default branch and closes its
94 * intent as shipped. Only the repository's owner may ship.
95 */
96 shipAttempt(actor: User, attemptId: string): Promise<Result<Attempt>>;
97 /** Attempts in progress that the viewer started, newest first. */
98 listActiveAttempts(viewer: Viewer): Promise<{ attempt: Attempt; intent: Intent }[]>;
99
100 appendSession(actor: User, attemptId: string, entries: NewSessionEntry[]): Promise<Result<{ count: number }>>;
101 readSession(attemptId: string, viewer: Viewer, afterSeq?: number): Promise<Result<SessionEntry[]>>;
102}