@agenticmail/enterprise 0.5.133 → 0.5.134
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-42YEZIIA.js +898 -0
- package/dist/chunk-4B5L7JVW.js +2195 -0
- package/dist/chunk-Y7ZDAH4R.js +16412 -0
- package/dist/cli-agent-MNQJEZI5.js +631 -0
- package/dist/cli-serve-OFAE722M.js +34 -0
- package/dist/cli.js +3 -3
- package/dist/index.js +3 -3
- package/dist/routes-GRSBOFFX.js +6959 -0
- package/dist/runtime-PHQ2QP2S.js +49 -0
- package/dist/server-7J734AN4.js +12 -0
- package/dist/setup-LP6RJ66F.js +20 -0
- package/package.json +1 -1
- package/src/agent-tools/tools/google/gmail.ts +35 -2
- package/src/agent-tools/tools/google/index.ts +3 -0
- package/src/agent-tools/tools/google/tasks.ts +202 -0
- package/src/cli-agent.ts +83 -66
- package/src/engine/agent-routes.ts +1 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AgentRuntime,
|
|
3
|
+
EmailChannel,
|
|
4
|
+
FollowUpScheduler,
|
|
5
|
+
SessionManager,
|
|
6
|
+
SubAgentManager,
|
|
7
|
+
ToolRegistry,
|
|
8
|
+
callLLM,
|
|
9
|
+
createAgentRuntime,
|
|
10
|
+
createNoopHooks,
|
|
11
|
+
createRuntimeHooks,
|
|
12
|
+
estimateMessageTokens,
|
|
13
|
+
estimateTokens,
|
|
14
|
+
executeTool,
|
|
15
|
+
runAgentLoop,
|
|
16
|
+
toolsToDefinitions
|
|
17
|
+
} from "./chunk-Y7ZDAH4R.js";
|
|
18
|
+
import "./chunk-TYW5XTOW.js";
|
|
19
|
+
import "./chunk-AQH4DFYV.js";
|
|
20
|
+
import {
|
|
21
|
+
PROVIDER_REGISTRY,
|
|
22
|
+
listAllProviders,
|
|
23
|
+
resolveApiKeyForProvider,
|
|
24
|
+
resolveProvider
|
|
25
|
+
} from "./chunk-67KZYSLU.js";
|
|
26
|
+
import "./chunk-JLSQOQ5L.js";
|
|
27
|
+
import "./chunk-NRF3YRF7.js";
|
|
28
|
+
import "./chunk-KFQGP6VL.js";
|
|
29
|
+
export {
|
|
30
|
+
AgentRuntime,
|
|
31
|
+
EmailChannel,
|
|
32
|
+
FollowUpScheduler,
|
|
33
|
+
PROVIDER_REGISTRY,
|
|
34
|
+
SessionManager,
|
|
35
|
+
SubAgentManager,
|
|
36
|
+
ToolRegistry,
|
|
37
|
+
callLLM,
|
|
38
|
+
createAgentRuntime,
|
|
39
|
+
createNoopHooks,
|
|
40
|
+
createRuntimeHooks,
|
|
41
|
+
estimateMessageTokens,
|
|
42
|
+
estimateTokens,
|
|
43
|
+
executeTool,
|
|
44
|
+
listAllProviders,
|
|
45
|
+
resolveApiKeyForProvider,
|
|
46
|
+
resolveProvider,
|
|
47
|
+
runAgentLoop,
|
|
48
|
+
toolsToDefinitions
|
|
49
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createServer
|
|
3
|
+
} from "./chunk-4B5L7JVW.js";
|
|
4
|
+
import "./chunk-3SMTCIR4.js";
|
|
5
|
+
import "./chunk-RO537U6H.js";
|
|
6
|
+
import "./chunk-DRXMYYKN.js";
|
|
7
|
+
import "./chunk-67KZYSLU.js";
|
|
8
|
+
import "./chunk-JLSQOQ5L.js";
|
|
9
|
+
import "./chunk-KFQGP6VL.js";
|
|
10
|
+
export {
|
|
11
|
+
createServer
|
|
12
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
promptCompanyInfo,
|
|
3
|
+
promptDatabase,
|
|
4
|
+
promptDeployment,
|
|
5
|
+
promptDomain,
|
|
6
|
+
promptRegistration,
|
|
7
|
+
provision,
|
|
8
|
+
runSetupWizard
|
|
9
|
+
} from "./chunk-42YEZIIA.js";
|
|
10
|
+
import "./chunk-MHIFVS5L.js";
|
|
11
|
+
import "./chunk-KFQGP6VL.js";
|
|
12
|
+
export {
|
|
13
|
+
promptCompanyInfo,
|
|
14
|
+
promptDatabase,
|
|
15
|
+
promptDeployment,
|
|
16
|
+
promptDomain,
|
|
17
|
+
promptRegistration,
|
|
18
|
+
provision,
|
|
19
|
+
runSetupWizard
|
|
20
|
+
};
|
package/package.json
CHANGED
|
@@ -177,6 +177,33 @@ function buildRawEmail(opts: {
|
|
|
177
177
|
return lines.join('\r\n');
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
// Cache for Gmail signature (fetched once per tool creation)
|
|
181
|
+
let cachedSignature: string | null = null;
|
|
182
|
+
let signatureFetched = false;
|
|
183
|
+
|
|
184
|
+
async function getSignature(token: string): Promise<string> {
|
|
185
|
+
if (signatureFetched) return cachedSignature || '';
|
|
186
|
+
signatureFetched = true;
|
|
187
|
+
try {
|
|
188
|
+
const res = await gmail(token, '/settings/sendAs');
|
|
189
|
+
const primary = res.sendAs?.find((s: any) => s.isPrimary) || res.sendAs?.[0];
|
|
190
|
+
cachedSignature = primary?.signature || null;
|
|
191
|
+
} catch { cachedSignature = null; }
|
|
192
|
+
return cachedSignature || '';
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function appendSignature(body: string, signatureHtml: string): string {
|
|
196
|
+
if (!signatureHtml) return body;
|
|
197
|
+
// For plain text, strip HTML tags from signature and append
|
|
198
|
+
const plainSig = signatureHtml.replace(/<br\s*\/?>/gi, '\n').replace(/<[^>]+>/g, '').trim();
|
|
199
|
+
return body + '\n\n' + plainSig;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function appendSignatureHtml(bodyHtml: string, signatureHtml: string): string {
|
|
203
|
+
if (!signatureHtml) return bodyHtml;
|
|
204
|
+
return bodyHtml + '<br><br>' + signatureHtml;
|
|
205
|
+
}
|
|
206
|
+
|
|
180
207
|
export function createGmailTools(config: GoogleToolsConfig, _options?: ToolCreationOptions): AnyAgentTool[] {
|
|
181
208
|
const tp = config.tokenProvider;
|
|
182
209
|
return [
|
|
@@ -317,9 +344,12 @@ export function createGmailTools(config: GoogleToolsConfig, _options?: ToolCreat
|
|
|
317
344
|
try {
|
|
318
345
|
const token = await tp.getAccessToken();
|
|
319
346
|
const email = tp.getEmail();
|
|
347
|
+
const sig = await getSignature(token);
|
|
348
|
+
const bodyWithSig = appendSignature(params.body, sig);
|
|
349
|
+
const htmlWithSig = params.html ? appendSignatureHtml(params.html, sig) : (sig ? `<div>${params.body.replace(/\n/g, '<br>')}</div><br><br>${sig}` : undefined);
|
|
320
350
|
const raw = buildRawEmail({
|
|
321
351
|
from: email, to: params.to, cc: params.cc, bcc: params.bcc,
|
|
322
|
-
subject: params.subject, body:
|
|
352
|
+
subject: params.subject, body: bodyWithSig, html: htmlWithSig,
|
|
323
353
|
replyTo: params.replyTo, inReplyTo: params.inReplyTo, references: params.references,
|
|
324
354
|
});
|
|
325
355
|
const sendBody: any = { raw: encodeBase64Url(raw) };
|
|
@@ -371,9 +401,12 @@ export function createGmailTools(config: GoogleToolsConfig, _options?: ToolCreat
|
|
|
371
401
|
const subject = oh.Subject?.startsWith('Re:') ? oh.Subject : `Re: ${oh.Subject || ''}`;
|
|
372
402
|
const references = [oh.References, oh['Message-ID']].filter(Boolean).join(' ');
|
|
373
403
|
|
|
404
|
+
const sig = await getSignature(token);
|
|
405
|
+
const bodyWithSig = appendSignature(params.body, sig);
|
|
406
|
+
const htmlWithSig = params.html ? appendSignatureHtml(params.html, sig) : (sig ? `<div>${params.body.replace(/\n/g, '<br>')}</div><br><br>${sig}` : undefined);
|
|
374
407
|
const raw = buildRawEmail({
|
|
375
408
|
from: email, to, cc: params.cc,
|
|
376
|
-
subject, body:
|
|
409
|
+
subject, body: bodyWithSig, html: htmlWithSig,
|
|
377
410
|
inReplyTo: oh['Message-ID'], references,
|
|
378
411
|
});
|
|
379
412
|
|
|
@@ -12,6 +12,7 @@ export { createGoogleSheetsTools } from './sheets.js';
|
|
|
12
12
|
export { createGoogleDocsTools } from './docs.js';
|
|
13
13
|
export { createGoogleContactsTools } from './contacts.js';
|
|
14
14
|
export { createMeetingTools } from './meetings.js';
|
|
15
|
+
export { createGoogleTasksTools } from './tasks.js';
|
|
15
16
|
|
|
16
17
|
import type { AnyAgentTool, ToolCreationOptions } from '../../types.js';
|
|
17
18
|
import type { TokenProvider } from '../oauth-token-provider.js';
|
|
@@ -22,6 +23,7 @@ import { createGoogleSheetsTools } from './sheets.js';
|
|
|
22
23
|
import { createGoogleDocsTools } from './docs.js';
|
|
23
24
|
import { createGoogleContactsTools } from './contacts.js';
|
|
24
25
|
import { createMeetingTools } from './meetings.js';
|
|
26
|
+
import { createGoogleTasksTools } from './tasks.js';
|
|
25
27
|
|
|
26
28
|
export interface GoogleToolsConfig {
|
|
27
29
|
tokenProvider: TokenProvider;
|
|
@@ -44,5 +46,6 @@ export function createAllGoogleTools(config: GoogleToolsConfig, options?: ToolCr
|
|
|
44
46
|
...createGoogleDocsTools(config, options),
|
|
45
47
|
...createGoogleContactsTools(config, options),
|
|
46
48
|
...createMeetingTools(config, options),
|
|
49
|
+
...createGoogleTasksTools(config.tokenProvider),
|
|
47
50
|
];
|
|
48
51
|
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Tasks API Tools
|
|
3
|
+
*
|
|
4
|
+
* Lets agents create, list, complete, and manage tasks.
|
|
5
|
+
* Uses Google Tasks API v1.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { AnyAgentTool } from '../../types.js';
|
|
9
|
+
import type { TokenProvider } from './index.js';
|
|
10
|
+
|
|
11
|
+
// ─── Helper ─────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
async function tasks(token: string, path: string, opts?: { method?: string; body?: any; query?: Record<string, string> }): Promise<any> {
|
|
14
|
+
const url = new URL(`https://tasks.googleapis.com/tasks/v1${path}`);
|
|
15
|
+
if (opts?.query) for (const [k, v] of Object.entries(opts.query)) url.searchParams.set(k, v);
|
|
16
|
+
const res = await fetch(url.toString(), {
|
|
17
|
+
method: opts?.method || 'GET',
|
|
18
|
+
headers: {
|
|
19
|
+
Authorization: `Bearer ${token}`,
|
|
20
|
+
...(opts?.body ? { 'Content-Type': 'application/json' } : {}),
|
|
21
|
+
},
|
|
22
|
+
...(opts?.body ? { body: JSON.stringify(opts.body) } : {}),
|
|
23
|
+
});
|
|
24
|
+
if (!res.ok) {
|
|
25
|
+
const errText = await res.text();
|
|
26
|
+
throw new Error(`Google Tasks API ${res.status}: ${errText}`);
|
|
27
|
+
}
|
|
28
|
+
if (res.status === 204) return {};
|
|
29
|
+
return res.json();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function jsonResult(data: any) { return { success: true, output: JSON.stringify(data, null, 2) }; }
|
|
33
|
+
function errorResult(msg: string) { return { success: false, output: `Error: ${msg}` }; }
|
|
34
|
+
|
|
35
|
+
// ─── Tool Definitions ───────────────────────────────────
|
|
36
|
+
|
|
37
|
+
export function createGoogleTasksTools(tp: TokenProvider): AnyAgentTool[] {
|
|
38
|
+
return [
|
|
39
|
+
{
|
|
40
|
+
name: 'google_tasks_list_tasklists',
|
|
41
|
+
description: 'List all task lists (like "My Tasks", custom lists). Returns task list IDs needed for other operations.',
|
|
42
|
+
category: 'utility' as const,
|
|
43
|
+
parameters: { type: 'object' as const, properties: {}, required: [] },
|
|
44
|
+
async execute() {
|
|
45
|
+
try {
|
|
46
|
+
const token = await tp.getAccessToken();
|
|
47
|
+
const result = await tasks(token, '/users/@me/lists');
|
|
48
|
+
const lists = (result.items || []).map((l: any) => ({ id: l.id, title: l.title, updated: l.updated }));
|
|
49
|
+
return jsonResult({ taskLists: lists });
|
|
50
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'google_tasks_list',
|
|
55
|
+
description: 'List tasks in a task list. Shows pending tasks by default. Use showCompleted=true to include completed tasks.',
|
|
56
|
+
category: 'utility' as const,
|
|
57
|
+
parameters: {
|
|
58
|
+
type: 'object' as const,
|
|
59
|
+
properties: {
|
|
60
|
+
taskListId: { type: 'string', description: 'Task list ID (default: "@default" for primary list)' },
|
|
61
|
+
showCompleted: { type: 'string', description: '"true" to include completed tasks' },
|
|
62
|
+
maxResults: { type: 'string', description: 'Max results (default: 20)' },
|
|
63
|
+
},
|
|
64
|
+
required: [],
|
|
65
|
+
},
|
|
66
|
+
async execute(_id: string, params: any) {
|
|
67
|
+
try {
|
|
68
|
+
const token = await tp.getAccessToken();
|
|
69
|
+
const listId = params.taskListId || '@default';
|
|
70
|
+
const query: Record<string, string> = { maxResults: params.maxResults || '20' };
|
|
71
|
+
if (params.showCompleted === 'true') query.showCompleted = 'true';
|
|
72
|
+
else query.showCompleted = 'false';
|
|
73
|
+
const result = await tasks(token, `/lists/${encodeURIComponent(listId)}/tasks`, { query });
|
|
74
|
+
const items = (result.items || []).map((t: any) => ({
|
|
75
|
+
id: t.id, title: t.title, notes: t.notes, status: t.status,
|
|
76
|
+
due: t.due, completed: t.completed, updated: t.updated, parent: t.parent,
|
|
77
|
+
}));
|
|
78
|
+
return jsonResult({ tasks: items, count: items.length });
|
|
79
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'google_tasks_create',
|
|
84
|
+
description: 'Create a new task. Use this to track work items, reminders, and follow-ups.',
|
|
85
|
+
category: 'utility' as const,
|
|
86
|
+
parameters: {
|
|
87
|
+
type: 'object' as const,
|
|
88
|
+
properties: {
|
|
89
|
+
title: { type: 'string', description: 'Task title (required)' },
|
|
90
|
+
notes: { type: 'string', description: 'Task notes/details' },
|
|
91
|
+
due: { type: 'string', description: 'Due date in RFC 3339 format (e.g. "2026-02-24T00:00:00.000Z")' },
|
|
92
|
+
taskListId: { type: 'string', description: 'Task list ID (default: "@default")' },
|
|
93
|
+
},
|
|
94
|
+
required: ['title'],
|
|
95
|
+
},
|
|
96
|
+
async execute(_id: string, params: any) {
|
|
97
|
+
try {
|
|
98
|
+
const token = await tp.getAccessToken();
|
|
99
|
+
const listId = params.taskListId || '@default';
|
|
100
|
+
const body: any = { title: params.title };
|
|
101
|
+
if (params.notes) body.notes = params.notes;
|
|
102
|
+
if (params.due) body.due = params.due;
|
|
103
|
+
const result = await tasks(token, `/lists/${encodeURIComponent(listId)}/tasks`, { method: 'POST', body });
|
|
104
|
+
return jsonResult({ created: true, id: result.id, title: result.title, due: result.due });
|
|
105
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: 'google_tasks_complete',
|
|
110
|
+
description: 'Mark a task as completed.',
|
|
111
|
+
category: 'utility' as const,
|
|
112
|
+
parameters: {
|
|
113
|
+
type: 'object' as const,
|
|
114
|
+
properties: {
|
|
115
|
+
taskId: { type: 'string', description: 'Task ID (required)' },
|
|
116
|
+
taskListId: { type: 'string', description: 'Task list ID (default: "@default")' },
|
|
117
|
+
},
|
|
118
|
+
required: ['taskId'],
|
|
119
|
+
},
|
|
120
|
+
async execute(_id: string, params: any) {
|
|
121
|
+
try {
|
|
122
|
+
const token = await tp.getAccessToken();
|
|
123
|
+
const listId = params.taskListId || '@default';
|
|
124
|
+
const result = await tasks(token, `/lists/${encodeURIComponent(listId)}/tasks/${encodeURIComponent(params.taskId)}`, {
|
|
125
|
+
method: 'PATCH', body: { status: 'completed' },
|
|
126
|
+
});
|
|
127
|
+
return jsonResult({ completed: true, id: result.id, title: result.title });
|
|
128
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: 'google_tasks_update',
|
|
133
|
+
description: 'Update a task (title, notes, due date).',
|
|
134
|
+
category: 'utility' as const,
|
|
135
|
+
parameters: {
|
|
136
|
+
type: 'object' as const,
|
|
137
|
+
properties: {
|
|
138
|
+
taskId: { type: 'string', description: 'Task ID (required)' },
|
|
139
|
+
title: { type: 'string', description: 'New title' },
|
|
140
|
+
notes: { type: 'string', description: 'New notes' },
|
|
141
|
+
due: { type: 'string', description: 'New due date (RFC 3339)' },
|
|
142
|
+
taskListId: { type: 'string', description: 'Task list ID (default: "@default")' },
|
|
143
|
+
},
|
|
144
|
+
required: ['taskId'],
|
|
145
|
+
},
|
|
146
|
+
async execute(_id: string, params: any) {
|
|
147
|
+
try {
|
|
148
|
+
const token = await tp.getAccessToken();
|
|
149
|
+
const listId = params.taskListId || '@default';
|
|
150
|
+
const body: any = {};
|
|
151
|
+
if (params.title) body.title = params.title;
|
|
152
|
+
if (params.notes) body.notes = params.notes;
|
|
153
|
+
if (params.due) body.due = params.due;
|
|
154
|
+
const result = await tasks(token, `/lists/${encodeURIComponent(listId)}/tasks/${encodeURIComponent(params.taskId)}`, {
|
|
155
|
+
method: 'PATCH', body,
|
|
156
|
+
});
|
|
157
|
+
return jsonResult({ updated: true, id: result.id, title: result.title, due: result.due });
|
|
158
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: 'google_tasks_delete',
|
|
163
|
+
description: 'Delete a task.',
|
|
164
|
+
category: 'utility' as const,
|
|
165
|
+
parameters: {
|
|
166
|
+
type: 'object' as const,
|
|
167
|
+
properties: {
|
|
168
|
+
taskId: { type: 'string', description: 'Task ID (required)' },
|
|
169
|
+
taskListId: { type: 'string', description: 'Task list ID (default: "@default")' },
|
|
170
|
+
},
|
|
171
|
+
required: ['taskId'],
|
|
172
|
+
},
|
|
173
|
+
async execute(_id: string, params: any) {
|
|
174
|
+
try {
|
|
175
|
+
const token = await tp.getAccessToken();
|
|
176
|
+
const listId = params.taskListId || '@default';
|
|
177
|
+
await tasks(token, `/lists/${encodeURIComponent(listId)}/tasks/${encodeURIComponent(params.taskId)}`, { method: 'DELETE' });
|
|
178
|
+
return jsonResult({ deleted: true, taskId: params.taskId });
|
|
179
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
name: 'google_tasks_create_list',
|
|
184
|
+
description: 'Create a new task list (e.g. "Follow-ups", "Customer Issues").',
|
|
185
|
+
category: 'utility' as const,
|
|
186
|
+
parameters: {
|
|
187
|
+
type: 'object' as const,
|
|
188
|
+
properties: {
|
|
189
|
+
title: { type: 'string', description: 'Task list title (required)' },
|
|
190
|
+
},
|
|
191
|
+
required: ['title'],
|
|
192
|
+
},
|
|
193
|
+
async execute(_id: string, params: any) {
|
|
194
|
+
try {
|
|
195
|
+
const token = await tp.getAccessToken();
|
|
196
|
+
const result = await tasks(token, '/users/@me/lists', { method: 'POST', body: { title: params.title } });
|
|
197
|
+
return jsonResult({ created: true, id: result.id, title: result.title });
|
|
198
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
];
|
|
202
|
+
}
|
package/src/cli-agent.ts
CHANGED
|
@@ -160,13 +160,17 @@ export async function runAgent(_args: string[]) {
|
|
|
160
160
|
await runtime.start();
|
|
161
161
|
const runtimeApp = runtime.getApp();
|
|
162
162
|
|
|
163
|
-
// 7b. Initialize
|
|
163
|
+
// 7b. Initialize shared singletons from routes.js so hooks work in standalone mode
|
|
164
164
|
try {
|
|
165
|
-
const
|
|
166
|
-
await permissionEngine.setDb(engineDb);
|
|
165
|
+
const routes = await import('./engine/routes.js');
|
|
166
|
+
await routes.permissionEngine.setDb(engineDb);
|
|
167
167
|
console.log(' Permissions: loaded from DB');
|
|
168
|
+
// Initialize lifecycle singleton so recordLLMUsage and other hooks work
|
|
169
|
+
await routes.lifecycle.setDb(engineDb);
|
|
170
|
+
await routes.lifecycle.loadFromDb();
|
|
171
|
+
console.log(' Hooks lifecycle: initialized');
|
|
168
172
|
} catch (permErr: any) {
|
|
169
|
-
console.warn(`
|
|
173
|
+
console.warn(` Routes init: failed (${permErr.message}) — some features may not work`);
|
|
170
174
|
}
|
|
171
175
|
|
|
172
176
|
// 8. Start health check HTTP server
|
|
@@ -284,7 +288,73 @@ export async function runAgent(_args: string[]) {
|
|
|
284
288
|
console.log(`[onboarding] ✅ Onboarding complete — ${policyNames.length} policies acknowledged`);
|
|
285
289
|
}
|
|
286
290
|
|
|
287
|
-
// 11.
|
|
291
|
+
// 11. Auto-setup Gmail signature from org template (BEFORE welcome email so it's included)
|
|
292
|
+
try {
|
|
293
|
+
const orgSettings = await db.getSettings();
|
|
294
|
+
const sigTemplate = (orgSettings as any)?.signatureTemplate;
|
|
295
|
+
const sigEmailConfig = config.emailConfig || {};
|
|
296
|
+
let sigToken = sigEmailConfig.oauthAccessToken;
|
|
297
|
+
if (sigEmailConfig.oauthRefreshToken && sigEmailConfig.oauthClientId) {
|
|
298
|
+
try {
|
|
299
|
+
const tokenUrl = (sigEmailConfig.provider || sigEmailConfig.oauthProvider) === 'google'
|
|
300
|
+
? 'https://oauth2.googleapis.com/token'
|
|
301
|
+
: 'https://login.microsoftonline.com/common/oauth2/v2.0/token';
|
|
302
|
+
const tokenRes = await fetch(tokenUrl, {
|
|
303
|
+
method: 'POST',
|
|
304
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
305
|
+
body: new URLSearchParams({
|
|
306
|
+
client_id: sigEmailConfig.oauthClientId,
|
|
307
|
+
client_secret: sigEmailConfig.oauthClientSecret,
|
|
308
|
+
refresh_token: sigEmailConfig.oauthRefreshToken,
|
|
309
|
+
grant_type: 'refresh_token',
|
|
310
|
+
}),
|
|
311
|
+
});
|
|
312
|
+
const tokenData = await tokenRes.json() as any;
|
|
313
|
+
if (tokenData.access_token) sigToken = tokenData.access_token;
|
|
314
|
+
} catch {}
|
|
315
|
+
}
|
|
316
|
+
if (sigTemplate && sigToken) {
|
|
317
|
+
const agName = config.displayName || config.name;
|
|
318
|
+
const agRole = config.identity?.role || 'AI Agent';
|
|
319
|
+
const agEmail = config.email?.address || sigEmailConfig?.email || '';
|
|
320
|
+
const companyName = orgSettings?.name || '';
|
|
321
|
+
const logoUrl = orgSettings?.logoUrl || '';
|
|
322
|
+
|
|
323
|
+
const signature = sigTemplate
|
|
324
|
+
.replace(/\{\{name\}\}/g, agName)
|
|
325
|
+
.replace(/\{\{role\}\}/g, agRole)
|
|
326
|
+
.replace(/\{\{email\}\}/g, agEmail)
|
|
327
|
+
.replace(/\{\{company\}\}/g, companyName)
|
|
328
|
+
.replace(/\{\{logo\}\}/g, logoUrl)
|
|
329
|
+
.replace(/\{\{phone\}\}/g, '');
|
|
330
|
+
|
|
331
|
+
const sendAsRes = await fetch('https://gmail.googleapis.com/gmail/v1/users/me/settings/sendAs', {
|
|
332
|
+
headers: { Authorization: `Bearer ${sigToken}` },
|
|
333
|
+
});
|
|
334
|
+
const sendAs = await sendAsRes.json() as any;
|
|
335
|
+
const primary = sendAs.sendAs?.find((s: any) => s.isPrimary) || sendAs.sendAs?.[0];
|
|
336
|
+
if (primary) {
|
|
337
|
+
const patchRes = await fetch(`https://gmail.googleapis.com/gmail/v1/users/me/settings/sendAs/${encodeURIComponent(primary.sendAsEmail)}`, {
|
|
338
|
+
method: 'PATCH',
|
|
339
|
+
headers: { Authorization: `Bearer ${sigToken}`, 'Content-Type': 'application/json' },
|
|
340
|
+
body: JSON.stringify({ signature }),
|
|
341
|
+
});
|
|
342
|
+
if (patchRes.ok) {
|
|
343
|
+
console.log(`[signature] ✅ Gmail signature set for ${primary.sendAsEmail}`);
|
|
344
|
+
} else {
|
|
345
|
+
const errBody = await patchRes.text();
|
|
346
|
+
console.log(`[signature] Failed (${patchRes.status}): ${errBody.slice(0, 200)}`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
} else {
|
|
350
|
+
if (!sigTemplate) console.log('[signature] No signature template configured');
|
|
351
|
+
if (!sigToken) console.log('[signature] No OAuth token for signature setup');
|
|
352
|
+
}
|
|
353
|
+
} catch (sigErr: any) {
|
|
354
|
+
console.log(`[signature] Skipped: ${sigErr.message}`);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// 12. Send welcome email to manager if configured
|
|
288
358
|
// Manager email can come from config.managerEmail or config.manager.email
|
|
289
359
|
const managerEmail = (config as any).managerEmail || ((config as any).manager?.type === 'external' ? (config as any).manager.email : null);
|
|
290
360
|
const emailConfig = (config as any).emailConfig;
|
|
@@ -416,66 +486,6 @@ Available tools: gmail_send (to, subject, body) or agenticmail_send (to, subject
|
|
|
416
486
|
console.error(`[onboarding] Error: ${err.message}`);
|
|
417
487
|
}
|
|
418
488
|
|
|
419
|
-
// 12. Auto-setup Gmail signature from org template
|
|
420
|
-
try {
|
|
421
|
-
const orgSettings = await db.getSettings();
|
|
422
|
-
const sigTemplate = (orgSettings as any)?.signatureTemplate;
|
|
423
|
-
const sigEmailConfig = config.emailConfig || {};
|
|
424
|
-
// Get a fresh access token for signature setup
|
|
425
|
-
let sigToken = sigEmailConfig.oauthAccessToken;
|
|
426
|
-
if (sigEmailConfig.oauthRefreshToken && sigEmailConfig.oauthClientId) {
|
|
427
|
-
try {
|
|
428
|
-
const tokenUrl = (sigEmailConfig.provider || sigEmailConfig.oauthProvider) === 'google'
|
|
429
|
-
? 'https://oauth2.googleapis.com/token'
|
|
430
|
-
: 'https://login.microsoftonline.com/common/oauth2/v2.0/token';
|
|
431
|
-
const tokenRes = await fetch(tokenUrl, {
|
|
432
|
-
method: 'POST',
|
|
433
|
-
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
434
|
-
body: new URLSearchParams({
|
|
435
|
-
client_id: sigEmailConfig.oauthClientId,
|
|
436
|
-
client_secret: sigEmailConfig.oauthClientSecret,
|
|
437
|
-
refresh_token: sigEmailConfig.oauthRefreshToken,
|
|
438
|
-
grant_type: 'refresh_token',
|
|
439
|
-
}),
|
|
440
|
-
});
|
|
441
|
-
const tokenData = await tokenRes.json() as any;
|
|
442
|
-
if (tokenData.access_token) sigToken = tokenData.access_token;
|
|
443
|
-
} catch {}
|
|
444
|
-
}
|
|
445
|
-
if (sigTemplate && sigToken) {
|
|
446
|
-
const agentName = config.displayName || config.name;
|
|
447
|
-
const role = config.identity?.role || 'AI Agent';
|
|
448
|
-
const agentEmailAddr = config.email?.address || sigEmailConfig?.email || '';
|
|
449
|
-
const companyName = orgSettings?.name || '';
|
|
450
|
-
const logoUrl = orgSettings?.logoUrl || '';
|
|
451
|
-
|
|
452
|
-
const signature = sigTemplate
|
|
453
|
-
.replace(/\{\{name\}\}/g, agentName)
|
|
454
|
-
.replace(/\{\{role\}\}/g, role)
|
|
455
|
-
.replace(/\{\{email\}\}/g, agentEmailAddr)
|
|
456
|
-
.replace(/\{\{company\}\}/g, companyName)
|
|
457
|
-
.replace(/\{\{logo\}\}/g, logoUrl)
|
|
458
|
-
.replace(/\{\{phone\}\}/g, '');
|
|
459
|
-
|
|
460
|
-
// Set Gmail signature via API
|
|
461
|
-
const sendAsRes = await fetch('https://gmail.googleapis.com/gmail/v1/users/me/settings/sendAs', {
|
|
462
|
-
headers: { Authorization: `Bearer ${sigToken}` },
|
|
463
|
-
});
|
|
464
|
-
const sendAs = await sendAsRes.json() as any;
|
|
465
|
-
const primary = sendAs.sendAs?.find((s: any) => s.isPrimary) || sendAs.sendAs?.[0];
|
|
466
|
-
if (primary) {
|
|
467
|
-
await fetch(`https://gmail.googleapis.com/gmail/v1/users/me/settings/sendAs/${encodeURIComponent(primary.sendAsEmail)}`, {
|
|
468
|
-
method: 'PATCH',
|
|
469
|
-
headers: { Authorization: `Bearer ${sigToken}`, 'Content-Type': 'application/json' },
|
|
470
|
-
body: JSON.stringify({ signature }),
|
|
471
|
-
});
|
|
472
|
-
console.log(`[signature] ✅ Gmail signature set for ${primary.sendAsEmail}`);
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
} catch (sigErr: any) {
|
|
476
|
-
console.log(`[signature] Skipped: ${sigErr.message}`);
|
|
477
|
-
}
|
|
478
|
-
|
|
479
489
|
// 13. Start email inbox polling loop
|
|
480
490
|
startEmailPolling(AGENT_ID, config, lifecycle, runtime, engineDb, memoryManager);
|
|
481
491
|
}, 3000);
|
|
@@ -689,7 +699,14 @@ FORMATTING RULES (STRICTLY ENFORCED):
|
|
|
689
699
|
- Use line breaks between paragraphs, nothing else for formatting
|
|
690
700
|
- Keep it warm and conversational, not robotic or formatted
|
|
691
701
|
|
|
692
|
-
CRITICAL: You MUST call gmail_reply EXACTLY ONCE to send your reply. Do NOT call it multiple times. Do NOT just generate text without calling the tool
|
|
702
|
+
CRITICAL: You MUST call gmail_reply EXACTLY ONCE to send your reply. Do NOT call it multiple times. Do NOT just generate text without calling the tool.
|
|
703
|
+
|
|
704
|
+
== TASK MANAGEMENT ==
|
|
705
|
+
When you receive work requests, action items, or follow-ups:
|
|
706
|
+
- Use google_tasks_create to track them (title, notes, due date)
|
|
707
|
+
- Check google_tasks_list at the start of interactions to see pending work
|
|
708
|
+
- Use google_tasks_complete when done with items
|
|
709
|
+
This helps you stay organized and ensures nothing falls through the cracks.`;
|
|
693
710
|
|
|
694
711
|
const session = await runtime.spawnSession({
|
|
695
712
|
agentId,
|
|
@@ -593,6 +593,7 @@ export function createAgentRoutes(opts: {
|
|
|
593
593
|
'https://www.googleapis.com/auth/spreadsheets',
|
|
594
594
|
'https://www.googleapis.com/auth/documents',
|
|
595
595
|
'https://www.googleapis.com/auth/contacts',
|
|
596
|
+
'https://www.googleapis.com/auth/tasks',
|
|
596
597
|
];
|
|
597
598
|
|
|
598
599
|
if (!oauthClientId) return c.json({ error: 'oauthClientId is required for Google OAuth' }, 400);
|