@vellumai/assistant 0.5.11 → 0.5.13
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/Dockerfile +42 -9
- package/docs/architecture/integrations.md +34 -32
- package/node_modules/@vellumai/ces-contracts/src/__tests__/grants.test.ts +7 -7
- package/node_modules/@vellumai/ces-contracts/src/handles.ts +5 -4
- package/node_modules/@vellumai/ces-contracts/src/index.ts +7 -0
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +5 -0
- package/node_modules/@vellumai/credential-storage/src/index.ts +1 -1
- package/openapi.yaml +87 -9
- package/package.json +1 -1
- package/src/__tests__/catalog-cache.test.ts +164 -0
- package/src/__tests__/catalog-search.test.ts +61 -0
- package/src/__tests__/cli-command-risk-guard.test.ts +181 -6
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +396 -0
- package/src/__tests__/conversation-error.test.ts +3 -2
- package/src/__tests__/credential-security-invariants.test.ts +9 -15
- package/src/__tests__/credential-vault-unit.test.ts +32 -34
- package/src/__tests__/credential-vault.test.ts +25 -33
- package/src/__tests__/credentials-cli.test.ts +3 -3
- package/src/__tests__/daemon-credential-client.test.ts +2 -2
- package/src/__tests__/first-greeting.test.ts +7 -0
- package/src/__tests__/host-bash-proxy.test.ts +79 -0
- package/src/__tests__/host-cu-proxy.test.ts +90 -0
- package/src/__tests__/host-file-proxy.test.ts +89 -0
- package/src/__tests__/integration-status.test.ts +5 -5
- package/src/__tests__/list-messages-attachments.test.ts +171 -0
- package/src/__tests__/mcp-abort-signal.test.ts +205 -0
- package/src/__tests__/messaging-send-tool.test.ts +5 -5
- package/src/__tests__/navigate-settings-tab.test.ts +6 -2
- package/src/__tests__/notification-telegram-adapter.test.ts +125 -0
- package/src/__tests__/oauth-cli.test.ts +126 -119
- package/src/__tests__/oauth-provider-profiles.test.ts +55 -20
- package/src/__tests__/oauth-scope-policy.test.ts +4 -6
- package/src/__tests__/onboarding-template-contract.test.ts +2 -2
- package/src/__tests__/platform.test.ts +3 -168
- package/src/__tests__/secret-routes-managed-proxy.test.ts +78 -0
- package/src/__tests__/secure-keys-managed-failover.test.ts +73 -0
- package/src/__tests__/skill-feature-flags.test.ts +8 -0
- package/src/__tests__/skill-secret-handling-guard.test.ts +212 -0
- package/src/__tests__/skills-uninstall.test.ts +2 -2
- package/src/__tests__/slack-messaging-token-resolution.test.ts +22 -24
- package/src/__tests__/slack-share-routes.test.ts +5 -5
- package/src/__tests__/system-prompt.test.ts +39 -0
- package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -1
- package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +5 -4
- package/src/cli/AGENTS.md +47 -7
- package/src/cli/commands/browser-relay.ts +2 -17
- package/src/cli/commands/contacts.ts +6 -4
- package/src/cli/commands/conversations.ts +13 -1
- package/src/cli/commands/credential-execution.ts +16 -1
- package/src/cli/commands/credentials.ts +2 -8
- package/src/cli/commands/oauth/__tests__/connect.test.ts +29 -108
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +13 -87
- package/src/cli/commands/oauth/__tests__/mode.test.ts +22 -69
- package/src/cli/commands/oauth/__tests__/ping.test.ts +20 -79
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +574 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +416 -0
- package/src/cli/commands/oauth/__tests__/status.test.ts +12 -40
- package/src/cli/commands/oauth/__tests__/token.test.ts +3 -50
- package/src/cli/commands/oauth/apps.ts +63 -44
- package/src/cli/commands/oauth/connect.ts +187 -155
- package/src/cli/commands/oauth/disconnect.ts +27 -75
- package/src/cli/commands/oauth/index.ts +36 -46
- package/src/cli/commands/oauth/mode.ts +22 -34
- package/src/cli/commands/oauth/ping.ts +19 -45
- package/src/cli/commands/oauth/providers.ts +569 -62
- package/src/cli/commands/oauth/request.ts +36 -48
- package/src/cli/commands/oauth/shared.ts +1 -19
- package/src/cli/commands/oauth/status.ts +14 -25
- package/src/cli/commands/oauth/token.ts +25 -34
- package/src/cli/commands/platform/__tests__/connect.test.ts +224 -0
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +237 -0
- package/src/cli/commands/platform/__tests__/status.test.ts +246 -0
- package/src/cli/commands/platform/connect.ts +104 -0
- package/src/cli/commands/platform/disconnect.ts +118 -0
- package/src/cli/commands/{platform.ts → platform/index.ts} +108 -38
- package/src/cli/commands/sequence.ts +5 -4
- package/src/cli/commands/shotgun.ts +16 -0
- package/src/cli/commands/skills.ts +173 -41
- package/src/cli/commands/usage.ts +5 -11
- package/src/cli/lib/daemon-credential-client.ts +22 -38
- package/src/cli/program.ts +1 -1
- package/src/config/assistant-feature-flags.ts +3 -7
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +1 -1
- package/src/config/bundled-skills/conversations/SKILL.md +20 -0
- package/src/config/bundled-skills/conversations/TOOLS.json +23 -0
- package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +66 -0
- package/src/config/bundled-skills/gmail/SKILL.md +13 -13
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +3 -3
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +2 -2
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +2 -2
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +1 -1
- package/src/config/bundled-skills/google-calendar/SKILL.md +10 -4
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +7 -7
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -2
- package/src/config/bundled-skills/messaging/tools/shared.ts +5 -6
- package/src/config/bundled-skills/settings/TOOLS.json +5 -3
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +4 -2
- package/src/config/bundled-tool-registry.ts +5 -0
- package/src/config/feature-flag-registry.json +2 -2
- package/src/credential-execution/client.ts +15 -3
- package/src/daemon/conversation-agent-loop.ts +2 -0
- package/src/daemon/conversation-error.ts +36 -6
- package/src/daemon/conversation-messaging.ts +9 -0
- package/src/daemon/conversation-runtime-assembly.ts +33 -0
- package/src/daemon/conversation-surfaces.ts +120 -14
- package/src/daemon/conversation.ts +5 -0
- package/src/daemon/first-greeting.ts +6 -1
- package/src/daemon/handlers/skills.ts +148 -3
- package/src/daemon/host-bash-proxy.ts +16 -0
- package/src/daemon/host-cu-proxy.ts +16 -0
- package/src/daemon/host-file-proxy.ts +16 -0
- package/src/daemon/lifecycle.ts +56 -5
- package/src/daemon/message-types/conversations.ts +1 -0
- package/src/daemon/message-types/guardian-actions.ts +2 -0
- package/src/daemon/message-types/host-bash.ts +6 -1
- package/src/daemon/message-types/host-cu.ts +6 -1
- package/src/daemon/message-types/host-file.ts +6 -1
- package/src/daemon/message-types/integrations.ts +0 -1
- package/src/daemon/server.ts +29 -2
- package/src/hooks/cli.ts +74 -0
- package/src/inbound/platform-callback-registration.ts +7 -12
- package/src/index.ts +0 -12
- package/src/mcp/client.ts +6 -1
- package/src/mcp/manager.ts +2 -1
- package/src/memory/conversation-crud.ts +92 -3
- package/src/memory/conversation-key-store.ts +26 -0
- package/src/memory/conversation-queries.ts +6 -6
- package/src/memory/db-init.ts +16 -0
- package/src/memory/journal-memory.ts +8 -2
- package/src/memory/migrations/196-messages-conversation-created-at-index.ts +9 -0
- package/src/memory/migrations/196-strip-integration-prefix-from-provider-keys.ts +186 -0
- package/src/memory/migrations/197-oauth-providers-behavior-columns.ts +29 -0
- package/src/memory/migrations/198-drop-setup-skill-id-column.ts +11 -0
- package/src/memory/migrations/index.ts +4 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/oauth.ts +11 -0
- package/src/messaging/provider.ts +13 -12
- package/src/messaging/providers/gmail/adapter.ts +44 -35
- package/src/messaging/providers/slack/adapter.ts +63 -33
- package/src/messaging/providers/telegram-bot/adapter.ts +6 -8
- package/src/messaging/providers/whatsapp/adapter.ts +6 -8
- package/src/notifications/adapters/telegram.ts +78 -2
- package/src/oauth/__tests__/identity-verifier.test.ts +464 -0
- package/src/oauth/byo-connection.test.ts +22 -24
- package/src/oauth/connect-orchestrator.ts +37 -76
- package/src/oauth/connect-types.ts +7 -65
- package/src/oauth/connection-resolver.test.ts +13 -13
- package/src/oauth/connection-resolver.ts +3 -4
- package/src/oauth/identity-verifier.ts +177 -0
- package/src/oauth/oauth-store.ts +228 -3
- package/src/oauth/platform-connection.test.ts +56 -6
- package/src/oauth/platform-connection.ts +8 -1
- package/src/oauth/seed-providers.ts +247 -34
- package/src/permissions/checker.ts +127 -1
- package/src/prompts/journal-context.ts +4 -1
- package/src/prompts/system-prompt.ts +54 -9
- package/src/prompts/templates/BOOTSTRAP.md +16 -5
- package/src/providers/anthropic/client.ts +2 -33
- package/src/runtime/guardian-action-service.ts +7 -2
- package/src/runtime/http-server.ts +12 -18
- package/src/runtime/http-types.ts +8 -1
- package/src/runtime/migrations/rebind-secrets-screen.ts +2 -2
- package/src/runtime/routes/conversation-management-routes.ts +31 -0
- package/src/runtime/routes/conversation-routes.ts +79 -4
- package/src/runtime/routes/guardian-action-routes.ts +15 -2
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -8
- package/src/runtime/routes/integrations/slack/share.ts +1 -1
- package/src/runtime/routes/oauth-apps.ts +2 -1
- package/src/runtime/routes/secret-routes.ts +45 -15
- package/src/runtime/routes/settings-routes.ts +12 -19
- package/src/runtime/routes/skills-routes.ts +45 -4
- package/src/schedule/integration-status.ts +2 -2
- package/src/security/ces-rpc-credential-backend.ts +19 -16
- package/src/security/oauth-completion-page.ts +153 -0
- package/src/security/oauth2.ts +3 -17
- package/src/security/secure-keys.ts +207 -7
- package/src/security/token-manager.ts +3 -6
- package/src/signals/bash.ts +6 -1
- package/src/skills/catalog-cache.ts +44 -0
- package/src/skills/catalog-search.ts +18 -0
- package/src/tools/browser/browser-manager.ts +2 -2
- package/src/tools/credentials/post-connect-hooks.ts +1 -1
- package/src/tools/credentials/vault.ts +34 -45
- package/src/tools/host-terminal/host-shell.ts +16 -3
- package/src/tools/mcp/mcp-tool-factory.ts +2 -1
- package/src/tools/skills/sandbox-runner.ts +16 -3
- package/src/tools/terminal/shell.ts +16 -3
- package/src/util/logger.ts +11 -1
- package/src/util/platform.ts +1 -91
- package/src/util/sentry-log-stream.ts +51 -0
- package/src/watcher/providers/github.ts +2 -2
- package/src/watcher/providers/gmail.ts +1 -1
- package/src/watcher/providers/google-calendar.ts +1 -1
- package/src/watcher/providers/linear.ts +2 -2
- package/src/workspace/migrations/011-backfill-installation-id.ts +5 -3
- package/src/workspace/migrations/020-rename-oauth-skill-dirs.ts +119 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/cli/commands/oauth/connections.ts +0 -255
- package/src/oauth/provider-behaviors.ts +0 -634
|
@@ -32,8 +32,30 @@ import type {
|
|
|
32
32
|
// Cache user display names to avoid repeated API calls within a session
|
|
33
33
|
const userNameCache = new Map<string, string>();
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Cached auth resolved during resolveConnection().
|
|
37
|
+
*
|
|
38
|
+
* For Socket Mode this holds a raw bot token string; for OAuth it holds the
|
|
39
|
+
* OAuthConnection. The Slack client functions accept both via their own
|
|
40
|
+
* OAuthConnection | string union, so we can pass this value through directly.
|
|
41
|
+
*/
|
|
42
|
+
let _cachedSlackAuth: OAuthConnection | string | null = null;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get the Slack auth value to pass to Slack client functions.
|
|
46
|
+
* Prefers the explicit connection from the caller; falls back to the cached
|
|
47
|
+
* value set during resolveConnection().
|
|
48
|
+
*/
|
|
49
|
+
function getSlackAuth(connection?: OAuthConnection): OAuthConnection | string {
|
|
50
|
+
if (connection) return connection;
|
|
51
|
+
if (_cachedSlackAuth) return _cachedSlackAuth;
|
|
52
|
+
throw new Error(
|
|
53
|
+
"Slack: no connection or cached token available. Was resolveConnection() called?",
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
35
57
|
async function resolveUserName(
|
|
36
|
-
|
|
58
|
+
auth: OAuthConnection | string,
|
|
37
59
|
userId: string,
|
|
38
60
|
): Promise<string> {
|
|
39
61
|
if (!userId) return "unknown";
|
|
@@ -41,7 +63,7 @@ async function resolveUserName(
|
|
|
41
63
|
if (cached) return cached;
|
|
42
64
|
|
|
43
65
|
try {
|
|
44
|
-
const resp = await slack.userInfo(
|
|
66
|
+
const resp = await slack.userInfo(auth, userId);
|
|
45
67
|
const name =
|
|
46
68
|
resp.user.profile?.display_name ||
|
|
47
69
|
resp.user.profile?.real_name ||
|
|
@@ -113,7 +135,7 @@ function mapSearchMatch(match: SlackSearchMatch): Message {
|
|
|
113
135
|
export const slackProvider: MessagingProvider = {
|
|
114
136
|
id: "slack",
|
|
115
137
|
displayName: "Slack",
|
|
116
|
-
credentialService: "
|
|
138
|
+
credentialService: "slack",
|
|
117
139
|
capabilities: new Set(["reactions", "threads", "leave_channel"]),
|
|
118
140
|
|
|
119
141
|
async isConnected(): Promise<boolean> {
|
|
@@ -124,25 +146,31 @@ export const slackProvider: MessagingProvider = {
|
|
|
124
146
|
credentialKey("slack_channel", "bot_token"),
|
|
125
147
|
);
|
|
126
148
|
if (botToken) return true;
|
|
127
|
-
// Preserve existing OAuth path
|
|
128
|
-
return isProviderConnected("
|
|
149
|
+
// Preserve existing OAuth path for backwards compat.
|
|
150
|
+
return isProviderConnected("slack");
|
|
129
151
|
},
|
|
130
152
|
|
|
131
|
-
async resolveConnection(
|
|
132
|
-
|
|
153
|
+
async resolveConnection(
|
|
154
|
+
account?: string,
|
|
155
|
+
): Promise<OAuthConnection | undefined> {
|
|
156
|
+
// Socket Mode: cache the raw bot token for use in adapter methods.
|
|
133
157
|
// Token presence is sufficient — no connection row required.
|
|
134
158
|
const botToken = await getSecureKeyAsync(
|
|
135
159
|
credentialKey("slack_channel", "bot_token"),
|
|
136
160
|
);
|
|
137
|
-
if (botToken)
|
|
138
|
-
|
|
139
|
-
|
|
161
|
+
if (botToken) {
|
|
162
|
+
_cachedSlackAuth = botToken;
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
// Preserve existing OAuth path for backwards compat.
|
|
166
|
+
const conn = await resolveOAuthConnection("slack", { account });
|
|
167
|
+
_cachedSlackAuth = conn;
|
|
168
|
+
return conn;
|
|
140
169
|
},
|
|
141
170
|
|
|
142
|
-
async testConnection(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const resp = await slack.authTest(connectionOrToken);
|
|
171
|
+
async testConnection(connection?: OAuthConnection): Promise<ConnectionInfo> {
|
|
172
|
+
const auth = getSlackAuth(connection);
|
|
173
|
+
const resp = await slack.authTest(auth);
|
|
146
174
|
return {
|
|
147
175
|
connected: true,
|
|
148
176
|
user: resp.user,
|
|
@@ -152,9 +180,10 @@ export const slackProvider: MessagingProvider = {
|
|
|
152
180
|
},
|
|
153
181
|
|
|
154
182
|
async listConversations(
|
|
155
|
-
|
|
183
|
+
connection: OAuthConnection | undefined,
|
|
156
184
|
options?: ListOptions,
|
|
157
185
|
): Promise<Conversation[]> {
|
|
186
|
+
const auth = getSlackAuth(connection);
|
|
158
187
|
const typeMap: Record<string, string> = {
|
|
159
188
|
channel: "public_channel,private_channel",
|
|
160
189
|
dm: "im",
|
|
@@ -174,7 +203,7 @@ export const slackProvider: MessagingProvider = {
|
|
|
174
203
|
// Paginate through all results
|
|
175
204
|
do {
|
|
176
205
|
const resp = await slack.listConversations(
|
|
177
|
-
|
|
206
|
+
auth,
|
|
178
207
|
types,
|
|
179
208
|
options?.excludeArchived ?? true,
|
|
180
209
|
options?.limit ?? 200,
|
|
@@ -191,7 +220,7 @@ export const slackProvider: MessagingProvider = {
|
|
|
191
220
|
for (const conv of conversations) {
|
|
192
221
|
if (conv.type === "dm" && conv.metadata?.dmUserId) {
|
|
193
222
|
conv.name = await resolveUserName(
|
|
194
|
-
|
|
223
|
+
auth,
|
|
195
224
|
conv.metadata.dmUserId as string,
|
|
196
225
|
);
|
|
197
226
|
}
|
|
@@ -201,12 +230,13 @@ export const slackProvider: MessagingProvider = {
|
|
|
201
230
|
},
|
|
202
231
|
|
|
203
232
|
async getHistory(
|
|
204
|
-
|
|
233
|
+
connection: OAuthConnection | undefined,
|
|
205
234
|
conversationId: string,
|
|
206
235
|
options?: HistoryOptions,
|
|
207
236
|
): Promise<Message[]> {
|
|
237
|
+
const auth = getSlackAuth(connection);
|
|
208
238
|
const resp = await slack.conversationHistory(
|
|
209
|
-
|
|
239
|
+
auth,
|
|
210
240
|
conversationId,
|
|
211
241
|
options?.limit ?? 50,
|
|
212
242
|
options?.before,
|
|
@@ -215,7 +245,7 @@ export const slackProvider: MessagingProvider = {
|
|
|
215
245
|
|
|
216
246
|
const messages: Message[] = [];
|
|
217
247
|
for (const msg of resp.messages) {
|
|
218
|
-
const name = await resolveUserName(
|
|
248
|
+
const name = await resolveUserName(auth, msg.user ?? "");
|
|
219
249
|
messages.push(mapMessage(msg, conversationId, name));
|
|
220
250
|
}
|
|
221
251
|
|
|
@@ -223,15 +253,12 @@ export const slackProvider: MessagingProvider = {
|
|
|
223
253
|
},
|
|
224
254
|
|
|
225
255
|
async search(
|
|
226
|
-
|
|
256
|
+
connection: OAuthConnection | undefined,
|
|
227
257
|
query: string,
|
|
228
258
|
options?: SearchOptions,
|
|
229
259
|
): Promise<SearchResult> {
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
query,
|
|
233
|
-
options?.count ?? 20,
|
|
234
|
-
);
|
|
260
|
+
const auth = getSlackAuth(connection);
|
|
261
|
+
const resp = await slack.searchMessages(auth, query, options?.count ?? 20);
|
|
235
262
|
return {
|
|
236
263
|
total: resp.messages.total,
|
|
237
264
|
messages: resp.messages.matches.map(mapSearchMatch),
|
|
@@ -240,13 +267,14 @@ export const slackProvider: MessagingProvider = {
|
|
|
240
267
|
},
|
|
241
268
|
|
|
242
269
|
async sendMessage(
|
|
243
|
-
|
|
270
|
+
connection: OAuthConnection | undefined,
|
|
244
271
|
conversationId: string,
|
|
245
272
|
text: string,
|
|
246
273
|
options?: SendOptions,
|
|
247
274
|
): Promise<SendResult> {
|
|
275
|
+
const auth = getSlackAuth(connection);
|
|
248
276
|
const resp = await slack.postMessage(
|
|
249
|
-
|
|
277
|
+
auth,
|
|
250
278
|
conversationId,
|
|
251
279
|
text,
|
|
252
280
|
options?.threadId,
|
|
@@ -259,32 +287,34 @@ export const slackProvider: MessagingProvider = {
|
|
|
259
287
|
},
|
|
260
288
|
|
|
261
289
|
async getThreadReplies(
|
|
262
|
-
|
|
290
|
+
connection: OAuthConnection | undefined,
|
|
263
291
|
conversationId: string,
|
|
264
292
|
threadId: string,
|
|
265
293
|
options?: HistoryOptions,
|
|
266
294
|
): Promise<Message[]> {
|
|
295
|
+
const auth = getSlackAuth(connection);
|
|
267
296
|
const resp = await slack.conversationReplies(
|
|
268
|
-
|
|
297
|
+
auth,
|
|
269
298
|
conversationId,
|
|
270
299
|
threadId,
|
|
271
300
|
options?.limit ?? 50,
|
|
272
301
|
);
|
|
273
302
|
const messages: Message[] = [];
|
|
274
303
|
for (const msg of resp.messages) {
|
|
275
|
-
const name = await resolveUserName(
|
|
304
|
+
const name = await resolveUserName(auth, msg.user ?? "");
|
|
276
305
|
messages.push(mapMessage(msg, conversationId, name));
|
|
277
306
|
}
|
|
278
307
|
return messages;
|
|
279
308
|
},
|
|
280
309
|
|
|
281
310
|
async markRead(
|
|
282
|
-
|
|
311
|
+
connection: OAuthConnection | undefined,
|
|
283
312
|
conversationId: string,
|
|
284
313
|
messageId?: string,
|
|
285
314
|
): Promise<void> {
|
|
315
|
+
const auth = getSlackAuth(connection);
|
|
286
316
|
// Slack's conversations.mark requires a timestamp — use the provided one or "now"
|
|
287
317
|
const ts = messageId ?? String(Date.now() / 1000);
|
|
288
|
-
await slack.conversationMark(
|
|
318
|
+
await slack.conversationMark(auth, conversationId, ts);
|
|
289
319
|
},
|
|
290
320
|
};
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* with OAuth tokens, Telegram delivery is proxied through the gateway which
|
|
7
7
|
* owns the bot token and handles Telegram API retries.
|
|
8
8
|
*
|
|
9
|
-
* The `
|
|
9
|
+
* The `connection` parameter in MessagingProvider methods is unused
|
|
10
10
|
* for Telegram because delivery is authenticated via the gateway's bearer
|
|
11
11
|
* token, not a per-user OAuth token.
|
|
12
12
|
*/
|
|
@@ -76,9 +76,7 @@ export const telegramBotMessagingProvider: MessagingProvider = {
|
|
|
76
76
|
return !!webhookSecret;
|
|
77
77
|
},
|
|
78
78
|
|
|
79
|
-
async testConnection(
|
|
80
|
-
_connectionOrToken: OAuthConnection | string,
|
|
81
|
-
): Promise<ConnectionInfo> {
|
|
79
|
+
async testConnection(_connection?: OAuthConnection): Promise<ConnectionInfo> {
|
|
82
80
|
const botToken = await getBotToken();
|
|
83
81
|
if (!botToken) {
|
|
84
82
|
return {
|
|
@@ -123,7 +121,7 @@ export const telegramBotMessagingProvider: MessagingProvider = {
|
|
|
123
121
|
},
|
|
124
122
|
|
|
125
123
|
async sendMessage(
|
|
126
|
-
|
|
124
|
+
_connection: OAuthConnection | undefined,
|
|
127
125
|
conversationId: string,
|
|
128
126
|
text: string,
|
|
129
127
|
_options?: SendOptions,
|
|
@@ -161,7 +159,7 @@ export const telegramBotMessagingProvider: MessagingProvider = {
|
|
|
161
159
|
// interact with chats where users have initiated contact or the bot
|
|
162
160
|
// has been added to a group.
|
|
163
161
|
async listConversations(
|
|
164
|
-
|
|
162
|
+
_connection?: OAuthConnection,
|
|
165
163
|
_options?: ListOptions,
|
|
166
164
|
): Promise<Conversation[]> {
|
|
167
165
|
return [];
|
|
@@ -169,7 +167,7 @@ export const telegramBotMessagingProvider: MessagingProvider = {
|
|
|
169
167
|
|
|
170
168
|
// Telegram Bot API does not provide message history retrieval.
|
|
171
169
|
async getHistory(
|
|
172
|
-
|
|
170
|
+
_connection: OAuthConnection | undefined,
|
|
173
171
|
_conversationId: string,
|
|
174
172
|
_options?: HistoryOptions,
|
|
175
173
|
): Promise<Message[]> {
|
|
@@ -178,7 +176,7 @@ export const telegramBotMessagingProvider: MessagingProvider = {
|
|
|
178
176
|
|
|
179
177
|
// Telegram Bot API does not support message search.
|
|
180
178
|
async search(
|
|
181
|
-
|
|
179
|
+
_connection: OAuthConnection | undefined,
|
|
182
180
|
_query: string,
|
|
183
181
|
_options?: SearchOptions,
|
|
184
182
|
): Promise<SearchResult> {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* endpoint. Delivery is proxied through the gateway which owns the Meta Cloud API
|
|
6
6
|
* credentials (phone_number_id + access_token).
|
|
7
7
|
*
|
|
8
|
-
* The `
|
|
8
|
+
* The `connection` parameter in MessagingProvider methods is unused
|
|
9
9
|
* for WhatsApp because delivery is authenticated via the gateway's bearer
|
|
10
10
|
* token, not a per-user OAuth token.
|
|
11
11
|
*/
|
|
@@ -66,9 +66,7 @@ export const whatsappMessagingProvider: MessagingProvider = {
|
|
|
66
66
|
return hasWhatsAppCredentials();
|
|
67
67
|
},
|
|
68
68
|
|
|
69
|
-
async testConnection(
|
|
70
|
-
_connectionOrToken: OAuthConnection | string,
|
|
71
|
-
): Promise<ConnectionInfo> {
|
|
69
|
+
async testConnection(_connection?: OAuthConnection): Promise<ConnectionInfo> {
|
|
72
70
|
if (!(await hasWhatsAppCredentials())) {
|
|
73
71
|
return {
|
|
74
72
|
connected: false,
|
|
@@ -96,7 +94,7 @@ export const whatsappMessagingProvider: MessagingProvider = {
|
|
|
96
94
|
},
|
|
97
95
|
|
|
98
96
|
async sendMessage(
|
|
99
|
-
|
|
97
|
+
_connection: OAuthConnection | undefined,
|
|
100
98
|
conversationId: string,
|
|
101
99
|
text: string,
|
|
102
100
|
options?: SendOptions,
|
|
@@ -140,7 +138,7 @@ export const whatsappMessagingProvider: MessagingProvider = {
|
|
|
140
138
|
|
|
141
139
|
// WhatsApp does not support listing conversations via this provider.
|
|
142
140
|
async listConversations(
|
|
143
|
-
|
|
141
|
+
_connection?: OAuthConnection,
|
|
144
142
|
_options?: ListOptions,
|
|
145
143
|
): Promise<Conversation[]> {
|
|
146
144
|
return [];
|
|
@@ -148,7 +146,7 @@ export const whatsappMessagingProvider: MessagingProvider = {
|
|
|
148
146
|
|
|
149
147
|
// WhatsApp does not provide message history retrieval via the gateway.
|
|
150
148
|
async getHistory(
|
|
151
|
-
|
|
149
|
+
_connection: OAuthConnection | undefined,
|
|
152
150
|
_conversationId: string,
|
|
153
151
|
_options?: HistoryOptions,
|
|
154
152
|
): Promise<Message[]> {
|
|
@@ -157,7 +155,7 @@ export const whatsappMessagingProvider: MessagingProvider = {
|
|
|
157
155
|
|
|
158
156
|
// WhatsApp does not support message search.
|
|
159
157
|
async search(
|
|
160
|
-
|
|
158
|
+
_connection: OAuthConnection | undefined,
|
|
161
159
|
_query: string,
|
|
162
160
|
_options?: SearchOptions,
|
|
163
161
|
): Promise<SearchResult> {
|
|
@@ -5,14 +5,20 @@
|
|
|
5
5
|
* Follows the same delivery pattern used by guardian-dispatch: POST to
|
|
6
6
|
* the gateway's `/deliver/telegram` endpoint with a chat ID and text
|
|
7
7
|
* payload. The gateway forwards the message to the Telegram Bot API.
|
|
8
|
+
*
|
|
9
|
+
* For access request notifications, inline keyboard buttons ("Approve once",
|
|
10
|
+
* "Reject") are attached via the approval payload so the guardian can act
|
|
11
|
+
* without typing a command. If the rich delivery fails, the adapter falls
|
|
12
|
+
* back to plain text with typed-command instructions.
|
|
8
13
|
*/
|
|
9
14
|
|
|
10
15
|
import { getGatewayInternalBaseUrl } from "../../config/env.js";
|
|
11
16
|
import { mintDaemonDeliveryToken } from "../../runtime/auth/token-service.js";
|
|
17
|
+
import type { ApprovalUIMetadata } from "../../runtime/channel-approval-types.js";
|
|
12
18
|
import { deliverChannelReply } from "../../runtime/gateway-client.js";
|
|
13
19
|
import { getLogger } from "../../util/logger.js";
|
|
14
20
|
import { isConversationSeedSane } from "../conversation-seed-composer.js";
|
|
15
|
-
import { nonEmpty } from "../copy-composer.js";
|
|
21
|
+
import { buildAccessRequestContractText, nonEmpty } from "../copy-composer.js";
|
|
16
22
|
import type {
|
|
17
23
|
ChannelAdapter,
|
|
18
24
|
ChannelDeliveryPayload,
|
|
@@ -40,6 +46,34 @@ function resolveTelegramMessageText(payload: ChannelDeliveryPayload): string {
|
|
|
40
46
|
return payload.sourceEventName.replace(/[._]/g, " ");
|
|
41
47
|
}
|
|
42
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Build an {@link ApprovalUIMetadata} for an access request so the gateway
|
|
51
|
+
* renders inline keyboard buttons in the Telegram message.
|
|
52
|
+
*
|
|
53
|
+
* Returns `undefined` when the context payload is missing the required
|
|
54
|
+
* `requestId`, in which case the caller should fall back to plain text.
|
|
55
|
+
*/
|
|
56
|
+
function buildAccessRequestApproval(
|
|
57
|
+
contextPayload: Record<string, unknown>,
|
|
58
|
+
): ApprovalUIMetadata | undefined {
|
|
59
|
+
const requestId =
|
|
60
|
+
typeof contextPayload.requestId === "string"
|
|
61
|
+
? contextPayload.requestId
|
|
62
|
+
: undefined;
|
|
63
|
+
if (!requestId) return undefined;
|
|
64
|
+
|
|
65
|
+
const plainTextFallback = buildAccessRequestContractText(contextPayload);
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
requestId,
|
|
69
|
+
actions: [
|
|
70
|
+
{ id: "approve_once", label: "Approve once" },
|
|
71
|
+
{ id: "reject", label: "Reject" },
|
|
72
|
+
],
|
|
73
|
+
plainTextFallback,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
43
77
|
export class TelegramAdapter implements ChannelAdapter {
|
|
44
78
|
readonly channel: NotificationChannel = "telegram";
|
|
45
79
|
|
|
@@ -66,10 +100,52 @@ export class TelegramAdapter implements ChannelAdapter {
|
|
|
66
100
|
// delivery copy when available and avoid deterministic label prefixes.
|
|
67
101
|
const messageText = resolveTelegramMessageText(payload);
|
|
68
102
|
|
|
103
|
+
// For access requests, attach inline keyboard buttons so the guardian
|
|
104
|
+
// can approve/reject with a single tap.
|
|
105
|
+
const isAccessRequest =
|
|
106
|
+
payload.sourceEventName === "ingress.access_request" &&
|
|
107
|
+
payload.contextPayload != null;
|
|
108
|
+
|
|
109
|
+
const approval = isAccessRequest
|
|
110
|
+
? buildAccessRequestApproval(payload.contextPayload!)
|
|
111
|
+
: undefined;
|
|
112
|
+
|
|
69
113
|
try {
|
|
114
|
+
if (approval) {
|
|
115
|
+
// Attempt rich delivery with inline keyboard buttons.
|
|
116
|
+
// On failure, fall back to plain text below.
|
|
117
|
+
try {
|
|
118
|
+
await deliverChannelReply(
|
|
119
|
+
deliverUrl,
|
|
120
|
+
{ chatId, text: messageText, approval },
|
|
121
|
+
mintDaemonDeliveryToken(),
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
log.info(
|
|
125
|
+
{ sourceEventName: payload.sourceEventName, chatId },
|
|
126
|
+
"Telegram access request notification delivered with inline buttons",
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
return { success: true };
|
|
130
|
+
} catch (richErr) {
|
|
131
|
+
log.warn(
|
|
132
|
+
{ err: richErr, sourceEventName: payload.sourceEventName, chatId },
|
|
133
|
+
"Rich Telegram delivery failed — falling back to plain text",
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// When falling back from rich delivery, append the plain-text
|
|
139
|
+
// instructions so the guardian still knows how to approve/reject.
|
|
140
|
+
const fallbackText =
|
|
141
|
+
approval?.plainTextFallback &&
|
|
142
|
+
!messageText.includes(approval.plainTextFallback)
|
|
143
|
+
? `${messageText}\n\n${approval.plainTextFallback}`
|
|
144
|
+
: messageText;
|
|
145
|
+
|
|
70
146
|
await deliverChannelReply(
|
|
71
147
|
deliverUrl,
|
|
72
|
-
{ chatId, text:
|
|
148
|
+
{ chatId, text: fallbackText },
|
|
73
149
|
mintDaemonDeliveryToken(),
|
|
74
150
|
);
|
|
75
151
|
|