@actagent/google-meet 2026.6.2
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/README.md +26 -0
- package/actagent.plugin.json +532 -0
- package/doctor-contract-api.ts +2 -0
- package/google-meet.live.test.ts +86 -0
- package/index.create.test.ts +672 -0
- package/index.test.ts +5130 -0
- package/index.ts +1225 -0
- package/node-host.test.ts +242 -0
- package/npm-shrinkwrap.json +39 -0
- package/package.json +46 -0
- package/src/agent-consult.ts +159 -0
- package/src/calendar.ts +253 -0
- package/src/cli.test.ts +1307 -0
- package/src/cli.ts +2382 -0
- package/src/config-compat.test.ts +99 -0
- package/src/config-compat.ts +79 -0
- package/src/config.test.ts +57 -0
- package/src/config.ts +598 -0
- package/src/create.ts +158 -0
- package/src/drive.ts +73 -0
- package/src/google-api-errors.ts +21 -0
- package/src/meet.ts +1027 -0
- package/src/node-host.ts +524 -0
- package/src/oauth.test.ts +164 -0
- package/src/oauth.ts +247 -0
- package/src/realtime-node.ts +771 -0
- package/src/realtime.ts +1355 -0
- package/src/runtime.ts +1009 -0
- package/src/setup.ts +277 -0
- package/src/test-support/plugin-harness.ts +233 -0
- package/src/transports/chrome-audio-device.ts +6 -0
- package/src/transports/chrome-browser-proxy.test.ts +67 -0
- package/src/transports/chrome-browser-proxy.ts +206 -0
- package/src/transports/chrome-create.ts +365 -0
- package/src/transports/chrome.test.ts +21 -0
- package/src/transports/chrome.ts +1073 -0
- package/src/transports/twilio.ts +58 -0
- package/src/transports/types.ts +148 -0
- package/src/voice-call-gateway.test.ts +153 -0
- package/src/voice-call-gateway.ts +242 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Google Meet tests cover config compat plugin behavior.
|
|
2
|
+
import type { ACTAgentConfig } from "actagent/plugin-sdk/config-contracts";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import {
|
|
5
|
+
legacyConfigRules,
|
|
6
|
+
migrateGoogleMeetLegacyRealtimeProvider,
|
|
7
|
+
normalizeCompatibilityConfig,
|
|
8
|
+
} from "./config-compat.js";
|
|
9
|
+
|
|
10
|
+
describe("google-meet config compatibility", () => {
|
|
11
|
+
it("detects legacy Google realtime provider config", () => {
|
|
12
|
+
expect(
|
|
13
|
+
legacyConfigRules[0]?.match({
|
|
14
|
+
provider: "google",
|
|
15
|
+
model: "gemini-2.5-flash-native-audio-preview-12-2025",
|
|
16
|
+
}),
|
|
17
|
+
).toBe(true);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("migrates legacy Google bidi provider intent to scoped realtime providers", () => {
|
|
21
|
+
const config = {
|
|
22
|
+
plugins: {
|
|
23
|
+
entries: {
|
|
24
|
+
"google-meet": {
|
|
25
|
+
enabled: true,
|
|
26
|
+
config: {
|
|
27
|
+
defaultMode: "agent",
|
|
28
|
+
realtime: {
|
|
29
|
+
provider: "google",
|
|
30
|
+
model: "gemini-2.5-flash-native-audio-preview-12-2025",
|
|
31
|
+
providers: {
|
|
32
|
+
google: {
|
|
33
|
+
voice: "Kore",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
} as ACTAgentConfig;
|
|
42
|
+
|
|
43
|
+
const migration = migrateGoogleMeetLegacyRealtimeProvider(config);
|
|
44
|
+
|
|
45
|
+
expect(migration?.changes).toEqual([
|
|
46
|
+
'Moved Google Meet legacy realtime.provider="google" intent to realtime.voiceProvider="google" and realtime.transcriptionProvider="openai".',
|
|
47
|
+
]);
|
|
48
|
+
expect(
|
|
49
|
+
(
|
|
50
|
+
migration!.config.plugins!.entries!["google-meet"] as {
|
|
51
|
+
config?: { realtime?: Record<string, unknown> };
|
|
52
|
+
}
|
|
53
|
+
).config?.realtime,
|
|
54
|
+
).toEqual({
|
|
55
|
+
provider: "openai",
|
|
56
|
+
transcriptionProvider: "openai",
|
|
57
|
+
voiceProvider: "google",
|
|
58
|
+
model: "gemini-2.5-flash-native-audio-preview-12-2025",
|
|
59
|
+
providers: {
|
|
60
|
+
google: {
|
|
61
|
+
voice: "Kore",
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("leaves fully scoped provider configs alone", () => {
|
|
68
|
+
const config = {
|
|
69
|
+
plugins: {
|
|
70
|
+
entries: {
|
|
71
|
+
"google-meet": {
|
|
72
|
+
config: {
|
|
73
|
+
realtime: {
|
|
74
|
+
provider: "google",
|
|
75
|
+
transcriptionProvider: "custom-stt",
|
|
76
|
+
voiceProvider: "custom-voice",
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
} as ACTAgentConfig;
|
|
83
|
+
|
|
84
|
+
const migration = normalizeCompatibilityConfig({ cfg: config });
|
|
85
|
+
|
|
86
|
+
expect(migration.changes).toStrictEqual([]);
|
|
87
|
+
expect(
|
|
88
|
+
(
|
|
89
|
+
migration.config.plugins!.entries!["google-meet"] as {
|
|
90
|
+
config?: { realtime?: Record<string, unknown> };
|
|
91
|
+
}
|
|
92
|
+
).config?.realtime,
|
|
93
|
+
).toEqual({
|
|
94
|
+
provider: "google",
|
|
95
|
+
transcriptionProvider: "custom-stt",
|
|
96
|
+
voiceProvider: "custom-voice",
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// Google Meet helper module supports config compat behavior.
|
|
2
|
+
import type { ACTAgentConfig } from "actagent/plugin-sdk/config-contracts";
|
|
3
|
+
import {
|
|
4
|
+
asNullableRecord as asRecord,
|
|
5
|
+
normalizeOptionalLowercaseString as normalizeProviderId,
|
|
6
|
+
} from "actagent/plugin-sdk/string-coerce-runtime";
|
|
7
|
+
|
|
8
|
+
type LegacyConfigRule = {
|
|
9
|
+
path: Array<string | number>;
|
|
10
|
+
message: string;
|
|
11
|
+
match: (value: unknown) => boolean;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function hasOwn(record: Record<string, unknown>, key: string): boolean {
|
|
15
|
+
return Object.hasOwn(record, key);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function hasLegacyGoogleRealtimeProvider(value: unknown): boolean {
|
|
19
|
+
const realtime = asRecord(value);
|
|
20
|
+
if (!realtime || normalizeProviderId(realtime.provider) !== "google") {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return !hasOwn(realtime, "voiceProvider") || !hasOwn(realtime, "transcriptionProvider");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const legacyConfigRules: LegacyConfigRule[] = [
|
|
27
|
+
{
|
|
28
|
+
path: ["plugins", "entries", "google-meet", "config", "realtime"],
|
|
29
|
+
message:
|
|
30
|
+
'plugins.entries.google-meet.config.realtime.provider="google" is legacy for Gemini Live bidi mode; use realtime.voiceProvider="google" and realtime.transcriptionProvider="openai". Run "actagent doctor --fix".',
|
|
31
|
+
match: hasLegacyGoogleRealtimeProvider,
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
export function migrateGoogleMeetLegacyRealtimeProvider(config: ACTAgentConfig): {
|
|
36
|
+
config: ACTAgentConfig;
|
|
37
|
+
changes: string[];
|
|
38
|
+
} | null {
|
|
39
|
+
const rawEntry = asRecord(config.plugins?.entries?.["google-meet"]);
|
|
40
|
+
const rawPluginConfig = asRecord(rawEntry?.config);
|
|
41
|
+
const rawRealtime = asRecord(rawPluginConfig?.realtime);
|
|
42
|
+
if (!rawRealtime || !hasLegacyGoogleRealtimeProvider(rawRealtime)) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const nextConfig = structuredClone(config);
|
|
47
|
+
const nextPlugins = asRecord(nextConfig.plugins) ?? {};
|
|
48
|
+
nextConfig.plugins = nextPlugins;
|
|
49
|
+
const nextEntries = asRecord(nextPlugins.entries) ?? {};
|
|
50
|
+
nextPlugins.entries = nextEntries;
|
|
51
|
+
const nextEntry = asRecord(nextEntries["google-meet"]) ?? {};
|
|
52
|
+
nextEntries["google-meet"] = nextEntry;
|
|
53
|
+
const nextPluginConfig = asRecord(nextEntry.config) ?? {};
|
|
54
|
+
nextEntry.config = nextPluginConfig;
|
|
55
|
+
const nextRealtime = asRecord(nextPluginConfig.realtime) ?? {};
|
|
56
|
+
nextPluginConfig.realtime = nextRealtime;
|
|
57
|
+
|
|
58
|
+
nextRealtime.provider = "openai";
|
|
59
|
+
if (!hasOwn(nextRealtime, "transcriptionProvider")) {
|
|
60
|
+
nextRealtime.transcriptionProvider = "openai";
|
|
61
|
+
}
|
|
62
|
+
if (!hasOwn(nextRealtime, "voiceProvider")) {
|
|
63
|
+
nextRealtime.voiceProvider = "google";
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
config: nextConfig,
|
|
68
|
+
changes: [
|
|
69
|
+
'Moved Google Meet legacy realtime.provider="google" intent to realtime.voiceProvider="google" and realtime.transcriptionProvider="openai".',
|
|
70
|
+
],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function normalizeCompatibilityConfig({ cfg }: { cfg: ACTAgentConfig }): {
|
|
75
|
+
config: ACTAgentConfig;
|
|
76
|
+
changes: string[];
|
|
77
|
+
} {
|
|
78
|
+
return migrateGoogleMeetLegacyRealtimeProvider(cfg) ?? { config: cfg, changes: [] };
|
|
79
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Google Meet tests cover config plugin behavior.
|
|
2
|
+
import { MAX_TIMER_TIMEOUT_MS } from "actagent/plugin-sdk/number-runtime";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import { resolveGoogleMeetConfig, resolveGoogleMeetGatewayOperationTimeoutMs } from "./config.js";
|
|
5
|
+
|
|
6
|
+
describe("google meet gateway operation timeout", () => {
|
|
7
|
+
it("caps timer config fields before runtime polling uses them", () => {
|
|
8
|
+
const config = resolveGoogleMeetConfig({
|
|
9
|
+
chrome: {
|
|
10
|
+
joinTimeoutMs: Number.MAX_VALUE,
|
|
11
|
+
waitForInCallMs: Number.MAX_VALUE,
|
|
12
|
+
bargeInCooldownMs: Number.MAX_VALUE,
|
|
13
|
+
},
|
|
14
|
+
voiceCall: {
|
|
15
|
+
requestTimeoutMs: Number.MAX_VALUE,
|
|
16
|
+
dtmfDelayMs: Number.MAX_VALUE,
|
|
17
|
+
postDtmfSpeechDelayMs: Number.MAX_VALUE,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
expect(config.chrome.joinTimeoutMs).toBe(MAX_TIMER_TIMEOUT_MS);
|
|
22
|
+
expect(config.chrome.waitForInCallMs).toBe(MAX_TIMER_TIMEOUT_MS);
|
|
23
|
+
expect(config.chrome.bargeInCooldownMs).toBe(MAX_TIMER_TIMEOUT_MS);
|
|
24
|
+
expect(config.voiceCall.requestTimeoutMs).toBe(MAX_TIMER_TIMEOUT_MS);
|
|
25
|
+
expect(config.voiceCall.dtmfDelayMs).toBe(MAX_TIMER_TIMEOUT_MS);
|
|
26
|
+
expect(config.voiceCall.postDtmfSpeechDelayMs).toBe(MAX_TIMER_TIMEOUT_MS);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("adds operation grace to normal transport timeouts", () => {
|
|
30
|
+
expect(resolveGoogleMeetGatewayOperationTimeoutMs(resolveGoogleMeetConfig({}))).toBe(60_000);
|
|
31
|
+
expect(
|
|
32
|
+
resolveGoogleMeetGatewayOperationTimeoutMs(
|
|
33
|
+
resolveGoogleMeetConfig({
|
|
34
|
+
chrome: { joinTimeoutMs: 120_000 },
|
|
35
|
+
voiceCall: { requestTimeoutMs: 30_000 },
|
|
36
|
+
}),
|
|
37
|
+
),
|
|
38
|
+
).toBe(150_000);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("caps overflowed transport timeout grace", () => {
|
|
42
|
+
expect(
|
|
43
|
+
resolveGoogleMeetGatewayOperationTimeoutMs(
|
|
44
|
+
resolveGoogleMeetConfig({
|
|
45
|
+
chrome: { joinTimeoutMs: Number.MAX_VALUE },
|
|
46
|
+
}),
|
|
47
|
+
),
|
|
48
|
+
).toBe(MAX_TIMER_TIMEOUT_MS);
|
|
49
|
+
expect(
|
|
50
|
+
resolveGoogleMeetGatewayOperationTimeoutMs(
|
|
51
|
+
resolveGoogleMeetConfig({
|
|
52
|
+
voiceCall: { requestTimeoutMs: Number.MAX_VALUE },
|
|
53
|
+
}),
|
|
54
|
+
),
|
|
55
|
+
).toBe(MAX_TIMER_TIMEOUT_MS);
|
|
56
|
+
});
|
|
57
|
+
});
|