pr_01m47d24b0e6n91zwymwxg0vpx/packages/contracts/src/work.ts

90 lines3,492 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 headCommit: string | null;
45 startedBy: User;
46 createdAt: number;
47 updatedAt: number;
48};
49
50export type SessionEntryKind = "prompt" | "message" | "tool_call" | "tool_result" | "note";
51
52/** One step of an agent's session: the "why" behind an attempt's commits. */
53export type SessionEntry = {
54 seq: number;
55 kind: SessionEntryKind;
56 text: string;
57 /** For tool calls and results. */
58 tool: string | null;
59 /** The fork's head commit when this entry was recorded, if known. */
60 commit: string | null;
61 at: number;
62};
63
64export type NewSessionEntry = Pick<SessionEntry, "kind" | "text"> &
65 Partial<Pick<SessionEntry, "tool" | "commit" | "at">>;
66
67export type IntentDetail = { intent: Intent; attempts: Attempt[] };
68
69export type OpenIntentInput = { title: string; brief: string; checks?: string[] };
70
71export type StartAttemptInput = { agent: string; runtime: AttemptRuntime };
72
73/** Intents, attempts and sessions. */
74export interface WorkApi extends EventSubscriber {
75 openIntent(actor: User, repo: RepoPath, input: OpenIntentInput): Promise<Result<Intent>>;
76 listIntents(repo: RepoPath, viewer: Viewer, status?: IntentStatus): Promise<Result<Intent[]>>;
77 getIntent(repo: RepoPath, number: number, viewer: Viewer): Promise<Result<IntentDetail>>;
78 withdrawIntent(actor: User, intentId: string): Promise<Result<Intent>>;
79
80 /** Forks the repo for the agent and returns the attempt to push to. */
81 startAttempt(actor: User, intentId: string, input: StartAttemptInput): Promise<Result<Attempt>>;
82 getAttempt(attemptId: string, viewer: Viewer): Promise<Result<{ attempt: Attempt; intent: Intent }>>;
83 submitAttempt(actor: User, attemptId: string, summary: string): Promise<Result<Attempt>>;
84 abandonAttempt(actor: User, attemptId: string): Promise<Result<Attempt>>;
85 /** Attempts in progress that the viewer started, newest first. */
86 listActiveAttempts(viewer: Viewer): Promise<{ attempt: Attempt; intent: Intent }[]>;
87
88 appendSession(actor: User, attemptId: string, entries: NewSessionEntry[]): Promise<Result<{ count: number }>>;
89 readSession(attemptId: string, viewer: Viewer, afterSeq?: number): Promise<Result<SessionEntry[]>>;
90}