@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.
- package/ARCHITECTURE.md +6 -6
- package/docs/architecture/memory.md +1 -1
- package/docs/architecture/scheduling.md +2 -3
- package/docs/architecture/security.md +5 -5
- package/docs/trusted-contact-access.md +5 -6
- package/package.json +4 -1
- package/src/__tests__/avatar-e2e.test.ts +18 -219
- package/src/__tests__/avatar-generator.test.ts +5 -57
- package/src/__tests__/browser-fill-credential.test.ts +5 -2
- package/src/__tests__/bundled-skill-retrieval-guard.test.ts +2 -1
- package/src/__tests__/channel-readiness-routes.test.ts +20 -19
- package/src/__tests__/cli.test.ts +23 -0
- package/src/__tests__/credential-broker-browser-fill.test.ts +23 -22
- package/src/__tests__/credential-broker-server-use.test.ts +22 -21
- package/src/__tests__/credential-broker.test.ts +2 -1
- package/src/__tests__/credential-metadata-store.test.ts +240 -18
- package/src/__tests__/credential-resolve.test.ts +5 -4
- package/src/__tests__/credential-security-e2e.test.ts +8 -8
- package/src/__tests__/credential-security-invariants.test.ts +104 -7
- package/src/__tests__/credential-vault-unit.test.ts +22 -20
- package/src/__tests__/credential-vault.test.ts +284 -12
- package/src/__tests__/credentials-cli.test.ts +11 -6
- package/src/__tests__/gateway-only-enforcement.test.ts +4 -2
- package/src/__tests__/gemini-image-service.test.ts +75 -45
- package/src/__tests__/gemini-provider.test.ts +9 -6
- package/src/__tests__/guardian-action-conversation-turn.test.ts +1 -33
- package/src/__tests__/guardian-action-copy-generator.test.ts +0 -20
- package/src/__tests__/guardian-action-followup-executor.test.ts +1 -28
- package/src/__tests__/guardian-action-followup-store.test.ts +1 -1
- package/src/__tests__/guardian-grant-minting.test.ts +35 -0
- package/src/__tests__/integration-status.test.ts +53 -21
- package/src/__tests__/managed-proxy-context.test.ts +5 -3
- package/src/__tests__/media-generate-image.test.ts +63 -2
- package/src/__tests__/media-reuse-story.e2e.test.ts +7 -3
- package/src/__tests__/messaging-send-tool.test.ts +4 -6
- package/src/__tests__/provider-fail-open-selection.test.ts +3 -1
- package/src/__tests__/provider-managed-proxy-integration.test.ts +70 -6
- package/src/__tests__/schedule-store.test.ts +1 -1
- package/src/__tests__/schema-transforms.test.ts +226 -0
- package/src/__tests__/script-proxy-injection-runtime.test.ts +23 -13
- package/src/__tests__/script-proxy-policy-runtime.test.ts +1 -1
- package/src/__tests__/script-proxy-session-manager.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +5 -3
- package/src/__tests__/session-messaging-secret-redirect.test.ts +5 -4
- package/src/__tests__/skills-uninstall.test.ts +2 -2
- package/src/__tests__/skills.test.ts +0 -9
- package/src/__tests__/slack-channel-config.test.ts +9 -8
- package/src/__tests__/slack-share-routes.test.ts +11 -6
- package/src/__tests__/telegram-bot-username-resolution.test.ts +3 -0
- package/src/__tests__/twilio-config.test.ts +2 -1
- package/src/__tests__/twilio-provider.test.ts +4 -2
- package/src/__tests__/twilio-routes.test.ts +5 -4
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -1
- package/src/approvals/AGENTS.md +1 -1
- package/src/calls/call-domain.ts +7 -4
- package/src/calls/twilio-config.ts +2 -1
- package/src/calls/twilio-provider.ts +2 -1
- package/src/calls/twilio-rest.ts +2 -2
- package/src/cli/commands/browser-relay.ts +40 -15
- package/src/cli/commands/credentials.ts +9 -8
- package/src/cli/commands/oauth.ts +1 -1
- package/src/cli.ts +3 -2
- package/src/config/bundled-skills/claude-code/TOOLS.json +0 -4
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +29 -32
- package/src/config/bundled-skills/gmail/SKILL.md +4 -4
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +54 -61
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +25 -28
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +14 -17
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +39 -44
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +61 -58
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +50 -49
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +11 -13
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +148 -146
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +4 -7
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +175 -173
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +4 -7
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +71 -76
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +32 -38
- package/src/config/bundled-skills/google-calendar/SKILL.md +2 -2
- package/src/config/bundled-skills/google-calendar/calendar-client.ts +70 -29
- package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +9 -10
- package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +5 -6
- package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +4 -5
- package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +14 -15
- package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +37 -37
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +4 -9
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +24 -3
- package/src/config/bundled-skills/messaging/SKILL.md +6 -6
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +62 -63
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +15 -16
- package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +4 -5
- package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +6 -7
- package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +4 -5
- package/src/config/bundled-skills/messaging/tools/messaging-read.ts +14 -15
- package/src/config/bundled-skills/messaging/tools/messaging-search.ts +4 -5
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +128 -128
- package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +33 -34
- package/src/config/bundled-skills/messaging/tools/shared.ts +11 -11
- package/src/config/bundled-skills/notifications/SKILL.md +1 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +5 -5
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/skill-management/SKILL.md +1 -1
- package/src/config/bundled-skills/slack/tools/shared.ts +4 -10
- package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +15 -16
- package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +95 -92
- package/src/config/loader.ts +6 -0
- package/src/daemon/computer-use-session.ts +7 -1
- package/src/daemon/guardian-action-generators.ts +4 -5
- package/src/daemon/handlers/config-slack-channel.ts +37 -20
- package/src/daemon/handlers/config-telegram.ts +33 -20
- package/src/daemon/lifecycle.ts +9 -1
- package/src/daemon/message-types/integrations.ts +1 -0
- package/src/daemon/ride-shotgun-handler.ts +3 -1
- package/src/daemon/session-messaging.ts +3 -1
- package/src/daemon/session-tool-setup.ts +18 -2
- package/src/daemon/session.ts +1 -1
- package/src/email/providers/index.ts +2 -1
- package/src/instrument.ts +15 -1
- package/src/media/app-icon-generator.ts +30 -4
- package/src/media/avatar-router.ts +28 -62
- package/src/media/gemini-image-service.ts +28 -2
- package/src/memory/canonical-guardian-store.ts +1 -1
- package/src/memory/guardian-action-store.ts +1 -1
- package/src/memory/schema/guardian.ts +1 -1
- package/src/messaging/provider.ts +16 -10
- package/src/messaging/providers/gmail/adapter.ts +40 -23
- package/src/messaging/providers/gmail/client.ts +203 -122
- package/src/messaging/providers/gmail/people-client.ts +26 -18
- package/src/messaging/providers/slack/adapter.ts +29 -19
- package/src/messaging/providers/slack/client.ts +265 -78
- package/src/messaging/providers/telegram-bot/adapter.ts +5 -4
- package/src/messaging/providers/whatsapp/adapter.ts +6 -3
- package/src/messaging/registry.ts +2 -1
- package/src/oauth/byo-connection.test.ts +436 -0
- package/src/oauth/byo-connection.ts +112 -0
- package/src/oauth/connect-orchestrator.ts +27 -0
- package/src/oauth/connection-resolver.ts +34 -0
- package/src/oauth/connection.ts +38 -0
- package/src/oauth/platform-connection.test.ts +163 -0
- package/src/oauth/platform-connection.ts +110 -0
- package/src/oauth/provider-base-urls.ts +21 -0
- package/src/oauth/provider-profiles.ts +1 -1
- package/src/oauth/token-persistence.ts +20 -20
- package/src/permissions/checker.ts +6 -1
- package/src/prompts/system-prompt.ts +52 -15
- package/src/prompts/templates/BOOTSTRAP.md +1 -1
- package/src/providers/gemini/client.ts +15 -6
- package/src/providers/managed-proxy/constants.ts +2 -2
- package/src/providers/managed-proxy/context.ts +5 -1
- package/src/providers/ratelimit.ts +17 -0
- package/src/providers/registry.ts +2 -2
- package/src/runtime/AGENTS.md +18 -1
- package/src/runtime/auth/route-policy.ts +1 -0
- package/src/runtime/channel-invite-transports/telegram.ts +2 -1
- package/src/runtime/channel-readiness-service.ts +168 -195
- package/src/runtime/channel-readiness-types.ts +4 -0
- package/src/runtime/guardian-action-conversation-turn.ts +1 -3
- package/src/runtime/guardian-action-followup-executor.ts +1 -2
- package/src/runtime/guardian-action-message-composer.ts +3 -23
- package/src/runtime/http-server.ts +9 -4
- package/src/runtime/http-types.ts +0 -1
- package/src/runtime/middleware/rate-limiter.ts +74 -20
- package/src/runtime/middleware/twilio-validation.ts +1 -3
- package/src/runtime/routes/channel-readiness-routes.ts +2 -0
- package/src/runtime/routes/diagnostics-routes.ts +11 -9
- package/src/runtime/routes/guardian-approval-interception.ts +20 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +71 -25
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +12 -5
- package/src/runtime/routes/integrations/slack/share.ts +3 -2
- package/src/runtime/routes/integrations/twilio.ts +6 -5
- package/src/runtime/routes/secret-routes.ts +3 -2
- package/src/runtime/routes/settings-routes.ts +75 -17
- package/src/runtime/telegram-streaming-delivery.test.ts +132 -0
- package/src/runtime/telegram-streaming-delivery.ts +11 -1
- package/src/schedule/integration-status.ts +5 -4
- package/src/security/credential-key.ts +170 -0
- package/src/security/token-manager.ts +36 -7
- package/src/tools/apps/definitions.ts +0 -5
- package/src/tools/assets/materialize.ts +0 -5
- package/src/tools/assets/search.ts +0 -5
- package/src/tools/browser/headless-browser.ts +1 -67
- package/src/tools/claude-code/claude-code.ts +0 -5
- package/src/tools/computer-use/request-computer-control.ts +0 -5
- package/src/tools/credentials/broker.ts +6 -4
- package/src/tools/credentials/metadata-store.ts +72 -20
- package/src/tools/credentials/resolve.ts +2 -1
- package/src/tools/credentials/vault.ts +77 -16
- package/src/tools/filesystem/edit.ts +1 -6
- package/src/tools/filesystem/read.ts +0 -5
- package/src/tools/filesystem/write.ts +1 -6
- package/src/tools/host-filesystem/edit.ts +1 -6
- package/src/tools/host-filesystem/read.ts +1 -6
- package/src/tools/host-filesystem/write.ts +1 -6
- package/src/tools/mcp/mcp-tool-factory.ts +18 -1
- package/src/tools/memory/definitions.ts +0 -5
- package/src/tools/network/web-fetch.ts +0 -5
- package/src/tools/network/web-search.ts +0 -5
- package/src/tools/schema-transforms.ts +99 -0
- package/src/tools/skills/load.ts +0 -5
- package/src/tools/swarm/delegate.ts +0 -5
- package/src/tools/system/avatar-generator.ts +3 -44
- package/src/tools/ui-surface/definitions.ts +0 -15
- package/src/tools/watch/screen-watch.ts +0 -5
- package/src/version.ts +10 -0
- package/src/watcher/providers/github.ts +51 -52
- package/src/watcher/providers/gmail.ts +88 -80
- package/src/watcher/providers/google-calendar.ts +93 -86
- package/src/watcher/providers/linear.ts +87 -93
- package/src/__tests__/avatar-router.test.ts +0 -149
- package/src/__tests__/managed-avatar-client.test.ts +0 -337
- package/src/config/bundled-skills/doordash/SKILL.md +0 -170
- package/src/config/bundled-skills/doordash/__tests__/doordash-client.test.ts +0 -205
- package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +0 -74
- package/src/config/bundled-skills/doordash/doordash-cli.ts +0 -1081
- package/src/config/bundled-skills/doordash/doordash-entry.ts +0 -22
- package/src/config/bundled-skills/doordash/lib/cart-queries.ts +0 -787
- package/src/config/bundled-skills/doordash/lib/client.ts +0 -1069
- package/src/config/bundled-skills/doordash/lib/order-queries.ts +0 -85
- package/src/config/bundled-skills/doordash/lib/queries.ts +0 -28
- package/src/config/bundled-skills/doordash/lib/query-extractor.ts +0 -94
- package/src/config/bundled-skills/doordash/lib/search-queries.ts +0 -203
- package/src/config/bundled-skills/doordash/lib/session.ts +0 -96
- package/src/config/bundled-skills/doordash/lib/shared/errors.ts +0 -61
- package/src/config/bundled-skills/doordash/lib/shared/network-recorder.ts +0 -380
- package/src/config/bundled-skills/doordash/lib/shared/platform.ts +0 -55
- package/src/config/bundled-skills/doordash/lib/shared/recording-store.ts +0 -43
- package/src/config/bundled-skills/doordash/lib/shared/recording-types.ts +0 -49
- package/src/config/bundled-skills/doordash/lib/shared/truncate.ts +0 -6
- package/src/config/bundled-skills/doordash/lib/store-queries.ts +0 -246
- package/src/config/bundled-skills/doordash/lib/types.ts +0 -367
- package/src/media/avatar-types.ts +0 -53
- package/src/media/managed-avatar-client.ts +0 -225
|
@@ -12,7 +12,7 @@ import type {
|
|
|
12
12
|
ToolExecutionResult,
|
|
13
13
|
} from "../../../../tools/types.js";
|
|
14
14
|
import { truncate } from "../../../../util/truncate.js";
|
|
15
|
-
import { err, ok, resolveProvider
|
|
15
|
+
import { err, getProviderConnection, ok, resolveProvider } from "./shared.js";
|
|
16
16
|
|
|
17
17
|
function upsertMemoryItem(opts: {
|
|
18
18
|
kind: string;
|
|
@@ -91,76 +91,75 @@ export async function run(
|
|
|
91
91
|
|
|
92
92
|
try {
|
|
93
93
|
const provider = resolveProvider(platform);
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
94
|
+
const conn = getProviderConnection(provider);
|
|
95
|
+
// Search for sent messages using the platform's search
|
|
96
|
+
const query =
|
|
97
|
+
queryFilter ?? (provider.id === "gmail" ? "in:sent" : "from:me");
|
|
98
|
+
const searchResult = await provider.search(conn, query, {
|
|
99
|
+
count: maxMessages,
|
|
100
|
+
});
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
102
|
+
if (searchResult.messages.length === 0) {
|
|
103
|
+
return err(
|
|
104
|
+
"No sent messages found. Send some messages first, then try again.",
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
const result = await extractStylePatterns(searchResult.messages);
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
if (result.stylePatterns.length === 0) {
|
|
111
|
+
return err("No style patterns were extracted. Try with more messages.");
|
|
112
|
+
}
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
const scopeId = context.memoryScopeId ?? "default";
|
|
115
|
+
let savedCount = 0;
|
|
116
116
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
117
|
+
for (const pattern of result.stylePatterns) {
|
|
118
|
+
const subject = `${provider.id} writing style: ${pattern.aspect}`;
|
|
119
|
+
const importance = clampUnitInterval(
|
|
120
|
+
Math.min(0.85, Math.max(0.55, pattern.importance ?? 0.65)),
|
|
121
|
+
);
|
|
122
|
+
upsertMemoryItem({
|
|
123
|
+
kind: "style",
|
|
124
|
+
subject,
|
|
125
|
+
statement: pattern.summary,
|
|
126
|
+
importance,
|
|
127
|
+
scopeId,
|
|
128
|
+
});
|
|
129
|
+
savedCount++;
|
|
130
|
+
}
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
132
|
+
for (const contact of result.contactObservations) {
|
|
133
|
+
if (!contact.name || !contact.toneNote) continue;
|
|
134
|
+
const subject = `${provider.id} relationship: ${contact.name}`;
|
|
135
|
+
upsertMemoryItem({
|
|
136
|
+
kind: "relationship",
|
|
137
|
+
subject,
|
|
138
|
+
statement: truncate(
|
|
139
|
+
`${contact.name} (${contact.email}): ${contact.toneNote}`,
|
|
140
|
+
500,
|
|
141
|
+
"",
|
|
142
|
+
),
|
|
143
|
+
importance: 0.6,
|
|
144
|
+
scopeId,
|
|
145
|
+
});
|
|
146
|
+
savedCount++;
|
|
147
|
+
}
|
|
148
148
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
149
|
+
const aspects = result.stylePatterns.map((p) => p.aspect).join(", ");
|
|
150
|
+
const contactCount = result.contactObservations.length;
|
|
151
|
+
const summary = [
|
|
152
|
+
`Analyzed ${searchResult.messages.length} messages on ${provider.displayName}.`,
|
|
153
|
+
`Extracted ${result.stylePatterns.length} style patterns (${aspects}).`,
|
|
154
|
+
contactCount > 0
|
|
155
|
+
? `Noted ${contactCount} recurring contact relationship(s).`
|
|
156
|
+
: "",
|
|
157
|
+
`Saved ${savedCount} memory items. Future drafts will automatically reflect your writing style.`,
|
|
158
|
+
]
|
|
159
|
+
.filter(Boolean)
|
|
160
|
+
.join(" ");
|
|
161
161
|
|
|
162
|
-
|
|
163
|
-
});
|
|
162
|
+
return ok(summary);
|
|
164
163
|
} catch (e) {
|
|
165
164
|
return err(e instanceof Error ? e.message : String(e));
|
|
166
165
|
}
|
|
@@ -2,7 +2,7 @@ import type {
|
|
|
2
2
|
ToolContext,
|
|
3
3
|
ToolExecutionResult,
|
|
4
4
|
} from "../../../../tools/types.js";
|
|
5
|
-
import { err, ok, resolveProvider
|
|
5
|
+
import { err, getProviderConnection, ok, resolveProvider } from "./shared.js";
|
|
6
6
|
|
|
7
7
|
export async function run(
|
|
8
8
|
input: Record<string, unknown>,
|
|
@@ -30,21 +30,20 @@ export async function run(
|
|
|
30
30
|
);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
});
|
|
33
|
+
const conn = getProviderConnection(provider);
|
|
34
|
+
const result = await provider.archiveByQuery!(conn, query);
|
|
35
|
+
|
|
36
|
+
if (result.archived === 0) {
|
|
37
|
+
return ok("No messages matched the query. Nothing archived.");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const summary = `Archived ${result.archived} message(s) matching query: ${query}`;
|
|
41
|
+
if (result.truncated) {
|
|
42
|
+
return ok(
|
|
43
|
+
`${summary}\n\nNote: this operation was capped at 5000 messages. Additional messages matching the query may remain in the inbox. Run the command again to archive more.`,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return ok(summary);
|
|
48
47
|
} catch (e) {
|
|
49
48
|
return err(e instanceof Error ? e.message : String(e));
|
|
50
49
|
}
|
|
@@ -2,7 +2,7 @@ import type {
|
|
|
2
2
|
ToolContext,
|
|
3
3
|
ToolExecutionResult,
|
|
4
4
|
} from "../../../../tools/types.js";
|
|
5
|
-
import { err, ok, resolveProvider
|
|
5
|
+
import { err, getProviderConnection, ok, resolveProvider } from "./shared.js";
|
|
6
6
|
|
|
7
7
|
export async function run(
|
|
8
8
|
input: Record<string, unknown>,
|
|
@@ -12,10 +12,9 @@ export async function run(
|
|
|
12
12
|
|
|
13
13
|
try {
|
|
14
14
|
const provider = resolveProvider(platform);
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
});
|
|
15
|
+
const conn = getProviderConnection(provider);
|
|
16
|
+
const info = await provider.testConnection(conn);
|
|
17
|
+
return ok(JSON.stringify(info, null, 2));
|
|
19
18
|
} catch (e) {
|
|
20
19
|
return err(e instanceof Error ? e.message : String(e));
|
|
21
20
|
}
|
|
@@ -2,7 +2,7 @@ import type {
|
|
|
2
2
|
ToolContext,
|
|
3
3
|
ToolExecutionResult,
|
|
4
4
|
} from "../../../../tools/types.js";
|
|
5
|
-
import { err, ok, resolveProvider
|
|
5
|
+
import { err, getProviderConnection, ok, resolveProvider } from "./shared.js";
|
|
6
6
|
|
|
7
7
|
export async function run(
|
|
8
8
|
input: Record<string, unknown>,
|
|
@@ -14,13 +14,12 @@ export async function run(
|
|
|
14
14
|
|
|
15
15
|
try {
|
|
16
16
|
const provider = resolveProvider(platform);
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
});
|
|
22
|
-
return ok(JSON.stringify(conversations, null, 2));
|
|
17
|
+
const conn = getProviderConnection(provider);
|
|
18
|
+
const conversations = await provider.listConversations(conn, {
|
|
19
|
+
types: types as Array<"channel" | "dm" | "group" | "inbox"> | undefined,
|
|
20
|
+
limit,
|
|
23
21
|
});
|
|
22
|
+
return ok(JSON.stringify(conversations, null, 2));
|
|
24
23
|
} catch (e) {
|
|
25
24
|
return err(e instanceof Error ? e.message : String(e));
|
|
26
25
|
}
|
|
@@ -2,7 +2,7 @@ import type {
|
|
|
2
2
|
ToolContext,
|
|
3
3
|
ToolExecutionResult,
|
|
4
4
|
} from "../../../../tools/types.js";
|
|
5
|
-
import { err, ok, resolveProvider
|
|
5
|
+
import { err, getProviderConnection, ok, resolveProvider } from "./shared.js";
|
|
6
6
|
|
|
7
7
|
export async function run(
|
|
8
8
|
input: Record<string, unknown>,
|
|
@@ -23,10 +23,9 @@ export async function run(
|
|
|
23
23
|
`${provider.displayName} does not support marking messages as read.`,
|
|
24
24
|
);
|
|
25
25
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
});
|
|
26
|
+
const conn = getProviderConnection(provider);
|
|
27
|
+
await provider.markRead(conn, conversationId, messageId);
|
|
28
|
+
return ok("Marked as read.");
|
|
30
29
|
} catch (e) {
|
|
31
30
|
return err(e instanceof Error ? e.message : String(e));
|
|
32
31
|
}
|
|
@@ -2,7 +2,7 @@ import type {
|
|
|
2
2
|
ToolContext,
|
|
3
3
|
ToolExecutionResult,
|
|
4
4
|
} from "../../../../tools/types.js";
|
|
5
|
-
import { err, ok, resolveProvider
|
|
5
|
+
import { err, getProviderConnection, ok, resolveProvider } from "./shared.js";
|
|
6
6
|
|
|
7
7
|
export async function run(
|
|
8
8
|
input: Record<string, unknown>,
|
|
@@ -19,20 +19,19 @@ export async function run(
|
|
|
19
19
|
|
|
20
20
|
try {
|
|
21
21
|
const provider = resolveProvider(platform);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
});
|
|
22
|
+
const conn = getProviderConnection(provider);
|
|
23
|
+
let messages;
|
|
24
|
+
if (threadId && provider.getThreadReplies) {
|
|
25
|
+
messages = await provider.getThreadReplies(
|
|
26
|
+
conn,
|
|
27
|
+
conversationId,
|
|
28
|
+
threadId,
|
|
29
|
+
{ limit },
|
|
30
|
+
);
|
|
31
|
+
} else {
|
|
32
|
+
messages = await provider.getHistory(conn, conversationId, { limit });
|
|
33
|
+
}
|
|
34
|
+
return ok(JSON.stringify(messages, null, 2));
|
|
36
35
|
} catch (e) {
|
|
37
36
|
return err(e instanceof Error ? e.message : String(e));
|
|
38
37
|
}
|
|
@@ -2,7 +2,7 @@ import type {
|
|
|
2
2
|
ToolContext,
|
|
3
3
|
ToolExecutionResult,
|
|
4
4
|
} from "../../../../tools/types.js";
|
|
5
|
-
import { err, ok, resolveProvider
|
|
5
|
+
import { err, getProviderConnection, ok, resolveProvider } from "./shared.js";
|
|
6
6
|
|
|
7
7
|
export async function run(
|
|
8
8
|
input: Record<string, unknown>,
|
|
@@ -18,10 +18,9 @@ export async function run(
|
|
|
18
18
|
|
|
19
19
|
try {
|
|
20
20
|
const provider = resolveProvider(platform);
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
});
|
|
21
|
+
const conn = getProviderConnection(provider);
|
|
22
|
+
const result = await provider.search(conn, query, { count: maxResults });
|
|
23
|
+
return ok(JSON.stringify(result, null, 2));
|
|
25
24
|
} catch (e) {
|
|
26
25
|
return err(e instanceof Error ? e.message : String(e));
|
|
27
26
|
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
listMessages,
|
|
10
10
|
} from "../../../../messaging/providers/gmail/client.js";
|
|
11
11
|
import { buildMultipartMime } from "../../../../messaging/providers/gmail/mime-builder.js";
|
|
12
|
+
import type { OAuthConnection } from "../../../../oauth/connection.js";
|
|
12
13
|
import type {
|
|
13
14
|
ToolContext,
|
|
14
15
|
ToolExecutionResult,
|
|
@@ -18,10 +19,10 @@ import {
|
|
|
18
19
|
err,
|
|
19
20
|
extractEmail,
|
|
20
21
|
extractHeader,
|
|
22
|
+
getProviderConnection,
|
|
21
23
|
ok,
|
|
22
24
|
parseAddressList,
|
|
23
25
|
resolveProvider,
|
|
24
|
-
withProviderToken,
|
|
25
26
|
} from "./shared.js";
|
|
26
27
|
|
|
27
28
|
export async function run(
|
|
@@ -51,115 +52,66 @@ export async function run(
|
|
|
51
52
|
return err("Attachments are only supported on Gmail.");
|
|
52
53
|
}
|
|
53
54
|
|
|
55
|
+
const conn = getProviderConnection(provider);
|
|
56
|
+
|
|
54
57
|
// Gmail: create a draft instead of sending directly
|
|
55
58
|
if (provider.id === "gmail") {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const messages = await batchGetMessages(
|
|
66
|
-
token,
|
|
67
|
-
list.messages.map((m) => m.id),
|
|
68
|
-
"metadata",
|
|
69
|
-
["From", "To", "Cc", "Message-ID", "Subject"],
|
|
70
|
-
);
|
|
59
|
+
const gmailConn = conn as OAuthConnection;
|
|
60
|
+
// Reply mode: thread_id provided — create a threaded draft with reply-all recipients
|
|
61
|
+
if (threadId) {
|
|
62
|
+
// Fetch thread messages to extract recipients and threading headers
|
|
63
|
+
const list = await listMessages(gmailConn, `thread:${threadId}`, 10);
|
|
64
|
+
if (!list.messages?.length) {
|
|
65
|
+
return err("No messages found in this thread.");
|
|
66
|
+
}
|
|
71
67
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (replySubject && !replySubject.startsWith("Re:")) {
|
|
79
|
-
replySubject = `Re: ${replySubject}`;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Build reply-all recipient list, excluding the user's own email
|
|
83
|
-
const profile = await getProfile(token);
|
|
84
|
-
const userEmail = profile.emailAddress.toLowerCase();
|
|
85
|
-
|
|
86
|
-
const allRecipients = new Set<string>();
|
|
87
|
-
const allCc = new Set<string>();
|
|
88
|
-
|
|
89
|
-
// From the latest message: From goes to To, original To/Cc go to Cc
|
|
90
|
-
const fromAddr = extractHeader(latestHeaders, "From");
|
|
91
|
-
const toAddrs = extractHeader(latestHeaders, "To");
|
|
92
|
-
const ccAddrs = extractHeader(latestHeaders, "Cc");
|
|
93
|
-
|
|
94
|
-
if (fromAddr) allRecipients.add(fromAddr);
|
|
95
|
-
for (const addr of parseAddressList(toAddrs)) {
|
|
96
|
-
allRecipients.add(addr);
|
|
97
|
-
}
|
|
98
|
-
for (const addr of parseAddressList(ccAddrs)) {
|
|
99
|
-
allCc.add(addr);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Remove user's own email from recipients using exact email comparison
|
|
103
|
-
const filterSelf = (addr: string) => extractEmail(addr) !== userEmail;
|
|
104
|
-
const toList = [...allRecipients].filter(filterSelf);
|
|
105
|
-
const ccList = [...allCc].filter(filterSelf);
|
|
106
|
-
|
|
107
|
-
if (toList.length === 0) {
|
|
108
|
-
return err("Could not determine reply recipients from thread.");
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// With attachments: build multipart MIME for threaded reply
|
|
112
|
-
if (attachmentPaths?.length) {
|
|
113
|
-
const attachments = await Promise.all(
|
|
114
|
-
attachmentPaths.map(async (filePath) => {
|
|
115
|
-
const data = await readFile(filePath);
|
|
116
|
-
const filename = basename(filePath);
|
|
117
|
-
const mimeType = guessMimeType(filePath);
|
|
118
|
-
return { filename, mimeType, data };
|
|
119
|
-
}),
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
const raw = buildMultipartMime({
|
|
123
|
-
to: toList.join(", "),
|
|
124
|
-
subject: replySubject,
|
|
125
|
-
body: text,
|
|
126
|
-
inReplyTo: messageIdHeader || undefined,
|
|
127
|
-
cc: ccList.length > 0 ? ccList.join(", ") : undefined,
|
|
128
|
-
attachments,
|
|
129
|
-
});
|
|
130
|
-
const draft = await createDraftRaw(token, raw, threadId);
|
|
131
|
-
|
|
132
|
-
const filenames = attachments.map((a) => a.filename).join(", ");
|
|
133
|
-
const recipientSummary =
|
|
134
|
-
ccList.length > 0
|
|
135
|
-
? `To: ${toList.join(", ")}; Cc: ${ccList.join(", ")}`
|
|
136
|
-
: `To: ${toList.join(", ")}`;
|
|
137
|
-
return ok(
|
|
138
|
-
`Gmail draft created with ${attachments.length} attachment(s): ${filenames} (Draft ID: ${draft.id}). ${recipientSummary}. Review in Gmail Drafts, then tell me to send it or send it yourself.`,
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const draft = await createDraft(
|
|
143
|
-
token,
|
|
144
|
-
toList.join(", "),
|
|
145
|
-
replySubject,
|
|
146
|
-
text,
|
|
147
|
-
messageIdHeader || undefined,
|
|
148
|
-
ccList.length > 0 ? ccList.join(", ") : undefined,
|
|
149
|
-
undefined,
|
|
150
|
-
threadId,
|
|
151
|
-
);
|
|
68
|
+
const messages = await batchGetMessages(
|
|
69
|
+
gmailConn,
|
|
70
|
+
list.messages.map((m) => m.id),
|
|
71
|
+
"metadata",
|
|
72
|
+
["From", "To", "Cc", "Message-ID", "Subject"],
|
|
73
|
+
);
|
|
152
74
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
75
|
+
// Use the latest message for threading and recipient extraction
|
|
76
|
+
const latest = messages[messages.length - 1];
|
|
77
|
+
const latestHeaders = latest.payload?.headers ?? [];
|
|
78
|
+
|
|
79
|
+
const messageIdHeader = extractHeader(latestHeaders, "Message-ID");
|
|
80
|
+
let replySubject = extractHeader(latestHeaders, "Subject");
|
|
81
|
+
if (replySubject && !replySubject.startsWith("Re:")) {
|
|
82
|
+
replySubject = `Re: ${replySubject}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Build reply-all recipient list, excluding the user's own email
|
|
86
|
+
const profile = await getProfile(gmailConn);
|
|
87
|
+
const userEmail = profile.emailAddress.toLowerCase();
|
|
88
|
+
|
|
89
|
+
const allRecipients = new Set<string>();
|
|
90
|
+
const allCc = new Set<string>();
|
|
91
|
+
|
|
92
|
+
// From the latest message: From goes to To, original To/Cc go to Cc
|
|
93
|
+
const fromAddr = extractHeader(latestHeaders, "From");
|
|
94
|
+
const toAddrs = extractHeader(latestHeaders, "To");
|
|
95
|
+
const ccAddrs = extractHeader(latestHeaders, "Cc");
|
|
96
|
+
|
|
97
|
+
if (fromAddr) allRecipients.add(fromAddr);
|
|
98
|
+
for (const addr of parseAddressList(toAddrs)) {
|
|
99
|
+
allRecipients.add(addr);
|
|
100
|
+
}
|
|
101
|
+
for (const addr of parseAddressList(ccAddrs)) {
|
|
102
|
+
allCc.add(addr);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Remove user's own email from recipients using exact email comparison
|
|
106
|
+
const filterSelf = (addr: string) => extractEmail(addr) !== userEmail;
|
|
107
|
+
const toList = [...allRecipients].filter(filterSelf);
|
|
108
|
+
const ccList = [...allCc].filter(filterSelf);
|
|
109
|
+
|
|
110
|
+
if (toList.length === 0) {
|
|
111
|
+
return err("Could not determine reply recipients from thread.");
|
|
160
112
|
}
|
|
161
113
|
|
|
162
|
-
// With attachments: build multipart MIME
|
|
114
|
+
// With attachments: build multipart MIME for threaded reply
|
|
163
115
|
if (attachmentPaths?.length) {
|
|
164
116
|
const attachments = await Promise.all(
|
|
165
117
|
attachmentPaths.map(async (filePath) => {
|
|
@@ -171,51 +123,99 @@ export async function run(
|
|
|
171
123
|
);
|
|
172
124
|
|
|
173
125
|
const raw = buildMultipartMime({
|
|
174
|
-
to:
|
|
175
|
-
subject:
|
|
126
|
+
to: toList.join(", "),
|
|
127
|
+
subject: replySubject,
|
|
176
128
|
body: text,
|
|
177
|
-
inReplyTo,
|
|
129
|
+
inReplyTo: messageIdHeader || undefined,
|
|
130
|
+
cc: ccList.length > 0 ? ccList.join(", ") : undefined,
|
|
178
131
|
attachments,
|
|
179
132
|
});
|
|
180
|
-
const draft = await createDraftRaw(
|
|
133
|
+
const draft = await createDraftRaw(gmailConn, raw, threadId);
|
|
181
134
|
|
|
182
135
|
const filenames = attachments.map((a) => a.filename).join(", ");
|
|
136
|
+
const recipientSummary =
|
|
137
|
+
ccList.length > 0
|
|
138
|
+
? `To: ${toList.join(", ")}; Cc: ${ccList.join(", ")}`
|
|
139
|
+
: `To: ${toList.join(", ")}`;
|
|
183
140
|
return ok(
|
|
184
|
-
`Gmail draft created with ${attachments.length} attachment(s): ${filenames} (Draft ID: ${draft.id}). Review in Gmail Drafts, then tell me to send it or send it yourself.`,
|
|
141
|
+
`Gmail draft created with ${attachments.length} attachment(s): ${filenames} (Draft ID: ${draft.id}). ${recipientSummary}. Review in Gmail Drafts, then tell me to send it or send it yourself.`,
|
|
185
142
|
);
|
|
186
143
|
}
|
|
187
144
|
|
|
188
|
-
// Without attachments: use standard createDraft
|
|
189
145
|
const draft = await createDraft(
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
146
|
+
gmailConn,
|
|
147
|
+
toList.join(", "),
|
|
148
|
+
replySubject,
|
|
193
149
|
text,
|
|
194
|
-
|
|
195
|
-
undefined,
|
|
150
|
+
messageIdHeader || undefined,
|
|
151
|
+
ccList.length > 0 ? ccList.join(", ") : undefined,
|
|
196
152
|
undefined,
|
|
197
153
|
threadId,
|
|
198
154
|
);
|
|
155
|
+
|
|
156
|
+
const recipientSummary =
|
|
157
|
+
ccList.length > 0
|
|
158
|
+
? `To: ${toList.join(", ")}; Cc: ${ccList.join(", ")}`
|
|
159
|
+
: `To: ${toList.join(", ")}`;
|
|
199
160
|
return ok(
|
|
200
|
-
`Gmail draft created (ID: ${draft.id}). Review
|
|
161
|
+
`Gmail draft created (ID: ${draft.id}). ${recipientSummary}. Review in Gmail Drafts, then tell me to send it or send it yourself.`,
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// With attachments: build multipart MIME and use createDraftRaw
|
|
166
|
+
if (attachmentPaths?.length) {
|
|
167
|
+
const attachments = await Promise.all(
|
|
168
|
+
attachmentPaths.map(async (filePath) => {
|
|
169
|
+
const data = await readFile(filePath);
|
|
170
|
+
const filename = basename(filePath);
|
|
171
|
+
const mimeType = guessMimeType(filePath);
|
|
172
|
+
return { filename, mimeType, data };
|
|
173
|
+
}),
|
|
201
174
|
);
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
175
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
176
|
+
const raw = buildMultipartMime({
|
|
177
|
+
to: conversationId,
|
|
178
|
+
subject: subject ?? "",
|
|
179
|
+
body: text,
|
|
180
|
+
inReplyTo,
|
|
181
|
+
attachments,
|
|
182
|
+
});
|
|
183
|
+
const draft = await createDraftRaw(gmailConn, raw, threadId);
|
|
184
|
+
|
|
185
|
+
const filenames = attachments.map((a) => a.filename).join(", ");
|
|
186
|
+
return ok(
|
|
187
|
+
`Gmail draft created with ${attachments.length} attachment(s): ${filenames} (Draft ID: ${draft.id}). Review in Gmail Drafts, then tell me to send it or send it yourself.`,
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Without attachments: use standard createDraft
|
|
192
|
+
const draft = await createDraft(
|
|
193
|
+
gmailConn,
|
|
194
|
+
conversationId,
|
|
195
|
+
subject ?? "",
|
|
196
|
+
text,
|
|
209
197
|
inReplyTo,
|
|
198
|
+
undefined,
|
|
199
|
+
undefined,
|
|
210
200
|
threadId,
|
|
211
|
-
|
|
212
|
-
|
|
201
|
+
);
|
|
202
|
+
return ok(
|
|
203
|
+
`Gmail draft created (ID: ${draft.id}). Review it in your Gmail Drafts, then tell me to send it or send it yourself from Gmail.`,
|
|
204
|
+
);
|
|
205
|
+
}
|
|
213
206
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
207
|
+
// Non-Gmail platforms
|
|
208
|
+
const result = await provider.sendMessage(conn, conversationId, text, {
|
|
209
|
+
subject,
|
|
210
|
+
inReplyTo,
|
|
211
|
+
threadId,
|
|
212
|
+
assistantId: context.assistantId,
|
|
218
213
|
});
|
|
214
|
+
|
|
215
|
+
const threadSuffix = result.threadId
|
|
216
|
+
? `, "thread_id": "${result.threadId}"`
|
|
217
|
+
: "";
|
|
218
|
+
return ok(`Message sent (ID: ${result.id}${threadSuffix}).`);
|
|
219
219
|
} catch (e) {
|
|
220
220
|
return err(e instanceof Error ? e.message : String(e));
|
|
221
221
|
}
|