@agenticmail/enterprise 0.5.83 → 0.5.85
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/browser-tool-KJMHSNK3.js +3997 -0
- package/dist/chunk-4DXQQPEC.js +15441 -0
- package/dist/chunk-AQH4DFYV.js +142 -0
- package/dist/chunk-CETB63LZ.js +15813 -0
- package/dist/chunk-LOA5LNXR.js +898 -0
- package/dist/chunk-PHZS6OIO.js +2191 -0
- package/dist/chunk-QISZRTVR.js +2154 -0
- package/dist/chunk-SEGTMIPI.js +2191 -0
- package/dist/chunk-TGOSFAYW.js +194 -0
- package/dist/chunk-ZTOVB5OQ.js +898 -0
- package/dist/cli.js +1 -1
- package/dist/environment-L6BN6KGK.js +11 -0
- package/dist/index.js +5 -3
- package/dist/pw-ai-IRORDXFW.js +2212 -0
- package/dist/routes-4NHH2DJW.js +6849 -0
- package/dist/routes-VIK7WDVW.js +6859 -0
- package/dist/runtime-DAVEFGHR.js +47 -0
- package/dist/runtime-NEO2ZB6O.js +49 -0
- package/dist/server-F6GBGLJY.js +12 -0
- package/dist/server-ZIB4HUUC.js +12 -0
- package/dist/setup-NKJBKCEG.js +20 -0
- package/dist/setup-YD3LJ6DT.js +20 -0
- package/package.json +1 -1
- package/scripts/vm-setup.sh +309 -0
- package/src/agent-tools/index.ts +70 -3
- package/src/agent-tools/tools/google/index.ts +4 -1
- package/src/agent-tools/tools/google/meetings.ts +468 -0
- package/src/agent-tools/tools/meeting-lifecycle.ts +437 -0
- package/src/engine/agent-routes.ts +19 -0
- package/src/runtime/agent-loop.ts +1 -1
- package/src/runtime/environment.ts +290 -0
- package/src/runtime/index.ts +3 -3
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enterprise Deployment Environment Detection
|
|
3
|
+
*
|
|
4
|
+
* Detects the deployment environment (container vs VM vs local dev)
|
|
5
|
+
* and available system capabilities (browser, audio, video, display).
|
|
6
|
+
* Tools use this to gracefully degrade or provide helpful error messages.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { existsSync } from 'node:fs';
|
|
10
|
+
import { execSync } from 'node:child_process';
|
|
11
|
+
|
|
12
|
+
export type DeploymentType = 'container' | 'vm' | 'local' | 'unknown';
|
|
13
|
+
|
|
14
|
+
export interface SystemCapabilities {
|
|
15
|
+
/** Deployment type detected */
|
|
16
|
+
deployment: DeploymentType;
|
|
17
|
+
/** Browser available (Chromium/Chrome installed) */
|
|
18
|
+
hasBrowser: boolean;
|
|
19
|
+
/** Browser executable path (if found) */
|
|
20
|
+
browserPath: string | null;
|
|
21
|
+
/** Display server available (X11/Wayland/macOS) */
|
|
22
|
+
hasDisplay: boolean;
|
|
23
|
+
/** Audio subsystem available (PulseAudio/PipeWire/ALSA/macOS CoreAudio) */
|
|
24
|
+
hasAudio: boolean;
|
|
25
|
+
/** Virtual camera available (v4l2loopback or macOS) */
|
|
26
|
+
hasVirtualCamera: boolean;
|
|
27
|
+
/** Can run headed browser (display + browser) */
|
|
28
|
+
canRunHeadedBrowser: boolean;
|
|
29
|
+
/** Can join video calls (display + browser + audio) */
|
|
30
|
+
canJoinMeetings: boolean;
|
|
31
|
+
/** Can record meetings (display + browser + audio + ffmpeg) */
|
|
32
|
+
canRecordMeetings: boolean;
|
|
33
|
+
/** ffmpeg available */
|
|
34
|
+
hasFfmpeg: boolean;
|
|
35
|
+
/** Persistent filesystem (not ephemeral container) */
|
|
36
|
+
hasPersistentDisk: boolean;
|
|
37
|
+
/** GPU available */
|
|
38
|
+
hasGpu: boolean;
|
|
39
|
+
/** Platform details */
|
|
40
|
+
platform: {
|
|
41
|
+
os: string;
|
|
42
|
+
arch: string;
|
|
43
|
+
isDocker: boolean;
|
|
44
|
+
isFlyio: boolean;
|
|
45
|
+
isRailway: boolean;
|
|
46
|
+
isRender: boolean;
|
|
47
|
+
isAWS: boolean;
|
|
48
|
+
isGCP: boolean;
|
|
49
|
+
isHetzner: boolean;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Cached result */
|
|
54
|
+
let _cachedCapabilities: SystemCapabilities | null = null;
|
|
55
|
+
|
|
56
|
+
function commandExists(cmd: string): boolean {
|
|
57
|
+
try {
|
|
58
|
+
execSync(`which ${cmd} 2>/dev/null`, { encoding: 'utf-8', timeout: 3000 });
|
|
59
|
+
return true;
|
|
60
|
+
} catch { return false; }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function envSet(key: string): boolean {
|
|
64
|
+
return !!process.env[key];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function detectDeploymentType(): DeploymentType {
|
|
68
|
+
// Fly.io
|
|
69
|
+
if (envSet('FLY_APP_NAME') || envSet('FLY_MACHINE_ID')) return 'container';
|
|
70
|
+
// Railway
|
|
71
|
+
if (envSet('RAILWAY_ENVIRONMENT') || envSet('RAILWAY_SERVICE_ID')) return 'container';
|
|
72
|
+
// Render
|
|
73
|
+
if (envSet('RENDER_SERVICE_ID') || envSet('RENDER')) return 'container';
|
|
74
|
+
// Generic Docker
|
|
75
|
+
if (existsSync('/.dockerenv') || existsSync('/run/.containerenv')) return 'container';
|
|
76
|
+
// Kubernetes
|
|
77
|
+
if (envSet('KUBERNETES_SERVICE_HOST')) return 'container';
|
|
78
|
+
// AWS ECS
|
|
79
|
+
if (envSet('ECS_CONTAINER_METADATA_URI')) return 'container';
|
|
80
|
+
|
|
81
|
+
// VM indicators
|
|
82
|
+
if (envSet('SSH_CONNECTION') || envSet('SSH_CLIENT')) return 'vm';
|
|
83
|
+
// systemd on Linux without Docker = likely VM
|
|
84
|
+
if (process.platform === 'linux' && existsSync('/run/systemd/system') && !existsSync('/.dockerenv')) return 'vm';
|
|
85
|
+
|
|
86
|
+
// macOS / Windows with display = local dev
|
|
87
|
+
if (process.platform === 'darwin' || process.platform === 'win32') return 'local';
|
|
88
|
+
|
|
89
|
+
// Linux with DISPLAY or Wayland = probably local or VM with desktop
|
|
90
|
+
if (process.platform === 'linux' && (envSet('DISPLAY') || envSet('WAYLAND_DISPLAY'))) return 'vm';
|
|
91
|
+
|
|
92
|
+
return 'unknown';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function findBrowser(): string | null {
|
|
96
|
+
// Check env var first (set in Docker)
|
|
97
|
+
const envPath = process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH || process.env.CHROME_PATH;
|
|
98
|
+
if (envPath && existsSync(envPath)) return envPath;
|
|
99
|
+
|
|
100
|
+
const candidates = [
|
|
101
|
+
// Linux
|
|
102
|
+
'/usr/bin/chromium', '/usr/bin/chromium-browser',
|
|
103
|
+
'/usr/bin/google-chrome', '/usr/bin/google-chrome-stable',
|
|
104
|
+
// macOS
|
|
105
|
+
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
106
|
+
'/Applications/Chromium.app/Contents/MacOS/Chromium',
|
|
107
|
+
// Snap
|
|
108
|
+
'/snap/bin/chromium',
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
for (const p of candidates) {
|
|
112
|
+
if (existsSync(p)) return p;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Try which
|
|
116
|
+
try {
|
|
117
|
+
const path = execSync('which chromium || which chromium-browser || which google-chrome 2>/dev/null', {
|
|
118
|
+
encoding: 'utf-8', timeout: 3000,
|
|
119
|
+
}).trim();
|
|
120
|
+
if (path) return path;
|
|
121
|
+
} catch { /* ignore */ }
|
|
122
|
+
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function hasDisplayServer(): boolean {
|
|
127
|
+
if (process.platform === 'darwin' || process.platform === 'win32') return true;
|
|
128
|
+
if (envSet('DISPLAY') || envSet('WAYLAND_DISPLAY')) return true;
|
|
129
|
+
// Check for Xvfb
|
|
130
|
+
if (commandExists('Xvfb') || commandExists('xvfb-run')) return true;
|
|
131
|
+
// Check if Xvfb is running
|
|
132
|
+
try {
|
|
133
|
+
execSync('pgrep -x Xvfb', { encoding: 'utf-8', timeout: 2000 });
|
|
134
|
+
return true;
|
|
135
|
+
} catch { /* not running */ }
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function hasAudioSystem(): boolean {
|
|
140
|
+
if (process.platform === 'darwin') return true; // CoreAudio always available
|
|
141
|
+
// PulseAudio
|
|
142
|
+
if (commandExists('pulseaudio') || commandExists('pactl')) {
|
|
143
|
+
try {
|
|
144
|
+
execSync('pactl info 2>/dev/null', { encoding: 'utf-8', timeout: 3000 });
|
|
145
|
+
return true;
|
|
146
|
+
} catch { /* not running */ }
|
|
147
|
+
}
|
|
148
|
+
// PipeWire
|
|
149
|
+
if (commandExists('pw-cli')) {
|
|
150
|
+
try {
|
|
151
|
+
execSync('pw-cli info 2>/dev/null', { encoding: 'utf-8', timeout: 3000 });
|
|
152
|
+
return true;
|
|
153
|
+
} catch { /* not running */ }
|
|
154
|
+
}
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function hasVCam(): boolean {
|
|
159
|
+
if (process.platform === 'darwin') return false; // Need OBS virtual cam or similar
|
|
160
|
+
// v4l2loopback
|
|
161
|
+
try {
|
|
162
|
+
execSync('ls /dev/video* 2>/dev/null', { encoding: 'utf-8', timeout: 2000 });
|
|
163
|
+
return true;
|
|
164
|
+
} catch { return false; }
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Detect system capabilities. Results are cached after first call.
|
|
169
|
+
*/
|
|
170
|
+
export function detectCapabilities(): SystemCapabilities {
|
|
171
|
+
if (_cachedCapabilities) return _cachedCapabilities;
|
|
172
|
+
|
|
173
|
+
const deployment = detectDeploymentType();
|
|
174
|
+
const browserPath = findBrowser();
|
|
175
|
+
const hasBrowser = !!browserPath;
|
|
176
|
+
const hasDisplay = hasDisplayServer();
|
|
177
|
+
const hasAudio = hasAudioSystem();
|
|
178
|
+
const hasVirtualCamera = hasVCam();
|
|
179
|
+
const hasFfmpeg = commandExists('ffmpeg');
|
|
180
|
+
|
|
181
|
+
// Persistent disk: containers are typically ephemeral
|
|
182
|
+
const hasPersistentDisk = deployment !== 'container' || envSet('FLY_VOLUME_NAME') || envSet('RAILWAY_VOLUME_MOUNT_PATH');
|
|
183
|
+
|
|
184
|
+
// GPU check
|
|
185
|
+
let hasGpu = false;
|
|
186
|
+
try {
|
|
187
|
+
if (commandExists('nvidia-smi')) { execSync('nvidia-smi', { timeout: 3000 }); hasGpu = true; }
|
|
188
|
+
} catch { /* no GPU */ }
|
|
189
|
+
|
|
190
|
+
const caps: SystemCapabilities = {
|
|
191
|
+
deployment,
|
|
192
|
+
hasBrowser,
|
|
193
|
+
browserPath,
|
|
194
|
+
hasDisplay,
|
|
195
|
+
hasAudio,
|
|
196
|
+
hasVirtualCamera,
|
|
197
|
+
canRunHeadedBrowser: hasBrowser && hasDisplay,
|
|
198
|
+
canJoinMeetings: hasBrowser && hasDisplay && hasAudio,
|
|
199
|
+
canRecordMeetings: hasBrowser && hasDisplay && hasAudio && hasFfmpeg,
|
|
200
|
+
hasFfmpeg,
|
|
201
|
+
hasPersistentDisk: !!hasPersistentDisk,
|
|
202
|
+
hasGpu,
|
|
203
|
+
platform: {
|
|
204
|
+
os: process.platform,
|
|
205
|
+
arch: process.arch,
|
|
206
|
+
isDocker: existsSync('/.dockerenv') || existsSync('/run/.containerenv'),
|
|
207
|
+
isFlyio: envSet('FLY_APP_NAME'),
|
|
208
|
+
isRailway: envSet('RAILWAY_ENVIRONMENT'),
|
|
209
|
+
isRender: envSet('RENDER'),
|
|
210
|
+
isAWS: envSet('AWS_REGION') || envSet('ECS_CONTAINER_METADATA_URI'),
|
|
211
|
+
isGCP: envSet('GOOGLE_CLOUD_PROJECT') || envSet('GCP_PROJECT'),
|
|
212
|
+
isHetzner: false, // No standard env var
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
_cachedCapabilities = caps;
|
|
217
|
+
return caps;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** Reset cache (for testing) */
|
|
221
|
+
export function resetCapabilitiesCache(): void {
|
|
222
|
+
_cachedCapabilities = null;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Get a human-readable summary of what this deployment can and cannot do.
|
|
227
|
+
* Used in tool error messages and dashboard status.
|
|
228
|
+
*/
|
|
229
|
+
export function getCapabilitySummary(caps?: SystemCapabilities): {
|
|
230
|
+
deployment: string;
|
|
231
|
+
available: string[];
|
|
232
|
+
unavailable: string[];
|
|
233
|
+
recommendations: string[];
|
|
234
|
+
} {
|
|
235
|
+
const c = caps || detectCapabilities();
|
|
236
|
+
const available: string[] = [];
|
|
237
|
+
const unavailable: string[] = [];
|
|
238
|
+
const recommendations: string[] = [];
|
|
239
|
+
|
|
240
|
+
if (c.hasBrowser) available.push('Browser (headless)');
|
|
241
|
+
else unavailable.push('Browser — no Chromium/Chrome found');
|
|
242
|
+
|
|
243
|
+
if (c.canRunHeadedBrowser) available.push('Browser (headed/visible)');
|
|
244
|
+
else if (c.hasBrowser) unavailable.push('Headed browser — no display server (install Xvfb)');
|
|
245
|
+
|
|
246
|
+
if (c.canJoinMeetings) available.push('Video meetings (Google Meet, Zoom, Teams)');
|
|
247
|
+
else unavailable.push('Video meetings — requires display + browser + audio');
|
|
248
|
+
|
|
249
|
+
if (c.canRecordMeetings) available.push('Meeting recording');
|
|
250
|
+
else if (c.canJoinMeetings) unavailable.push('Meeting recording — install ffmpeg');
|
|
251
|
+
|
|
252
|
+
if (c.hasAudio) available.push('Audio subsystem');
|
|
253
|
+
else unavailable.push('Audio — no PulseAudio/PipeWire');
|
|
254
|
+
|
|
255
|
+
if (c.hasVirtualCamera) available.push('Virtual camera');
|
|
256
|
+
else unavailable.push('Virtual camera — no v4l2loopback');
|
|
257
|
+
|
|
258
|
+
if (c.hasFfmpeg) available.push('FFmpeg (video/audio processing)');
|
|
259
|
+
else unavailable.push('FFmpeg — install for recording/transcoding');
|
|
260
|
+
|
|
261
|
+
if (c.hasPersistentDisk) available.push('Persistent storage');
|
|
262
|
+
else unavailable.push('Persistent storage — ephemeral container filesystem');
|
|
263
|
+
|
|
264
|
+
// Recommendations based on deployment
|
|
265
|
+
if (c.deployment === 'container' && !c.canJoinMeetings) {
|
|
266
|
+
recommendations.push(
|
|
267
|
+
'This is a container deployment. For video meetings, deploy on a VM instead.',
|
|
268
|
+
'Recommended: Hetzner CPX31 ($15/mo) or GCP e2-standard-2 ($50/mo) with the VM setup script.',
|
|
269
|
+
'Container deployments work great for API-only tasks: email, calendar, docs, drive, sheets.'
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (c.deployment === 'vm' && !c.hasDisplay) {
|
|
274
|
+
recommendations.push('Install Xvfb for virtual display: apt install xvfb');
|
|
275
|
+
}
|
|
276
|
+
if (c.deployment === 'vm' && !c.hasAudio) {
|
|
277
|
+
recommendations.push('Install PulseAudio for audio: apt install pulseaudio');
|
|
278
|
+
}
|
|
279
|
+
if (c.deployment === 'vm' && !c.hasBrowser) {
|
|
280
|
+
recommendations.push('Install Chromium: apt install chromium-browser');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
let deployLabel = c.deployment;
|
|
284
|
+
if (c.platform.isFlyio) deployLabel = 'Fly.io (container)';
|
|
285
|
+
else if (c.platform.isRailway) deployLabel = 'Railway (container)';
|
|
286
|
+
else if (c.platform.isRender) deployLabel = 'Render (container)';
|
|
287
|
+
else if (c.deployment === 'local') deployLabel = `Local (${c.platform.os})`;
|
|
288
|
+
|
|
289
|
+
return { deployment: deployLabel, available, unavailable, recommendations };
|
|
290
|
+
}
|
package/src/runtime/index.ts
CHANGED
|
@@ -256,7 +256,7 @@ export class AgentRuntime {
|
|
|
256
256
|
var session = await this.sessionManager!.createSession(agentId, orgId, opts.parentSessionId);
|
|
257
257
|
|
|
258
258
|
// Build agent config
|
|
259
|
-
var tools = opts.tools || createAllTools(this.buildToolOptions(agentId));
|
|
259
|
+
var tools = opts.tools || await createAllTools(this.buildToolOptions(agentId));
|
|
260
260
|
|
|
261
261
|
var systemPrompt = opts.systemPrompt || buildDefaultSystemPrompt(agentId);
|
|
262
262
|
|
|
@@ -298,7 +298,7 @@ export class AgentRuntime {
|
|
|
298
298
|
var apiKey = this.resolveApiKey(model.provider);
|
|
299
299
|
if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
|
|
300
300
|
|
|
301
|
-
var tools = createAllTools(this.buildToolOptions(session.agentId));
|
|
301
|
+
var tools = await createAllTools(this.buildToolOptions(session.agentId));
|
|
302
302
|
|
|
303
303
|
var agentConfig: AgentConfig = {
|
|
304
304
|
agentId: session.agentId,
|
|
@@ -589,7 +589,7 @@ export class AgentRuntime {
|
|
|
589
589
|
continue;
|
|
590
590
|
}
|
|
591
591
|
|
|
592
|
-
var tools = createAllTools(this.buildToolOptions(session.agentId));
|
|
592
|
+
var tools = await createAllTools(this.buildToolOptions(session.agentId));
|
|
593
593
|
|
|
594
594
|
var agentConfig: AgentConfig = {
|
|
595
595
|
agentId: session.agentId,
|