@michael-joseph-miller/ant-bot 0.4.4 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,25 @@ All notable changes to ant-bot are recorded here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow
5
5
  [semantic versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.4.5] — 2026-08-24
8
+
9
+ ### Changed
10
+
11
+ - **The built-in Gmail connector asks for the whole mailbox.** It requested `gmail.modify`; it now
12
+ requests `https://mail.google.com/` plus `gmail.settings.basic` and `gmail.settings.sharing` —
13
+ read, compose, send, permanent deletion, filters, forwarding and vacation settings. What keeps a
14
+ Bot in check is not the size of the token: `send_message` and `create_draft` carry seeded
15
+ `require` rules, so a human is asked every time regardless. Existing sign-ins keep the narrower
16
+ token until you sign in again.
17
+
18
+ ### Fixed
19
+
20
+ - **A sign-in older than the scopes now asked for says so.** Widening what a connector requests
21
+ does nothing until the human re-consents, and until then everything reads as healthy while any
22
+ call needing the new permission is refused. `check` now compares the stored token's scopes
23
+ against what the connector asks for and reports `needs sign-in — signed in, but without N newer
24
+ permission(s)`.
25
+
7
26
  ## [0.4.4] — 2026-08-24
8
27
 
9
28
  ### Changed
package/README.md CHANGED
@@ -281,10 +281,10 @@ Current totals, as run against this checkout:
281
281
  | Package | Test files | Tests |
282
282
  |---|---|---|
283
283
  | `@antbot/contract` | 1 | 34 |
284
- | `@antbot/daemon` | 29 | 619 |
284
+ | `@antbot/daemon` | 29 | 624 |
285
285
  | `@antbot/ui` | 10 | 114 |
286
286
  | `@antbot/cli` | 8 | 142 |
287
- | **Total** | **48** | **909** |
287
+ | **Total** | **48** | **914** |
288
288
 
289
289
  ## Status / not built
290
290
 
package/dist/server.js CHANGED
@@ -2455,6 +2455,25 @@ var ConnectorAuthService = class {
2455
2455
  async write(connectorName, tokens) {
2456
2456
  await this.secrets.set(tokenSecretName(connectorName), JSON.stringify(tokens));
2457
2457
  }
2458
+ /**
2459
+ * The scopes a stored sign-in actually carries, or null when there is no sign-in.
2460
+ *
2461
+ * A token grants what was consented to when it was minted, not what the client is configured
2462
+ * to be allowed to ask for. Widening the requested list does nothing until the human signs in
2463
+ * again — and without checking, that shows up as a tool that keeps returning "insufficient
2464
+ * permission" with everything looking correctly connected.
2465
+ */
2466
+ async grantedScopes(connectorName) {
2467
+ const key = tokenSecretName(connectorName);
2468
+ const raw = (await this.secrets.resolve([key])).get(key);
2469
+ if (!raw) return null;
2470
+ try {
2471
+ const parsed = JSON.parse(raw);
2472
+ return typeof parsed.scope === "string" ? parsed.scope.split(/\s+/).filter(Boolean) : [];
2473
+ } catch {
2474
+ return null;
2475
+ }
2476
+ }
2458
2477
  async signOut(connectorName) {
2459
2478
  await this.secrets.remove(tokenSecretName(connectorName));
2460
2479
  }
@@ -2844,8 +2863,18 @@ var BUILTIN_CATALOG = {
2844
2863
  displayName: "Gmail",
2845
2864
  description: "Read, search and draft email in the signed-in Gmail account.",
2846
2865
  provider: GOOGLE,
2847
- // modify covers read + draft + send; asking for less means a second consent screen later.
2848
- scopes: ["https://www.googleapis.com/auth/gmail.modify"],
2866
+ // The full mailbox, plus settings. `mail.google.com` subsumes read/compose/send/modify and
2867
+ // adds permanent deletion; the two settings scopes are separate and cover filters, forwarding
2868
+ // and vacation. Listing narrower scopes as well would only lengthen the consent screen.
2869
+ //
2870
+ // Breadth here is not what keeps a bot in check — `mcp__gmail__send_message` and
2871
+ // `mcp__gmail__create_draft` carry seeded `require` rules, so a human is asked every time
2872
+ // whatever the token allows.
2873
+ scopes: [
2874
+ "https://mail.google.com/",
2875
+ "https://www.googleapis.com/auth/gmail.settings.basic",
2876
+ "https://www.googleapis.com/auth/gmail.settings.sharing"
2877
+ ],
2849
2878
  tools: gmailTools
2850
2879
  }
2851
2880
  };
@@ -2889,6 +2918,18 @@ var BuiltinService = class {
2889
2918
  authorized(name) {
2890
2919
  return this.auth?.isAuthorized(name) ?? false;
2891
2920
  }
2921
+ /**
2922
+ * Scopes this connector asks for that the stored sign-in does not carry. Empty when the token
2923
+ * covers everything — including when the provider granted a broader scope that subsumes one
2924
+ * asked for, which is why the comparison is by exact string and errs toward "sign in again".
2925
+ */
2926
+ async missingScopes(name) {
2927
+ const def = this.get(name);
2928
+ if (!def || !this.auth) return [];
2929
+ const granted = await this.auth.grantedScopes(name);
2930
+ if (granted === null) return [];
2931
+ return def.scopes.filter((s) => !granted.includes(s));
2932
+ }
2892
2933
  /** Constant-time compare so the bearer cannot be guessed a byte at a time. */
2893
2934
  checkBearer(header) {
2894
2935
  const given = (header ?? "").replace(/^Bearer\s+/i, "");
@@ -3097,6 +3138,15 @@ async function probeConnector(config, opts = {}) {
3097
3138
  // daemon/src/connectors/check.ts
3098
3139
  function decideCheck(signals) {
3099
3140
  if (signals.builtinProvider) {
3141
+ if (signals.builtinSignedIn && signals.builtinMissingScopes?.length) {
3142
+ return {
3143
+ status: "needs-sign-in",
3144
+ selfRegistration: signals.builtinProvider.dynamicRegistration,
3145
+ provider: signals.builtinProvider.name,
3146
+ tools: signals.probe?.tools ?? [],
3147
+ detail: `signed in, but without ${signals.builtinMissingScopes.length} newer permission(s) \u2014 sign in again to grant them`
3148
+ };
3149
+ }
3100
3150
  return signals.builtinSignedIn ? { status: "ready", tools: signals.probe?.tools ?? [], provider: signals.builtinProvider.name } : {
3101
3151
  status: "needs-sign-in",
3102
3152
  selfRegistration: signals.builtinProvider.dynamicRegistration,
@@ -3559,6 +3609,7 @@ async function createApp(opts = {}) {
3559
3609
  challenge: "none",
3560
3610
  missingSecrets: [],
3561
3611
  builtinSignedIn: app.builtin?.authorized(connector.name) ?? false,
3612
+ builtinMissingScopes: await app.builtin?.missingScopes(connector.name) ?? [],
3562
3613
  builtinProvider: def ? { name: def.provider.displayName, dynamicRegistration: def.provider.dynamicRegistration } : void 0
3563
3614
  });
3564
3615
  } else {