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.
| Merge branch 'ask-g1t' | 1 | /** |
| g1t coordinates the work and the person's own sessions do it: Ask g1t reads g1t with the same tools any agent gets, answers in one conversation shared with the @g1t direct message in Chat, and hands work to sessions in the person's own harness, whose subagents g1t records, ties to issues and pull requests, and lets see each other. | 2 | * Ask g1t (components/ask-composer): where the person is when they ask, |
| 3 | * which goes with each question to their direct message with @g1t, so g1t | |
| 4 | * is told the page and reads the issue or pull request on it. Pure, so it | |
| 5 | * is tested on its own. | |
| Merge branch 'ask-g1t' | 6 | */ |
| g1t coordinates the work and the person's own sessions do it: Ask g1t reads g1t with the same tools any agent gets, answers in one conversation shared with the @g1t direct message in Chat, and hands work to sessions in the person's own harness, whose subagents g1t records, ties to issues and pull requests, and lets see each other. | 7 | import type { AskEvent, AskPage, AskSource, AskToolStatus } from "@g1t/contracts"; |
| Merge branch 'ask-g1t' | 8 | |
| 9 | /** First path segments that are g1t's own pages, never a workspace. */ | |
| 10 | const RESERVED = new Set([ | |
| 11 | "settings", "u", "auth", "oauth", "integrations", "new", "invite", "workspaces", "device", "verify", | |
| 12 | "confirm-email", "login", "register", "logout", "forgot", "reset", "search", "status", "avatars", "docs", "-", | |
| 13 | ]); | |
| 14 | ||
| 15 | /** Where the person is: the page, and the repository, issue or pull request on it. */ | |
| 16 | export function askPage(pathname: string): AskPage { | |
| 17 | const path = pathname.split(/[?#]/)[0] ?? ""; | |
| 18 | const parts = path.split("/").filter(Boolean).map((part) => decodeURIComponent(part)); | |
| 19 | const page: AskPage = { path: path || "/" }; | |
| 20 | const [owner, name, section, number] = parts; | |
| 21 | if (!owner || !name || RESERVED.has(owner.toLowerCase()) || name === "-") return page; | |
| 22 | page.repo = { namespace: owner.toLowerCase(), name }; | |
| 23 | const numbered = number && /^\d+$/.test(number) ? Number(number) : null; | |
| 24 | if (numbered != null && section === "issues") page.issue = numbered; | |
| 25 | if (numbered != null && section === "pull") page.pull = numbered; | |
| 26 | return page; | |
| 27 | } | |
| 28 | ||
| g1t coordinates the work and the person's own sessions do it: Ask g1t reads g1t with the same tools any agent gets, answers in one conversation shared with the @g1t direct message in Chat, and hands work to sessions in the person's own harness, whose subagents g1t records, ties to issues and pull requests, and lets see each other. | 29 | /** What the composer says about where the question is asked from. */ |
| 30 | export function askWhere(page: AskPage): string | null { | |
| 31 | if (page.issue) return `On issue #${page.issue}`; | |
| 32 | if (page.pull) return `On pull request #${page.pull}`; | |
| 33 | if (page.repo) return `In ${page.repo.name}`; | |
| 34 | return null; | |
| 35 | } | |
| Merge branch 'ask-g1t' | 36 | |
| g1t coordinates the work and the person's own sessions do it: Ask g1t reads g1t with the same tools any agent gets, answers in one conversation shared with the @g1t direct message in Chat, and hands work to sessions in the person's own harness, whose subagents g1t records, ties to issues and pull requests, and lets see each other. | 37 | /** |
| 38 | * A step an answer took: one of g1t's tools, run as the person asking. Its | |
| 39 | * `tool` line comes when it starts and again, with the same id, when it ends. | |
| 40 | */ | |
| 41 | export type Step = { | |
| Merge branch 'ask-g1t' | 42 | id: string; |
| g1t coordinates the work and the person's own sessions do it: Ask g1t reads g1t with the same tools any agent gets, answers in one conversation shared with the @g1t direct message in Chat, and hands work to sessions in the person's own harness, whose subagents g1t records, ties to issues and pull requests, and lets see each other. | 43 | name: string; |
| 44 | action: string | null; | |
| 45 | label: string; | |
| 46 | status: AskToolStatus; | |
| 47 | summary: string | null; | |
| 48 | link: string | null; | |
| Merge branch 'ask-g1t' | 49 | }; |
| 50 | ||
| g1t coordinates the work and the person's own sessions do it: Ask g1t reads g1t with the same tools any agent gets, answers in one conversation shared with the @g1t direct message in Chat, and hands work to sessions in the person's own harness, whose subagents g1t records, ties to issues and pull requests, and lets see each other. | 51 | /** A session g1t proposed in an answer: the person starts it, or copies its command. */ |
| 52 | export type Proposal = Extract<AskEvent, { type: "proposal" }>; | |
| Merge branch 'ask-g1t' | 53 | |
| g1t coordinates the work and the person's own sessions do it: Ask g1t reads g1t with the same tools any agent gets, answers in one conversation shared with the @g1t direct message in Chat, and hands work to sessions in the person's own harness, whose subagents g1t records, ties to issues and pull requests, and lets see each other. | 54 | /** What a proposal asks the session to do, in a line: "Implement #12 in acme/web". */ |
| 55 | export function proposalTitle(proposal: Proposal): string { | |
| 56 | const verb = { plan: "Plan", implement: "Implement", review: "Review" }[proposal.mode] ?? "Work on"; | |
| 57 | return proposal.issue ? `${verb} #${proposal.issue} in ${proposal.repo}` : `${verb} “${proposal.title}” in ${proposal.repo}`; | |
| Merge branch 'ask-g1t' | 58 | } |
| 59 | ||
| 60 | /** | |
| g1t coordinates the work and the person's own sessions do it: Ask g1t reads g1t with the same tools any agent gets, answers in one conversation shared with the @g1t direct message in Chat, and hands work to sessions in the person's own harness, whose subagents g1t records, ties to issues and pull requests, and lets see each other. | 61 | * An answer's steps and sources from the raw events kept with it (a chat |
| 62 | * message keeps the `tool` and `sources` lines it was streamed with): | |
| 63 | * each step once, as it last was, in the order they started; the last | |
| 64 | * `sources`; each proposal once. Anything else, or anything malformed, is skipped. | |
| Merge branch 'ask-g1t' | 65 | */ |
| g1t coordinates the work and the person's own sessions do it: Ask g1t reads g1t with the same tools any agent gets, answers in one conversation shared with the @g1t direct message in Chat, and hands work to sessions in the person's own harness, whose subagents g1t records, ties to issues and pull requests, and lets see each other. | 66 | export function foldAnswer(events: unknown): { steps: Step[]; sources: AskSource[]; proposals: Proposal[] } { |
| 67 | let steps: Step[] = []; | |
| 68 | let sources: AskSource[] = []; | |
| 69 | const proposals: Proposal[] = []; | |
| 70 | for (const event of Array.isArray(events) ? events : []) { | |
| 71 | if (!event || typeof event !== "object") continue; | |
| 72 | const line = event as AskEvent; | |
| 73 | if (line.type === "tool" && typeof line.id === "string" && typeof line.label === "string") steps = withStep(steps, line); | |
| 74 | if (line.type === "sources" && Array.isArray(line.sources)) { | |
| 75 | sources = line.sources.filter((source) => source && typeof source.url === "string" && typeof source.title === "string"); | |
| 76 | } | |
| 77 | if (line.type === "proposal" && typeof line.id === "string" && typeof line.repo === "string" && (line.issue || line.title)) { | |
| 78 | if (!proposals.some((known) => known.id === line.id)) proposals.push(line); | |
| Merge branch 'ask-g1t' | 79 | } |
| 80 | } | |
| g1t coordinates the work and the person's own sessions do it: Ask g1t reads g1t with the same tools any agent gets, answers in one conversation shared with the @g1t direct message in Chat, and hands work to sessions in the person's own harness, whose subagents g1t records, ties to issues and pull requests, and lets see each other. | 81 | return { steps, sources, proposals }; |
| Merge branch 'ask-g1t' | 82 | } |
| 83 | ||
| g1t coordinates the work and the person's own sessions do it: Ask g1t reads g1t with the same tools any agent gets, answers in one conversation shared with the @g1t direct message in Chat, and hands work to sessions in the person's own harness, whose subagents g1t records, ties to issues and pull requests, and lets see each other. | 84 | /** The steps with this one in its place: a new one at the end, a known one updated. */ |
| 85 | export function withStep(steps: Step[] | undefined, event: Extract<AskEvent, { type: "tool" }>): Step[] { | |
| 86 | const step: Step = { | |
| 87 | id: event.id, | |
| 88 | name: event.name, | |
| 89 | action: event.action ?? null, | |
| 90 | label: event.label, | |
| 91 | status: event.status, | |
| 92 | summary: event.summary ?? null, | |
| 93 | link: event.link ?? null, | |
| 94 | }; | |
| 95 | const list = steps ?? []; | |
| 96 | const at = list.findIndex((known) => known.id === step.id); | |
| 97 | if (at === -1) return [...list, step]; | |
| 98 | return list.map((known, index) => (index === at ? step : known)); | |
| Merge branch 'ask-g1t' | 99 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.