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 | import { ArrowUp, CircleAlert, RotateCcw, Settings2, Square } from "lucide-react"; |
| 2 | import { type FormEvent, type KeyboardEvent, useCallback, useEffect, useMemo, useReducer, useRef, useState } from "react"; | |
| 3 | import { Link, useLocation } from "react-router"; | |
| 4 | ||
| 5 | import { ASK_MAX_MESSAGE_CHARS, type AskEvent, type AskSetup } from "@g1t/contracts"; | |
| The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens. | 6 | |
| Merge branch 'ask-g1t' | 7 | import { EMPTY_ASK, type Turn, askPage, askReducer, cutOff, history, readLines } from "../lib/ask"; |
| 8 | import { MessageText, type TextContext } from "./chat/text"; | |
| The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens. | 9 | import { MarkGlyph } from "./logo"; |
| Merge branch 'ask-g1t' | 10 | import { Alert, AlertDescription, AlertTitle } from "./ui/alert"; |
| The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens. | 11 | import { Badge } from "./ui/badge"; |
| 12 | import { Button } from "./ui/button"; | |
| 13 | import { Hint } from "./ui/hint"; | |
| Merge branch 'ask-g1t' | 14 | import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger } from "./ui/sheet"; |
| 15 | import { Skeleton } from "./ui/skeleton"; | |
| The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens. | 16 | |
| 17 | /** | |
| Merge branch 'ask-g1t' | 18 | * Ask g1t: a conversation with g1t about your work, from the top bar. The |
| 19 | * answers come from the AI provider and model an owner chose for the | |
| 20 | * workspace (Integrations → Your AI providers), on the workspace's own key: | |
| 21 | * g1t hosts no model for it. Each question goes to `-/ask` | |
| 22 | * (routes/workspace/ask.ts) with the page you are on, and the answer | |
| 23 | * streams back. | |
| 24 | * | |
| 25 | * The conversation lives in this browser tab (session storage), so it | |
| 26 | * survives moving between pages and reloading, and goes with the tab. | |
| 27 | * Keeping conversations on the server comes later. | |
| The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens. | 28 | */ |
| Merge branch 'ask-g1t' | 29 | |
| 30 | type Setup = AskSetup & { owner: boolean }; | |
| 31 | type SetupAnswer = { ok: boolean; value?: AskSetup; error?: { message: string }; owner: boolean }; | |
| 32 | ||
| 33 | const storageKey = (slug: string) => `g1t:ask:${slug}`; | |
| 34 | ||
| 35 | function load(slug: string) { | |
| 36 | try { | |
| 37 | const kept = sessionStorage.getItem(storageKey(slug)); | |
| 38 | return kept ? (JSON.parse(kept) as unknown) : null; | |
| 39 | } catch { | |
| 40 | return null; | |
| 41 | } | |
| 42 | } | |
| 43 | ||
| 44 | function save(slug: string, value: unknown) { | |
| 45 | try { | |
| 46 | sessionStorage.setItem(storageKey(slug), JSON.stringify(value)); | |
| 47 | } catch { | |
| 48 | // Storage full or blocked: the conversation stays for this page. | |
| 49 | } | |
| 50 | } | |
| 51 | ||
| 52 | /** | |
| 53 | * The conversation and what it can do. It belongs to the top bar's button, | |
| 54 | * not the panel, so closing the panel keeps an answer coming. | |
| 55 | */ | |
| 56 | function useAskConversation(slug: string) { | |
| 57 | const { pathname } = useLocation(); | |
| 58 | const [state, dispatch] = useReducer(askReducer, EMPTY_ASK); | |
| 59 | const [draft, setDraft] = useState(""); | |
| 60 | const controller = useRef<AbortController | null>(null); | |
| 61 | const restored = useRef(false); | |
| 62 | ||
| 63 | // The conversation kept in this tab, once; then kept as it changes. | |
| 64 | useEffect(() => { | |
| 65 | if (!restored.current) { | |
| 66 | restored.current = true; | |
| 67 | const kept = load(slug); | |
| 68 | if (kept) dispatch({ type: "restore", state: kept as never }); | |
| 69 | return; | |
| 70 | } | |
| 71 | save(slug, state); | |
| 72 | }, [slug, state]); | |
| 73 | ||
| 74 | // Leaving the workspace stops an answer still coming. | |
| 75 | useEffect(() => () => controller.current?.abort(), []); | |
| 76 | ||
| 77 | const ask = useCallback( | |
| 78 | async (question: string) => { | |
| 79 | const text = question.trim(); | |
| 80 | if (!text || state.busy) return; | |
| 81 | const messages = history(state.turns, text); | |
| 82 | const id = `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`; | |
| 83 | dispatch({ type: "ask", question: text, id }); | |
| 84 | setDraft(""); | |
| 85 | const abort = new AbortController(); | |
| 86 | controller.current = abort; | |
| 87 | let ended = false; | |
| 88 | const take = (events: AskEvent[]) => { | |
| 89 | for (const event of events) { | |
| 90 | dispatch({ type: "event", event }); | |
| 91 | if (event.type === "done" || event.type === "error") ended = true; | |
| 92 | } | |
| 93 | }; | |
| 94 | try { | |
| 95 | const response = await fetch(`/${slug}/-/ask`, { | |
| 96 | method: "POST", | |
| 97 | headers: { "content-type": "application/json", accept: "application/x-ndjson" }, | |
| 98 | body: JSON.stringify({ messages, page: askPage(pathname) }), | |
| 99 | signal: abort.signal, | |
| 100 | }); | |
| 101 | if (!response.body) throw new Error("no body"); | |
| 102 | const reader = response.body.pipeThrough(new TextDecoderStream()).getReader(); | |
| 103 | let rest = ""; | |
| 104 | for (;;) { | |
| 105 | const { value, done } = await reader.read(); | |
| 106 | if (done) break; | |
| 107 | const read = readLines(rest, value); | |
| 108 | rest = read.rest; | |
| 109 | take(read.events); | |
| 110 | } | |
| 111 | take(readLines(rest, "\n").events); | |
| 112 | if (!ended) dispatch({ type: "failed", message: "The answer broke off. Ask again." }); | |
| 113 | } catch { | |
| 114 | if (abort.signal.aborted) dispatch({ type: "stop" }); | |
| 115 | else dispatch({ type: "failed", message: "Ask could not be reached. Check your connection and ask again." }); | |
| 116 | } finally { | |
| 117 | if (controller.current === abort) controller.current = null; | |
| 118 | } | |
| 119 | }, | |
| 120 | [pathname, slug, state.busy, state.turns], | |
| 121 | ); | |
| 122 | ||
| 123 | const stop = useCallback(() => { | |
| 124 | controller.current?.abort(); | |
| 125 | dispatch({ type: "stop" }); | |
| 126 | }, []); | |
| 127 | ||
| 128 | const fresh = useCallback(() => { | |
| 129 | controller.current?.abort(); | |
| 130 | dispatch({ type: "reset" }); | |
| 131 | setDraft(""); | |
| 132 | }, []); | |
| 133 | ||
| 134 | return { state, draft, setDraft, ask, stop, fresh }; | |
| 135 | } | |
| 136 | ||
| 137 | type Conversation = ReturnType<typeof useAskConversation>; | |
| 138 | ||
| 139 | /** The top bar's Ask: the lavender pixel 1 beside its words. Opens the conversation beside the page. */ | |
| 140 | export function AskButton({ slug }: { slug: string }) { | |
| 141 | const [open, setOpen] = useState(false); | |
| 142 | const conversation = useAskConversation(slug); | |
| 143 | return ( | |
| 144 | <Sheet open={open} onOpenChange={setOpen}> | |
| 145 | <Hint label="Ask g1t about your work"> | |
| 146 | <SheetTrigger asChild> | |
| 147 | <Button variant="soft" size="bar" aria-label="Ask g1t" className="gap-2 max-md:ring-0 lg:px-3"> | |
| 148 | <MarkGlyph className="h-3.5" /> | |
| 149 | <span className="max-lg:hidden">Ask</span> | |
| 150 | </Button> | |
| 151 | </SheetTrigger> | |
| 152 | </Hint> | |
| 153 | <SheetContent side="right" className="sm:max-w-lg"> | |
| 154 | <AskPanel slug={slug} open={open} conversation={conversation} /> | |
| 155 | </SheetContent> | |
| 156 | </Sheet> | |
| 157 | ); | |
| 158 | } | |
| 159 | ||
| 160 | function AskPanel({ slug, open, conversation }: { slug: string; open: boolean; conversation: Conversation }) { | |
| 161 | const { pathname } = useLocation(); | |
| 162 | const { state, draft, setDraft, ask, stop } = conversation; | |
| 163 | const [setup, setSetup] = useState<Setup | null>(null); | |
| 164 | const [setupError, setSetupError] = useState<string | null>(null); | |
| 165 | const end = useRef<HTMLDivElement>(null); | |
| 166 | const input = useRef<HTMLTextAreaElement>(null); | |
| 167 | ||
| 168 | // What Ask answers with, each time it opens: an owner may have changed it. | |
| 169 | useEffect(() => { | |
| 170 | if (!open) return; | |
| 171 | let live = true; | |
| 172 | fetch(`/${slug}/-/ask`, { headers: { accept: "application/json" } }) | |
| 173 | .then((response) => (response.ok ? (response.json() as Promise<SetupAnswer>) : Promise.reject(new Error(String(response.status))))) | |
| 174 | .then((answer) => { | |
| 175 | if (!live) return; | |
| 176 | if (answer.ok && answer.value) { | |
| 177 | setSetup({ ...answer.value, owner: answer.owner }); | |
| 178 | setSetupError(null); | |
| 179 | } else setSetupError(answer.error?.message ?? "Ask could not be loaded."); | |
| 180 | }) | |
| 181 | .catch(() => live && setSetupError("Ask could not be loaded. Try again in a moment.")); | |
| 182 | return () => { | |
| 183 | live = false; | |
| 184 | }; | |
| 185 | }, [open, slug]); | |
| 186 | ||
| 187 | useEffect(() => { | |
| 188 | if (open) requestAnimationFrame(() => input.current?.focus()); | |
| 189 | }, [open]); | |
| 190 | ||
| 191 | // Keep the newest words in view as they come. | |
| 192 | const last = state.turns.at(-1); | |
| 193 | useEffect(() => { | |
| 194 | end.current?.scrollIntoView({ block: "end" }); | |
| 195 | }, [state.turns.length, last?.content.length]); | |
| 196 | ||
| 197 | const fresh = () => { | |
| 198 | conversation.fresh(); | |
| 199 | input.current?.focus(); | |
| 200 | }; | |
| 201 | ||
| 202 | const submit = (event: FormEvent) => { | |
| 203 | event.preventDefault(); | |
| 204 | void ask(draft); | |
| 205 | }; | |
| 206 | ||
| 207 | const onKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => { | |
| 208 | if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) { | |
| 209 | event.preventDefault(); | |
| 210 | void ask(draft); | |
| 211 | } | |
| 212 | }; | |
| 213 | ||
| 214 | const ready = setup != null && setup.problem == null; | |
| 215 | const context: TextContext = useMemo(() => ({ slug, me: null, channels: new Set(), project: null }), [slug]); | |
| 216 | const where = askPage(pathname); | |
| 217 | ||
| The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens. | 218 | return ( |
| Merge branch 'ask-g1t' | 219 | <> |
| 220 | <SheetHeader className="gap-1"> | |
| 221 | <SheetTitle className="flex items-center gap-2"> | |
| 222 | <MarkGlyph className="h-3.5" /> | |
| 223 | Ask g1t | |
| 224 | </SheetTitle> | |
| 225 | <SheetDescription className="sr-only">A conversation with g1t about your work in {slug}.</SheetDescription> | |
| 226 | <div className="flex min-h-6 flex-wrap items-center gap-2 text-xs text-muted"> | |
| 227 | {setup == null && !setupError ? ( | |
| 228 | <Skeleton className="h-4 w-40" /> | |
| 229 | ) : ready ? ( | |
| 230 | <span className="truncate"> | |
| 231 | Answers with {setup.connection_name} · <span className="font-mono">{setup.model}</span> | |
| 232 | </span> | |
| 233 | ) : ( | |
| 234 | <span>Not set up yet</span> | |
| 235 | )} | |
| 236 | <span className="ml-auto flex items-center gap-1"> | |
| 237 | {setup?.owner && ( | |
| 238 | <Hint label="Choose the provider and model"> | |
| 239 | <Button asChild variant="ghost" size="icon-xs" aria-label="Choose the provider and model"> | |
| 240 | <Link to={`/${slug}/-/integrations/models#ask`}> | |
| 241 | <Settings2 size={14} /> | |
| 242 | </Link> | |
| 243 | </Button> | |
| 244 | </Hint> | |
| 245 | )} | |
| 246 | <Button variant="ghost" size="xs" onClick={fresh} disabled={state.turns.length === 0}> | |
| 247 | <RotateCcw size={13} /> | |
| 248 | New conversation | |
| 249 | </Button> | |
| 250 | </span> | |
| 251 | </div> | |
| 252 | </SheetHeader> | |
| 253 | ||
| 254 | <div className="min-h-0 grow overflow-y-auto px-5 py-4" aria-live="polite" aria-busy={state.busy}> | |
| 255 | {setupError && ( | |
| 256 | <Alert tone="danger"> | |
| 257 | <AlertDescription className="text-danger">{setupError}</AlertDescription> | |
| 258 | </Alert> | |
| 259 | )} | |
| 260 | {setup && setup.problem && <NotSetUp slug={slug} setup={setup} />} | |
| 261 | {state.turns.length === 0 && ready && <Welcome where={where.repo ? `${where.repo.namespace}/${where.repo.name}` : null} />} | |
| 262 | <ol className="space-y-5"> | |
| 263 | {state.turns.map((turn) => ( | |
| 264 | <li key={turn.id}>{turn.role === "user" ? <Question turn={turn} /> : <Answer turn={turn} context={context} />}</li> | |
| 265 | ))} | |
| 266 | </ol> | |
| 267 | <div ref={end} /> | |
| 268 | </div> | |
| 269 | ||
| 270 | <form onSubmit={submit} className="border-t border-line px-4 py-3"> | |
| 271 | <div className="rounded-lg border border-line bg-bg focus-within:border-accent-dim focus-within:ring-2 focus-within:ring-accent/25"> | |
| 272 | <label htmlFor="ask-g1t" className="sr-only"> | |
| 273 | Ask g1t | |
| 274 | </label> | |
| 275 | <textarea | |
| 276 | id="ask-g1t" | |
| 277 | ref={input} | |
| 278 | value={draft} | |
| 279 | onChange={(event) => setDraft(event.target.value)} | |
| 280 | onKeyDown={onKeyDown} | |
| 281 | rows={2} | |
| 282 | maxLength={ASK_MAX_MESSAGE_CHARS} | |
| 283 | disabled={!ready} | |
| 284 | placeholder={ready ? "Ask about this page, a repository, or your work" : "Ask is not set up yet"} | |
| 285 | className="block max-h-48 min-h-14 w-full resize-none bg-transparent px-3 pt-2.5 pb-1 text-sm text-fg placeholder:text-faint focus:outline-none disabled:cursor-not-allowed" | |
| 286 | /> | |
| 287 | <div className="flex items-center gap-2 px-2 pb-2"> | |
| 288 | <span className="truncate pl-1 text-xs text-faint"> | |
| 289 | {where.issue ? `On issue #${where.issue}` : where.pull ? `On pull request #${where.pull}` : where.repo ? `In ${where.repo.name}` : null} | |
| 290 | </span> | |
| 291 | <span className="ml-auto" /> | |
| 292 | {state.busy ? ( | |
| 293 | <Hint label="Stop the answer"> | |
| 294 | <Button type="button" variant="outline" size="icon-sm" onClick={stop} aria-label="Stop"> | |
| 295 | <Square size={12} className="fill-current" /> | |
| 296 | </Button> | |
| 297 | </Hint> | |
| 298 | ) : ( | |
| 299 | <Hint label="Send (Enter)"> | |
| 300 | <Button type="submit" variant="accent" size="icon-sm" disabled={!ready || !draft.trim()} aria-label="Send"> | |
| 301 | <ArrowUp size={15} /> | |
| 302 | </Button> | |
| 303 | </Hint> | |
| 304 | )} | |
| 305 | </div> | |
| 306 | </div> | |
| 307 | <p className="mt-2 px-1 text-[0.6875rem] text-faint"> | |
| 308 | Answers come from your workspace's own AI provider and can be wrong. Kept in this tab only. | |
| 309 | </p> | |
| 310 | </form> | |
| 311 | </> | |
| 312 | ); | |
| 313 | } | |
| 314 | ||
| 315 | function NotSetUp({ slug, setup }: { slug: string; setup: Setup }) { | |
| 316 | return ( | |
| 317 | <Alert tone="info" className="mb-4"> | |
| 318 | <AlertTitle>Ask needs one of your workspace's AI providers</AlertTitle> | |
| 319 | <AlertDescription> | |
| 320 | <p>{setup.problem}</p> | |
| 321 | <p className="mt-2"> | |
| 322 | Ask answers with an AI provider your workspace connects, on its own key: g1t hosts no model for it.{" "} | |
| 323 | {setup.owner ? ( | |
| 324 | <Link to={`/${slug}/-/integrations/models#ask`} className="text-accent hover:underline"> | |
| 325 | Connect a provider and choose it for Ask | |
| 326 | </Link> | |
| 327 | ) : ( | |
| 328 | <>Ask an owner of {slug} to connect one and choose it for Ask.</> | |
| 329 | )} | |
| The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens. | 330 | </p> |
| Merge branch 'ask-g1t' | 331 | </AlertDescription> |
| 332 | </Alert> | |
| 333 | ); | |
| 334 | } | |
| 335 | ||
| 336 | function Welcome({ where }: { where: string | null }) { | |
| 337 | return ( | |
| 338 | <div className="mb-4 rounded-lg border border-dashed border-line px-4 py-5 text-sm text-muted"> | |
| 339 | <p className="font-medium text-fg">Ask about your work</p> | |
| 340 | <p className="mt-1"> | |
| 341 | g1t tells the model which page you are on{where ? `, here ${where}` : ""}, and the issue or pull request you are looking at, so you can ask | |
| 342 | about it directly. | |
| The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens. | 343 | </p> |
| Merge branch 'ask-g1t' | 344 | </div> |
| The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens. | 345 | ); |
| 346 | } | |
| 347 | ||
| Merge branch 'ask-g1t' | 348 | function Question({ turn }: { turn: Turn }) { |
| The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens. | 349 | return ( |
| Merge branch 'ask-g1t' | 350 | <div className="ml-auto w-fit max-w-[85%] rounded-lg bg-raised px-3 py-2 text-[0.9375rem] leading-[1.55] wrap-break-word whitespace-pre-wrap text-fg"> |
| 351 | {turn.content} | |
| 352 | </div> | |
| The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens. | 353 | ); |
| 354 | } | |
| 355 | ||
| Merge branch 'ask-g1t' | 356 | function Answer({ turn, context }: { turn: Turn; context: TextContext }) { |
| 357 | const waiting = turn.status === "streaming" && !turn.content; | |
| The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens. | 358 | return ( |
| Merge branch 'ask-g1t' | 359 | <div className="space-y-2"> |
| 360 | {waiting ? ( | |
| 361 | <div className="flex items-center gap-2 text-sm text-muted" role="status"> | |
| 362 | <MarkGlyph className="h-3 animate-pulse motion-reduce:animate-none" /> | |
| 363 | Thinking… | |
| 364 | </div> | |
| 365 | ) : ( | |
| 366 | turn.content && <MessageText body={turn.content} context={context} /> | |
| 367 | )} | |
| 368 | {turn.status === "error" && ( | |
| 369 | <Alert tone="danger" className="flex items-start gap-2"> | |
| 370 | <CircleAlert size={14} className="mt-0.5 shrink-0" /> | |
| 371 | <span>{turn.error}</span> | |
| 372 | </Alert> | |
| 373 | )} | |
| 374 | <div className="flex flex-wrap items-center gap-1.5 text-xs text-faint"> | |
| 375 | {turn.provider && ( | |
| 376 | <Badge className="px-1.5 py-0 font-normal"> | |
| 377 | {turn.provider} · <span className="font-mono">{turn.model}</span> | |
| 378 | </Badge> | |
| 379 | )} | |
| 380 | {turn.status === "stopped" && <span>Stopped</span>} | |
| 381 | {turn.status === "done" && cutOff(turn) && <span>Cut off at the length limit</span>} | |
| 382 | </div> | |
| 383 | </div> | |
| The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens. | 384 | ); |
| 385 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.