@agenticmail/enterprise 0.5.145 → 0.5.147
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-3ODL7JXF.js +8844 -0
- package/dist/chunk-75367ZJT.js +898 -0
- package/dist/chunk-AOR6GSZQ.js +16482 -0
- package/dist/chunk-BO2INMG7.js +16481 -0
- package/dist/chunk-I3N7IYKU.js +898 -0
- package/dist/chunk-IRGPBOTO.js +8844 -0
- package/dist/chunk-ROB2PPZ2.js +2195 -0
- package/dist/chunk-Y3Y6JZRR.js +2195 -0
- package/dist/cli-agent-C5GYBTEP.js +810 -0
- package/dist/cli-agent-PUH5JU4E.js +810 -0
- package/dist/cli-serve-PAZBBNYO.js +34 -0
- package/dist/cli-serve-ZKXQZQWK.js +34 -0
- package/dist/cli.js +3 -3
- package/dist/index.js +25 -27
- package/dist/routes-4FV4L473.js +6972 -0
- package/dist/routes-WULXKYTH.js +6972 -0
- package/dist/runtime-HSHTH7C4.js +49 -0
- package/dist/runtime-SQWDOPRH.js +49 -0
- package/dist/server-4U35WLPR.js +12 -0
- package/dist/server-7TML6P53.js +12 -0
- package/dist/setup-FYDVXY3G.js +20 -0
- package/dist/setup-H5HIWYD4.js +20 -0
- package/package.json +1 -1
- package/src/agent-tools/tools/meeting-lifecycle.ts +72 -0
- package/src/cli-agent.ts +170 -13
- package/src/engine/deployer.ts +1 -1
- package/src/runtime/agent-loop.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-AOR6GSZQ.js";
|
|
18
|
+
import "./chunk-AQH4DFYV.js";
|
|
19
|
+
import "./chunk-JLSQOQ5L.js";
|
|
20
|
+
import {
|
|
21
|
+
PROVIDER_REGISTRY,
|
|
22
|
+
listAllProviders,
|
|
23
|
+
resolveApiKeyForProvider,
|
|
24
|
+
resolveProvider
|
|
25
|
+
} from "./chunk-67KZYSLU.js";
|
|
26
|
+
import "./chunk-NRF3YRF7.js";
|
|
27
|
+
import "./chunk-TYW5XTOW.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,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-BO2INMG7.js";
|
|
18
|
+
import "./chunk-AQH4DFYV.js";
|
|
19
|
+
import "./chunk-JLSQOQ5L.js";
|
|
20
|
+
import {
|
|
21
|
+
PROVIDER_REGISTRY,
|
|
22
|
+
listAllProviders,
|
|
23
|
+
resolveApiKeyForProvider,
|
|
24
|
+
resolveProvider
|
|
25
|
+
} from "./chunk-67KZYSLU.js";
|
|
26
|
+
import "./chunk-NRF3YRF7.js";
|
|
27
|
+
import "./chunk-TYW5XTOW.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-ROB2PPZ2.js";
|
|
4
|
+
import "./chunk-3SMTCIR4.js";
|
|
5
|
+
import "./chunk-JLSQOQ5L.js";
|
|
6
|
+
import "./chunk-RO537U6H.js";
|
|
7
|
+
import "./chunk-DRXMYYKN.js";
|
|
8
|
+
import "./chunk-67KZYSLU.js";
|
|
9
|
+
import "./chunk-KFQGP6VL.js";
|
|
10
|
+
export {
|
|
11
|
+
createServer
|
|
12
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createServer
|
|
3
|
+
} from "./chunk-Y3Y6JZRR.js";
|
|
4
|
+
import "./chunk-3SMTCIR4.js";
|
|
5
|
+
import "./chunk-JLSQOQ5L.js";
|
|
6
|
+
import "./chunk-RO537U6H.js";
|
|
7
|
+
import "./chunk-DRXMYYKN.js";
|
|
8
|
+
import "./chunk-67KZYSLU.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-75367ZJT.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
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
promptCompanyInfo,
|
|
3
|
+
promptDatabase,
|
|
4
|
+
promptDeployment,
|
|
5
|
+
promptDomain,
|
|
6
|
+
promptRegistration,
|
|
7
|
+
provision,
|
|
8
|
+
runSetupWizard
|
|
9
|
+
} from "./chunk-I3N7IYKU.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
|
@@ -373,6 +373,78 @@ export function createMeetingLifecycleTools(config: MeetingLifecycleConfig, _opt
|
|
|
373
373
|
},
|
|
374
374
|
|
|
375
375
|
// ─── Check Meeting Joinability ─────────────────────
|
|
376
|
+
{
|
|
377
|
+
name: 'meeting_join',
|
|
378
|
+
description: 'Join a video meeting (Google Meet, Zoom, or Teams) by opening the meeting URL in a browser. Works in observer mode on containers (can see screen/chat but no real audio/video). On VMs, full audio/video is available.',
|
|
379
|
+
category: 'utility' as const,
|
|
380
|
+
parameters: {
|
|
381
|
+
type: 'object' as const,
|
|
382
|
+
properties: {
|
|
383
|
+
url: { type: 'string', description: 'Meeting URL (e.g. https://meet.google.com/abc-defg-hij)' },
|
|
384
|
+
platform: { type: 'string', description: 'Meeting platform: google_meet, zoom, teams (auto-detected from URL if omitted)' },
|
|
385
|
+
muted: { type: 'boolean', description: 'Join with mic muted (default: true)' },
|
|
386
|
+
cameraOff: { type: 'boolean', description: 'Join with camera off (default: true)' },
|
|
387
|
+
},
|
|
388
|
+
required: ['url'],
|
|
389
|
+
},
|
|
390
|
+
async execute(_id: string, params: any) {
|
|
391
|
+
const meetUrl = params.url;
|
|
392
|
+
if (!meetUrl) return errorResult('Meeting URL is required');
|
|
393
|
+
|
|
394
|
+
const caps = getCaps();
|
|
395
|
+
const summary = getCapabilitySummary(caps);
|
|
396
|
+
|
|
397
|
+
// Auto-detect platform
|
|
398
|
+
let platform = params.platform || 'unknown';
|
|
399
|
+
if (meetUrl.includes('meet.google.com')) platform = 'google_meet';
|
|
400
|
+
else if (meetUrl.includes('zoom.us') || meetUrl.includes('zoom.com')) platform = 'zoom';
|
|
401
|
+
else if (meetUrl.includes('teams.microsoft.com') || meetUrl.includes('teams.live.com')) platform = 'teams';
|
|
402
|
+
|
|
403
|
+
// Return instructions for the agent to use browser tool
|
|
404
|
+
// The agent has browser tool access and should use it to navigate to the URL
|
|
405
|
+
const isObserverOnly = !caps.canJoinMeetings;
|
|
406
|
+
const instructions: string[] = [];
|
|
407
|
+
|
|
408
|
+
if (platform === 'google_meet') {
|
|
409
|
+
instructions.push(
|
|
410
|
+
`Navigate to: ${meetUrl}`,
|
|
411
|
+
'Wait for the pre-join screen to load.',
|
|
412
|
+
'If prompted to sign in, you should already be authenticated via Google OAuth.',
|
|
413
|
+
isObserverOnly
|
|
414
|
+
? 'You are in OBSERVER mode: camera and mic will show as unavailable. Click "Join now" or "Ask to join" anyway.'
|
|
415
|
+
: 'Toggle mic OFF (Ctrl+D) and camera OFF (Ctrl+E) before joining.',
|
|
416
|
+
'Click the "Join now" or "Ask to join" button.',
|
|
417
|
+
'Once in the meeting, you can observe the screen and read the chat.',
|
|
418
|
+
);
|
|
419
|
+
} else if (platform === 'zoom') {
|
|
420
|
+
instructions.push(
|
|
421
|
+
`Navigate to: ${meetUrl}`,
|
|
422
|
+
'Click "Join from Your Browser" (do NOT install the Zoom client).',
|
|
423
|
+
'Enter your name if prompted.',
|
|
424
|
+
'Join with mic and camera off.',
|
|
425
|
+
);
|
|
426
|
+
} else if (platform === 'teams') {
|
|
427
|
+
instructions.push(
|
|
428
|
+
`Navigate to: ${meetUrl}`,
|
|
429
|
+
'Click "Continue on this browser" (do NOT use the Teams app).',
|
|
430
|
+
'Enter your name if prompted.',
|
|
431
|
+
'Join with mic and camera off.',
|
|
432
|
+
);
|
|
433
|
+
} else {
|
|
434
|
+
instructions.push(`Navigate to: ${meetUrl}`, 'Follow the on-screen prompts to join.');
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return jsonResult({
|
|
438
|
+
status: 'ready_to_join',
|
|
439
|
+
platform,
|
|
440
|
+
url: meetUrl,
|
|
441
|
+
mode: isObserverOnly ? 'observer' : 'full',
|
|
442
|
+
deployment: summary.deployment,
|
|
443
|
+
instructions,
|
|
444
|
+
nextStep: 'Use the browser tool to navigate to the URL and follow the instructions above. Use browser(action="navigate", targetUrl="' + meetUrl + '") then browser(action="snapshot") to see the page.',
|
|
445
|
+
});
|
|
446
|
+
},
|
|
447
|
+
},
|
|
376
448
|
{
|
|
377
449
|
name: 'meeting_can_join',
|
|
378
450
|
description: 'Check if this agent can join a video meeting on the current deployment. Returns capabilities and specific instructions based on what is available.',
|
package/src/cli-agent.ts
CHANGED
|
@@ -78,9 +78,13 @@ export async function runAgent(_args: string[]) {
|
|
|
78
78
|
console.log(` State: ${agent.state}`);
|
|
79
79
|
|
|
80
80
|
// 4. Initialize lifecycle (manages agent state, config decryption)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
// IMPORTANT: We use the routes.js singleton lifecycle so hooks.ts and this file
|
|
82
|
+
// share the SAME instance. This prevents the "two lifecycle" bug where
|
|
83
|
+
// lifecycle.saveAgent() overwrites usage counters written by routes.lifecycle.recordLLMUsage().
|
|
84
|
+
const routes = await import('./engine/routes.js');
|
|
85
|
+
await routes.lifecycle.setDb(engineDb);
|
|
86
|
+
await routes.lifecycle.loadFromDb();
|
|
87
|
+
const lifecycle = routes.lifecycle; // Use the singleton everywhere
|
|
84
88
|
|
|
85
89
|
const managed = lifecycle.getAgent(AGENT_ID);
|
|
86
90
|
if (!managed) {
|
|
@@ -160,15 +164,12 @@ export async function runAgent(_args: string[]) {
|
|
|
160
164
|
await runtime.start();
|
|
161
165
|
const runtimeApp = runtime.getApp();
|
|
162
166
|
|
|
163
|
-
// 7b. Initialize shared singletons from routes.js so hooks work in standalone mode
|
|
167
|
+
// 7b. Initialize remaining shared singletons from routes.js so hooks work in standalone mode
|
|
168
|
+
// Note: lifecycle was already initialized in step 4 (we use routes.lifecycle as the single instance)
|
|
164
169
|
try {
|
|
165
|
-
const routes = await import('./engine/routes.js');
|
|
166
170
|
await routes.permissionEngine.setDb(engineDb);
|
|
167
171
|
console.log(' Permissions: loaded from DB');
|
|
168
|
-
|
|
169
|
-
await routes.lifecycle.setDb(engineDb);
|
|
170
|
-
await routes.lifecycle.loadFromDb();
|
|
171
|
-
console.log(' Hooks lifecycle: initialized');
|
|
172
|
+
console.log(' Hooks lifecycle: initialized (shared singleton from step 4)');
|
|
172
173
|
} catch (permErr: any) {
|
|
173
174
|
console.warn(` Routes init: failed (${permErr.message}) — some features may not work`);
|
|
174
175
|
}
|
|
@@ -488,6 +489,9 @@ Available tools: gmail_send (to, subject, body) or agenticmail_send (to, subject
|
|
|
488
489
|
|
|
489
490
|
// 13. Start email inbox polling loop
|
|
490
491
|
startEmailPolling(AGENT_ID, config, lifecycle, runtime, engineDb, memoryManager);
|
|
492
|
+
|
|
493
|
+
// 14. Start calendar polling loop (checks for upcoming meetings to auto-join)
|
|
494
|
+
startCalendarPolling(AGENT_ID, config, runtime, engineDb, memoryManager);
|
|
491
495
|
}, 3000);
|
|
492
496
|
}
|
|
493
497
|
|
|
@@ -601,11 +605,14 @@ async function startEmailPolling(
|
|
|
601
605
|
|
|
602
606
|
// Persist processed ID to DB so it survives restarts
|
|
603
607
|
try {
|
|
608
|
+
const ts = new Date().toISOString();
|
|
604
609
|
await engineDb.execute(
|
|
605
|
-
`INSERT INTO agent_memory (id, agent_id, org_id, category, content, importance, confidence, access_count, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`,
|
|
606
|
-
[crypto.randomUUID(), agentId, orgId, 'processed_email', envelope.uid, 'low', 1.0, 0,
|
|
610
|
+
`INSERT INTO agent_memory (id, agent_id, org_id, category, title, content, source, importance, confidence, access_count, tags, metadata, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)`,
|
|
611
|
+
[crypto.randomUUID(), agentId, orgId, 'processed_email', `Processed: ${envelope.subject || envelope.uid}`, envelope.uid, 'system', 'low', 1.0, 0, '[]', '{}', ts, ts]
|
|
607
612
|
);
|
|
608
|
-
} catch {
|
|
613
|
+
} catch (peErr: any) {
|
|
614
|
+
console.warn(`[email-poll] Failed to persist processed ID: ${peErr.message}`);
|
|
615
|
+
}
|
|
609
616
|
|
|
610
617
|
// Read full message
|
|
611
618
|
const fullMsg = await emailProvider.readMessage(envelope.uid, 'INBOX');
|
|
@@ -759,7 +766,25 @@ EXAMPLES of things to remember:
|
|
|
759
766
|
BEFORE starting work, call memory(action: "search", query: "relevant topic") to check if you already know something useful.
|
|
760
767
|
This avoids repeating work and helps you recall folder IDs, preferences, and context.
|
|
761
768
|
|
|
762
|
-
The goal: get smarter with every interaction. Never do the same research twice. Never ask for info you've already stored
|
|
769
|
+
The goal: get smarter with every interaction. Never do the same research twice. Never ask for info you've already stored.
|
|
770
|
+
|
|
771
|
+
== CALENDAR & MEETINGS (AUTONOMOUS) ==
|
|
772
|
+
When you receive a calendar invite or meeting link (Google Meet, Zoom, Teams), you MUST:
|
|
773
|
+
1. Check google_calendar_list for upcoming events in the next 24 hours
|
|
774
|
+
2. If a meeting is starting soon (within 15 minutes), attempt to join it using the meeting_join tool
|
|
775
|
+
3. Store the meeting details in your memory (meeting ID, time, link, attendees)
|
|
776
|
+
|
|
777
|
+
For meetings you organize or are invited to:
|
|
778
|
+
- ALWAYS check your calendar autonomously — don't wait to be told
|
|
779
|
+
- If you created a meeting, you are the HOST — join BEFORE other attendees so they can be admitted
|
|
780
|
+
- Use meeting_join to open the meeting link in your browser
|
|
781
|
+
- In observer mode (container deployment): you can see the screen and chat, but cannot send real audio/video
|
|
782
|
+
|
|
783
|
+
When you receive an email containing a meeting link (meet.google.com, zoom.us, teams.microsoft.com):
|
|
784
|
+
- Extract the link and attempt to join immediately if the meeting is now
|
|
785
|
+
- If the meeting is in the future, store it in memory and note the time`;
|
|
786
|
+
|
|
787
|
+
|
|
763
788
|
|
|
764
789
|
const session = await runtime.spawnSession({
|
|
765
790
|
agentId,
|
|
@@ -798,3 +823,135 @@ The goal: get smarter with every interaction. Never do the same research twice.
|
|
|
798
823
|
// First poll after 5s
|
|
799
824
|
setTimeout(pollOnce, 5000);
|
|
800
825
|
}
|
|
826
|
+
|
|
827
|
+
// ─── Calendar Polling Loop ──────────────────────────────────
|
|
828
|
+
|
|
829
|
+
async function startCalendarPolling(
|
|
830
|
+
agentId: string, config: any, runtime: any,
|
|
831
|
+
engineDb: any, memoryManager: any
|
|
832
|
+
) {
|
|
833
|
+
const emailConfig = config.emailConfig;
|
|
834
|
+
if (!emailConfig?.oauthAccessToken) {
|
|
835
|
+
console.log('[calendar-poll] No OAuth token, calendar polling disabled');
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
const providerType = emailConfig.provider || (emailConfig.oauthProvider === 'google' ? 'google' : 'microsoft');
|
|
840
|
+
if (providerType !== 'google') {
|
|
841
|
+
console.log('[calendar-poll] Calendar polling only supports Google for now');
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// Token refresh function
|
|
846
|
+
const refreshToken = async (): Promise<string> => {
|
|
847
|
+
const res = await fetch('https://oauth2.googleapis.com/token', {
|
|
848
|
+
method: 'POST',
|
|
849
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
850
|
+
body: new URLSearchParams({
|
|
851
|
+
client_id: emailConfig.oauthClientId,
|
|
852
|
+
client_secret: emailConfig.oauthClientSecret,
|
|
853
|
+
refresh_token: emailConfig.oauthRefreshToken,
|
|
854
|
+
grant_type: 'refresh_token',
|
|
855
|
+
}),
|
|
856
|
+
});
|
|
857
|
+
const data = await res.json() as any;
|
|
858
|
+
if (data.access_token) {
|
|
859
|
+
emailConfig.oauthAccessToken = data.access_token;
|
|
860
|
+
return data.access_token;
|
|
861
|
+
}
|
|
862
|
+
throw new Error('Token refresh failed');
|
|
863
|
+
};
|
|
864
|
+
|
|
865
|
+
const CALENDAR_POLL_INTERVAL = 5 * 60_000; // Check every 5 minutes
|
|
866
|
+
const joinedMeetings = new Set<string>(); // Track already-joined meeting IDs
|
|
867
|
+
|
|
868
|
+
console.log('[calendar-poll] ✅ Calendar polling started (every 5 min)');
|
|
869
|
+
|
|
870
|
+
async function checkCalendar() {
|
|
871
|
+
try {
|
|
872
|
+
let token = emailConfig.oauthAccessToken;
|
|
873
|
+
|
|
874
|
+
// Get events in the next 30 minutes
|
|
875
|
+
const now = new Date();
|
|
876
|
+
const soon = new Date(now.getTime() + 30 * 60_000);
|
|
877
|
+
const params = new URLSearchParams({
|
|
878
|
+
timeMin: now.toISOString(),
|
|
879
|
+
timeMax: soon.toISOString(),
|
|
880
|
+
singleEvents: 'true',
|
|
881
|
+
orderBy: 'startTime',
|
|
882
|
+
maxResults: '10',
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
let res = await fetch(`https://www.googleapis.com/calendar/v3/calendars/primary/events?${params}`, {
|
|
886
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
// Token expired — refresh
|
|
890
|
+
if (res.status === 401) {
|
|
891
|
+
try { token = await refreshToken(); } catch { return; }
|
|
892
|
+
res = await fetch(`https://www.googleapis.com/calendar/v3/calendars/primary/events?${params}`, {
|
|
893
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
if (!res.ok) return;
|
|
898
|
+
const data = await res.json() as any;
|
|
899
|
+
const events = data.items || [];
|
|
900
|
+
|
|
901
|
+
for (const event of events) {
|
|
902
|
+
const meetLink = event.hangoutLink || event.conferenceData?.entryPoints?.find((e: any) => e.entryPointType === 'video')?.uri;
|
|
903
|
+
if (!meetLink) continue;
|
|
904
|
+
if (joinedMeetings.has(event.id)) continue;
|
|
905
|
+
|
|
906
|
+
// Check if meeting starts within 10 minutes
|
|
907
|
+
const startTime = new Date(event.start?.dateTime || event.start?.date);
|
|
908
|
+
const minutesUntilStart = (startTime.getTime() - now.getTime()) / 60_000;
|
|
909
|
+
|
|
910
|
+
if (minutesUntilStart <= 10) {
|
|
911
|
+
console.log(`[calendar-poll] Meeting starting soon: "${event.summary}" in ${Math.round(minutesUntilStart)} min — ${meetLink}`);
|
|
912
|
+
joinedMeetings.add(event.id);
|
|
913
|
+
|
|
914
|
+
// Spawn a session to join the meeting
|
|
915
|
+
const agentName = config.displayName || config.name;
|
|
916
|
+
const role = config.identity?.role || 'AI Agent';
|
|
917
|
+
const identity = config.identity || {};
|
|
918
|
+
|
|
919
|
+
try {
|
|
920
|
+
await runtime.spawnSession({
|
|
921
|
+
agentId,
|
|
922
|
+
message: `[Calendar Alert] You have a meeting starting ${minutesUntilStart <= 0 ? 'NOW' : `in ${Math.round(minutesUntilStart)} minutes`}:
|
|
923
|
+
|
|
924
|
+
Title: ${event.summary || 'Untitled Meeting'}
|
|
925
|
+
Time: ${startTime.toISOString()}
|
|
926
|
+
Link: ${meetLink}
|
|
927
|
+
Attendees: ${(event.attendees || []).map((a: any) => a.email).join(', ') || 'none listed'}
|
|
928
|
+
${event.description ? `Description: ${event.description.slice(0, 300)}` : ''}
|
|
929
|
+
|
|
930
|
+
Join this meeting now using the meeting_join tool with the link above. You are ${event.organizer?.self ? 'the HOST — join immediately so attendees can be admitted' : 'an attendee'}.`,
|
|
931
|
+
systemPrompt: `You are ${agentName}, a ${role}. ${identity.personality || ''}
|
|
932
|
+
|
|
933
|
+
You have a meeting to join RIGHT NOW. Use the meeting_join tool to open the meeting link in your browser.
|
|
934
|
+
|
|
935
|
+
Steps:
|
|
936
|
+
1. Call meeting_join with the meeting link
|
|
937
|
+
2. If that fails, try using the browser tool to navigate to the link directly
|
|
938
|
+
3. Once in the meeting, monitor the chat and screen
|
|
939
|
+
4. Store meeting notes in memory after the meeting
|
|
940
|
+
|
|
941
|
+
IMPORTANT: Join the meeting IMMEDIATELY. Do not email anyone about it — just join.`,
|
|
942
|
+
});
|
|
943
|
+
console.log(`[calendar-poll] ✅ Spawned meeting join session for "${event.summary}"`);
|
|
944
|
+
} catch (err: any) {
|
|
945
|
+
console.error(`[calendar-poll] Failed to spawn meeting session: ${err.message}`);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
} catch (err: any) {
|
|
950
|
+
console.error(`[calendar-poll] Error: ${err.message}`);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
// First check after 10s, then every 5 min
|
|
955
|
+
setTimeout(checkCalendar, 10_000);
|
|
956
|
+
setInterval(checkCalendar, CALENDAR_POLL_INTERVAL);
|
|
957
|
+
}
|
package/src/engine/deployer.ts
CHANGED
|
@@ -487,7 +487,7 @@ export class DeploymentEngine {
|
|
|
487
487
|
memory_mb: size.includes('2x') ? 1024 : 512,
|
|
488
488
|
},
|
|
489
489
|
init: {
|
|
490
|
-
cmd: ['sh', '-c', 'rm -rf /root/.npm && mkdir -p /tmp/agent && cd /tmp/agent && npm init -y > /dev/null 2>&1 && npm install --no-save @agenticmail/enterprise pg && npx @agenticmail/enterprise agent'],
|
|
490
|
+
cmd: ['sh', '-c', 'rm -rf /root/.npm && mkdir -p /tmp/agent && cd /tmp/agent && npm init -y > /dev/null 2>&1 && npm install --no-save @agenticmail/enterprise openai pg && npx @agenticmail/enterprise agent'],
|
|
491
491
|
},
|
|
492
492
|
auto_destroy: false,
|
|
493
493
|
restart: { policy: 'always' },
|
|
@@ -154,6 +154,7 @@ export async function runAgentLoop(
|
|
|
154
154
|
options.retryConfig,
|
|
155
155
|
);
|
|
156
156
|
} catch (err: any) {
|
|
157
|
+
console.error(`[agent-loop] LLM call failed: ${err.message}`);
|
|
157
158
|
var errorEvent: StreamEvent = { type: 'error', message: err.message, retryable: false };
|
|
158
159
|
options.onEvent?.(errorEvent);
|
|
159
160
|
return buildResult(messages, 'failed', turnCount, totalTextContent, 'error');
|