@agenticmail/enterprise 0.5.75 → 0.5.76
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/dist/chunk-QZHWUMPS.js +898 -0
- package/dist/chunk-R5JPVOVE.js +2191 -0
- package/dist/chunk-SXSA3OQS.js +14351 -0
- package/dist/cli.js +1 -1
- package/dist/index.js +3 -3
- package/dist/runtime-IQ3PYZN5.js +47 -0
- package/dist/server-5DCT62CG.js +12 -0
- package/dist/setup-OLC7UNFL.js +20 -0
- package/package.json +1 -1
- package/src/agent-tools/index.ts +59 -1
- package/src/agent-tools/tools/google/calendar.ts +230 -0
- package/src/agent-tools/tools/google/contacts.ts +209 -0
- package/src/agent-tools/tools/google/docs.ts +162 -0
- package/src/agent-tools/tools/google/drive.ts +262 -0
- package/src/agent-tools/tools/google/index.ts +38 -0
- package/src/agent-tools/tools/google/sheets.ts +215 -0
- package/src/agent-tools/tools/oauth-token-provider.ts +101 -0
- package/src/runtime/index.ts +23 -7
- package/src/runtime/types.ts +4 -0
- package/src/server.ts +23 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Sheets Tools
|
|
3
|
+
*
|
|
4
|
+
* Read, write, and manipulate spreadsheets via Google Sheets API v4.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { AnyAgentTool, ToolCreationOptions } from '../../types.js';
|
|
8
|
+
import { jsonResult, errorResult } from '../../common.js';
|
|
9
|
+
import type { GoogleToolsConfig } from './index.js';
|
|
10
|
+
|
|
11
|
+
const BASE = 'https://sheets.googleapis.com/v4/spreadsheets';
|
|
12
|
+
|
|
13
|
+
async function sapi(token: string, path: string, opts?: { method?: string; body?: any; query?: Record<string, string> }): Promise<any> {
|
|
14
|
+
const method = opts?.method || 'GET';
|
|
15
|
+
const url = new URL(BASE + path);
|
|
16
|
+
if (opts?.query) for (const [k, v] of Object.entries(opts.query)) { if (v) url.searchParams.set(k, v); }
|
|
17
|
+
const res = await fetch(url.toString(), {
|
|
18
|
+
method,
|
|
19
|
+
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
|
|
20
|
+
body: opts?.body ? JSON.stringify(opts.body) : undefined,
|
|
21
|
+
});
|
|
22
|
+
if (!res.ok) { const err = await res.text(); throw new Error(`Google Sheets API ${res.status}: ${err}`); }
|
|
23
|
+
if (res.status === 204) return {};
|
|
24
|
+
return res.json();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function createGoogleSheetsTools(config: GoogleToolsConfig, _options?: ToolCreationOptions): AnyAgentTool[] {
|
|
28
|
+
const tp = config.tokenProvider;
|
|
29
|
+
return [
|
|
30
|
+
{
|
|
31
|
+
name: 'google_sheets_get',
|
|
32
|
+
description: 'Get spreadsheet metadata: title, sheets/tabs, row counts.',
|
|
33
|
+
category: 'utility' as const,
|
|
34
|
+
parameters: {
|
|
35
|
+
type: 'object' as const,
|
|
36
|
+
properties: {
|
|
37
|
+
spreadsheetId: { type: 'string', description: 'Spreadsheet ID (required — from the URL)' },
|
|
38
|
+
},
|
|
39
|
+
required: ['spreadsheetId'],
|
|
40
|
+
},
|
|
41
|
+
async execute(_id: string, params: any) {
|
|
42
|
+
try {
|
|
43
|
+
const token = await tp.getAccessToken();
|
|
44
|
+
const data = await sapi(token, `/${params.spreadsheetId}`, {
|
|
45
|
+
query: { fields: 'spreadsheetId,properties.title,sheets.properties' },
|
|
46
|
+
});
|
|
47
|
+
const sheets = (data.sheets || []).map((s: any) => ({
|
|
48
|
+
sheetId: s.properties.sheetId, title: s.properties.title,
|
|
49
|
+
rowCount: s.properties.gridProperties?.rowCount,
|
|
50
|
+
columnCount: s.properties.gridProperties?.columnCount,
|
|
51
|
+
}));
|
|
52
|
+
return jsonResult({ spreadsheetId: data.spreadsheetId, title: data.properties?.title, sheets });
|
|
53
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: 'google_sheets_read',
|
|
58
|
+
description: 'Read cell values from a sheet range. Returns a 2D array of values.',
|
|
59
|
+
category: 'utility' as const,
|
|
60
|
+
parameters: {
|
|
61
|
+
type: 'object' as const,
|
|
62
|
+
properties: {
|
|
63
|
+
spreadsheetId: { type: 'string', description: 'Spreadsheet ID (required)' },
|
|
64
|
+
range: { type: 'string', description: 'A1 notation range, e.g. "Sheet1!A1:D10" or "Sheet1" for entire sheet (required)' },
|
|
65
|
+
majorDimension: { type: 'string', description: '"ROWS" (default) or "COLUMNS"' },
|
|
66
|
+
},
|
|
67
|
+
required: ['spreadsheetId', 'range'],
|
|
68
|
+
},
|
|
69
|
+
async execute(_id: string, params: any) {
|
|
70
|
+
try {
|
|
71
|
+
const token = await tp.getAccessToken();
|
|
72
|
+
const query: Record<string, string> = {};
|
|
73
|
+
if (params.majorDimension) query.majorDimension = params.majorDimension;
|
|
74
|
+
const data = await sapi(token, `/${params.spreadsheetId}/values/${encodeURIComponent(params.range)}`, { query });
|
|
75
|
+
const values = data.values || [];
|
|
76
|
+
return jsonResult({
|
|
77
|
+
range: data.range, majorDimension: data.majorDimension || 'ROWS',
|
|
78
|
+
values, rowCount: values.length, columnCount: values[0]?.length || 0,
|
|
79
|
+
});
|
|
80
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: 'google_sheets_write',
|
|
85
|
+
description: 'Write values to a sheet range. Provide rows as JSON array of arrays.',
|
|
86
|
+
category: 'utility' as const,
|
|
87
|
+
parameters: {
|
|
88
|
+
type: 'object' as const,
|
|
89
|
+
properties: {
|
|
90
|
+
spreadsheetId: { type: 'string', description: 'Spreadsheet ID (required)' },
|
|
91
|
+
range: { type: 'string', description: 'A1 notation range to write to, e.g. "Sheet1!A1" (required)' },
|
|
92
|
+
values: { type: 'string', description: 'JSON array of arrays, e.g. [["Name","Score"],["Alice",95],["Bob",87]] (required)' },
|
|
93
|
+
inputOption: { type: 'string', description: '"RAW" or "USER_ENTERED" (default: "USER_ENTERED" — parses formulas/numbers)' },
|
|
94
|
+
},
|
|
95
|
+
required: ['spreadsheetId', 'range', 'values'],
|
|
96
|
+
},
|
|
97
|
+
async execute(_id: string, params: any) {
|
|
98
|
+
try {
|
|
99
|
+
const token = await tp.getAccessToken();
|
|
100
|
+
let values: any[][];
|
|
101
|
+
try { values = JSON.parse(params.values); } catch { return errorResult('Invalid JSON for values — must be array of arrays'); }
|
|
102
|
+
const data = await sapi(token, `/${params.spreadsheetId}/values/${encodeURIComponent(params.range)}`, {
|
|
103
|
+
method: 'PUT',
|
|
104
|
+
query: { valueInputOption: params.inputOption || 'USER_ENTERED' },
|
|
105
|
+
body: { range: params.range, majorDimension: 'ROWS', values },
|
|
106
|
+
});
|
|
107
|
+
return jsonResult({ updated: true, updatedRange: data.updatedRange, updatedRows: data.updatedRows, updatedColumns: data.updatedColumns, updatedCells: data.updatedCells });
|
|
108
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: 'google_sheets_append',
|
|
113
|
+
description: 'Append rows to the end of a sheet (after existing data).',
|
|
114
|
+
category: 'utility' as const,
|
|
115
|
+
parameters: {
|
|
116
|
+
type: 'object' as const,
|
|
117
|
+
properties: {
|
|
118
|
+
spreadsheetId: { type: 'string', description: 'Spreadsheet ID (required)' },
|
|
119
|
+
range: { type: 'string', description: 'Sheet range to append to, e.g. "Sheet1" (required)' },
|
|
120
|
+
values: { type: 'string', description: 'JSON array of arrays to append (required)' },
|
|
121
|
+
inputOption: { type: 'string', description: '"RAW" or "USER_ENTERED" (default: "USER_ENTERED")' },
|
|
122
|
+
},
|
|
123
|
+
required: ['spreadsheetId', 'range', 'values'],
|
|
124
|
+
},
|
|
125
|
+
async execute(_id: string, params: any) {
|
|
126
|
+
try {
|
|
127
|
+
const token = await tp.getAccessToken();
|
|
128
|
+
let values: any[][];
|
|
129
|
+
try { values = JSON.parse(params.values); } catch { return errorResult('Invalid JSON for values'); }
|
|
130
|
+
const data = await sapi(token, `/${params.spreadsheetId}/values/${encodeURIComponent(params.range)}:append`, {
|
|
131
|
+
method: 'POST',
|
|
132
|
+
query: { valueInputOption: params.inputOption || 'USER_ENTERED', insertDataOption: 'INSERT_ROWS' },
|
|
133
|
+
body: { range: params.range, majorDimension: 'ROWS', values },
|
|
134
|
+
});
|
|
135
|
+
return jsonResult({ appended: true, updatedRange: data.updates?.updatedRange, updatedRows: data.updates?.updatedRows, updatedCells: data.updates?.updatedCells });
|
|
136
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: 'google_sheets_clear',
|
|
141
|
+
description: 'Clear values from a range (keeps formatting).',
|
|
142
|
+
category: 'utility' as const,
|
|
143
|
+
parameters: {
|
|
144
|
+
type: 'object' as const,
|
|
145
|
+
properties: {
|
|
146
|
+
spreadsheetId: { type: 'string', description: 'Spreadsheet ID (required)' },
|
|
147
|
+
range: { type: 'string', description: 'Range to clear, e.g. "Sheet1!A2:D100" (required)' },
|
|
148
|
+
},
|
|
149
|
+
required: ['spreadsheetId', 'range'],
|
|
150
|
+
},
|
|
151
|
+
async execute(_id: string, params: any) {
|
|
152
|
+
try {
|
|
153
|
+
const token = await tp.getAccessToken();
|
|
154
|
+
const data = await sapi(token, `/${params.spreadsheetId}/values/${encodeURIComponent(params.range)}:clear`, { method: 'POST', body: {} });
|
|
155
|
+
return jsonResult({ cleared: true, clearedRange: data.clearedRange });
|
|
156
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: 'google_sheets_create',
|
|
161
|
+
description: 'Create a new spreadsheet.',
|
|
162
|
+
category: 'utility' as const,
|
|
163
|
+
parameters: {
|
|
164
|
+
type: 'object' as const,
|
|
165
|
+
properties: {
|
|
166
|
+
title: { type: 'string', description: 'Spreadsheet title (required)' },
|
|
167
|
+
sheetTitles: { type: 'string', description: 'Comma-separated sheet/tab names (default: "Sheet1")' },
|
|
168
|
+
},
|
|
169
|
+
required: ['title'],
|
|
170
|
+
},
|
|
171
|
+
async execute(_id: string, params: any) {
|
|
172
|
+
try {
|
|
173
|
+
const token = await tp.getAccessToken();
|
|
174
|
+
const sheets = (params.sheetTitles || 'Sheet1').split(',').map((t: string, i: number) => ({
|
|
175
|
+
properties: { title: t.trim(), index: i },
|
|
176
|
+
}));
|
|
177
|
+
const data = await sapi(token, '', {
|
|
178
|
+
method: 'POST',
|
|
179
|
+
body: { properties: { title: params.title }, sheets },
|
|
180
|
+
});
|
|
181
|
+
return jsonResult({
|
|
182
|
+
created: true, spreadsheetId: data.spreadsheetId,
|
|
183
|
+
title: data.properties?.title,
|
|
184
|
+
url: data.spreadsheetUrl,
|
|
185
|
+
sheets: (data.sheets || []).map((s: any) => s.properties?.title),
|
|
186
|
+
});
|
|
187
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
name: 'google_sheets_add_sheet',
|
|
192
|
+
description: 'Add a new sheet/tab to an existing spreadsheet.',
|
|
193
|
+
category: 'utility' as const,
|
|
194
|
+
parameters: {
|
|
195
|
+
type: 'object' as const,
|
|
196
|
+
properties: {
|
|
197
|
+
spreadsheetId: { type: 'string', description: 'Spreadsheet ID (required)' },
|
|
198
|
+
title: { type: 'string', description: 'New sheet/tab name (required)' },
|
|
199
|
+
},
|
|
200
|
+
required: ['spreadsheetId', 'title'],
|
|
201
|
+
},
|
|
202
|
+
async execute(_id: string, params: any) {
|
|
203
|
+
try {
|
|
204
|
+
const token = await tp.getAccessToken();
|
|
205
|
+
const data = await sapi(token, `/${params.spreadsheetId}:batchUpdate`, {
|
|
206
|
+
method: 'POST',
|
|
207
|
+
body: { requests: [{ addSheet: { properties: { title: params.title } } }] },
|
|
208
|
+
});
|
|
209
|
+
const reply = data.replies?.[0]?.addSheet?.properties;
|
|
210
|
+
return jsonResult({ added: true, sheetId: reply?.sheetId, title: reply?.title });
|
|
211
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
];
|
|
215
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Token Provider
|
|
3
|
+
*
|
|
4
|
+
* Shared abstraction for getting valid OAuth access tokens for agent tools.
|
|
5
|
+
* Handles token refresh automatically when tokens expire.
|
|
6
|
+
* Used by both Google Workspace and Microsoft Graph tool suites.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export interface OAuthTokens {
|
|
10
|
+
accessToken: string;
|
|
11
|
+
refreshToken?: string;
|
|
12
|
+
expiresAt?: string; // ISO timestamp
|
|
13
|
+
provider: 'google' | 'microsoft';
|
|
14
|
+
clientId: string;
|
|
15
|
+
clientSecret: string;
|
|
16
|
+
scopes?: string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface TokenProvider {
|
|
20
|
+
/** Get a valid access token, refreshing if necessary */
|
|
21
|
+
getAccessToken(): Promise<string>;
|
|
22
|
+
/** Get the provider type */
|
|
23
|
+
getProvider(): 'google' | 'microsoft';
|
|
24
|
+
/** Get the agent's email */
|
|
25
|
+
getEmail(): string | undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface TokenProviderConfig {
|
|
29
|
+
getTokens: () => OAuthTokens | null;
|
|
30
|
+
saveTokens: (tokens: Partial<OAuthTokens>) => void;
|
|
31
|
+
getEmail?: () => string | undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1000; // Refresh 5 min before expiry
|
|
35
|
+
|
|
36
|
+
export function createTokenProvider(config: TokenProviderConfig): TokenProvider {
|
|
37
|
+
return {
|
|
38
|
+
async getAccessToken(): Promise<string> {
|
|
39
|
+
const tokens = config.getTokens();
|
|
40
|
+
if (!tokens) throw new Error('No OAuth tokens configured. Connect email via the agent Email tab first.');
|
|
41
|
+
if (!tokens.accessToken) throw new Error('No access token available. Re-authorize via the Email tab.');
|
|
42
|
+
|
|
43
|
+
// Check if token is expired or about to expire
|
|
44
|
+
if (tokens.expiresAt) {
|
|
45
|
+
const expiresAt = new Date(tokens.expiresAt).getTime();
|
|
46
|
+
if (Date.now() > expiresAt - TOKEN_REFRESH_BUFFER_MS) {
|
|
47
|
+
// Need to refresh
|
|
48
|
+
if (!tokens.refreshToken) throw new Error('Access token expired and no refresh token available. Re-authorize via the Email tab.');
|
|
49
|
+
return await refreshAccessToken(tokens, config);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return tokens.accessToken;
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
getProvider(): 'google' | 'microsoft' {
|
|
57
|
+
const tokens = config.getTokens();
|
|
58
|
+
return tokens?.provider || 'google';
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
getEmail(): string | undefined {
|
|
62
|
+
return config.getEmail?.();
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function refreshAccessToken(tokens: OAuthTokens, config: TokenProviderConfig): Promise<string> {
|
|
68
|
+
const tokenUrl = tokens.provider === 'google'
|
|
69
|
+
? 'https://oauth2.googleapis.com/token'
|
|
70
|
+
: `https://login.microsoftonline.com/common/oauth2/v2.0/token`;
|
|
71
|
+
|
|
72
|
+
const body = new URLSearchParams({
|
|
73
|
+
client_id: tokens.clientId,
|
|
74
|
+
client_secret: tokens.clientSecret,
|
|
75
|
+
refresh_token: tokens.refreshToken!,
|
|
76
|
+
grant_type: 'refresh_token',
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const res = await fetch(tokenUrl, {
|
|
80
|
+
method: 'POST',
|
|
81
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
82
|
+
body,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (!res.ok) {
|
|
86
|
+
const errText = await res.text();
|
|
87
|
+
throw new Error(`Token refresh failed (${res.status}): ${errText}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const data = await res.json() as any;
|
|
91
|
+
const newTokens: Partial<OAuthTokens> = {
|
|
92
|
+
accessToken: data.access_token,
|
|
93
|
+
expiresAt: data.expires_in
|
|
94
|
+
? new Date(Date.now() + data.expires_in * 1000).toISOString()
|
|
95
|
+
: undefined,
|
|
96
|
+
};
|
|
97
|
+
if (data.refresh_token) newTokens.refreshToken = data.refresh_token;
|
|
98
|
+
|
|
99
|
+
config.saveTokens(newTokens);
|
|
100
|
+
return data.access_token;
|
|
101
|
+
}
|
package/src/runtime/index.ts
CHANGED
|
@@ -131,6 +131,26 @@ export class AgentRuntime {
|
|
|
131
131
|
});
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
/** Build tool options for a given agent, including OAuth email config if available */
|
|
135
|
+
private buildToolOptions(agentId: string): any {
|
|
136
|
+
const base: any = {
|
|
137
|
+
agentId,
|
|
138
|
+
workspaceDir: process.cwd(),
|
|
139
|
+
agenticmailManager: this.config.agenticmailManager,
|
|
140
|
+
};
|
|
141
|
+
if (this.config.getEmailConfig) {
|
|
142
|
+
const ec = this.config.getEmailConfig(agentId);
|
|
143
|
+
if (ec?.oauthAccessToken) {
|
|
144
|
+
base.emailConfig = ec;
|
|
145
|
+
if (this.config.onTokenRefresh) {
|
|
146
|
+
const onRefresh = this.config.onTokenRefresh;
|
|
147
|
+
base.onTokenRefresh = (tokens: any) => onRefresh(agentId, tokens);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return base;
|
|
152
|
+
}
|
|
153
|
+
|
|
134
154
|
/**
|
|
135
155
|
* Start the runtime — initializes session manager, gateway, schedulers,
|
|
136
156
|
* heartbeat, stale detection, and resumes active sessions.
|
|
@@ -236,11 +256,7 @@ export class AgentRuntime {
|
|
|
236
256
|
var session = await this.sessionManager!.createSession(agentId, orgId, opts.parentSessionId);
|
|
237
257
|
|
|
238
258
|
// Build agent config
|
|
239
|
-
var tools = opts.tools || createAllTools(
|
|
240
|
-
agentId,
|
|
241
|
-
workspaceDir: process.cwd(),
|
|
242
|
-
agenticmailManager: this.config.agenticmailManager,
|
|
243
|
-
});
|
|
259
|
+
var tools = opts.tools || createAllTools(this.buildToolOptions(agentId));
|
|
244
260
|
|
|
245
261
|
var systemPrompt = opts.systemPrompt || buildDefaultSystemPrompt(agentId);
|
|
246
262
|
|
|
@@ -282,7 +298,7 @@ export class AgentRuntime {
|
|
|
282
298
|
var apiKey = this.resolveApiKey(model.provider);
|
|
283
299
|
if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
|
|
284
300
|
|
|
285
|
-
var tools = createAllTools(
|
|
301
|
+
var tools = createAllTools(this.buildToolOptions(session.agentId));
|
|
286
302
|
|
|
287
303
|
var agentConfig: AgentConfig = {
|
|
288
304
|
agentId: session.agentId,
|
|
@@ -573,7 +589,7 @@ export class AgentRuntime {
|
|
|
573
589
|
continue;
|
|
574
590
|
}
|
|
575
591
|
|
|
576
|
-
var tools = createAllTools(
|
|
592
|
+
var tools = createAllTools(this.buildToolOptions(session.agentId));
|
|
577
593
|
|
|
578
594
|
var agentConfig: AgentConfig = {
|
|
579
595
|
agentId: session.agentId,
|
package/src/runtime/types.ts
CHANGED
|
@@ -118,6 +118,10 @@ export interface RuntimeConfig {
|
|
|
118
118
|
gatewayEnabled?: boolean;
|
|
119
119
|
/** AgenticMail manager for org email access (optional — enables agenticmail_* tools) */
|
|
120
120
|
agenticmailManager?: import('../agent-tools/tools/agenticmail.js').AgenticMailManagerRef;
|
|
121
|
+
/** Get OAuth email config for an agent (enables Google/Microsoft Workspace tools) */
|
|
122
|
+
getEmailConfig?: (agentId: string) => any;
|
|
123
|
+
/** Callback to persist refreshed OAuth tokens */
|
|
124
|
+
onTokenRefresh?: (agentId: string, tokens: any) => void;
|
|
121
125
|
/** Resume active sessions on startup (default: true) */
|
|
122
126
|
resumeOnStartup?: boolean;
|
|
123
127
|
/** Heartbeat interval in ms (default: 30000 = 30s) */
|
package/src/server.ts
CHANGED
|
@@ -265,12 +265,35 @@ export function createServer(config: ServerConfig): ServerInstance {
|
|
|
265
265
|
try {
|
|
266
266
|
const { createAgentRuntime } = await import('./runtime/index.js');
|
|
267
267
|
const { mountRuntimeApp } = await import('./engine/routes.js');
|
|
268
|
+
// Import lifecycle for email config access
|
|
269
|
+
let getEmailConfig: ((agentId: string) => any) | undefined;
|
|
270
|
+
let onTokenRefresh: ((agentId: string, tokens: any) => void) | undefined;
|
|
271
|
+
try {
|
|
272
|
+
const { lifecycle: lc } = await import('./engine/routes.js');
|
|
273
|
+
if (lc) {
|
|
274
|
+
getEmailConfig = (agentId: string) => {
|
|
275
|
+
const managed = lc.getAgent(agentId);
|
|
276
|
+
return managed?.config?.emailConfig || null;
|
|
277
|
+
};
|
|
278
|
+
onTokenRefresh = (agentId: string, tokens: any) => {
|
|
279
|
+
const managed = lc.getAgent(agentId);
|
|
280
|
+
if (managed?.config?.emailConfig) {
|
|
281
|
+
if (tokens.accessToken) managed.config.emailConfig.oauthAccessToken = tokens.accessToken;
|
|
282
|
+
if (tokens.refreshToken) managed.config.emailConfig.oauthRefreshToken = tokens.refreshToken;
|
|
283
|
+
if (tokens.expiresAt) managed.config.emailConfig.oauthTokenExpiry = tokens.expiresAt;
|
|
284
|
+
lc.saveAgent(agentId).catch(() => {});
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
} catch {}
|
|
268
289
|
const runtime = createAgentRuntime({
|
|
269
290
|
engineDb,
|
|
270
291
|
adminDb: config.db,
|
|
271
292
|
defaultModel: config.runtime.defaultModel as any,
|
|
272
293
|
apiKeys: config.runtime.apiKeys,
|
|
273
294
|
gatewayEnabled: true,
|
|
295
|
+
getEmailConfig,
|
|
296
|
+
onTokenRefresh,
|
|
274
297
|
});
|
|
275
298
|
await runtime.start();
|
|
276
299
|
const runtimeApp = runtime.getApp();
|