| 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 | }); |