crewly 1.20.264 → 1.20.266

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.
Files changed (144) hide show
  1. package/config/skills/agent/core/docs-comment/SKILL.md +6 -3
  2. package/config/skills/agent/core/docs-comment/execute.sh +1 -1
  3. package/config/skills/agent/core/gmail-send/SKILL.md +4 -0
  4. package/config/skills/agent/core/gmail-send/execute.sh +1 -1
  5. package/config/skills/agent/core/gmail-send/execute.test.sh +11 -0
  6. package/config/skills/agent/core/slack-file/SKILL.md +53 -0
  7. package/config/skills/agent/core/slack-file/execute.sh +72 -0
  8. package/config/skills/agent/core/slack-file/execute.test.sh +34 -0
  9. package/dist/backend/backend/src/constants.d.ts +77 -0
  10. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  11. package/dist/backend/backend/src/constants.js +77 -0
  12. package/dist/backend/backend/src/constants.js.map +1 -1
  13. package/dist/backend/backend/src/controllers/connector/connector.routes.d.ts.map +1 -1
  14. package/dist/backend/backend/src/controllers/connector/connector.routes.js +6 -1
  15. package/dist/backend/backend/src/controllers/connector/connector.routes.js.map +1 -1
  16. package/dist/backend/backend/src/controllers/connector/remote-mcp-proxy.controller.d.ts +69 -0
  17. package/dist/backend/backend/src/controllers/connector/remote-mcp-proxy.controller.d.ts.map +1 -0
  18. package/dist/backend/backend/src/controllers/connector/remote-mcp-proxy.controller.js +203 -0
  19. package/dist/backend/backend/src/controllers/connector/remote-mcp-proxy.controller.js.map +1 -0
  20. package/dist/backend/backend/src/controllers/connector/remote-mcp.controller.d.ts +16 -0
  21. package/dist/backend/backend/src/controllers/connector/remote-mcp.controller.d.ts.map +1 -1
  22. package/dist/backend/backend/src/controllers/connector/remote-mcp.controller.js +126 -5
  23. package/dist/backend/backend/src/controllers/connector/remote-mcp.controller.js.map +1 -1
  24. package/dist/backend/backend/src/controllers/google/google-connect-card.d.ts +12 -1
  25. package/dist/backend/backend/src/controllers/google/google-connect-card.d.ts.map +1 -1
  26. package/dist/backend/backend/src/controllers/google/google-connect-card.js +28 -10
  27. package/dist/backend/backend/src/controllers/google/google-connect-card.js.map +1 -1
  28. package/dist/backend/backend/src/controllers/google/google.controller.d.ts +26 -2
  29. package/dist/backend/backend/src/controllers/google/google.controller.d.ts.map +1 -1
  30. package/dist/backend/backend/src/controllers/google/google.controller.js +123 -33
  31. package/dist/backend/backend/src/controllers/google/google.controller.js.map +1 -1
  32. package/dist/backend/backend/src/controllers/slack/slack.controller.d.ts.map +1 -1
  33. package/dist/backend/backend/src/controllers/slack/slack.controller.js +37 -0
  34. package/dist/backend/backend/src/controllers/slack/slack.controller.js.map +1 -1
  35. package/dist/backend/backend/src/index.d.ts.map +1 -1
  36. package/dist/backend/backend/src/index.js +15 -0
  37. package/dist/backend/backend/src/index.js.map +1 -1
  38. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
  39. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js +2 -1
  40. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
  41. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.d.ts.map +1 -1
  42. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js +4 -3
  43. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js.map +1 -1
  44. package/dist/backend/backend/src/services/connector/remote-mcp-auth-card.d.ts +43 -0
  45. package/dist/backend/backend/src/services/connector/remote-mcp-auth-card.d.ts.map +1 -0
  46. package/dist/backend/backend/src/services/connector/remote-mcp-auth-card.js +98 -0
  47. package/dist/backend/backend/src/services/connector/remote-mcp-auth-card.js.map +1 -0
  48. package/dist/backend/backend/src/services/connector/remote-mcp-auth.service.d.ts +277 -0
  49. package/dist/backend/backend/src/services/connector/remote-mcp-auth.service.d.ts.map +1 -0
  50. package/dist/backend/backend/src/services/connector/remote-mcp-auth.service.js +719 -0
  51. package/dist/backend/backend/src/services/connector/remote-mcp-auth.service.js.map +1 -0
  52. package/dist/backend/backend/src/services/connector/remote-mcp-launch.service.d.ts +29 -5
  53. package/dist/backend/backend/src/services/connector/remote-mcp-launch.service.d.ts.map +1 -1
  54. package/dist/backend/backend/src/services/connector/remote-mcp-launch.service.js +40 -12
  55. package/dist/backend/backend/src/services/connector/remote-mcp-launch.service.js.map +1 -1
  56. package/dist/backend/backend/src/services/connector/remote-mcp-oauth.d.ts +196 -0
  57. package/dist/backend/backend/src/services/connector/remote-mcp-oauth.d.ts.map +1 -0
  58. package/dist/backend/backend/src/services/connector/remote-mcp-oauth.js +351 -0
  59. package/dist/backend/backend/src/services/connector/remote-mcp-oauth.js.map +1 -0
  60. package/dist/backend/backend/src/services/connector/remote-mcp-probe.service.d.ts +3 -1
  61. package/dist/backend/backend/src/services/connector/remote-mcp-probe.service.d.ts.map +1 -1
  62. package/dist/backend/backend/src/services/connector/remote-mcp-probe.service.js +8 -0
  63. package/dist/backend/backend/src/services/connector/remote-mcp-probe.service.js.map +1 -1
  64. package/dist/backend/backend/src/services/core/credential-files.d.ts.map +1 -1
  65. package/dist/backend/backend/src/services/core/credential-files.js +2 -0
  66. package/dist/backend/backend/src/services/core/credential-files.js.map +1 -1
  67. package/dist/backend/backend/src/services/google/docs-comments.service.d.ts.map +1 -1
  68. package/dist/backend/backend/src/services/google/docs-comments.service.js +4 -1
  69. package/dist/backend/backend/src/services/google/docs-comments.service.js.map +1 -1
  70. package/dist/backend/backend/src/services/google/google-api.client.d.ts +16 -0
  71. package/dist/backend/backend/src/services/google/google-api.client.d.ts.map +1 -1
  72. package/dist/backend/backend/src/services/google/google-api.client.js +44 -0
  73. package/dist/backend/backend/src/services/google/google-api.client.js.map +1 -1
  74. package/dist/backend/backend/src/services/google/google-reauth-notifier.service.d.ts +159 -0
  75. package/dist/backend/backend/src/services/google/google-reauth-notifier.service.d.ts.map +1 -0
  76. package/dist/backend/backend/src/services/google/google-reauth-notifier.service.js +283 -0
  77. package/dist/backend/backend/src/services/google/google-reauth-notifier.service.js.map +1 -0
  78. package/dist/backend/backend/src/services/google/google-reauth.wiring.d.ts +71 -0
  79. package/dist/backend/backend/src/services/google/google-reauth.wiring.d.ts.map +1 -0
  80. package/dist/backend/backend/src/services/google/google-reauth.wiring.js +132 -0
  81. package/dist/backend/backend/src/services/google/google-reauth.wiring.js.map +1 -0
  82. package/dist/backend/backend/src/services/google/google-workspace-token.service.d.ts +28 -10
  83. package/dist/backend/backend/src/services/google/google-workspace-token.service.d.ts.map +1 -1
  84. package/dist/backend/backend/src/services/google/google-workspace-token.service.js +24 -11
  85. package/dist/backend/backend/src/services/google/google-workspace-token.service.js.map +1 -1
  86. package/dist/backend/backend/src/services/slack/slack-file-fetch.service.d.ts +119 -0
  87. package/dist/backend/backend/src/services/slack/slack-file-fetch.service.d.ts.map +1 -0
  88. package/dist/backend/backend/src/services/slack/slack-file-fetch.service.js +415 -0
  89. package/dist/backend/backend/src/services/slack/slack-file-fetch.service.js.map +1 -0
  90. package/dist/backend/backend/src/services/slack/slack-file-ref.d.ts +69 -0
  91. package/dist/backend/backend/src/services/slack/slack-file-ref.d.ts.map +1 -0
  92. package/dist/backend/backend/src/services/slack/slack-file-ref.js +177 -0
  93. package/dist/backend/backend/src/services/slack/slack-file-ref.js.map +1 -0
  94. package/dist/backend/backend/src/services/slack/slack-initializer.d.ts.map +1 -1
  95. package/dist/backend/backend/src/services/slack/slack-initializer.js +16 -0
  96. package/dist/backend/backend/src/services/slack/slack-initializer.js.map +1 -1
  97. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts +15 -0
  98. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
  99. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +84 -1
  100. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
  101. package/dist/backend/backend/src/services/slack/slack.service.d.ts +2 -1
  102. package/dist/backend/backend/src/services/slack/slack.service.d.ts.map +1 -1
  103. package/dist/backend/backend/src/services/slack/slack.service.js +3 -1
  104. package/dist/backend/backend/src/services/slack/slack.service.js.map +1 -1
  105. package/dist/backend/backend/src/types/slack.types.d.ts +11 -0
  106. package/dist/backend/backend/src/types/slack.types.d.ts.map +1 -1
  107. package/dist/backend/backend/src/types/slack.types.js.map +1 -1
  108. package/dist/backend/build-info.json +2 -2
  109. package/dist/cli/backend/src/constants.d.ts +77 -0
  110. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  111. package/dist/cli/backend/src/constants.js +77 -0
  112. package/dist/cli/backend/src/constants.js.map +1 -1
  113. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
  114. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js +2 -1
  115. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
  116. package/dist/cli/backend/src/services/connector/remote-mcp-launch.service.d.ts +29 -5
  117. package/dist/cli/backend/src/services/connector/remote-mcp-launch.service.d.ts.map +1 -1
  118. package/dist/cli/backend/src/services/connector/remote-mcp-launch.service.js +40 -12
  119. package/dist/cli/backend/src/services/connector/remote-mcp-launch.service.js.map +1 -1
  120. package/dist/cli/backend/src/services/core/credential-files.d.ts.map +1 -1
  121. package/dist/cli/backend/src/services/core/credential-files.js +2 -0
  122. package/dist/cli/backend/src/services/core/credential-files.js.map +1 -1
  123. package/dist/cli/backend/src/services/slack/slack-file-fetch.service.d.ts +119 -0
  124. package/dist/cli/backend/src/services/slack/slack-file-fetch.service.d.ts.map +1 -0
  125. package/dist/cli/backend/src/services/slack/slack-file-fetch.service.js +415 -0
  126. package/dist/cli/backend/src/services/slack/slack-file-fetch.service.js.map +1 -0
  127. package/dist/cli/backend/src/services/slack/slack-file-ref.d.ts +69 -0
  128. package/dist/cli/backend/src/services/slack/slack-file-ref.d.ts.map +1 -0
  129. package/dist/cli/backend/src/services/slack/slack-file-ref.js +177 -0
  130. package/dist/cli/backend/src/services/slack/slack-file-ref.js.map +1 -0
  131. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.d.ts +15 -0
  132. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
  133. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js +84 -1
  134. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
  135. package/dist/cli/backend/src/services/slack/slack.service.d.ts +2 -1
  136. package/dist/cli/backend/src/services/slack/slack.service.d.ts.map +1 -1
  137. package/dist/cli/backend/src/services/slack/slack.service.js +3 -1
  138. package/dist/cli/backend/src/services/slack/slack.service.js.map +1 -1
  139. package/dist/cli/backend/src/types/slack.types.d.ts +11 -0
  140. package/dist/cli/backend/src/types/slack.types.d.ts.map +1 -1
  141. package/dist/cli/backend/src/types/slack.types.js.map +1 -1
  142. package/frontend/dist/assets/{index-83188f0b.js → index-7e5ebf78.js} +111 -111
  143. package/frontend/dist/index.html +1 -1
  144. package/package.json +1 -1
@@ -9,6 +9,39 @@
9
9
  */
10
10
  import { GOOGLE_WORKSPACE_CONSTANTS } from '../../constants.js';
11
11
  import { GoogleWorkspaceError } from './google-workspace-token.service.js';
12
+ /**
13
+ * Whether a Google 403 means "this token lacks a scope" rather than "this
14
+ * user may not touch this resource".
15
+ *
16
+ * Google says it two ways, often both at once: the legacy
17
+ * `errors[].reason: insufficientPermissions` and the newer
18
+ * `details[].reason: ACCESS_TOKEN_SCOPE_INSUFFICIENT` (an ErrorInfo), with
19
+ * the message "Request had insufficient authentication scopes."
20
+ * `insufficientFilePermissions` (Drive: no access to this file) is a
21
+ * different thing and deliberately does not match.
22
+ *
23
+ * @param status - HTTP status
24
+ * @param text - Raw response body
25
+ * @returns True for a scope refusal
26
+ */
27
+ export function isInsufficientScopeError(status, text) {
28
+ if (status !== 403)
29
+ return false;
30
+ try {
31
+ const parsed = JSON.parse(text);
32
+ const err = typeof parsed.error === 'object' ? parsed.error : undefined;
33
+ if (!err)
34
+ return false;
35
+ if (err.errors?.some((e) => e.reason === 'insufficientPermissions'))
36
+ return true;
37
+ if (err.details?.some((d) => d.reason === 'ACCESS_TOKEN_SCOPE_INSUFFICIENT'))
38
+ return true;
39
+ return /insufficient authentication scopes/i.test(err.message ?? '');
40
+ }
41
+ catch {
42
+ return /insufficient authentication scopes/i.test(text);
43
+ }
44
+ }
12
45
  /**
13
46
  * Extract the human message from a Google error body, tolerating both the
14
47
  * object and the plain-string shapes Google uses.
@@ -100,6 +133,17 @@ export async function googleRequest(deps, url, init = {}) {
100
133
  deps.tokens.clearCache(deps.account);
101
134
  throw new GoogleWorkspaceError(401, CODES.GOOGLE_ERROR, `Google rejected the access token: ${message}`);
102
135
  }
136
+ if (isInsufficientScopeError(res.status, text)) {
137
+ // The grant is narrower than this call (2026-10-08: Gmail drafts need
138
+ // gmail.compose). Only the owner can widen it; the cached token must
139
+ // go so the call after the reconnect carries the new scope.
140
+ deps.tokens.clearCache(deps.account);
141
+ throw new GoogleWorkspaceError(403, CODES.REAUTH_REQUIRED, message, {
142
+ reason: 'insufficient_scope',
143
+ ...(deps.product ? { product: deps.product } : {}),
144
+ ...(deps.account ? { account: deps.account } : {}),
145
+ });
146
+ }
103
147
  const passthrough = res.status === 403 || res.status === 404 || res.status === 429;
104
148
  throw new GoogleWorkspaceError(passthrough ? res.status : 502, CODES.GOOGLE_ERROR, message);
105
149
  }
@@ -1 +1 @@
1
- {"version":3,"file":"google-api.client.js","sourceRoot":"","sources":["../../../../../../backend/src/services/google/google-api.client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"google-api.client.js","sourceRoot":"","sources":["../../../../../../backend/src/services/google/google-api.client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Google reconnect cards — the harness asks the owner, not the agent.
3
+ *
4
+ * 2026-10-08: the Personal Assistant tried to save a reply as a Gmail draft,
5
+ * Google answered 403 "insufficient authentication scopes" (no grant carried
6
+ * `gmail.compose`), and the agent told the owner to "re-authorize Gmail in
7
+ * Crewly Connections" — a page the owner cannot reach from a phone. The
8
+ * same dead end follows an expired or revoked grant.
9
+ *
10
+ * So when a Google call fails because the grant is too narrow or no longer
11
+ * honoured, this posts the existing one-tap connect card (single-use ticket,
12
+ * `google-connect-card.ts`) to the owner in Slack — where the agent is
13
+ * working if that is known, else the owner's DM with that agent — at most
14
+ * once per product and Google account per
15
+ * {@link GOOGLE_WORKSPACE_CONSTANTS.REAUTH.CARD_THROTTLE_MS}. It then watches
16
+ * Cloud until the reconnect lands and tells the waiting agents to retry.
17
+ *
18
+ * Everything that touches Slack, Cloud or an agent is injected, so the
19
+ * machine the failure happened on is the one that posts — any machine with
20
+ * Slack and a Cloud session works the same.
21
+ *
22
+ * @module services/google/google-reauth-notifier.service
23
+ */
24
+ import { type GoogleProduct } from '../../constants.js';
25
+ import type { GoogleWorkspaceStatus } from './google-workspace-token.service.js';
26
+ /** Why the owner has to reconnect. */
27
+ export type ReauthKind = 'missing_scope' | 'expired';
28
+ /** A Google failure that only the owner can fix. */
29
+ export interface ReauthTrigger {
30
+ product: GoogleProduct;
31
+ kind: ReauthKind;
32
+ /** Google account the call acted as; undefined = the default connection */
33
+ account?: string;
34
+ /** The agent whose call failed; it is told when to retry */
35
+ agentSession?: string;
36
+ }
37
+ /** Where a card goes. */
38
+ export interface ReauthPlace {
39
+ slackChannelId: string;
40
+ threadTs?: string;
41
+ /** Post as this bot (the agent's own); the shared bot when absent */
42
+ botToken?: string;
43
+ }
44
+ /** What {@link GoogleReauthNotifier.notify} did. */
45
+ export type ReauthNotifyResult = {
46
+ status: 'posted';
47
+ expiresAt: string;
48
+ } | {
49
+ status: 'already_sent';
50
+ sentAt: string;
51
+ nextCardAfter: string;
52
+ } | {
53
+ status: 'unavailable';
54
+ why: string;
55
+ };
56
+ /** Collaborators. */
57
+ export interface ReauthNotifierDeps {
58
+ /** The owner's Slack user id */
59
+ ownerUserId: () => string | null;
60
+ /** The thread the agent is working in, else the owner's DM with it */
61
+ placeFor: (agentSession: string | undefined, ownerUserId: string) => Promise<ReauthPlace | null>;
62
+ /** A single-use connect link from Cloud */
63
+ connectUrl: (args: {
64
+ products: GoogleProduct[];
65
+ email?: string;
66
+ slackUserId: string;
67
+ slackChannelId: string;
68
+ slackThreadTs?: string;
69
+ }) => Promise<{
70
+ url: string;
71
+ expiresAt: string;
72
+ }>;
73
+ /** Post so only the owner sees it; true when Slack accepted */
74
+ postCard: (place: ReauthPlace, ownerUserId: string, text: string, blocks: unknown[]) => Promise<boolean>;
75
+ /** Cloud's view of the connections now (fresh, not cached) */
76
+ status: () => Promise<GoogleWorkspaceStatus>;
77
+ /** Drop cached Google tokens so the retry carries the new grant */
78
+ clearTokenCache: (account?: string) => void;
79
+ /** Tell an agent something (wakes or queues it) */
80
+ tellAgent: (session: string, text: string) => Promise<boolean>;
81
+ now?: () => number;
82
+ /** Timer seam for tests */
83
+ setTimer?: (fn: () => void, ms: number) => {
84
+ cancel: () => void;
85
+ };
86
+ }
87
+ /**
88
+ * The card's first line. Owner-facing, so it says what broke and that it is
89
+ * quick — not which scope is missing.
90
+ *
91
+ * @param trigger - The failure
92
+ * @returns Card message
93
+ */
94
+ export declare function reauthCardMessage(trigger: Pick<ReauthTrigger, 'product' | 'kind'>): string;
95
+ /**
96
+ * What the agent is told once the owner has reconnected.
97
+ *
98
+ * @param trigger - The failure
99
+ * @returns Notice text
100
+ */
101
+ export declare function reauthRetryNotice(trigger: Pick<ReauthTrigger, 'product' | 'kind'>): string;
102
+ /**
103
+ * Whether a status from Cloud shows the failure fixed.
104
+ *
105
+ * - `missing_scope`: the account is connected for the product and now
106
+ * carries the scope the call lacked (or, for a product with no known
107
+ * extra, reports nothing missing).
108
+ * - `expired`: the account is connected for the product again (Cloud
109
+ * deleted the dead grant, so being there at all is new).
110
+ *
111
+ * @param status - Cloud's connections
112
+ * @param trigger - The failure
113
+ * @returns True when the agent can retry
114
+ */
115
+ export declare function isReconnected(status: GoogleWorkspaceStatus, trigger: ReauthTrigger): boolean;
116
+ /**
117
+ * Posts reconnect cards and tells agents when to retry.
118
+ */
119
+ export declare class GoogleReauthNotifier {
120
+ private readonly deps;
121
+ private static instance;
122
+ private readonly pending;
123
+ /** Posts on the wire, so two failures at once post one card */
124
+ private readonly posting;
125
+ private readonly now;
126
+ private readonly setTimer;
127
+ /**
128
+ * @param deps - Collaborators
129
+ */
130
+ constructor(deps: ReauthNotifierDeps);
131
+ /** The wired instance, or null when Slack is not set up on this machine. */
132
+ static getInstance(): GoogleReauthNotifier | null;
133
+ /**
134
+ * Install (or, with null, remove) the process-wide instance.
135
+ *
136
+ * @param next - Notifier
137
+ */
138
+ static setInstance(next: GoogleReauthNotifier | null): void;
139
+ /**
140
+ * Ask the owner to reconnect, unless a card for this product and account
141
+ * went out within the throttle window — then only remember that this
142
+ * agent is waiting too.
143
+ *
144
+ * @param trigger - The failure
145
+ * @returns What happened, for the agent's hint
146
+ */
147
+ notify(trigger: ReauthTrigger): Promise<ReauthNotifyResult>;
148
+ /** Stop every watch (shutdown, tests). */
149
+ stop(): void;
150
+ private post;
151
+ /** Check again after the poll interval, until the watch window closes. */
152
+ private schedule;
153
+ /**
154
+ * One look at Cloud. On success the waiting agents are told to retry and
155
+ * the throttle is lifted, so a later, different failure gets its own card.
156
+ */
157
+ private check;
158
+ }
159
+ //# sourceMappingURL=google-reauth-notifier.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-reauth-notifier.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/google/google-reauth-notifier.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,283 @@
1
+ /**
2
+ * Google reconnect cards — the harness asks the owner, not the agent.
3
+ *
4
+ * 2026-10-08: the Personal Assistant tried to save a reply as a Gmail draft,
5
+ * Google answered 403 "insufficient authentication scopes" (no grant carried
6
+ * `gmail.compose`), and the agent told the owner to "re-authorize Gmail in
7
+ * Crewly Connections" — a page the owner cannot reach from a phone. The
8
+ * same dead end follows an expired or revoked grant.
9
+ *
10
+ * So when a Google call fails because the grant is too narrow or no longer
11
+ * honoured, this posts the existing one-tap connect card (single-use ticket,
12
+ * `google-connect-card.ts`) to the owner in Slack — where the agent is
13
+ * working if that is known, else the owner's DM with that agent — at most
14
+ * once per product and Google account per
15
+ * {@link GOOGLE_WORKSPACE_CONSTANTS.REAUTH.CARD_THROTTLE_MS}. It then watches
16
+ * Cloud until the reconnect lands and tells the waiting agents to retry.
17
+ *
18
+ * Everything that touches Slack, Cloud or an agent is injected, so the
19
+ * machine the failure happened on is the one that posts — any machine with
20
+ * Slack and a Cloud session works the same.
21
+ *
22
+ * @module services/google/google-reauth-notifier.service
23
+ */
24
+ import { GOOGLE_WORKSPACE_CONSTANTS } from '../../constants.js';
25
+ import { LoggerService } from '../core/logger.service.js';
26
+ import { buildConnectCard, PRODUCT_LABELS } from '../../controllers/google/google-connect-card.js';
27
+ const logger = LoggerService.getInstance().createComponentLogger('GoogleReauthNotifier');
28
+ /**
29
+ * The scope a `missing_scope` failure is about, per product — what a
30
+ * reconnect must have added for the retry to work.
31
+ */
32
+ const EXTRA_SCOPE = {
33
+ gmail: GOOGLE_WORKSPACE_CONSTANTS.GMAIL_COMPOSE_SCOPE,
34
+ drive: GOOGLE_WORKSPACE_CONSTANTS.DRIVE_FULL_SCOPE,
35
+ };
36
+ /** What the missing scope lets the agent do, for the card. */
37
+ const EXTRA_PURPOSE = {
38
+ gmail: 'save drafts',
39
+ drive: 'comment on documents',
40
+ };
41
+ /** What the agent retries once the scope is there. */
42
+ const EXTRA_RETRY = {
43
+ gmail: 'your draft',
44
+ drive: 'your comment',
45
+ };
46
+ /**
47
+ * The card's first line. Owner-facing, so it says what broke and that it is
48
+ * quick — not which scope is missing.
49
+ *
50
+ * @param trigger - The failure
51
+ * @returns Card message
52
+ */
53
+ export function reauthCardMessage(trigger) {
54
+ const label = PRODUCT_LABELS[trigger.product] ?? trigger.product;
55
+ if (trigger.kind === 'expired')
56
+ return `${label} access expired — tap to reconnect.`;
57
+ const purpose = EXTRA_PURPOSE[trigger.product];
58
+ return `${label} needs re-authorization${purpose ? ` to ${purpose}` : ''} (missing permission). Tap to reconnect — takes 30 seconds on your phone.`;
59
+ }
60
+ /**
61
+ * What the agent is told once the owner has reconnected.
62
+ *
63
+ * @param trigger - The failure
64
+ * @returns Notice text
65
+ */
66
+ export function reauthRetryNotice(trigger) {
67
+ const label = PRODUCT_LABELS[trigger.product] ?? trigger.product;
68
+ const what = trigger.kind === 'missing_scope' ? EXTRA_RETRY[trigger.product] : undefined;
69
+ return `[GOOGLE] ${label} reconnected — retry ${what ?? 'what failed'}.`;
70
+ }
71
+ /**
72
+ * Whether a status from Cloud shows the failure fixed.
73
+ *
74
+ * - `missing_scope`: the account is connected for the product and now
75
+ * carries the scope the call lacked (or, for a product with no known
76
+ * extra, reports nothing missing).
77
+ * - `expired`: the account is connected for the product again (Cloud
78
+ * deleted the dead grant, so being there at all is new).
79
+ *
80
+ * @param status - Cloud's connections
81
+ * @param trigger - The failure
82
+ * @returns True when the agent can retry
83
+ */
84
+ export function isReconnected(status, trigger) {
85
+ if (!status.connected)
86
+ return false;
87
+ const account = trigger.account?.toLowerCase();
88
+ const connection = account
89
+ ? status.connections.find((c) => c.email.toLowerCase() === account)
90
+ : status.connections.find((c) => c.isDefault) ?? status.connections[0];
91
+ if (!connection || !connection.products.includes(trigger.product))
92
+ return false;
93
+ if (trigger.kind === 'expired')
94
+ return true;
95
+ const needed = EXTRA_SCOPE[trigger.product];
96
+ if (needed)
97
+ return connection.scopes.includes(needed);
98
+ return (connection.missingScopes ?? []).length === 0;
99
+ }
100
+ /**
101
+ * Throttle key: one card per product and Google account.
102
+ *
103
+ * @param trigger - The failure
104
+ * @returns Key
105
+ */
106
+ function keyOf(trigger) {
107
+ return `${trigger.product}\u0000${(trigger.account ?? '').toLowerCase()}`;
108
+ }
109
+ /**
110
+ * Posts reconnect cards and tells agents when to retry.
111
+ */
112
+ export class GoogleReauthNotifier {
113
+ deps;
114
+ static instance = null;
115
+ pending = new Map();
116
+ /** Posts on the wire, so two failures at once post one card */
117
+ posting = new Map();
118
+ now;
119
+ setTimer;
120
+ /**
121
+ * @param deps - Collaborators
122
+ */
123
+ constructor(deps) {
124
+ this.deps = deps;
125
+ this.now = deps.now ?? (() => Date.now());
126
+ this.setTimer =
127
+ deps.setTimer ??
128
+ ((fn, ms) => {
129
+ const t = setTimeout(fn, ms);
130
+ t.unref?.();
131
+ return { cancel: () => clearTimeout(t) };
132
+ });
133
+ }
134
+ /** The wired instance, or null when Slack is not set up on this machine. */
135
+ static getInstance() {
136
+ return GoogleReauthNotifier.instance;
137
+ }
138
+ /**
139
+ * Install (or, with null, remove) the process-wide instance.
140
+ *
141
+ * @param next - Notifier
142
+ */
143
+ static setInstance(next) {
144
+ GoogleReauthNotifier.instance?.stop();
145
+ GoogleReauthNotifier.instance = next;
146
+ }
147
+ /**
148
+ * Ask the owner to reconnect, unless a card for this product and account
149
+ * went out within the throttle window — then only remember that this
150
+ * agent is waiting too.
151
+ *
152
+ * @param trigger - The failure
153
+ * @returns What happened, for the agent's hint
154
+ */
155
+ async notify(trigger) {
156
+ const key = keyOf(trigger);
157
+ const nowMs = this.now();
158
+ const existing = this.pending.get(key);
159
+ if (existing && nowMs - existing.sentAtMs < GOOGLE_WORKSPACE_CONSTANTS.REAUTH.CARD_THROTTLE_MS) {
160
+ if (trigger.agentSession)
161
+ existing.waiters.add(trigger.agentSession);
162
+ // The watch on the card may have run out; someone is waiting again, so
163
+ // look for a reconnect made some other way (the Connections page).
164
+ if (!existing.timer) {
165
+ existing.watchUntilMs = nowMs + GOOGLE_WORKSPACE_CONSTANTS.REAUTH.POLL_GRACE_MS;
166
+ this.schedule(key, existing);
167
+ }
168
+ return {
169
+ status: 'already_sent',
170
+ sentAt: new Date(existing.sentAtMs).toISOString(),
171
+ nextCardAfter: new Date(existing.sentAtMs + GOOGLE_WORKSPACE_CONSTANTS.REAUTH.CARD_THROTTLE_MS).toISOString(),
172
+ };
173
+ }
174
+ const inFlight = this.posting.get(key);
175
+ if (inFlight) {
176
+ const result = await inFlight;
177
+ if (trigger.agentSession)
178
+ this.pending.get(key)?.waiters.add(trigger.agentSession);
179
+ return result;
180
+ }
181
+ const task = this.post(key, trigger).finally(() => this.posting.delete(key));
182
+ this.posting.set(key, task);
183
+ return task;
184
+ }
185
+ /** Stop every watch (shutdown, tests). */
186
+ stop() {
187
+ for (const p of this.pending.values())
188
+ p.timer?.cancel();
189
+ this.pending.clear();
190
+ }
191
+ async post(key, trigger) {
192
+ const owner = this.deps.ownerUserId();
193
+ if (!owner)
194
+ return { status: 'unavailable', why: 'the owner is not known on Slack' };
195
+ const place = await this.deps.placeFor(trigger.agentSession, owner).catch(() => null);
196
+ if (!place)
197
+ return { status: 'unavailable', why: 'no Slack conversation with the owner from this machine' };
198
+ let link;
199
+ try {
200
+ link = await this.deps.connectUrl({
201
+ products: [trigger.product],
202
+ ...(trigger.account ? { email: trigger.account } : {}),
203
+ slackUserId: owner,
204
+ slackChannelId: place.slackChannelId,
205
+ ...(place.threadTs ? { slackThreadTs: place.threadTs } : {}),
206
+ });
207
+ }
208
+ catch (err) {
209
+ const why = err instanceof Error ? err.message : String(err);
210
+ logger.warn('Could not mint a Google reconnect link', { product: trigger.product, error: why });
211
+ return { status: 'unavailable', why: `Crewly Cloud did not issue a link (${why})` };
212
+ }
213
+ const card = buildConnectCard(trigger.product, link.url, link.expiresAt, {
214
+ message: reauthCardMessage(trigger),
215
+ ...(trigger.account ? { account: trigger.account } : {}),
216
+ });
217
+ const posted = await this.deps.postCard(place, owner, card.text, card.blocks).catch(() => false);
218
+ if (!posted)
219
+ return { status: 'unavailable', why: 'Slack refused the card' };
220
+ const sentAtMs = this.now();
221
+ const expiresMs = Date.parse(link.expiresAt);
222
+ const pending = {
223
+ trigger,
224
+ sentAtMs,
225
+ watchUntilMs: (Number.isFinite(expiresMs) ? expiresMs : sentAtMs) + GOOGLE_WORKSPACE_CONSTANTS.REAUTH.POLL_GRACE_MS,
226
+ waiters: new Set(trigger.agentSession ? [trigger.agentSession] : []),
227
+ };
228
+ this.pending.get(key)?.timer?.cancel();
229
+ this.pending.set(key, pending);
230
+ this.schedule(key, pending);
231
+ logger.info('Posted a Google reconnect card', {
232
+ product: trigger.product,
233
+ kind: trigger.kind,
234
+ account: trigger.account,
235
+ slackChannelId: place.slackChannelId,
236
+ threaded: !!place.threadTs,
237
+ agentSession: trigger.agentSession,
238
+ });
239
+ return { status: 'posted', expiresAt: link.expiresAt };
240
+ }
241
+ /** Check again after the poll interval, until the watch window closes. */
242
+ schedule(key, pending) {
243
+ pending.timer = this.setTimer(() => {
244
+ void this.check(key, pending);
245
+ }, GOOGLE_WORKSPACE_CONSTANTS.REAUTH.POLL_INTERVAL_MS);
246
+ }
247
+ /**
248
+ * One look at Cloud. On success the waiting agents are told to retry and
249
+ * the throttle is lifted, so a later, different failure gets its own card.
250
+ */
251
+ async check(key, pending) {
252
+ if (this.pending.get(key) !== pending)
253
+ return;
254
+ let fixed = false;
255
+ try {
256
+ fixed = isReconnected(await this.deps.status(), pending.trigger);
257
+ }
258
+ catch (err) {
259
+ logger.debug('Could not read Google status while waiting for a reconnect', {
260
+ error: err instanceof Error ? err.message : String(err),
261
+ });
262
+ }
263
+ if (!fixed) {
264
+ if (this.now() < pending.watchUntilMs)
265
+ this.schedule(key, pending);
266
+ else
267
+ pending.timer = undefined;
268
+ return;
269
+ }
270
+ this.pending.delete(key);
271
+ this.deps.clearTokenCache(pending.trigger.account);
272
+ const notice = reauthRetryNotice(pending.trigger);
273
+ for (const session of pending.waiters) {
274
+ await this.deps.tellAgent(session, notice).catch(() => false);
275
+ }
276
+ logger.info('Google reconnected; told the waiting agents to retry', {
277
+ product: pending.trigger.product,
278
+ account: pending.trigger.account,
279
+ agents: [...pending.waiters],
280
+ });
281
+ }
282
+ }
283
+ //# sourceMappingURL=google-reauth-notifier.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-reauth-notifier.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/google/google-reauth-notifier.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Google reconnect cards — wiring with the real Slack, Cloud and agents.
3
+ *
4
+ * Kept apart from the notifier so its rules (throttle, wording, when a
5
+ * reconnect counts) are testable without Slack. Everything here runs on the
6
+ * machine where the Google call failed: its Slack transport posts the card,
7
+ * its Cloud session mints the ticket, its agents are told to retry. That is
8
+ * the same on every machine, so an agent on a second machine (the Personal
9
+ * Assistant on iriss-air, 2026-10-08) gets the same card as one on the main
10
+ * Mac.
11
+ *
12
+ * @module services/google/google-reauth.wiring
13
+ */
14
+ import type { SlackBlock } from '../../types/slack.types.js';
15
+ import { GoogleReauthNotifier, type ReauthNotifierDeps } from './google-reauth-notifier.service.js';
16
+ /** The Slack calls the wiring makes (a slice of SlackService). */
17
+ export interface ReauthSlackApi {
18
+ getOwnerUserId?: (() => string | null) | null;
19
+ openDirectMessage: (userId: string, botToken?: string) => Promise<string>;
20
+ sendMessage: (message: {
21
+ channelId: string;
22
+ text: string;
23
+ blocks?: SlackBlock[];
24
+ threadTs?: string;
25
+ botToken?: string;
26
+ skipChatV2Mirror?: boolean;
27
+ }) => Promise<string>;
28
+ sendEphemeral: (channelId: string, userId: string, text: string, blocks?: unknown[], botToken?: string, threadTs?: string) => Promise<boolean>;
29
+ }
30
+ /** What the wiring needs from the rest of the process. */
31
+ export interface ReauthWiringInput {
32
+ /** Deliver text to an agent; wakes it when needed */
33
+ sendToAgent: (session: string, text: string) => Promise<boolean>;
34
+ /** Seams for tests; default to the real singletons */
35
+ slack?: () => ReauthSlackApi;
36
+ /** The Slack place of the agent's current work */
37
+ workDestination?: (session: string) => Promise<{
38
+ slackChannelId: string;
39
+ threadTs?: string;
40
+ } | null>;
41
+ /** The owner↔agent Slack DM, when there is one */
42
+ agentDm?: (session: string) => {
43
+ slackChannelId: string;
44
+ } | null;
45
+ /** The agent's own installed bot token */
46
+ agentBotToken?: (session: string) => string | undefined;
47
+ }
48
+ /**
49
+ * A Slack DM channel id. DMs are private to the owner and the bot, so a
50
+ * card there can be an ordinary message — which, unlike an ephemeral one,
51
+ * reaches the owner's phone as a notification and survives a reload.
52
+ *
53
+ * @param channelId - Slack channel id
54
+ * @returns True for `D…`
55
+ */
56
+ export declare function isDirectMessage(channelId: string): boolean;
57
+ /**
58
+ * Build the notifier's collaborators.
59
+ *
60
+ * @param input - Process hooks and test seams
61
+ * @returns Notifier deps
62
+ */
63
+ export declare function createReauthNotifierDeps(input: ReauthWiringInput): ReauthNotifierDeps;
64
+ /**
65
+ * Install the process-wide notifier. Called once Slack is up.
66
+ *
67
+ * @param input - Process hooks
68
+ * @returns The notifier
69
+ */
70
+ export declare function startGoogleReauthNotifier(input: ReauthWiringInput): GoogleReauthNotifier;
71
+ //# sourceMappingURL=google-reauth.wiring.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-reauth.wiring.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/google/google-reauth.wiring.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAM7D,OAAO,EAAE,oBAAoB,EAAE,KAAK,kBAAkB,EAAoB,MAAM,qCAAqC,CAAC;AAItH,kEAAkE;AAClE,MAAM,WAAW,cAAc;IAC7B,cAAc,CAAC,EAAE,CAAC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9C,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1E,WAAW,EAAE,CAAC,OAAO,EAAE;QACrB,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,gBAAgB,CAAC,EAAE,OAAO,CAAC;KAC5B,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACtB,aAAa,EAAE,CACb,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,OAAO,EAAE,EAClB,QAAQ,CAAC,EAAE,MAAM,EACjB,QAAQ,CAAC,EAAE,MAAM,KACd,OAAO,CAAC,OAAO,CAAC,CAAC;CACvB;AAED,0DAA0D;AAC1D,MAAM,WAAW,iBAAiB;IAChC,qDAAqD;IACrD,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACjE,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,cAAc,CAAC;IAC7B,kDAAkD;IAClD,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACrG,kDAAkD;IAClD,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK;QAAE,cAAc,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjE,0CAA0C;IAC1C,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CACzD;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,iBAAiB,GAAG,kBAAkB,CAkFrF;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,iBAAiB,GAAG,oBAAoB,CAKxF"}