@vellumai/assistant 0.4.45 → 0.4.48

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 (236) hide show
  1. package/ARCHITECTURE.md +6 -6
  2. package/docs/architecture/memory.md +1 -1
  3. package/docs/architecture/scheduling.md +2 -3
  4. package/docs/architecture/security.md +5 -5
  5. package/docs/trusted-contact-access.md +5 -6
  6. package/package.json +4 -1
  7. package/src/__tests__/avatar-e2e.test.ts +18 -219
  8. package/src/__tests__/avatar-generator.test.ts +5 -57
  9. package/src/__tests__/browser-fill-credential.test.ts +5 -2
  10. package/src/__tests__/bundled-skill-retrieval-guard.test.ts +2 -1
  11. package/src/__tests__/channel-readiness-routes.test.ts +20 -19
  12. package/src/__tests__/cli.test.ts +23 -0
  13. package/src/__tests__/credential-broker-browser-fill.test.ts +23 -22
  14. package/src/__tests__/credential-broker-server-use.test.ts +22 -21
  15. package/src/__tests__/credential-broker.test.ts +2 -1
  16. package/src/__tests__/credential-metadata-store.test.ts +240 -18
  17. package/src/__tests__/credential-resolve.test.ts +5 -4
  18. package/src/__tests__/credential-security-e2e.test.ts +8 -8
  19. package/src/__tests__/credential-security-invariants.test.ts +104 -7
  20. package/src/__tests__/credential-vault-unit.test.ts +22 -20
  21. package/src/__tests__/credential-vault.test.ts +284 -12
  22. package/src/__tests__/credentials-cli.test.ts +11 -6
  23. package/src/__tests__/gateway-only-enforcement.test.ts +4 -2
  24. package/src/__tests__/gemini-image-service.test.ts +75 -45
  25. package/src/__tests__/gemini-provider.test.ts +9 -6
  26. package/src/__tests__/guardian-action-conversation-turn.test.ts +1 -33
  27. package/src/__tests__/guardian-action-copy-generator.test.ts +0 -20
  28. package/src/__tests__/guardian-action-followup-executor.test.ts +1 -28
  29. package/src/__tests__/guardian-action-followup-store.test.ts +1 -1
  30. package/src/__tests__/guardian-grant-minting.test.ts +35 -0
  31. package/src/__tests__/integration-status.test.ts +53 -21
  32. package/src/__tests__/managed-proxy-context.test.ts +5 -3
  33. package/src/__tests__/media-generate-image.test.ts +63 -2
  34. package/src/__tests__/media-reuse-story.e2e.test.ts +7 -3
  35. package/src/__tests__/messaging-send-tool.test.ts +4 -6
  36. package/src/__tests__/provider-fail-open-selection.test.ts +3 -1
  37. package/src/__tests__/provider-managed-proxy-integration.test.ts +70 -6
  38. package/src/__tests__/schedule-store.test.ts +1 -1
  39. package/src/__tests__/schema-transforms.test.ts +226 -0
  40. package/src/__tests__/script-proxy-injection-runtime.test.ts +23 -13
  41. package/src/__tests__/script-proxy-policy-runtime.test.ts +1 -1
  42. package/src/__tests__/script-proxy-session-manager.test.ts +1 -1
  43. package/src/__tests__/secret-onetime-send.test.ts +5 -3
  44. package/src/__tests__/session-messaging-secret-redirect.test.ts +5 -4
  45. package/src/__tests__/skills-uninstall.test.ts +2 -2
  46. package/src/__tests__/skills.test.ts +0 -9
  47. package/src/__tests__/slack-channel-config.test.ts +9 -8
  48. package/src/__tests__/slack-share-routes.test.ts +11 -6
  49. package/src/__tests__/telegram-bot-username-resolution.test.ts +3 -0
  50. package/src/__tests__/twilio-config.test.ts +2 -1
  51. package/src/__tests__/twilio-provider.test.ts +4 -2
  52. package/src/__tests__/twilio-routes.test.ts +5 -4
  53. package/src/__tests__/verification-control-plane-policy.test.ts +1 -1
  54. package/src/approvals/AGENTS.md +1 -1
  55. package/src/calls/call-domain.ts +7 -4
  56. package/src/calls/twilio-config.ts +2 -1
  57. package/src/calls/twilio-provider.ts +2 -1
  58. package/src/calls/twilio-rest.ts +2 -2
  59. package/src/cli/commands/browser-relay.ts +40 -15
  60. package/src/cli/commands/credentials.ts +9 -8
  61. package/src/cli/commands/oauth.ts +1 -1
  62. package/src/cli.ts +3 -2
  63. package/src/config/bundled-skills/claude-code/TOOLS.json +0 -4
  64. package/src/config/bundled-skills/contacts/tools/google-contacts.ts +29 -32
  65. package/src/config/bundled-skills/gmail/SKILL.md +4 -4
  66. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +54 -61
  67. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +25 -28
  68. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +14 -17
  69. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +39 -44
  70. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +61 -58
  71. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +50 -49
  72. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +11 -13
  73. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +148 -146
  74. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +4 -7
  75. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +175 -173
  76. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +4 -7
  77. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +71 -76
  78. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +32 -38
  79. package/src/config/bundled-skills/google-calendar/SKILL.md +2 -2
  80. package/src/config/bundled-skills/google-calendar/calendar-client.ts +70 -29
  81. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +9 -10
  82. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +5 -6
  83. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +4 -5
  84. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +14 -15
  85. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +37 -37
  86. package/src/config/bundled-skills/google-calendar/tools/shared.ts +4 -9
  87. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +24 -3
  88. package/src/config/bundled-skills/messaging/SKILL.md +6 -6
  89. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +62 -63
  90. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +15 -16
  91. package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +4 -5
  92. package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +6 -7
  93. package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +4 -5
  94. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +14 -15
  95. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +4 -5
  96. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +128 -128
  97. package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +33 -34
  98. package/src/config/bundled-skills/messaging/tools/shared.ts +11 -11
  99. package/src/config/bundled-skills/notifications/SKILL.md +1 -1
  100. package/src/config/bundled-skills/phone-calls/SKILL.md +5 -5
  101. package/src/config/bundled-skills/schedule/SKILL.md +1 -1
  102. package/src/config/bundled-skills/skill-management/SKILL.md +1 -1
  103. package/src/config/bundled-skills/slack/tools/shared.ts +4 -10
  104. package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +4 -5
  105. package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +15 -16
  106. package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +4 -5
  107. package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +4 -5
  108. package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +4 -5
  109. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +95 -92
  110. package/src/config/loader.ts +6 -0
  111. package/src/daemon/computer-use-session.ts +7 -1
  112. package/src/daemon/guardian-action-generators.ts +4 -5
  113. package/src/daemon/handlers/config-slack-channel.ts +37 -20
  114. package/src/daemon/handlers/config-telegram.ts +33 -20
  115. package/src/daemon/lifecycle.ts +9 -1
  116. package/src/daemon/message-types/integrations.ts +1 -0
  117. package/src/daemon/ride-shotgun-handler.ts +3 -1
  118. package/src/daemon/session-messaging.ts +3 -1
  119. package/src/daemon/session-tool-setup.ts +18 -2
  120. package/src/daemon/session.ts +1 -1
  121. package/src/email/providers/index.ts +2 -1
  122. package/src/instrument.ts +15 -1
  123. package/src/media/app-icon-generator.ts +30 -4
  124. package/src/media/avatar-router.ts +28 -62
  125. package/src/media/gemini-image-service.ts +28 -2
  126. package/src/memory/canonical-guardian-store.ts +1 -1
  127. package/src/memory/guardian-action-store.ts +1 -1
  128. package/src/memory/schema/guardian.ts +1 -1
  129. package/src/messaging/provider.ts +16 -10
  130. package/src/messaging/providers/gmail/adapter.ts +40 -23
  131. package/src/messaging/providers/gmail/client.ts +203 -122
  132. package/src/messaging/providers/gmail/people-client.ts +26 -18
  133. package/src/messaging/providers/slack/adapter.ts +29 -19
  134. package/src/messaging/providers/slack/client.ts +265 -78
  135. package/src/messaging/providers/telegram-bot/adapter.ts +5 -4
  136. package/src/messaging/providers/whatsapp/adapter.ts +6 -3
  137. package/src/messaging/registry.ts +2 -1
  138. package/src/oauth/byo-connection.test.ts +436 -0
  139. package/src/oauth/byo-connection.ts +112 -0
  140. package/src/oauth/connect-orchestrator.ts +27 -0
  141. package/src/oauth/connection-resolver.ts +34 -0
  142. package/src/oauth/connection.ts +38 -0
  143. package/src/oauth/platform-connection.test.ts +163 -0
  144. package/src/oauth/platform-connection.ts +110 -0
  145. package/src/oauth/provider-base-urls.ts +21 -0
  146. package/src/oauth/provider-profiles.ts +1 -1
  147. package/src/oauth/token-persistence.ts +20 -20
  148. package/src/permissions/checker.ts +6 -1
  149. package/src/prompts/system-prompt.ts +52 -15
  150. package/src/prompts/templates/BOOTSTRAP.md +1 -1
  151. package/src/providers/gemini/client.ts +15 -6
  152. package/src/providers/managed-proxy/constants.ts +2 -2
  153. package/src/providers/managed-proxy/context.ts +5 -1
  154. package/src/providers/ratelimit.ts +17 -0
  155. package/src/providers/registry.ts +2 -2
  156. package/src/runtime/AGENTS.md +18 -1
  157. package/src/runtime/auth/route-policy.ts +1 -0
  158. package/src/runtime/channel-invite-transports/telegram.ts +2 -1
  159. package/src/runtime/channel-readiness-service.ts +168 -195
  160. package/src/runtime/channel-readiness-types.ts +4 -0
  161. package/src/runtime/guardian-action-conversation-turn.ts +1 -3
  162. package/src/runtime/guardian-action-followup-executor.ts +1 -2
  163. package/src/runtime/guardian-action-message-composer.ts +3 -23
  164. package/src/runtime/http-server.ts +9 -4
  165. package/src/runtime/http-types.ts +0 -1
  166. package/src/runtime/middleware/rate-limiter.ts +74 -20
  167. package/src/runtime/middleware/twilio-validation.ts +1 -3
  168. package/src/runtime/routes/channel-readiness-routes.ts +2 -0
  169. package/src/runtime/routes/diagnostics-routes.ts +11 -9
  170. package/src/runtime/routes/guardian-approval-interception.ts +20 -5
  171. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +71 -25
  172. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +12 -5
  173. package/src/runtime/routes/integrations/slack/share.ts +3 -2
  174. package/src/runtime/routes/integrations/twilio.ts +6 -5
  175. package/src/runtime/routes/secret-routes.ts +3 -2
  176. package/src/runtime/routes/settings-routes.ts +75 -17
  177. package/src/runtime/telegram-streaming-delivery.test.ts +132 -0
  178. package/src/runtime/telegram-streaming-delivery.ts +11 -1
  179. package/src/schedule/integration-status.ts +5 -4
  180. package/src/security/credential-key.ts +170 -0
  181. package/src/security/token-manager.ts +36 -7
  182. package/src/tools/apps/definitions.ts +0 -5
  183. package/src/tools/assets/materialize.ts +0 -5
  184. package/src/tools/assets/search.ts +0 -5
  185. package/src/tools/browser/headless-browser.ts +1 -67
  186. package/src/tools/claude-code/claude-code.ts +0 -5
  187. package/src/tools/computer-use/request-computer-control.ts +0 -5
  188. package/src/tools/credentials/broker.ts +6 -4
  189. package/src/tools/credentials/metadata-store.ts +72 -20
  190. package/src/tools/credentials/resolve.ts +2 -1
  191. package/src/tools/credentials/vault.ts +77 -16
  192. package/src/tools/filesystem/edit.ts +1 -6
  193. package/src/tools/filesystem/read.ts +0 -5
  194. package/src/tools/filesystem/write.ts +1 -6
  195. package/src/tools/host-filesystem/edit.ts +1 -6
  196. package/src/tools/host-filesystem/read.ts +1 -6
  197. package/src/tools/host-filesystem/write.ts +1 -6
  198. package/src/tools/mcp/mcp-tool-factory.ts +18 -1
  199. package/src/tools/memory/definitions.ts +0 -5
  200. package/src/tools/network/web-fetch.ts +0 -5
  201. package/src/tools/network/web-search.ts +0 -5
  202. package/src/tools/schema-transforms.ts +99 -0
  203. package/src/tools/skills/load.ts +0 -5
  204. package/src/tools/swarm/delegate.ts +0 -5
  205. package/src/tools/system/avatar-generator.ts +3 -44
  206. package/src/tools/ui-surface/definitions.ts +0 -15
  207. package/src/tools/watch/screen-watch.ts +0 -5
  208. package/src/version.ts +10 -0
  209. package/src/watcher/providers/github.ts +51 -52
  210. package/src/watcher/providers/gmail.ts +88 -80
  211. package/src/watcher/providers/google-calendar.ts +93 -86
  212. package/src/watcher/providers/linear.ts +87 -93
  213. package/src/__tests__/avatar-router.test.ts +0 -149
  214. package/src/__tests__/managed-avatar-client.test.ts +0 -337
  215. package/src/config/bundled-skills/doordash/SKILL.md +0 -170
  216. package/src/config/bundled-skills/doordash/__tests__/doordash-client.test.ts +0 -205
  217. package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +0 -74
  218. package/src/config/bundled-skills/doordash/doordash-cli.ts +0 -1081
  219. package/src/config/bundled-skills/doordash/doordash-entry.ts +0 -22
  220. package/src/config/bundled-skills/doordash/lib/cart-queries.ts +0 -787
  221. package/src/config/bundled-skills/doordash/lib/client.ts +0 -1069
  222. package/src/config/bundled-skills/doordash/lib/order-queries.ts +0 -85
  223. package/src/config/bundled-skills/doordash/lib/queries.ts +0 -28
  224. package/src/config/bundled-skills/doordash/lib/query-extractor.ts +0 -94
  225. package/src/config/bundled-skills/doordash/lib/search-queries.ts +0 -203
  226. package/src/config/bundled-skills/doordash/lib/session.ts +0 -96
  227. package/src/config/bundled-skills/doordash/lib/shared/errors.ts +0 -61
  228. package/src/config/bundled-skills/doordash/lib/shared/network-recorder.ts +0 -380
  229. package/src/config/bundled-skills/doordash/lib/shared/platform.ts +0 -55
  230. package/src/config/bundled-skills/doordash/lib/shared/recording-store.ts +0 -43
  231. package/src/config/bundled-skills/doordash/lib/shared/recording-types.ts +0 -49
  232. package/src/config/bundled-skills/doordash/lib/shared/truncate.ts +0 -6
  233. package/src/config/bundled-skills/doordash/lib/store-queries.ts +0 -246
  234. package/src/config/bundled-skills/doordash/lib/types.ts +0 -367
  235. package/src/media/avatar-types.ts +0 -53
  236. package/src/media/managed-avatar-client.ts +0 -225
@@ -1,33 +1,42 @@
1
- /**
2
- * Avatar generation router.
3
- * Tries managed platform path if available, falls back to local Gemini.
4
- */
5
-
6
1
  import { getConfig } from "../config/loader.js";
2
+ import {
3
+ buildManagedBaseUrl,
4
+ resolveManagedProxyContext,
5
+ } from "../providers/managed-proxy/context.js";
7
6
  import { ConfigError, ProviderError } from "../util/errors.js";
8
- import { getLogger } from "../util/logger.js";
9
- import type { AvatarGenerationResult } from "./avatar-types.js";
10
- import { generateImage } from "./gemini-image-service.js";
11
7
  import {
12
- generateManagedAvatar,
13
- isManagedAvailable,
14
- } from "./managed-avatar-client.js";
8
+ generateImage,
9
+ type ImageGenCredentials,
10
+ } from "./gemini-image-service.js";
15
11
 
16
- const log = getLogger("avatar-router");
17
-
18
- async function generateLocal(
12
+ export async function generateAvatar(
19
13
  prompt: string,
20
- correlationId?: string,
21
- ): Promise<AvatarGenerationResult> {
14
+ ): Promise<{ imageBase64: string; mimeType: string }> {
22
15
  const config = getConfig();
23
16
  const geminiKey = config.apiKeys.gemini ?? process.env.GEMINI_API_KEY;
24
- if (!geminiKey) {
17
+
18
+ let credentials: ImageGenCredentials | undefined;
19
+ if (geminiKey) {
20
+ credentials = { type: "direct", apiKey: geminiKey };
21
+ } else {
22
+ const managedBaseUrl = buildManagedBaseUrl("vertex");
23
+ if (managedBaseUrl) {
24
+ const ctx = resolveManagedProxyContext();
25
+ credentials = {
26
+ type: "managed-proxy",
27
+ assistantApiKey: ctx.assistantApiKey,
28
+ baseUrl: managedBaseUrl,
29
+ };
30
+ }
31
+ }
32
+
33
+ if (!credentials) {
25
34
  throw new ConfigError(
26
35
  "Gemini API key is not configured. Set it via `config set apiKeys.gemini <key>` or the GEMINI_API_KEY environment variable.",
27
36
  );
28
37
  }
29
38
 
30
- const result = await generateImage(geminiKey, {
39
+ const result = await generateImage(credentials, {
31
40
  prompt,
32
41
  mode: "generate",
33
42
  model: config.imageGenModel,
@@ -36,7 +45,7 @@ async function generateLocal(
36
45
  const image = result.images[0];
37
46
  if (!image) {
38
47
  throw new ProviderError(
39
- "Local Gemini image generation returned no images.",
48
+ "Gemini image generation returned no images.",
40
49
  "gemini",
41
50
  );
42
51
  }
@@ -44,48 +53,5 @@ async function generateLocal(
44
53
  return {
45
54
  imageBase64: image.dataBase64,
46
55
  mimeType: image.mimeType,
47
- pathUsed: "local",
48
- correlationId,
49
56
  };
50
57
  }
51
-
52
- export async function routedGenerateAvatar(
53
- prompt: string,
54
- options?: { correlationId?: string; model?: string },
55
- ): Promise<AvatarGenerationResult> {
56
- const correlationId = options?.correlationId;
57
- const model = options?.model;
58
-
59
- // Try managed platform path if available, fall back to local Gemini
60
- if (isManagedAvailable()) {
61
- try {
62
- const managed = await generateManagedAvatar(prompt, {
63
- correlationId,
64
- model,
65
- });
66
- return {
67
- imageBase64: managed.image.data_base64,
68
- mimeType: managed.image.mime_type,
69
- pathUsed: "managed",
70
- correlationId: managed.correlation_id,
71
- model,
72
- };
73
- } catch (err) {
74
- const config = getConfig();
75
- const geminiKey = config.apiKeys.gemini ?? process.env.GEMINI_API_KEY;
76
- if (!geminiKey) {
77
- log.warn(
78
- { err: err instanceof Error ? err.message : String(err) },
79
- "Managed avatar generation failed and no local Gemini key configured; re-throwing",
80
- );
81
- throw err;
82
- }
83
- log.warn(
84
- { err: err instanceof Error ? err.message : String(err) },
85
- "Managed avatar generation failed, falling back to local Gemini",
86
- );
87
- }
88
- }
89
-
90
- return generateLocal(prompt, correlationId);
91
- }
@@ -13,6 +13,21 @@ export interface ImageGenerationRequest {
13
13
  variants?: number;
14
14
  }
15
15
 
16
+ /** Credentials for direct Gemini API access. */
17
+ export interface DirectCredentials {
18
+ type: "direct";
19
+ apiKey: string;
20
+ }
21
+
22
+ /** Credentials for managed proxy access via Vertex AI. */
23
+ export interface ManagedProxyCredentials {
24
+ type: "managed-proxy";
25
+ assistantApiKey: string;
26
+ baseUrl: string;
27
+ }
28
+
29
+ export type ImageGenCredentials = DirectCredentials | ManagedProxyCredentials;
30
+
16
31
  export interface GeneratedImage {
17
32
  mimeType: string;
18
33
  dataBase64: string;
@@ -64,7 +79,7 @@ export function mapGeminiError(error: unknown): string {
64
79
  // --- Core function ---
65
80
 
66
81
  export async function generateImage(
67
- apiKey: string,
82
+ credentials: ImageGenCredentials,
68
83
  request: ImageGenerationRequest,
69
84
  ): Promise<ImageGenerationResult> {
70
85
  const model =
@@ -74,7 +89,18 @@ export async function generateImage(
74
89
 
75
90
  const variants = Math.max(1, Math.min(request.variants ?? 1, MAX_VARIANTS));
76
91
 
77
- const client = new GoogleGenAI({ apiKey });
92
+ const client =
93
+ credentials.type === "managed-proxy"
94
+ ? new GoogleGenAI({
95
+ vertexai: true,
96
+ project: "proxy",
97
+ location: "us-central1",
98
+ httpOptions: {
99
+ baseUrl: credentials.baseUrl,
100
+ headers: { Authorization: `Bearer ${credentials.assistantApiKey}` },
101
+ },
102
+ })
103
+ : new GoogleGenAI({ apiKey: credentials.apiKey });
78
104
 
79
105
  // Build contents array — append a title request so the model's text
80
106
  // response contains a short filename-safe title for the generated image.
@@ -551,7 +551,7 @@ export function listPendingCanonicalGuardianRequestsByDestinationConversation(
551
551
  * This bridges inbound guardian replies (which arrive on a specific chat)
552
552
  * back to their canonical request records. Unlike the conversation-based
553
553
  * variant, this uses the chat-level addressing that channel transports
554
- * (Telegram, SMS) natively provide — critical for voice-originated
554
+ * (Telegram) natively provide — critical for voice-originated
555
555
  * `pending_question` requests that lack `guardianExternalUserId`.
556
556
  */
557
557
  export function listPendingCanonicalGuardianRequestsByDestinationChat(
@@ -44,7 +44,7 @@ export type FollowupState =
44
44
  | "completed"
45
45
  | "declined"
46
46
  | "failed";
47
- export type FollowupAction = "call_back" | "message_back" | "decline";
47
+ export type FollowupAction = "call_back" | "decline";
48
48
 
49
49
  export interface GuardianActionRequest {
50
50
  id: string;
@@ -27,7 +27,7 @@ export const guardianActionRequests = sqliteTable(
27
27
  followupState: text("followup_state").notNull().default("none"), // none | awaiting_guardian_choice | dispatching | completed | declined | failed
28
28
  lateAnswerText: text("late_answer_text"),
29
29
  lateAnsweredAt: integer("late_answered_at"),
30
- followupAction: text("followup_action"), // call_back | message_back | decline
30
+ followupAction: text("followup_action"), // call_back | decline
31
31
  followupCompletedAt: integer("followup_completed_at"),
32
32
  toolName: text("tool_name"), // tool identity for tool-approval requests
33
33
  inputDigest: text("input_digest"), // canonical SHA-256 digest of tool input
@@ -5,6 +5,7 @@
5
5
  * implementing one adapter file + an OAuth setup skill.
6
6
  */
7
7
 
8
+ import type { OAuthConnection } from "../oauth/connection.js";
8
9
  import type {
9
10
  ArchiveResult,
10
11
  ConnectionInfo,
@@ -29,23 +30,25 @@ export interface MessagingProvider {
29
30
 
30
31
  // ── Universal operations (every platform must implement) ──────────
31
32
 
32
- testConnection(token: string): Promise<ConnectionInfo>;
33
+ testConnection(
34
+ connectionOrToken: OAuthConnection | string,
35
+ ): Promise<ConnectionInfo>;
33
36
  listConversations(
34
- token: string,
37
+ connectionOrToken: OAuthConnection | string,
35
38
  options?: ListOptions,
36
39
  ): Promise<Conversation[]>;
37
40
  getHistory(
38
- token: string,
41
+ connectionOrToken: OAuthConnection | string,
39
42
  conversationId: string,
40
43
  options?: HistoryOptions,
41
44
  ): Promise<Message[]>;
42
45
  search(
43
- token: string,
46
+ connectionOrToken: OAuthConnection | string,
44
47
  query: string,
45
48
  options?: SearchOptions,
46
49
  ): Promise<SearchResult>;
47
50
  sendMessage(
48
- token: string,
51
+ connectionOrToken: OAuthConnection | string,
49
52
  conversationId: string,
50
53
  text: string,
51
54
  options?: SendOptions,
@@ -54,30 +57,33 @@ export interface MessagingProvider {
54
57
  // ── Optional operations (platforms implement what they support) ───
55
58
 
56
59
  getThreadReplies?(
57
- token: string,
60
+ connectionOrToken: OAuthConnection | string,
58
61
  conversationId: string,
59
62
  threadId: string,
60
63
  options?: HistoryOptions,
61
64
  ): Promise<Message[]>;
62
65
  markRead?(
63
- token: string,
66
+ connectionOrToken: OAuthConnection | string,
64
67
  conversationId: string,
65
68
  messageId?: string,
66
69
  ): Promise<void>;
67
70
 
68
71
  /** Scan messages and group by sender for bulk cleanup (e.g. newsletter decluttering). */
69
72
  senderDigest?(
70
- token: string,
73
+ connectionOrToken: OAuthConnection | string,
71
74
  query: string,
72
75
  options?: { maxMessages?: number; maxSenders?: number; pageToken?: string },
73
76
  ): Promise<SenderDigestResult>;
74
77
  /** Archive messages matching a search query. */
75
- archiveByQuery?(token: string, query: string): Promise<ArchiveResult>;
78
+ archiveByQuery?(
79
+ connectionOrToken: OAuthConnection | string,
80
+ query: string,
81
+ ): Promise<ArchiveResult>;
76
82
 
77
83
  /**
78
84
  * Override the default credential check used by getConnectedProviders().
79
85
  * When present, the registry calls this instead of looking for
80
- * credential:${credentialService}:access_token. Useful for providers
86
+ * credential/{credentialService}/access_token. Useful for providers
81
87
  * that don't use OAuth (e.g. Telegram bot tokens stored under a
82
88
  * non-standard key).
83
89
  */
@@ -5,6 +5,7 @@
5
5
  * and implements the MessagingProvider interface.
6
6
  */
7
7
 
8
+ import type { OAuthConnection } from "../../../oauth/connection.js";
8
9
  import type { MessagingProvider } from "../../provider.js";
9
10
  import type {
10
11
  ArchiveResult,
@@ -94,8 +95,11 @@ export const gmailMessagingProvider: MessagingProvider = {
94
95
  "unsubscribe",
95
96
  ]),
96
97
 
97
- async testConnection(token: string): Promise<ConnectionInfo> {
98
- const profile = await gmail.getProfile(token);
98
+ async testConnection(
99
+ connectionOrToken: OAuthConnection | string,
100
+ ): Promise<ConnectionInfo> {
101
+ const connection = connectionOrToken as OAuthConnection;
102
+ const profile = await gmail.getProfile(connection);
99
103
  return {
100
104
  connected: true,
101
105
  user: profile.emailAddress,
@@ -108,11 +112,12 @@ export const gmailMessagingProvider: MessagingProvider = {
108
112
  },
109
113
 
110
114
  async listConversations(
111
- token: string,
115
+ connectionOrToken: OAuthConnection | string,
112
116
  _options?: ListOptions,
113
117
  ): Promise<Conversation[]> {
118
+ const connection = connectionOrToken as OAuthConnection;
114
119
  // Gmail "conversations" are modeled as labels with unread counts
115
- const labels = await gmail.listLabels(token);
120
+ const labels = await gmail.listLabels(connection);
116
121
  const conversations: Conversation[] = [];
117
122
 
118
123
  for (const label of labels) {
@@ -151,14 +156,15 @@ export const gmailMessagingProvider: MessagingProvider = {
151
156
  },
152
157
 
153
158
  async getHistory(
154
- token: string,
159
+ connectionOrToken: OAuthConnection | string,
155
160
  conversationId: string,
156
161
  options?: HistoryOptions,
157
162
  ): Promise<Message[]> {
163
+ const connection = connectionOrToken as OAuthConnection;
158
164
  // conversationId is a label ID — list messages in that label
159
165
  const limit = options?.limit ?? 50;
160
166
  const listResult = await gmail.listMessages(
161
- token,
167
+ connection,
162
168
  undefined,
163
169
  limit,
164
170
  undefined,
@@ -168,7 +174,7 @@ export const gmailMessagingProvider: MessagingProvider = {
168
174
  if (!listResult.messages?.length) return [];
169
175
 
170
176
  const messages = await gmail.batchGetMessages(
171
- token,
177
+ connection,
172
178
  listResult.messages.map((m) => m.id),
173
179
  "full",
174
180
  );
@@ -177,19 +183,20 @@ export const gmailMessagingProvider: MessagingProvider = {
177
183
  },
178
184
 
179
185
  async search(
180
- token: string,
186
+ connectionOrToken: OAuthConnection | string,
181
187
  query: string,
182
188
  options?: SearchOptions,
183
189
  ): Promise<SearchResult> {
190
+ const connection = connectionOrToken as OAuthConnection;
184
191
  const count = options?.count ?? 20;
185
- const listResult = await gmail.listMessages(token, query, count);
192
+ const listResult = await gmail.listMessages(connection, query, count);
186
193
 
187
194
  if (!listResult.messages?.length) {
188
195
  return { total: 0, messages: [], hasMore: false };
189
196
  }
190
197
 
191
198
  const messages = await gmail.batchGetMessages(
192
- token,
199
+ connection,
193
200
  listResult.messages.map((m) => m.id),
194
201
  "full",
195
202
  );
@@ -203,16 +210,17 @@ export const gmailMessagingProvider: MessagingProvider = {
203
210
  },
204
211
 
205
212
  async sendMessage(
206
- token: string,
213
+ connectionOrToken: OAuthConnection | string,
207
214
  conversationId: string,
208
215
  text: string,
209
216
  options?: SendOptions,
210
217
  ): Promise<SendResult> {
218
+ const connection = connectionOrToken as OAuthConnection;
211
219
  // conversationId is the recipient email for Gmail
212
220
  const to = conversationId;
213
221
  const subject = options?.subject ?? "";
214
222
  const msg = await gmail.sendMessage(
215
- token,
223
+ connection,
216
224
  to,
217
225
  subject,
218
226
  text,
@@ -228,15 +236,16 @@ export const gmailMessagingProvider: MessagingProvider = {
228
236
  },
229
237
 
230
238
  async getThreadReplies(
231
- token: string,
239
+ connectionOrToken: OAuthConnection | string,
232
240
  _conversationId: string,
233
241
  threadId: string,
234
242
  options?: HistoryOptions,
235
243
  ): Promise<Message[]> {
244
+ const connection = connectionOrToken as OAuthConnection;
236
245
  // Get all messages in a Gmail thread
237
246
  const limit = options?.limit ?? 50;
238
247
  const listResult = await gmail.listMessages(
239
- token,
248
+ connection,
240
249
  `thread:${threadId}`,
241
250
  limit,
242
251
  );
@@ -244,7 +253,7 @@ export const gmailMessagingProvider: MessagingProvider = {
244
253
  if (!listResult.messages?.length) return [];
245
254
 
246
255
  const messages = await gmail.batchGetMessages(
247
- token,
256
+ connection,
248
257
  listResult.messages.map((m) => m.id),
249
258
  "full",
250
259
  );
@@ -253,19 +262,23 @@ export const gmailMessagingProvider: MessagingProvider = {
253
262
  },
254
263
 
255
264
  async markRead(
256
- token: string,
265
+ connectionOrToken: OAuthConnection | string,
257
266
  _conversationId: string,
258
267
  messageId?: string,
259
268
  ): Promise<void> {
269
+ const connection = connectionOrToken as OAuthConnection;
260
270
  if (!messageId) return;
261
- await gmail.modifyMessage(token, messageId, { removeLabelIds: ["UNREAD"] });
271
+ await gmail.modifyMessage(connection, messageId, {
272
+ removeLabelIds: ["UNREAD"],
273
+ });
262
274
  },
263
275
 
264
276
  async senderDigest(
265
- token: string,
277
+ connectionOrToken: OAuthConnection | string,
266
278
  query: string,
267
279
  options?: { maxMessages?: number; maxSenders?: number; pageToken?: string },
268
280
  ): Promise<SenderDigestResult> {
281
+ const connection = connectionOrToken as OAuthConnection;
269
282
  const maxMessages = Math.min(options?.maxMessages ?? 5000, 5000);
270
283
  const maxSenders = options?.maxSenders ?? 30;
271
284
  const maxIdsPerSender = 5000;
@@ -285,7 +298,7 @@ export const gmailMessagingProvider: MessagingProvider = {
285
298
  }
286
299
  const pageSize = Math.min(100, maxMessages - allMessageIds.length);
287
300
  const listResp = await gmail.listMessages(
288
- token,
301
+ connection,
289
302
  query,
290
303
  pageSize,
291
304
  pageToken,
@@ -295,7 +308,7 @@ export const gmailMessagingProvider: MessagingProvider = {
295
308
  allMessageIds.push(...ids);
296
309
  fetchPromises.push(
297
310
  gmail.batchGetMessages(
298
- token,
311
+ connection,
299
312
  ids,
300
313
  "metadata",
301
314
  metadataHeaders,
@@ -409,7 +422,11 @@ export const gmailMessagingProvider: MessagingProvider = {
409
422
  };
410
423
  },
411
424
 
412
- async archiveByQuery(token: string, query: string): Promise<ArchiveResult> {
425
+ async archiveByQuery(
426
+ connectionOrToken: OAuthConnection | string,
427
+ query: string,
428
+ ): Promise<ArchiveResult> {
429
+ const connection = connectionOrToken as OAuthConnection;
413
430
  const maxMessages = 5000;
414
431
  const batchModifyLimit = 1000;
415
432
 
@@ -419,7 +436,7 @@ export const gmailMessagingProvider: MessagingProvider = {
419
436
 
420
437
  while (allMessageIds.length < maxMessages) {
421
438
  const listResp = await gmail.listMessages(
422
- token,
439
+ connection,
423
440
  query,
424
441
  Math.min(500, maxMessages - allMessageIds.length),
425
442
  pageToken,
@@ -441,7 +458,7 @@ export const gmailMessagingProvider: MessagingProvider = {
441
458
 
442
459
  for (let i = 0; i < allMessageIds.length; i += batchModifyLimit) {
443
460
  const chunk = allMessageIds.slice(i, i + batchModifyLimit);
444
- await gmail.batchModifyMessages(token, chunk, {
461
+ await gmail.batchModifyMessages(connection, chunk, {
445
462
  removeLabelIds: ["INBOX"],
446
463
  });
447
464
  }