@vellumai/assistant 0.4.46 → 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 +5 -5
- package/docs/architecture/security.md +5 -5
- package/package.json +1 -1
- 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 -6
- 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__/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/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 -1
- 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/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 +26 -3
- package/src/media/gemini-image-service.ts +28 -2
- 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 +5 -1
- package/src/prompts/system-prompt.ts +49 -12
- 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 +17 -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 -1
- 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/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/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 +0 -5
- 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
|
@@ -16,8 +16,8 @@ You are a Google Calendar assistant with full access to the user's calendar. Use
|
|
|
16
16
|
Before using any Calendar tool, verify that Google Calendar is connected by attempting a lightweight call (e.g., `calendar_list_events` with a narrow date range). If the call fails with a token/authorization error:
|
|
17
17
|
|
|
18
18
|
1. **Do NOT call `credential_store oauth2_connect` yourself.** You do not have valid OAuth client credentials, and fabricating a client_id will cause a "401: invalid_client" error from Google.
|
|
19
|
-
2. Instead, load the **google-oauth-
|
|
20
|
-
- Call `skill_load` with `skill: "google-oauth-
|
|
19
|
+
2. Instead, load the **google-oauth-applescript** skill, which walks the user through creating real credentials in Google Cloud Console:
|
|
20
|
+
- Call `skill_load` with `skill: "google-oauth-applescript"` to load the dependency skill.
|
|
21
21
|
3. Tell the user: _"Google Calendar isn't connected yet. I've loaded a setup guide that will walk you through connecting your Google account — it only takes a couple of minutes."_
|
|
22
22
|
|
|
23
23
|
## Capabilities
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { OAuthConnection } from "../../../oauth/connection.js";
|
|
2
|
+
import { GOOGLE_CALENDAR_BASE_URL } from "../../../oauth/provider-base-urls.js";
|
|
1
3
|
import type {
|
|
2
4
|
CalendarEvent,
|
|
3
5
|
CalendarEventsListResponse,
|
|
@@ -6,8 +8,6 @@ import type {
|
|
|
6
8
|
FreeBusyResponse,
|
|
7
9
|
} from "./types.js";
|
|
8
10
|
|
|
9
|
-
const CALENDAR_API_BASE = "https://www.googleapis.com/calendar/v3";
|
|
10
|
-
|
|
11
11
|
export class CalendarApiError extends Error {
|
|
12
12
|
constructor(
|
|
13
13
|
public readonly status: number,
|
|
@@ -20,39 +20,80 @@ export class CalendarApiError extends Error {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
async function request<T>(
|
|
23
|
-
|
|
23
|
+
connection: OAuthConnection,
|
|
24
24
|
path: string,
|
|
25
25
|
options?: RequestInit,
|
|
26
26
|
): Promise<T> {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
const method = (options?.method ?? "GET").toUpperCase();
|
|
28
|
+
|
|
29
|
+
// Extract non-auth headers
|
|
30
|
+
let extraHeaders: Record<string, string> | undefined;
|
|
31
|
+
if (options?.headers) {
|
|
32
|
+
const raw = options.headers;
|
|
33
|
+
const result: Record<string, string> = {};
|
|
34
|
+
if (raw instanceof Headers) {
|
|
35
|
+
raw.forEach((v, k) => {
|
|
36
|
+
if (k.toLowerCase() !== "authorization") result[k] = v;
|
|
37
|
+
});
|
|
38
|
+
} else if (Array.isArray(raw)) {
|
|
39
|
+
for (const [k, v] of raw) {
|
|
40
|
+
if (k.toLowerCase() !== "authorization") result[k] = v;
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
44
|
+
if (k.toLowerCase() !== "authorization" && v !== undefined)
|
|
45
|
+
result[k] = v;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (Object.keys(result).length > 0) extraHeaders = result;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Extract body
|
|
52
|
+
let reqBody: unknown | undefined;
|
|
53
|
+
if (options?.body) {
|
|
54
|
+
if (typeof options.body === "string") {
|
|
55
|
+
try {
|
|
56
|
+
reqBody = JSON.parse(options.body);
|
|
57
|
+
} catch {
|
|
58
|
+
reqBody = options.body;
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
reqBody = options.body;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const resp = await connection.request({
|
|
66
|
+
method,
|
|
67
|
+
path,
|
|
68
|
+
baseUrl: GOOGLE_CALENDAR_BASE_URL,
|
|
30
69
|
headers: {
|
|
31
|
-
Authorization: `Bearer ${token}`,
|
|
32
70
|
"Content-Type": "application/json",
|
|
33
|
-
...
|
|
71
|
+
...extraHeaders,
|
|
34
72
|
},
|
|
73
|
+
body: reqBody,
|
|
35
74
|
});
|
|
36
|
-
|
|
37
|
-
|
|
75
|
+
|
|
76
|
+
if (resp.status < 200 || resp.status >= 300) {
|
|
77
|
+
const bodyStr =
|
|
78
|
+
typeof resp.body === "string"
|
|
79
|
+
? resp.body
|
|
80
|
+
: JSON.stringify(resp.body ?? "");
|
|
38
81
|
throw new CalendarApiError(
|
|
39
82
|
resp.status,
|
|
40
|
-
|
|
41
|
-
`Calendar API ${resp.status}: ${
|
|
83
|
+
"",
|
|
84
|
+
`Calendar API ${resp.status}: ${bodyStr}`,
|
|
42
85
|
);
|
|
43
86
|
}
|
|
44
|
-
|
|
45
|
-
if (resp.status === 204 ||
|
|
87
|
+
|
|
88
|
+
if (resp.status === 204 || resp.body === undefined) {
|
|
46
89
|
return undefined as T;
|
|
47
90
|
}
|
|
48
|
-
|
|
49
|
-
if (!text) return undefined as T;
|
|
50
|
-
return JSON.parse(text) as T;
|
|
91
|
+
return resp.body as T;
|
|
51
92
|
}
|
|
52
93
|
|
|
53
94
|
/** List events from a calendar. */
|
|
54
95
|
export async function listEvents(
|
|
55
|
-
|
|
96
|
+
connection: OAuthConnection,
|
|
56
97
|
calendarId = "primary",
|
|
57
98
|
options?: {
|
|
58
99
|
timeMin?: string;
|
|
@@ -86,19 +127,19 @@ export async function listEvents(
|
|
|
86
127
|
if (options?.syncToken) params.set("syncToken", options.syncToken);
|
|
87
128
|
|
|
88
129
|
return request<CalendarEventsListResponse>(
|
|
89
|
-
|
|
130
|
+
connection,
|
|
90
131
|
`/calendars/${encodeURIComponent(calendarId)}/events?${params}`,
|
|
91
132
|
);
|
|
92
133
|
}
|
|
93
134
|
|
|
94
135
|
/** Get a single event by ID. */
|
|
95
136
|
export async function getEvent(
|
|
96
|
-
|
|
137
|
+
connection: OAuthConnection,
|
|
97
138
|
eventId: string,
|
|
98
139
|
calendarId = "primary",
|
|
99
140
|
): Promise<CalendarEvent> {
|
|
100
141
|
return request<CalendarEvent>(
|
|
101
|
-
|
|
142
|
+
connection,
|
|
102
143
|
`/calendars/${encodeURIComponent(calendarId)}/events/${encodeURIComponent(
|
|
103
144
|
eventId,
|
|
104
145
|
)}`,
|
|
@@ -107,7 +148,7 @@ export async function getEvent(
|
|
|
107
148
|
|
|
108
149
|
/** Create a new event. */
|
|
109
150
|
export async function createEvent(
|
|
110
|
-
|
|
151
|
+
connection: OAuthConnection,
|
|
111
152
|
event: {
|
|
112
153
|
summary: string;
|
|
113
154
|
start: { dateTime?: string; date?: string; timeZone?: string };
|
|
@@ -121,7 +162,7 @@ export async function createEvent(
|
|
|
121
162
|
): Promise<CalendarEvent> {
|
|
122
163
|
const params = new URLSearchParams({ sendUpdates });
|
|
123
164
|
return request<CalendarEvent>(
|
|
124
|
-
|
|
165
|
+
connection,
|
|
125
166
|
`/calendars/${encodeURIComponent(calendarId)}/events?${params}`,
|
|
126
167
|
{
|
|
127
168
|
method: "POST",
|
|
@@ -132,7 +173,7 @@ export async function createEvent(
|
|
|
132
173
|
|
|
133
174
|
/** Update an event (patch). */
|
|
134
175
|
export async function patchEvent(
|
|
135
|
-
|
|
176
|
+
connection: OAuthConnection,
|
|
136
177
|
eventId: string,
|
|
137
178
|
updates: Partial<{
|
|
138
179
|
summary: string;
|
|
@@ -147,7 +188,7 @@ export async function patchEvent(
|
|
|
147
188
|
): Promise<CalendarEvent> {
|
|
148
189
|
const params = new URLSearchParams({ sendUpdates });
|
|
149
190
|
return request<CalendarEvent>(
|
|
150
|
-
|
|
191
|
+
connection,
|
|
151
192
|
`/calendars/${encodeURIComponent(calendarId)}/events/${encodeURIComponent(
|
|
152
193
|
eventId,
|
|
153
194
|
)}?${params}`,
|
|
@@ -160,10 +201,10 @@ export async function patchEvent(
|
|
|
160
201
|
|
|
161
202
|
/** Query free/busy information. */
|
|
162
203
|
export async function freeBusy(
|
|
163
|
-
|
|
204
|
+
connection: OAuthConnection,
|
|
164
205
|
query: FreeBusyRequest,
|
|
165
206
|
): Promise<FreeBusyResponse> {
|
|
166
|
-
return request<FreeBusyResponse>(
|
|
207
|
+
return request<FreeBusyResponse>(connection, "/freeBusy", {
|
|
167
208
|
method: "POST",
|
|
168
209
|
body: JSON.stringify(query),
|
|
169
210
|
});
|
|
@@ -171,7 +212,7 @@ export async function freeBusy(
|
|
|
171
212
|
|
|
172
213
|
/** List calendars the user has access to. */
|
|
173
214
|
export async function listCalendars(
|
|
174
|
-
|
|
215
|
+
connection: OAuthConnection,
|
|
175
216
|
): Promise<CalendarListResponse> {
|
|
176
|
-
return request<CalendarListResponse>(
|
|
217
|
+
return request<CalendarListResponse>(connection, "/users/me/calendarList");
|
|
177
218
|
}
|
|
@@ -3,7 +3,7 @@ import type {
|
|
|
3
3
|
ToolExecutionResult,
|
|
4
4
|
} from "../../../../tools/types.js";
|
|
5
5
|
import * as calendar from "../calendar-client.js";
|
|
6
|
-
import {
|
|
6
|
+
import { getCalendarConnection, ok } from "./shared.js";
|
|
7
7
|
|
|
8
8
|
export async function run(
|
|
9
9
|
input: Record<string, unknown>,
|
|
@@ -14,14 +14,13 @@ export async function run(
|
|
|
14
14
|
const calendarIds = (input.calendar_ids as string[]) ?? ["primary"];
|
|
15
15
|
const timezone = input.timezone as string | undefined;
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
return ok(JSON.stringify(result, null, 2));
|
|
17
|
+
const connection = getCalendarConnection();
|
|
18
|
+
const result = await calendar.freeBusy(connection, {
|
|
19
|
+
timeMin,
|
|
20
|
+
timeMax,
|
|
21
|
+
timeZone: timezone,
|
|
22
|
+
items: calendarIds.map((id) => ({ id })),
|
|
26
23
|
});
|
|
24
|
+
|
|
25
|
+
return ok(JSON.stringify(result, null, 2));
|
|
27
26
|
}
|
|
@@ -3,7 +3,7 @@ import type {
|
|
|
3
3
|
ToolExecutionResult,
|
|
4
4
|
} from "../../../../tools/types.js";
|
|
5
5
|
import * as calendar from "../calendar-client.js";
|
|
6
|
-
import {
|
|
6
|
+
import { getCalendarConnection, ok } from "./shared.js";
|
|
7
7
|
|
|
8
8
|
export async function run(
|
|
9
9
|
input: Record<string, unknown>,
|
|
@@ -40,9 +40,8 @@ export async function run(
|
|
|
40
40
|
eventBody.attendees = attendees.map((email) => ({ email }));
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
});
|
|
43
|
+
const connection = getCalendarConnection();
|
|
44
|
+
const event = await calendar.createEvent(connection, eventBody, calendarId);
|
|
45
|
+
const link = event.htmlLink ? ` View it here: ${event.htmlLink}` : "";
|
|
46
|
+
return ok(`Event created (ID: ${event.id}).${link}`);
|
|
48
47
|
}
|
|
@@ -3,7 +3,7 @@ import type {
|
|
|
3
3
|
ToolExecutionResult,
|
|
4
4
|
} from "../../../../tools/types.js";
|
|
5
5
|
import * as calendar from "../calendar-client.js";
|
|
6
|
-
import {
|
|
6
|
+
import { getCalendarConnection, ok } from "./shared.js";
|
|
7
7
|
|
|
8
8
|
export async function run(
|
|
9
9
|
input: Record<string, unknown>,
|
|
@@ -12,8 +12,7 @@ export async function run(
|
|
|
12
12
|
const eventId = input.event_id as string;
|
|
13
13
|
const calendarId = (input.calendar_id as string) ?? "primary";
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
});
|
|
15
|
+
const connection = getCalendarConnection();
|
|
16
|
+
const event = await calendar.getEvent(connection, eventId, calendarId);
|
|
17
|
+
return ok(JSON.stringify(event, null, 2));
|
|
19
18
|
}
|
|
@@ -3,7 +3,7 @@ import type {
|
|
|
3
3
|
ToolExecutionResult,
|
|
4
4
|
} from "../../../../tools/types.js";
|
|
5
5
|
import * as calendar from "../calendar-client.js";
|
|
6
|
-
import {
|
|
6
|
+
import { getCalendarConnection, ok } from "./shared.js";
|
|
7
7
|
|
|
8
8
|
export async function run(
|
|
9
9
|
input: Record<string, unknown>,
|
|
@@ -17,20 +17,19 @@ export async function run(
|
|
|
17
17
|
const singleEvents = (input.single_events as boolean) ?? true;
|
|
18
18
|
const orderBy = input.order_by as "startTime" | "updated" | undefined;
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
const connection = getCalendarConnection();
|
|
21
|
+
const result = await calendar.listEvents(connection, calendarId, {
|
|
22
|
+
timeMin,
|
|
23
|
+
timeMax,
|
|
24
|
+
maxResults,
|
|
25
|
+
query,
|
|
26
|
+
singleEvents,
|
|
27
|
+
orderBy,
|
|
28
|
+
});
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
if (!result.items?.length) {
|
|
31
|
+
return ok("No events found in the specified time range.");
|
|
32
|
+
}
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
});
|
|
34
|
+
return ok(JSON.stringify(result, null, 2));
|
|
36
35
|
}
|
|
@@ -3,7 +3,7 @@ import type {
|
|
|
3
3
|
ToolExecutionResult,
|
|
4
4
|
} from "../../../../tools/types.js";
|
|
5
5
|
import * as calendar from "../calendar-client.js";
|
|
6
|
-
import {
|
|
6
|
+
import { getCalendarConnection, ok } from "./shared.js";
|
|
7
7
|
|
|
8
8
|
export async function run(
|
|
9
9
|
input: Record<string, unknown>,
|
|
@@ -13,45 +13,45 @@ export async function run(
|
|
|
13
13
|
const response = input.response as "accepted" | "declined" | "tentative";
|
|
14
14
|
const calendarId = (input.calendar_id as string) ?? "primary";
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
// First get the event to find the user's attendee entry
|
|
18
|
-
const event = await calendar.getEvent(token, eventId, calendarId);
|
|
19
|
-
const selfAttendee = event.attendees?.find((a) => a.self);
|
|
16
|
+
const connection = getCalendarConnection();
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (event.organizer?.self) {
|
|
25
|
-
return ok("You are the organizer of this event. No RSVP needed.");
|
|
26
|
-
}
|
|
27
|
-
return ok(
|
|
28
|
-
"Could not find your attendee entry for this event. You may not be invited.",
|
|
29
|
-
);
|
|
30
|
-
}
|
|
18
|
+
// First get the event to find the user's attendee entry
|
|
19
|
+
const event = await calendar.getEvent(connection, eventId, calendarId);
|
|
20
|
+
const selfAttendee = event.attendees?.find((a) => a.self);
|
|
31
21
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
22
|
+
if (!selfAttendee) {
|
|
23
|
+
// If the user is the organizer and not in the attendees list,
|
|
24
|
+
// they don't need to RSVP
|
|
25
|
+
if (event.organizer?.self) {
|
|
26
|
+
return ok("You are the organizer of this event. No RSVP needed.");
|
|
27
|
+
}
|
|
28
|
+
return ok(
|
|
29
|
+
"Could not find your attendee entry for this event. You may not be invited.",
|
|
35
30
|
);
|
|
31
|
+
}
|
|
36
32
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
33
|
+
// Update the attendee's response status
|
|
34
|
+
const updatedAttendees = event.attendees!.map((a) =>
|
|
35
|
+
a.self ? { ...a, responseStatus: response } : a,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
await calendar.patchEvent(
|
|
39
|
+
connection,
|
|
40
|
+
eventId,
|
|
41
|
+
{
|
|
42
|
+
attendees: updatedAttendees as Array<{
|
|
43
|
+
email: string;
|
|
44
|
+
responseStatus?: string;
|
|
45
|
+
}>,
|
|
46
|
+
},
|
|
47
|
+
calendarId,
|
|
48
|
+
);
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
});
|
|
50
|
+
const responseLabel =
|
|
51
|
+
response === "accepted"
|
|
52
|
+
? "Accepted"
|
|
53
|
+
: response === "declined"
|
|
54
|
+
? "Declined"
|
|
55
|
+
: "Tentatively accepted";
|
|
56
|
+
return ok(`${responseLabel} the event "${event.summary ?? eventId}".`);
|
|
57
57
|
}
|
|
@@ -1,20 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { OAuthConnection } from "../../../../oauth/connection.js";
|
|
2
|
+
import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
|
|
2
3
|
import type { ToolExecutionResult } from "../../../../tools/types.js";
|
|
3
4
|
|
|
4
5
|
export function ok(content: string): ToolExecutionResult {
|
|
5
6
|
return { content, isError: false };
|
|
6
7
|
}
|
|
7
8
|
|
|
8
|
-
export function err(message: string): ToolExecutionResult {
|
|
9
|
-
return { content: message, isError: true };
|
|
10
|
-
}
|
|
11
|
-
|
|
12
9
|
/**
|
|
13
10
|
* Calendar uses the same OAuth credential service as Gmail since both
|
|
14
11
|
* scopes are granted in a single OAuth consent flow.
|
|
15
12
|
*/
|
|
16
|
-
export
|
|
17
|
-
|
|
18
|
-
): Promise<T> {
|
|
19
|
-
return withValidToken("integration:gmail", fn);
|
|
13
|
+
export function getCalendarConnection(): OAuthConnection {
|
|
14
|
+
return resolveOAuthConnection("integration:gmail");
|
|
20
15
|
}
|
|
@@ -5,10 +5,15 @@ import {
|
|
|
5
5
|
} from "../../../../daemon/media-visibility-policy.js";
|
|
6
6
|
import {
|
|
7
7
|
generateImage,
|
|
8
|
+
type ImageGenCredentials,
|
|
8
9
|
mapGeminiError,
|
|
9
10
|
} from "../../../../media/gemini-image-service.js";
|
|
10
11
|
import { getAttachmentsByIds } from "../../../../memory/attachments-store.js";
|
|
11
12
|
import { getConversationThreadType } from "../../../../memory/conversation-crud.js";
|
|
13
|
+
import {
|
|
14
|
+
buildManagedBaseUrl,
|
|
15
|
+
resolveManagedProxyContext,
|
|
16
|
+
} from "../../../../providers/managed-proxy/context.js";
|
|
12
17
|
import type { ImageContent } from "../../../../providers/types.js";
|
|
13
18
|
import { getAttachmentSourceConversations } from "../../../../tools/assets/search.js";
|
|
14
19
|
import type {
|
|
@@ -46,9 +51,25 @@ export async function run(
|
|
|
46
51
|
context: ToolContext,
|
|
47
52
|
): Promise<ToolExecutionResult> {
|
|
48
53
|
const config = getConfig();
|
|
49
|
-
const apiKey = config.apiKeys.gemini;
|
|
54
|
+
const apiKey = config.apiKeys.gemini ?? process.env.GEMINI_API_KEY;
|
|
55
|
+
|
|
56
|
+
// Resolve credentials: prefer direct API key, fall back to managed proxy
|
|
57
|
+
let credentials: ImageGenCredentials | undefined;
|
|
58
|
+
if (apiKey) {
|
|
59
|
+
credentials = { type: "direct", apiKey };
|
|
60
|
+
} else {
|
|
61
|
+
const managedBaseUrl = buildManagedBaseUrl("vertex");
|
|
62
|
+
if (managedBaseUrl) {
|
|
63
|
+
const ctx = resolveManagedProxyContext();
|
|
64
|
+
credentials = {
|
|
65
|
+
type: "managed-proxy",
|
|
66
|
+
assistantApiKey: ctx.assistantApiKey,
|
|
67
|
+
baseUrl: managedBaseUrl,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
50
71
|
|
|
51
|
-
if (!
|
|
72
|
+
if (!credentials) {
|
|
52
73
|
return {
|
|
53
74
|
content:
|
|
54
75
|
"No Gemini API key configured. Please set your Gemini API key to use image generation.",
|
|
@@ -95,7 +116,7 @@ export async function run(
|
|
|
95
116
|
}
|
|
96
117
|
|
|
97
118
|
try {
|
|
98
|
-
const result = await generateImage(
|
|
119
|
+
const result = await generateImage(credentials, {
|
|
99
120
|
prompt,
|
|
100
121
|
mode,
|
|
101
122
|
sourceImages,
|
|
@@ -33,9 +33,9 @@ When a platform is connected (auth test succeeds), always use the messaging API
|
|
|
33
33
|
|
|
34
34
|
Before using any messaging tool, verify that the platform is connected by calling `messaging_auth_test` with the appropriate `platform` parameter. If the call fails with a token/authorization error, follow the steps below.
|
|
35
35
|
|
|
36
|
-
### Public Ingress (required for
|
|
36
|
+
### Public Ingress (required for Slack and Telegram)
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
Slack and Telegram setup require a publicly reachable URL for OAuth callbacks or webhook delivery. The **public-ingress** skill handles ngrok tunnel setup and persists the URL as `ingress.publicBaseUrl`. Gmail on the desktop app uses a loopback callback and does not require public ingress; the channel path (Path B in the google-oauth-applescript skill) handles public ingress internally when needed.
|
|
39
39
|
|
|
40
40
|
### Email Connection Flow
|
|
41
41
|
|
|
@@ -48,11 +48,11 @@ When the user asks to "connect my email", "set up email", "manage my email", or
|
|
|
48
48
|
### Gmail
|
|
49
49
|
|
|
50
50
|
1. **Try connecting directly first.** Call `credential_store` with `action: "oauth2_connect"` and `service: "gmail"`. The tool auto-fills Google's OAuth endpoints and looks up any previously stored client credentials — so this single call may be all that's needed.
|
|
51
|
-
2. **If it fails because no client_id is found:** The user needs to create Google Cloud OAuth credentials first. Load the **google-oauth-
|
|
52
|
-
- Call `skill_load` with `skill: "google-oauth-
|
|
51
|
+
2. **If it fails because no client_id is found:** The user needs to create Google Cloud OAuth credentials first. Load the **google-oauth-applescript** skill:
|
|
52
|
+
- Call `skill_load` with `skill: "google-oauth-applescript"` to load the dependency skill.
|
|
53
53
|
- Tell the user Gmail isn't connected yet and briefly explain what the setup involves, then use `ui_show` with `surface_type: "confirmation"` to ask for permission to start:
|
|
54
54
|
- **message:** "Ready to set up Gmail?"
|
|
55
|
-
- **detail:** "I'll open a browser
|
|
55
|
+
- **detail:** "I'll open a few pages in your browser and walk you through setting up Google Cloud credentials — creating a project, enabling APIs, and connecting your account. Takes about 5 minutes."
|
|
56
56
|
- **confirmLabel:** "Get Started"
|
|
57
57
|
- **cancelLabel:** "Not Now"
|
|
58
58
|
- If the user confirms, briefly acknowledge (e.g., "Setting up Gmail now...") and proceed with the setup guide. If they decline, acknowledge and let them know they can set it up later.
|
|
@@ -95,7 +95,7 @@ The guardian-verify-setup skill handles the full outbound verification flow for
|
|
|
95
95
|
When a messaging tool fails with a token or authorization error:
|
|
96
96
|
|
|
97
97
|
1. **Try to reconnect silently.** Call `credential_store` with `action: "oauth2_connect"` and the appropriate `service`. This often resolves expired tokens automatically.
|
|
98
|
-
2. **If reconnection fails, go straight to setup.** Don't present options, ask which route the user prefers, or explain what went wrong technically. Just tell the user briefly (e.g., "Gmail needs to be reconnected — let me set that up") and immediately follow the connection setup flow for that platform (e.g., install and load **google-oauth-
|
|
98
|
+
2. **If reconnection fails, go straight to setup.** Don't present options, ask which route the user prefers, or explain what went wrong technically. Just tell the user briefly (e.g., "Gmail needs to be reconnected — let me set that up") and immediately follow the connection setup flow for that platform (e.g., install and load **google-oauth-applescript** for Gmail). The user came to you to get something done, not to troubleshoot OAuth — make it seamless.
|
|
99
99
|
3. **Never try alternative approaches.** Don't use bash, curl, browser automation, or any workaround. If the messaging tools can't do it, the reconnection flow is the answer.
|
|
100
100
|
4. **Never expose error details.** The user doesn't need to see error messages about tokens, OAuth, or API failures. Translate errors into plain language.
|
|
101
101
|
|