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.
| The Marketplace adds what g1t doesn't do and connects what you already use: extensions (Mail, Support, CRM, Recruiting, On-call, and bridges to a helpdesk, CRM or ERP you run, all Soon), starter kits, and integrations connected by the workspace or by each person; agents aren't installed there. Agent templates live in Agents, as starting points you configure, at Agents → Templates, and the Marketplace's old agent addresses lead there. The marketplace and agents guides say how. | 1 | /** |
| 2 | * Agent templates (routes/workspace/agents/templates.tsx): the starting | |
| 3 | * points g1t provides for an agent, each a role with responsibilities, | |
| 4 | * helpers, a voice, model limits and instructions. A template isn't | |
| 5 | * installed: an owner starts a new agent from one and configures it, and | |
| 6 | * the agent is the workspace's own from then on. | |
| 7 | * | |
| 8 | * No Workers or React imports, so it can be tested under Node. | |
| 9 | */ | |
| 10 | import type { AgentTemplate, WorkspaceAgent } from "@g1t/contracts"; | |
| 11 | ||
| 12 | /** Every template, under Agents. */ | |
| 13 | export function templatesPath(slug: string): string { | |
| 14 | return `/${slug}/-/agents/templates`; | |
| 15 | } | |
| 16 | ||
| 17 | /** One template's page. */ | |
| 18 | export function templatePath(slug: string, template: string): string { | |
| 19 | return `/${slug}/-/agents/templates/${template}`; | |
| 20 | } | |
| 21 | ||
| 22 | /** Where an owner starts an agent from a template: the new-agent form, with the template chosen. */ | |
| 23 | export function startPath(slug: string, template: string): string { | |
| 24 | return `/${slug}/-/agents/new?template=${encodeURIComponent(template)}`; | |
| 25 | } | |
| 26 | ||
| 27 | /** An agent as a template's page needs it: who it is and which template it started from. */ | |
| 28 | export type StartedAgent = Pick<WorkspaceAgent, "id" | "handle" | "display_name" | "template" | "archived_at">; | |
| 29 | ||
| 30 | /** A template, with the workspace's agents that started from it. */ | |
| 31 | export type TemplateListing = { | |
| 32 | template: AgentTemplate; | |
| 33 | /** Agents started from it, not archived. */ | |
| 34 | agents: Pick<WorkspaceAgent, "id" | "handle" | "display_name">[]; | |
| 35 | }; | |
| 36 | ||
| 37 | /** | |
| 38 | * Every template, in the order the agents service gives them, with the | |
| 39 | * agents started from each. `agents` null: the agents service could not | |
| 40 | * say, so none is counted. | |
| 41 | */ | |
| 42 | export function templateListings(templates: AgentTemplate[], agents: StartedAgent[] | null): TemplateListing[] { | |
| 43 | return templates.map((template) => ({ | |
| 44 | template, | |
| 45 | agents: (agents ?? []) | |
| 46 | .filter((agent) => agent.template === template.id && !agent.archived_at) | |
| 47 | .map(({ id, handle, display_name }) => ({ id, handle, display_name })), | |
| 48 | })); | |
| 49 | } | |
| 50 | ||
| 51 | /** Listings by department, in the order the templates come, "Other" for none. */ | |
| 52 | export function byDepartment(listings: TemplateListing[]): [string, TemplateListing[]][] { | |
| 53 | const departments = new Map<string, TemplateListing[]>(); | |
| 54 | for (const listing of listings) { | |
| 55 | const key = listing.template.department || "Other"; | |
| 56 | departments.set(key, [...(departments.get(key) ?? []), listing]); | |
| 57 | } | |
| 58 | return [...departments]; | |
| 59 | } | |
| 60 | ||
| 61 | /** "Software Engineer · Engineering": a role as one line. */ | |
| 62 | export function roleLine(template: Pick<AgentTemplate, "title" | "department">): string { | |
| 63 | return template.department ? `${template.title} · ${template.department}` : template.title; | |
| 64 | } | |
| 65 | ||
| 66 | /** How a routing limit reads: "Any model the work needs", "Large models or better", "Up to large models", "Large to frontier models". */ | |
| 67 | export function routingWords(routing: Pick<AgentTemplate["routing"], "floor" | "ceiling">): string { | |
| 68 | const name = (tier: string) => tier.charAt(0).toUpperCase() + tier.slice(1); | |
| 69 | if (routing.floor && routing.ceiling) return `${name(routing.floor)} to ${routing.ceiling} models`; | |
| 70 | if (routing.floor) return `${name(routing.floor)} models or better`; | |
| 71 | if (routing.ceiling) return `Up to ${routing.ceiling} models`; | |
| 72 | return "Any model the work needs"; | |
| 73 | } |