toolroll 0.9.40 → 0.9.41

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
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.41 — 2026-10-06
4
+
5
+ - **A read-only PostHog connection works for research.** PostHog adds
6
+ `introspection` to every connection it grants, asked for or not; it only
7
+ lets a connection be checked and reaches no data. Toolroll counted it as
8
+ write access, so research skipped PostHog even after **Reconnect read-only**.
9
+ It now counts as read-only alongside the reads it came with. Nothing is
10
+ requested differently, and a connection that already reconnected read-only
11
+ works without signing in again.
12
+
3
13
  ## 0.9.40 — 2026-10-06
4
14
 
5
15
  - **Confirming a proposal checks the same things the card does, everywhere.**
@@ -64,7 +64,6 @@ export declare function connectionsOf(store: Store, repo: string, home?: string)
64
64
  */
65
65
  export declare function readsOnly(action: string, subjects: readonly string[]): boolean;
66
66
  export declare const readScope: (scope: string) => boolean;
67
- /** Whether a saved grant only reads: it names at least one scope and every one is a read or identity scope. Unknown (null) never is. */
68
67
  export declare const readOnlyGrant: (grant: readonly string[] | null) => boolean;
69
68
  /** An OAuth `scope` value as scope tokens (RFC 6749), sorted and deduplicated; null when absent, empty or malformed. */
70
69
  export declare function scopeList(value: unknown): string[] | null;
@@ -133,7 +133,10 @@ const IDENTITY_SCOPES = new Set(["openid", "profile", "email"]);
133
133
  const READ_SCOPE = /^[a-z][a-z0-9_]{0,63}:read$/;
134
134
  export const readScope = (scope) => IDENTITY_SCOPES.has(scope) || READ_SCOPE.test(scope);
135
135
  /** Whether a saved grant only reads: it names at least one scope and every one is a read or identity scope. Unknown (null) never is. */
136
- export const readOnlyGrant = (grant) => grant !== null && grant.length > 0 && grant.every(readScope);
136
+ // A scope a server adds to every token without being asked, that grants no access to data. PostHog adds
137
+ // `introspection` (its ALWAYS_ALLOWED_SCOPES): it lets the token be checked, and has no write form. Never requested.
138
+ const GRANTED_ANYWAY = new Set(["introspection"]);
139
+ export const readOnlyGrant = (grant) => grant !== null && grant.some(readScope) && grant.every(scope => readScope(scope) || GRANTED_ANYWAY.has(scope));
137
140
  /** An OAuth `scope` value as scope tokens (RFC 6749), sorted and deduplicated; null when absent, empty or malformed. */
138
141
  export function scopeList(value) {
139
142
  if (typeof value !== "string")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolroll",
3
- "version": "0.9.40",
3
+ "version": "0.9.41",
4
4
  "description": "A control plane for unattended coding agents — queue tasks, walk away, come back to pull requests. Agents build in leased worktrees, never touch your default branch, and interrupt you only for decisions that need a human.",
5
5
  "directories": {
6
6
  "doc": "docs"