Skip to content
197 linesCodeBlameRaw

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.

Each agent has an Abilities tab: g1t's built-ins, always on within the asker's access; its computer, coming; each connected integration's actions one row each, read, import, comment or resolve, with a level for each, alone, alone when the person asked for it, ask first or never, and whose connection it runs on; and MCP servers an owner adds. Reading is alone, writing inside g1t follows today's choices, anything that leaves g1t asks first, and production deploys can't go above ask. The agents service enforces every level: ask first posts a card to allow or deny and parks a session until it's answered, a refusal names its rule in the transcript and the audit log, and a missing ability posts a request to owners. The agent abilities guide says how.1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import {
5 ABILITY_LEVELS,
6 COMPUTER_ABILITIES,
7 G1T_ABILITIES,
8 abilitiesSummary,
9 allAbilities,
10 askedFor,
11 autonomyOfLevel,
12 checkMcpName,
13 checkMcpUrl,
14 connectorsWithAbilities,
15 defaultLevel,
16 findAbility,
17 integrationAbilities,
18 levelOfAutonomy,
19 maxLevel,
20 mcpAbility,
21 mcpToolName,
22 resolveAbilities,
23 withSetting,
24 withinLevel,
25} from "./abilities.ts";
26import { CONNECTORS } from "./connectors.ts";
27
28const AUTONOMY = { open_pull_requests: "alone", merge: "approval", deploy_production: "approval", edit_docs: "suggest" } as const;
29const resolve = (over: Partial<Parameters<typeof resolveAbilities>[0]> = {}) => resolveAbilities({ connectors: CONNECTORS, abilities: null, autonomy: AUTONOMY, connected: [], ...over });
30
31test("defaults by kind: reads alone, writes inside g1t when asked for, sending outside asks first, restricted never and no freer than asking", () => {
32 assert.equal(defaultLevel("read"), "alone");
33 assert.equal(defaultLevel("write"), "asked");
34 assert.equal(defaultLevel("send"), "ask");
35 assert.equal(defaultLevel("restricted"), "never");
36 assert.equal(maxLevel("restricted"), "ask");
37 assert.equal(maxLevel("send"), "alone");
38 assert.ok(withinLevel("never", "ask"));
39 assert.ok(!withinLevel("alone", "ask"));
40 assert.deepEqual(ABILITY_LEVELS, ["alone", "asked", "ask", "never"]);
41});
42
43test("every integration ability belongs to an available workspace connector with a provider, and its kind fits the connector's capabilities", () => {
44 const withAbilities = connectorsWithAbilities(CONNECTORS);
45 assert.ok(withAbilities.length >= 3, "Linear, Jira and Sentry at least");
46 for (const connector of withAbilities) {
47 assert.equal(connector.status, "available", `${connector.id} is connectable today`);
48 assert.ok(connector.scopes.includes("workspace"), `${connector.id} connects for a workspace`);
49 assert.ok(connector.provider, `${connector.id} has a provider the integrations service knows`);
50 const defs = integrationAbilities(connector.id);
51 const caps = connector.capabilities ?? [];
52 if (defs.some((d) => d.kind === "read")) assert.ok(caps.includes("Agents can read"), `${connector.id} says agents can read`);
53 if (defs.some((d) => d.kind === "send")) assert.ok(caps.includes("Writes back"), `${connector.id} says it writes back`);
54 for (const def of defs) {
55 assert.equal(def.id, `integration:${connector.id}:${def.id.split(":")[2]}`);
56 assert.equal(def.tools.length, 1, "one tool per integration ability");
57 }
58 }
59 assert.deepEqual(integrationAbilities("datadog"), [], "Datadog opens issues by itself: nothing for an agent to call");
60});
61
62test("g1t's own are always on and the four autonomy ones keep their choices as levels", () => {
63 const sections = resolve();
64 const g1t = sections.find((s) => s.group === "g1t")!;
65 const artifacts = g1t.sources[0]!.abilities.find((a) => a.id === "g1t:artifacts")!;
66 assert.equal(artifacts.level, "alone");
67 assert.equal(artifacts.can_change, false);
68 const merge = g1t.sources[0]!.abilities.find((a) => a.id === "g1t:merge")!;
69 assert.equal(merge.level, "ask", "approval reads as Ask first");
70 assert.deepEqual(merge.choices, ["alone", "ask", "never"]);
71 const deploy = g1t.sources[0]!.abilities.find((a) => a.id === "g1t:deploy")!;
72 assert.equal(deploy.kind, "restricted");
73 assert.ok(!deploy.choices.includes("alone"), "production deploys never go alone");
74 assert.equal(levelOfAutonomy("suggest"), "ask");
75 assert.equal(autonomyOfLevel("edit_docs", "ask"), "suggest");
76 assert.equal(autonomyOfLevel("merge", "ask"), "approval");
77 assert.equal(autonomyOfLevel("merge", "never"), "never");
78 assert.equal(G1T_ABILITIES.filter((d) => d.autonomy).length, 4);
79 const computer = sections.find((s) => s.group === "computer")!;
CI is green again: the agent computer landed with its five operations in the Rust scope table but not the TypeScript mirror, and with its shell and files switched on while the ability tests still expected every computer ability to be coming. The mirror lists them under agents:run, and the tests say what ships: commands run when asked for and files change on its own, while the browser and web search are still coming.80 const shell = computer.sources[0]!.abilities.find((a) => a.id === "computer:shell")!;
81 assert.equal(shell.status, "ready");
82 assert.equal(shell.level, "asked", "commands run when asked for, until the person says otherwise");
83 assert.deepEqual(shell.choices, ["alone", "asked", "ask", "never"]);
84 const files = computer.sources[0]!.abilities.find((a) => a.id === "computer:files")!;
85 assert.equal(files.level, "alone");
86 assert.ok(files.can_change);
87 const coming = computer.sources[0]!.abilities.filter((a) => a.id === "computer:browser" || a.id === "computer:web");
88 assert.equal(coming.length, 2);
89 assert.ok(coming.every((a) => a.status === "coming" && a.choices.length === 0 && !a.can_change), "the browser and web search are still coming");
Each agent has an Abilities tab: g1t's built-ins, always on within the asker's access; its computer, coming; each connected integration's actions one row each, read, import, comment or resolve, with a level for each, alone, alone when the person asked for it, ask first or never, and whose connection it runs on; and MCP servers an owner adds. Reading is alone, writing inside g1t follows today's choices, anything that leaves g1t asks first, and production deploys can't go above ask. The agents service enforces every level: ask first posts a card to allow or deny and parks a session until it's answered, a refusal names its rule in the transcript and the audit log, and a missing ability posts a request to owners. The agent abilities guide says how.90 assert.equal(COMPUTER_ABILITIES.length, 4);
91});
92
93test("a connected integration lists its rows at their defaults; one that isn't is listed, but nothing there can be changed", () => {
94 const sections = resolve({ connected: ["linear"] });
95 const integrations = sections.find((s) => s.group === "integration")!;
96 const linear = integrations.sources.find((s) => s.id === "linear")!;
97 assert.equal(linear.connected, true);
98 assert.deepEqual(
99 linear.abilities.map((a) => [a.id, a.level, a.credentials, a.can_change]),
100 [
101 ["integration:linear:read", "alone", "workspace", true],
102 ["integration:linear:import", "asked", "workspace", true],
103 ["integration:linear:comment", "ask", "workspace", true],
104 ],
105 );
106 assert.equal(linear.abilities[0]!.personal_available, false, "Linear's personal side is still coming");
107 const jira = integrations.sources.find((s) => s.id === "jira")!;
108 assert.equal(jira.connected, false);
109 assert.ok(jira.abilities.every((a) => !a.can_change));
110 assert.equal(integrations.sources[0]!.id, "linear", "connected first");
111 assert.ok(!integrations.sources.some((s) => s.id === "webhooks" || s.id === "anthropic"), "nothing to call, not connected: not listed");
112 const withDatadog = resolve({ connected: ["datadog"] }).find((s) => s.group === "integration")!;
113 const datadog = withDatadog.sources.find((s) => s.id === "datadog")!;
114 assert.equal(datadog.abilities.length, 0);
115 assert.match(datadog.note ?? "", /Opens issues by itself/);
116});
117
118test("a setting moves a level within its kind's limit, and is dropped when it is the default again", () => {
119 let abilities = withSetting(null, "integration:linear:comment", { level: "alone" });
120 assert.deepEqual(abilities.settings, { "integration:linear:comment": { level: "alone" } });
121 const sections = resolve({ connected: ["linear"], abilities });
122 assert.equal(findAbility(sections, "integration:linear:comment")!.ability.level, "alone");
123 abilities = withSetting(abilities, "integration:linear:comment", { credentials: "asker" });
124 assert.deepEqual(abilities.settings["integration:linear:comment"], { level: "alone", credentials: "asker" });
125 abilities = withSetting(abilities, "integration:linear:comment", { level: null, credentials: null });
126 assert.deepEqual(abilities.settings, {}, "nothing kept once everything is the default");
127 // A restricted ability set freer than Ask is held at Ask.
128 const held = resolveAbilities({ connectors: CONNECTORS, abilities: null, autonomy: { ...AUTONOMY, deploy_production: "approval" }, connected: [] });
129 assert.equal(findAbility(held, "g1t:deploy")!.ability.max, "ask");
130});
131
132test("a personal agent runs on the asker's connections unless an owner chose the workspace's", () => {
133 const sections = resolve({ connected: ["linear"], personal: true });
134 assert.equal(findAbility(sections, "integration:linear:read")!.ability.credentials, "asker");
135 const chosen = resolve({ connected: ["linear"], personal: true, abilities: withSetting(null, "integration:linear:read", { credentials: "workspace" }) });
136 assert.equal(findAbility(chosen, "integration:linear:read")!.ability.credentials, "workspace");
137});
138
139test("MCP servers: each listed tool is a row, a write unless the server says it reads, offered under <server>__<tool>", () => {
140 const server = {
141 id: "mcp_1",
142 name: "weather",
143 url: "https://mcp.example.com/",
144 tools: [
145 { name: "get_forecast", description: "Today's forecast", kind: "read" as const, input_schema: { type: "object", properties: {} } },
146 { name: "set_alert", description: "", kind: "write" as const, input_schema: { type: "object", properties: {} } },
147 ],
148 added_by: "ana",
149 added_at: "2026-10-10T00:00:00.000Z",
150 checked_at: null,
151 problem: null,
152 };
153 const sections = resolve({ abilities: { settings: {}, mcp_servers: [server] } });
154 const mcp = sections.find((s) => s.group === "mcp")!;
155 assert.equal(mcp.sources.length, 1);
156 const [read, write] = mcp.sources[0]!.abilities;
157 assert.equal(read!.level, "alone");
158 assert.equal(write!.level, "ask", "a write outside g1t asks first");
159 assert.equal(write!.kind, "send");
160 assert.deepEqual(read!.tools, ["weather__get_forecast"]);
161 assert.equal(mcpToolName("My Server", "do.thing"), "my_server__do_thing");
162 assert.equal(mcpAbility(server, server.tools[1]!).id, "mcp:mcp_1:set_alert");
163});
164
165test("an MCP server's address is HTTPS on a public host, never local, an address or g1t's own", () => {
166 assert.equal(checkMcpUrl("https://mcp.example.com/sse").ok, true);
167 assert.equal(checkMcpUrl("http://mcp.example.com/").ok, false);
168 assert.equal(checkMcpUrl("https://localhost:3000/").ok, false);
169 assert.equal(checkMcpUrl("https://10.0.0.5/").ok, false);
170 assert.equal(checkMcpUrl("https://[::1]/").ok, false);
171 assert.equal(checkMcpUrl("https://mcp.internal/").ok, false);
172 assert.equal(checkMcpUrl("https://api.g1t.sh/mcp").ok, false);
173 assert.equal(checkMcpUrl("https://user:pw@mcp.example.com/").ok, false);
174 assert.equal(checkMcpUrl("not a url").ok, false);
175 assert.equal(checkMcpName("Weather").ok, true);
176 assert.equal(checkMcpName("a").ok, false);
177 assert.equal(checkMcpName("bad name").ok, false);
178});
179
180test("alone when asked for it: the item's key or the ability's name in what the person said", () => {
181 assert.ok(askedFor("Can you comment on ENG-42 with the test plan?", ["ENG-42", "comment"]));
182 assert.ok(askedFor("what's eng-42 about", ["ENG-42"]), "any case");
183 assert.ok(!askedFor("Summarise the thread", ["ENG-42", "import"]));
184 assert.ok(!askedFor("", ["ENG-42"]));
185});
186
187test("the summary says what it does alone, when asked, after asking, and never", () => {
188 const abilities = withSetting(withSetting(null, "integration:linear:comment", { level: "never" }), "integration:sentry:resolve", { level: "alone" });
189 const sections = resolve({ connected: ["linear", "sentry"], abilities });
190 const summary = abilitiesSummary(sections);
191 assert.equal(
192 summary,
CI is green again: the agent computer landed with its five operations in the Rust scope table but not the TypeScript mirror, and with its shell and files switched on while the ability tests still expected every computer ability to be coming. The mirror lists them under agents:run, and the tests say what ships: commands run when asked for and files change on its own, while the browser and web search are still coming.193 "Can open pull requests, change files on its computer, read issues in Linear, read issues in Sentry and resolve issues in Sentry on its own; runs commands on its computer, imports issues in Linear and imports issues in Sentry when asked for it; asks before merging, deploying to production, editing docs and commenting in Sentry; never comments in Linear.",
Each agent has an Abilities tab: g1t's built-ins, always on within the asker's access; its computer, coming; each connected integration's actions one row each, read, import, comment or resolve, with a level for each, alone, alone when the person asked for it, ask first or never, and whose connection it runs on; and MCP servers an owner adds. Reading is alone, writing inside g1t follows today's choices, anything that leaves g1t asks first, and production deploys can't go above ask. The agents service enforces every level: ask first posts a card to allow or deny and parks a session until it's answered, a refusal names its rule in the transcript and the audit log, and a missing ability posts a request to owners. The agent abilities guide says how.194 );
195 assert.equal(allAbilities(sections).filter((a) => a.group === "integration" && a.can_change).length, 7);
CI is green again: the agent computer landed with its five operations in the Rust scope table but not the TypeScript mirror, and with its shell and files switched on while the ability tests still expected every computer ability to be coming. The mirror lists them under agents:run, and the tests say what ships: commands run when asked for and files change on its own, while the browser and web search are still coming.196 assert.match(abilitiesSummary(resolve()), /open pull requests and change files on its computer on its own/);
Each agent has an Abilities tab: g1t's built-ins, always on within the asker's access; its computer, coming; each connected integration's actions one row each, read, import, comment or resolve, with a level for each, alone, alone when the person asked for it, ask first or never, and whose connection it runs on; and MCP servers an owner adds. Reading is alone, writing inside g1t follows today's choices, anything that leaves g1t asks first, and production deploys can't go above ask. The agents service enforces every level: ask first posts a card to allow or deny and parks a session until it's answered, a refusal names its rule in the transcript and the audit log, and a missing ability posts a request to owners. The agent abilities guide says how.197});