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 is where a workspace finds what to add, reached from Apps: the agent catalog of roles with who already does each job and Add to workspace; every integration with whether it's connected; g1t's own extensions, listed as Soon; and requests, where members ask and owners add or turn down, each told by a notification. Underneath, an extension is a manifest (publisher and tier, source and version from a tag, scopes, the domains its data goes to, its page on g1tusercontent.com, no price) and an install pins its version with an off switch, a budget and the audit log. Apps lists only what was added from the Marketplace, with pins; g1t's own pages stay in their sidebars, and AI Gateway joins Workspace's. The marketplace guide says how. | 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; | |
| 3 | ||
| 4 | import type { AgentTemplate, InstallRequest } from "@g1t/contracts"; | |
| 5 | import { connectorsFor } from "@g1t/contracts/connectors"; | |
| 6 | ||
| 7 | import { FIRST_PARTY_EXTENSIONS } from "@g1t/contracts/marketplace"; | |
| 8 | ||
| 9 | import { addPath, agentListings, comingIntegrations, extensionListings, integrationListings, integrationMatches, marketplaceForm, routingWords } from "./marketplace.ts"; | |
| 10 | import { modeOf } from "./workspace-nav.ts"; | |
| 11 | ||
| 12 | const template = (id: string, title: string): AgentTemplate => ({ | |
| 13 | id, | |
| 14 | display_name: title.split(" ")[0]!, | |
| 15 | handle: id, | |
| 16 | name_ideas: [], | |
| 17 | role: title, | |
| 18 | title, | |
| 19 | department: "Engineering", | |
| 20 | responsibilities: ["Do the work"], | |
| 21 | subagents: [], | |
| 22 | instructions: "", | |
| 23 | personality_preset: "crisp", | |
| 24 | routing: { floor: null, ceiling: null, providers: [], pinned: null }, | |
| 25 | }); | |
| 26 | ||
| 27 | const request = (listing: string, by: string, status: InstallRequest["status"] = "open"): InstallRequest => ({ | |
| 28 | id: `ins_${listing}_${by}`, | |
| 29 | listing, | |
| 30 | kind: listing.startsWith("agent:") ? "agent" : "integration", | |
| 31 | name: listing, | |
| 32 | note: null, | |
| 33 | requested_by: by, | |
| 34 | requested_at: "2026-10-10T12:00:00Z", | |
| 35 | status, | |
| 36 | resolved_by: null, | |
| 37 | resolved_at: null, | |
| 38 | }); | |
| 39 | ||
| 40 | test("a role counts the agents hired into it, not archived ones", () => { | |
| 41 | const [eng, qa] = agentListings( | |
| 42 | [template("engineering", "Software Engineer"), template("qa", "QA Engineer")], | |
| 43 | [ | |
| 44 | { id: "a1", handle: "otto", display_name: "Otto", template: "engineering", archived_at: null }, | |
| 45 | { id: "a2", handle: "bolt", display_name: "Bolt", template: "engineering", archived_at: "2026-10-01T00:00:00Z" }, | |
| 46 | { id: "a3", handle: "g1t", display_name: "g1t", template: null, archived_at: null }, | |
| 47 | ], | |
| 48 | [request("agent:qa", "ana"), request("agent:qa", "bo"), request("agent:engineering", "ana", "declined")], | |
| 49 | "Ana", | |
| 50 | ); | |
| 51 | assert.deepEqual(eng!.hired.map((a) => a.handle), ["otto"]); | |
| 52 | assert.equal(eng!.requested, false, "a turned-down request isn't waiting"); | |
| 53 | assert.equal(qa!.hired.length, 0); | |
| 54 | assert.equal(qa!.requested, true, "usernames match whatever their case"); | |
| 55 | assert.equal(qa!.waiting, 2); | |
| 56 | }); | |
| 57 | ||
| 58 | test("without the agents service, nobody counts as hired", () => { | |
| 59 | const [eng] = agentListings([template("engineering", "Software Engineer")], null, [], "ana"); | |
| 60 | assert.deepEqual(eng!.hired, []); | |
| 61 | }); | |
| 62 | ||
| 63 | test("only integrations a workspace can connect today are listed, connected first", () => { | |
| 64 | const views = connectorsFor("workspace"); | |
| 65 | const listings = integrationListings(views, { sentry: { detail: "acme", problem: null, manage: "/acme/-/integrations/alerts" } }, [], "ana", "acme"); | |
| 66 | assert.ok(listings.every((l) => l.view.status === "available")); | |
| 67 | assert.equal(listings[0]!.view.id, "sentry"); | |
| 68 | assert.equal(listings[0]!.href, "/acme/-/integrations/alerts", "managed where it is managed"); | |
| 69 | const linear = listings.find((l) => l.view.id === "linear")!; | |
| 70 | assert.equal(linear.href, "/acme/-/integrations/trackers?add=linear#add", "connected on its setup page"); | |
| 71 | assert.ok(!listings.some((l) => l.view.id === "slack")); | |
| 72 | assert.ok(comingIntegrations(views).some((v) => v.id === "slack")); | |
| 73 | assert.ok(integrationMatches(linear, "tracker")); | |
| 74 | assert.ok(!integrationMatches(linear, "deploys")); | |
| 75 | }); | |
| 76 | ||
| 77 | test("an owner adds what a request asks for where it is added", () => { | |
| 78 | const views = connectorsFor("workspace"); | |
| 79 | assert.equal(addPath({ listing: "agent:qa" }, "acme", views), "/acme/-/agents/new?template=qa"); | |
| 80 | assert.equal(addPath({ listing: "integration:jira" }, "acme", views), "/acme/-/integrations/trackers?add=jira#add"); | |
| 81 | assert.equal(addPath({ listing: "nonsense" }, "acme", views), null); | |
| 82 | }); | |
| 83 | ||
| 84 | test("the Marketplace's forms are read strictly", () => { | |
| 85 | const form = (fields: Record<string, string>) => { | |
| 86 | const data = new FormData(); | |
| 87 | for (const [k, v] of Object.entries(fields)) data.set(k, v); | |
| 88 | return data; | |
| 89 | }; | |
| 90 | assert.deepEqual(marketplaceForm(form({ intent: "request", listing: "agent:qa", note: " flaky " })), { intent: "request", listing: "agent:qa", note: "flaky" }); | |
| 91 | assert.deepEqual(marketplaceForm(form({ intent: "request", listing: "agent:qa" })), { intent: "request", listing: "agent:qa", note: null }); | |
| 92 | assert.equal(marketplaceForm(form({ intent: "request", listing: "app:qa" })), null); | |
| 93 | assert.deepEqual(marketplaceForm(form({ intent: "resolve", id: "ins_1", status: "declined" })), { intent: "resolve", id: "ins_1", status: "declined" }); | |
| 94 | assert.equal(marketplaceForm(form({ intent: "resolve", id: "ins_1", status: "open" })), null); | |
| 95 | assert.equal(marketplaceForm(form({ intent: "install" })), null); | |
| 96 | }); | |
| 97 | ||
| 98 | test("extensions list published ones first, each with its install", () => { | |
| 99 | const published = { ...FIRST_PARTY_EXTENSIONS[1]!, id: "standup", status: "available" as const, source: { repo: "flagon-io/standup", tag: "v1.0.0" }, version: "1.0.0" }; | |
| 100 | const install = { id: "ins_1", listing: "extension:standup", version: "1.0.0", plan: "free", installed_by: "chase", installed_at: "2026-10-10T00:00:00Z", enabled: true, disabled_by: null, disabled_at: null, budget_monthly_micros: null }; | |
| 101 | const listings = extensionListings([...FIRST_PARTY_EXTENSIONS, published], [install], [request("extension:support", "ana")], "ana"); | |
| 102 | assert.equal(listings[0]!.manifest.id, "standup"); | |
| 103 | assert.equal(listings[0]!.install?.version, "1.0.0"); | |
| 104 | assert.equal(listings.find((l) => l.manifest.id === "support")!.requested, true); | |
| 105 | assert.equal(listings.find((l) => l.manifest.id === "mail")!.install, null); | |
| 106 | }); | |
| 107 | ||
| 108 | test("coming integrations include the ones each person connects, once", () => { | |
| 109 | const workspace = connectorsFor("workspace"); | |
| 110 | const personal = connectorsFor("personal"); | |
| 111 | const coming = comingIntegrations(workspace, personal).map((v) => v.id); | |
| 112 | assert.ok(coming.includes("slack")); | |
| 113 | assert.ok(coming.includes("gmail"), "Gmail is each person's"); | |
| 114 | assert.ok(coming.includes("google-calendar")); | |
| 115 | assert.equal(new Set(coming).size, coming.length); | |
| 116 | }); | |
| 117 | ||
| 118 | test("installing, switching and removing an extension are read from forms", () => { | |
| 119 | const form = (fields: Record<string, string>) => { | |
| 120 | const data = new FormData(); | |
| 121 | for (const [k, v] of Object.entries(fields)) data.set(k, v); | |
| 122 | return data; | |
| 123 | }; | |
| 124 | assert.deepEqual(marketplaceForm(form({ intent: "install", extension: "support" })), { intent: "install", extension: "support" }); | |
| 125 | assert.deepEqual(marketplaceForm(form({ intent: "switch", listing: "extension:support", enabled: "off" })), { intent: "switch", listing: "extension:support", enabled: false }); | |
| 126 | assert.equal(marketplaceForm(form({ intent: "switch", listing: "integration:sentry", enabled: "off" })), null); | |
| 127 | assert.deepEqual(marketplaceForm(form({ intent: "uninstall", listing: "extension:support" })), { intent: "uninstall", listing: "extension:support" }); | |
| 128 | assert.equal(addPath({ listing: "extension:mail" }, "acme", []), "/acme/-/marketplace/extensions/mail"); | |
| 129 | }); | |
| 130 | ||
| 131 | test("routing limits read as words", () => { | |
| 132 | assert.equal(routingWords({ floor: null, ceiling: null }), "Any model the work needs"); | |
| 133 | assert.equal(routingWords({ floor: "large", ceiling: null }), "Large models or better"); | |
| 134 | assert.equal(routingWords({ floor: null, ceiling: "large" }), "Up to large models"); | |
| 135 | }); | |
| 136 | ||
| 137 | test("the Marketplace is part of Apps, not a mode of its own", () => { | |
| 138 | assert.equal(modeOf("/acme/-/marketplace", "acme"), "apps"); | |
| 139 | assert.equal(modeOf("/acme/-/marketplace/agents/qa", "acme"), "apps"); | |
| 140 | }); |