@agent-native/core 0.157.17 → 0.157.19
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/corpus/templates/calendar/server/handlers/google-auth.ts +19 -2
- package/corpus/templates/dispatch/shared/feature-flags.ts +6 -2
- package/dist/a2a/index.d.ts +1 -0
- package/dist/a2a/index.js +1 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/auth.js +13 -2
- package/dist/server/embed-route.js +13 -2
- package/dist/server/google-oauth.d.ts +10 -0
- package/dist/server/google-oauth.js +10 -2
- package/dist/server/onboarding-html.js +15 -0
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/mcp-embed-headers.d.ts +7 -0
- package/dist/shared/mcp-embed-headers.js +28 -5
- package/package.json +1 -1
|
@@ -50,6 +50,7 @@ type CalendarOAuthStateOptions = {
|
|
|
50
50
|
owner?: string;
|
|
51
51
|
orgId?: string;
|
|
52
52
|
desktop?: boolean;
|
|
53
|
+
mobile?: boolean;
|
|
53
54
|
addAccount?: boolean;
|
|
54
55
|
app?: string;
|
|
55
56
|
returnUrl?: string;
|
|
@@ -245,6 +246,7 @@ export const getGoogleAuthUrl = defineEventHandler(async (event: H3Event) => {
|
|
|
245
246
|
const orgId = session?.orgId;
|
|
246
247
|
const desktop =
|
|
247
248
|
isElectron(event) || q.desktop === "1" || q.desktop === "true";
|
|
249
|
+
const mobile = q.mobile === "1" || q.mobile === "true";
|
|
248
250
|
const flowId = desktop ? (q.flow_id as string) || undefined : undefined;
|
|
249
251
|
const calendarConnect = isCalendarConnectRequest(q, owner);
|
|
250
252
|
const credentials = calendarConnect
|
|
@@ -279,6 +281,7 @@ export const getGoogleAuthUrl = defineEventHandler(async (event: H3Event) => {
|
|
|
279
281
|
owner,
|
|
280
282
|
orgId,
|
|
281
283
|
desktop,
|
|
284
|
+
mobile,
|
|
282
285
|
addAccount: calendarConnect,
|
|
283
286
|
app: OAUTH_STATE_APP_ID,
|
|
284
287
|
returnUrl,
|
|
@@ -309,6 +312,7 @@ export const getGoogleAuthUrl = defineEventHandler(async (event: H3Event) => {
|
|
|
309
312
|
export const handleGoogleCallback = defineEventHandler(
|
|
310
313
|
async (event: H3Event) => {
|
|
311
314
|
let desktop = false;
|
|
315
|
+
let mobile = false;
|
|
312
316
|
let flowId: string | undefined;
|
|
313
317
|
try {
|
|
314
318
|
const query = getQuery(event);
|
|
@@ -317,6 +321,7 @@ export const handleGoogleCallback = defineEventHandler(
|
|
|
317
321
|
getAppUrl(event, "/_agent-native/google/callback"),
|
|
318
322
|
);
|
|
319
323
|
desktop = state.desktop ?? false;
|
|
324
|
+
mobile = state.mobile ?? false;
|
|
320
325
|
flowId = state.flowId;
|
|
321
326
|
|
|
322
327
|
const googleError = query.error as string | undefined;
|
|
@@ -358,6 +363,7 @@ export const handleGoogleCallback = defineEventHandler(
|
|
|
358
363
|
{
|
|
359
364
|
hasProductionSession,
|
|
360
365
|
desktop,
|
|
366
|
+
...(mobile ? { mobile: true } : {}),
|
|
361
367
|
trackSignup: {
|
|
362
368
|
authProvider: "google",
|
|
363
369
|
authUserId: identity.id,
|
|
@@ -373,6 +379,7 @@ export const handleGoogleCallback = defineEventHandler(
|
|
|
373
379
|
return oauthCallbackResponse(event, identity.email, {
|
|
374
380
|
sessionToken,
|
|
375
381
|
desktop,
|
|
382
|
+
mobile,
|
|
376
383
|
returnUrl,
|
|
377
384
|
flowId,
|
|
378
385
|
appName: "Calendar",
|
|
@@ -398,11 +405,14 @@ export const handleGoogleCallback = defineEventHandler(
|
|
|
398
405
|
addAccount || (owner !== undefined && email !== owner);
|
|
399
406
|
const sessionOwner = isAddAccount ? (owner ?? email) : email;
|
|
400
407
|
const shouldCreateSession =
|
|
401
|
-
!isAddAccount ||
|
|
408
|
+
!isAddAccount ||
|
|
409
|
+
(desktop && flowId && sessionOwner) ||
|
|
410
|
+
(mobile && sessionOwner);
|
|
402
411
|
const { sessionToken } = shouldCreateSession
|
|
403
412
|
? await createOAuthSession(event, sessionOwner, {
|
|
404
413
|
hasProductionSession,
|
|
405
414
|
desktop,
|
|
415
|
+
...(mobile ? { mobile: true } : {}),
|
|
406
416
|
})
|
|
407
417
|
: { sessionToken: undefined };
|
|
408
418
|
|
|
@@ -414,6 +424,7 @@ export const handleGoogleCallback = defineEventHandler(
|
|
|
414
424
|
return oauthCallbackResponse(event, email, {
|
|
415
425
|
sessionToken,
|
|
416
426
|
desktop,
|
|
427
|
+
mobile,
|
|
417
428
|
addAccount: isAddAccount,
|
|
418
429
|
flowId,
|
|
419
430
|
appName: "Calendar",
|
|
@@ -434,6 +445,7 @@ export const getGoogleAddAccountUrl = defineEventHandler(
|
|
|
434
445
|
const q = getQuery(event);
|
|
435
446
|
const desktop =
|
|
436
447
|
isElectron(event) || q.desktop === "1" || q.desktop === "true";
|
|
448
|
+
const mobile = q.mobile === "1" || q.mobile === "true";
|
|
437
449
|
const flowId = desktop ? (q.flow_id as string) || undefined : undefined;
|
|
438
450
|
if (!(await resolveCalendarOAuthCredentials(event))) {
|
|
439
451
|
return missingCredentialsResponse(
|
|
@@ -456,6 +468,7 @@ export const getGoogleAddAccountUrl = defineEventHandler(
|
|
|
456
468
|
owner: session.email,
|
|
457
469
|
orgId: session.orgId,
|
|
458
470
|
desktop,
|
|
471
|
+
mobile,
|
|
459
472
|
addAccount: true,
|
|
460
473
|
app: OAUTH_STATE_APP_ID,
|
|
461
474
|
flowId,
|
|
@@ -481,6 +494,7 @@ export const getGoogleAddAccountUrl = defineEventHandler(
|
|
|
481
494
|
export const handleGoogleAddAccountCallback = defineEventHandler(
|
|
482
495
|
async (event: H3Event) => {
|
|
483
496
|
let desktop = false;
|
|
497
|
+
let mobile = false;
|
|
484
498
|
let flowId: string | undefined;
|
|
485
499
|
try {
|
|
486
500
|
const session = await getSession(event);
|
|
@@ -490,6 +504,7 @@ export const handleGoogleAddAccountCallback = defineEventHandler(
|
|
|
490
504
|
getAppUrl(event, "/_agent-native/google/add-account/callback"),
|
|
491
505
|
);
|
|
492
506
|
desktop = state.desktop ?? false;
|
|
507
|
+
mobile = state.mobile ?? false;
|
|
493
508
|
flowId = state.flowId;
|
|
494
509
|
|
|
495
510
|
const googleError = query.error as string | undefined;
|
|
@@ -530,10 +545,11 @@ export const handleGoogleAddAccountCallback = defineEventHandler(
|
|
|
530
545
|
session?.orgId ?? stateOrgId,
|
|
531
546
|
);
|
|
532
547
|
const { sessionToken } =
|
|
533
|
-
desktop && flowId
|
|
548
|
+
(desktop && flowId) || mobile
|
|
534
549
|
? await createOAuthSession(event, ownerEmail, {
|
|
535
550
|
hasProductionSession: !!session?.email,
|
|
536
551
|
desktop,
|
|
552
|
+
...(mobile ? { mobile: true } : {}),
|
|
537
553
|
})
|
|
538
554
|
: { sessionToken: undefined };
|
|
539
555
|
|
|
@@ -544,6 +560,7 @@ export const handleGoogleAddAccountCallback = defineEventHandler(
|
|
|
544
560
|
return oauthCallbackResponse(event, addedEmail, {
|
|
545
561
|
sessionToken,
|
|
546
562
|
desktop,
|
|
563
|
+
mobile,
|
|
547
564
|
addAccount: true,
|
|
548
565
|
flowId,
|
|
549
566
|
appName: "Calendar",
|
|
@@ -2,9 +2,12 @@ import {
|
|
|
2
2
|
defineFeatureFlag,
|
|
3
3
|
defineFeatureFlags,
|
|
4
4
|
} from "@agent-native/core/feature-flags";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
DISPATCH_WORKSPACE_APP_LIST_FLAG,
|
|
7
|
+
DISPATCH_WORKSPACE_SSO_FLAG,
|
|
8
|
+
} from "@agent-native/dispatch/shared/feature-flags";
|
|
6
9
|
|
|
7
|
-
export { DISPATCH_WORKSPACE_SSO_FLAG };
|
|
10
|
+
export { DISPATCH_WORKSPACE_APP_LIST_FLAG, DISPATCH_WORKSPACE_SSO_FLAG };
|
|
8
11
|
|
|
9
12
|
export const DESKTOP_WORKSPACE_SSO_FLAG = defineFeatureFlag({
|
|
10
13
|
key: "desktop.workspace-sso",
|
|
@@ -16,4 +19,5 @@ export const DESKTOP_WORKSPACE_SSO_FLAG = defineFeatureFlag({
|
|
|
16
19
|
export const DISPATCH_FEATURE_FLAGS = defineFeatureFlags([
|
|
17
20
|
DESKTOP_WORKSPACE_SSO_FLAG,
|
|
18
21
|
DISPATCH_WORKSPACE_SSO_FLAG,
|
|
22
|
+
DISPATCH_WORKSPACE_APP_LIST_FLAG,
|
|
19
23
|
]);
|
package/dist/a2a/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export type { A2ATokenPayload } from "./server.js";
|
|
|
3
3
|
export { generateAgentCard } from "./agent-card.js";
|
|
4
4
|
export { A2A_AGENT_ACTIVITY_KIND, A2A_AGENT_ACTIVITY_VERSION, MAX_A2A_ACTIVITY_REASONING_CHARS, MAX_A2A_ACTIVITY_REASONING_SEGMENTS, MAX_A2A_ACTIVITY_RESPONSE_CHARS, MAX_A2A_ACTIVITY_TOOL_CALLS, MAX_A2A_ACTIVITY_TOTAL_CHARS, MAX_A2A_ACTIVITY_TOOL_ID_CHARS, MAX_A2A_ACTIVITY_TOOL_INPUT_CHARS, MAX_A2A_ACTIVITY_TOOL_NAME_CHARS, MAX_A2A_ACTIVITY_TOOL_PAYLOAD_CHARS, MAX_A2A_ACTIVITY_TOOL_RESULT_CHARS, applyA2AAgentActivityEvent, buildA2AAgentActivityPart, buildA2AAgentActivitySnapshot, createA2AAgentActivityState, parseA2AAgentActivityPart, } from "./activity.js";
|
|
5
5
|
export { A2AClient, callAction, callAgent, signA2AToken } from "./client.js";
|
|
6
|
+
export { canonicalA2AAudience } from "./audience.js";
|
|
6
7
|
export { resolveA2ACallerAuth } from "./caller-auth.js";
|
|
7
8
|
export type { A2ACallerAuth } from "./caller-auth.js";
|
|
8
9
|
export { AgentInvocationError, buildAgentInvocationPrompt, invokeAgent, invokeAgentAction, looksLikeAgentUrl, resolveAgentInvocationTarget, } from "./invoke.js";
|
package/dist/a2a/index.js
CHANGED
|
@@ -4,5 +4,6 @@ export { generateAgentCard } from "./agent-card.js";
|
|
|
4
4
|
export { A2A_AGENT_ACTIVITY_KIND, A2A_AGENT_ACTIVITY_VERSION, MAX_A2A_ACTIVITY_REASONING_CHARS, MAX_A2A_ACTIVITY_REASONING_SEGMENTS, MAX_A2A_ACTIVITY_RESPONSE_CHARS, MAX_A2A_ACTIVITY_TOOL_CALLS, MAX_A2A_ACTIVITY_TOTAL_CHARS, MAX_A2A_ACTIVITY_TOOL_ID_CHARS, MAX_A2A_ACTIVITY_TOOL_INPUT_CHARS, MAX_A2A_ACTIVITY_TOOL_NAME_CHARS, MAX_A2A_ACTIVITY_TOOL_PAYLOAD_CHARS, MAX_A2A_ACTIVITY_TOOL_RESULT_CHARS, applyA2AAgentActivityEvent, buildA2AAgentActivityPart, buildA2AAgentActivitySnapshot, createA2AAgentActivityState, parseA2AAgentActivityPart, } from "./activity.js";
|
|
5
5
|
// Client
|
|
6
6
|
export { A2AClient, callAction, callAgent, signA2AToken } from "./client.js";
|
|
7
|
+
export { canonicalA2AAudience } from "./audience.js";
|
|
7
8
|
export { resolveA2ACallerAuth } from "./caller-auth.js";
|
|
8
9
|
export { AgentInvocationError, buildAgentInvocationPrompt, invokeAgent, invokeAgentAction, looksLikeAgentUrl, resolveAgentInvocationTarget, } from "./invoke.js";
|
|
@@ -62,11 +62,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
|
|
|
62
62
|
error: string;
|
|
63
63
|
states?: undefined;
|
|
64
64
|
} | {
|
|
65
|
+
error?: undefined;
|
|
65
66
|
states: {
|
|
66
67
|
clientId: number;
|
|
67
68
|
state: string;
|
|
68
69
|
}[];
|
|
69
|
-
error?: undefined;
|
|
70
70
|
}>>;
|
|
71
71
|
/**
|
|
72
72
|
* GET /_agent-native/collab/:docId/users
|
|
@@ -77,9 +77,9 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
|
|
|
77
77
|
error: string;
|
|
78
78
|
users?: undefined;
|
|
79
79
|
} | {
|
|
80
|
+
error?: undefined;
|
|
80
81
|
users: {
|
|
81
82
|
clientId: number;
|
|
82
83
|
lastSeen: number;
|
|
83
84
|
}[];
|
|
84
|
-
error?: undefined;
|
|
85
85
|
}>>;
|
|
@@ -17,11 +17,11 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
17
17
|
id?: undefined;
|
|
18
18
|
provider?: undefined;
|
|
19
19
|
} | {
|
|
20
|
+
error?: undefined;
|
|
20
21
|
configured?: undefined;
|
|
21
22
|
connectPath?: undefined;
|
|
22
23
|
url: string;
|
|
23
24
|
id: string;
|
|
24
25
|
provider: string;
|
|
25
|
-
error?: undefined;
|
|
26
26
|
}>;
|
|
27
27
|
export default _default;
|
|
@@ -137,13 +137,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
137
137
|
inputSchema: {
|
|
138
138
|
type: string;
|
|
139
139
|
properties: {
|
|
140
|
+
count?: undefined;
|
|
140
141
|
query?: undefined;
|
|
141
142
|
minutes?: undefined;
|
|
142
143
|
limit?: undefined;
|
|
143
144
|
clientHint?: undefined;
|
|
144
145
|
timestamp?: undefined;
|
|
145
146
|
chapterId?: undefined;
|
|
146
|
-
count?: undefined;
|
|
147
147
|
startAt?: undefined;
|
|
148
148
|
endAt?: undefined;
|
|
149
149
|
reason?: undefined;
|
|
@@ -159,6 +159,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
159
159
|
inputSchema: {
|
|
160
160
|
type: string;
|
|
161
161
|
properties: {
|
|
162
|
+
count?: undefined;
|
|
162
163
|
query: {
|
|
163
164
|
type: string;
|
|
164
165
|
description: string;
|
|
@@ -174,7 +175,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
174
175
|
clientHint?: undefined;
|
|
175
176
|
timestamp?: undefined;
|
|
176
177
|
chapterId?: undefined;
|
|
177
|
-
count?: undefined;
|
|
178
178
|
startAt?: undefined;
|
|
179
179
|
endAt?: undefined;
|
|
180
180
|
reason?: undefined;
|
|
@@ -190,6 +190,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
190
190
|
inputSchema: {
|
|
191
191
|
type: string;
|
|
192
192
|
properties: {
|
|
193
|
+
count?: undefined;
|
|
193
194
|
minutes: {
|
|
194
195
|
type: string;
|
|
195
196
|
description: string;
|
|
@@ -199,7 +200,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
199
200
|
clientHint?: undefined;
|
|
200
201
|
timestamp?: undefined;
|
|
201
202
|
chapterId?: undefined;
|
|
202
|
-
count?: undefined;
|
|
203
203
|
startAt?: undefined;
|
|
204
204
|
endAt?: undefined;
|
|
205
205
|
reason?: undefined;
|
|
@@ -216,6 +216,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
216
216
|
type: string;
|
|
217
217
|
required: string[];
|
|
218
218
|
properties: {
|
|
219
|
+
count?: undefined;
|
|
219
220
|
query: {
|
|
220
221
|
type: string;
|
|
221
222
|
description: string;
|
|
@@ -234,7 +235,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
234
235
|
};
|
|
235
236
|
timestamp?: undefined;
|
|
236
237
|
chapterId?: undefined;
|
|
237
|
-
count?: undefined;
|
|
238
238
|
startAt?: undefined;
|
|
239
239
|
endAt?: undefined;
|
|
240
240
|
reason?: undefined;
|
|
@@ -250,6 +250,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
250
250
|
type: string;
|
|
251
251
|
required: string[];
|
|
252
252
|
properties: {
|
|
253
|
+
count?: undefined;
|
|
253
254
|
query?: undefined;
|
|
254
255
|
minutes?: undefined;
|
|
255
256
|
limit?: undefined;
|
|
@@ -263,7 +264,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
263
264
|
description: string;
|
|
264
265
|
};
|
|
265
266
|
chapterId?: undefined;
|
|
266
|
-
count?: undefined;
|
|
267
267
|
startAt?: undefined;
|
|
268
268
|
endAt?: undefined;
|
|
269
269
|
includeMicrophone?: undefined;
|
|
@@ -314,13 +314,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
314
314
|
type: string;
|
|
315
315
|
required: string[];
|
|
316
316
|
properties: {
|
|
317
|
+
count?: undefined;
|
|
317
318
|
query?: undefined;
|
|
318
319
|
minutes?: undefined;
|
|
319
320
|
limit?: undefined;
|
|
320
321
|
clientHint?: undefined;
|
|
321
322
|
timestamp?: undefined;
|
|
322
323
|
chapterId?: undefined;
|
|
323
|
-
count?: undefined;
|
|
324
324
|
startAt: {
|
|
325
325
|
type: string;
|
|
326
326
|
description: string;
|
|
@@ -349,13 +349,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
349
349
|
type: string;
|
|
350
350
|
required: string[];
|
|
351
351
|
properties: {
|
|
352
|
+
count?: undefined;
|
|
352
353
|
query?: undefined;
|
|
353
354
|
minutes?: undefined;
|
|
354
355
|
limit?: undefined;
|
|
355
356
|
clientHint?: undefined;
|
|
356
357
|
timestamp?: undefined;
|
|
357
358
|
chapterId?: undefined;
|
|
358
|
-
count?: undefined;
|
|
359
359
|
startAt?: undefined;
|
|
360
360
|
endAt?: undefined;
|
|
361
361
|
reason?: undefined;
|
|
@@ -16,18 +16,18 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
|
|
|
16
16
|
error?: undefined;
|
|
17
17
|
ok?: undefined;
|
|
18
18
|
} | {
|
|
19
|
+
count?: undefined;
|
|
19
20
|
updated: number;
|
|
20
21
|
error?: undefined;
|
|
21
22
|
ok?: undefined;
|
|
22
|
-
count?: undefined;
|
|
23
23
|
} | {
|
|
24
|
+
count?: undefined;
|
|
24
25
|
updated?: undefined;
|
|
25
26
|
error: string;
|
|
26
27
|
ok?: undefined;
|
|
27
|
-
count?: undefined;
|
|
28
28
|
} | {
|
|
29
|
+
count?: undefined;
|
|
29
30
|
updated?: undefined;
|
|
30
31
|
error?: undefined;
|
|
31
32
|
ok: boolean;
|
|
32
|
-
count?: undefined;
|
|
33
33
|
}>>;
|
|
@@ -48,8 +48,8 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
|
|
|
48
48
|
}>;
|
|
49
49
|
/** DELETE /_agent-native/resources/:id — delete a resource */
|
|
50
50
|
export declare function handleDeleteResource(event: any): Promise<{
|
|
51
|
-
error: string;
|
|
52
51
|
ok?: undefined;
|
|
52
|
+
error: string;
|
|
53
53
|
} | {
|
|
54
54
|
error?: undefined;
|
|
55
55
|
ok: boolean;
|
package/dist/server/auth.js
CHANGED
|
@@ -564,7 +564,8 @@ function shouldExposeSessionTokenInBody(event) {
|
|
|
564
564
|
// X-Request-Source; browsers can only use that cross-origin after our CORS
|
|
565
565
|
// allowlist has approved the origin, and same-origin pages already receive
|
|
566
566
|
// an equivalent httpOnly session cookie on successful login.
|
|
567
|
-
|
|
567
|
+
const requestSource = getHeader(event, "x-request-source");
|
|
568
|
+
return (!origin && (requestSource === "clips-desktop" || requestSource === "mobile"));
|
|
568
569
|
}
|
|
569
570
|
function authLoginResponse(event, token, email) {
|
|
570
571
|
if (!shouldExposeSessionTokenInBody(event))
|
|
@@ -2683,6 +2684,7 @@ async function mountBetterAuthRoutes(app, options) {
|
|
|
2683
2684
|
}
|
|
2684
2685
|
const q = getQuery(event);
|
|
2685
2686
|
const desktop = isElectronRequest(event) || q.desktop === "1" || q.desktop === "true";
|
|
2687
|
+
const mobile = q.mobile === "1" || q.mobile === "true";
|
|
2686
2688
|
const flowId = desktop ? q.flow_id || undefined : undefined;
|
|
2687
2689
|
// Validate the caller's return param up front and only embed it
|
|
2688
2690
|
// into the OAuth state when it normalises to a non-root path —
|
|
@@ -2701,6 +2703,7 @@ async function mountBetterAuthRoutes(app, options) {
|
|
|
2701
2703
|
const state = encodeOAuthState({
|
|
2702
2704
|
redirectUri,
|
|
2703
2705
|
desktop,
|
|
2706
|
+
mobile,
|
|
2704
2707
|
addAccount: false,
|
|
2705
2708
|
app: getOAuthStateAppId(),
|
|
2706
2709
|
returnUrl,
|
|
@@ -2711,6 +2714,7 @@ async function mountBetterAuthRoutes(app, options) {
|
|
|
2711
2714
|
logGoogleOAuthDebug(event, "auth-url", {
|
|
2712
2715
|
flowId,
|
|
2713
2716
|
desktop,
|
|
2717
|
+
mobile,
|
|
2714
2718
|
redirectPath: oauthDebugUrlPath(redirectUri),
|
|
2715
2719
|
returnUrl,
|
|
2716
2720
|
redirect: q.redirect === "1",
|
|
@@ -2755,15 +2759,18 @@ async function mountBetterAuthRoutes(app, options) {
|
|
|
2755
2759
|
return callbackRelay;
|
|
2756
2760
|
let callbackFlowId;
|
|
2757
2761
|
let callbackDesktop = false;
|
|
2762
|
+
let callbackMobile = false;
|
|
2758
2763
|
try {
|
|
2759
2764
|
const query = getQuery(event);
|
|
2760
2765
|
const code = query.code;
|
|
2761
|
-
const { redirectUri, desktop, returnUrl, flowId, signupAttribution, signupAnonymousId, } = decodeOAuthState(query.state, getAppUrl(event, "/_agent-native/google/callback"));
|
|
2766
|
+
const { redirectUri, desktop, mobile, returnUrl, flowId, signupAttribution, signupAnonymousId, } = decodeOAuthState(query.state, getAppUrl(event, "/_agent-native/google/callback"));
|
|
2762
2767
|
callbackFlowId = flowId;
|
|
2763
2768
|
callbackDesktop = desktop ?? false;
|
|
2769
|
+
callbackMobile = mobile ?? false;
|
|
2764
2770
|
logGoogleOAuthDebug(event, "callback-start", {
|
|
2765
2771
|
flowId,
|
|
2766
2772
|
desktop,
|
|
2773
|
+
mobile,
|
|
2767
2774
|
redirectPath: oauthDebugUrlPath(redirectUri),
|
|
2768
2775
|
hasCode: !!code,
|
|
2769
2776
|
returnUrl,
|
|
@@ -2870,6 +2877,7 @@ async function mountBetterAuthRoutes(app, options) {
|
|
|
2870
2877
|
const { sessionToken } = await createOAuthSession(event, email, {
|
|
2871
2878
|
hasProductionSession: false,
|
|
2872
2879
|
desktop,
|
|
2880
|
+
mobile,
|
|
2873
2881
|
trackSignup: {
|
|
2874
2882
|
authProvider: "google",
|
|
2875
2883
|
authUserId: typeof user.id === "string" ? user.id : undefined,
|
|
@@ -2881,6 +2889,7 @@ async function mountBetterAuthRoutes(app, options) {
|
|
|
2881
2889
|
logGoogleOAuthDebug(event, "callback-session-created", {
|
|
2882
2890
|
flowId,
|
|
2883
2891
|
desktop,
|
|
2892
|
+
mobile,
|
|
2884
2893
|
hasSessionToken: !!sessionToken,
|
|
2885
2894
|
emailDomain: email.split("@")[1] || "",
|
|
2886
2895
|
});
|
|
@@ -2894,6 +2903,7 @@ async function mountBetterAuthRoutes(app, options) {
|
|
|
2894
2903
|
return oauthCallbackResponse(event, email, {
|
|
2895
2904
|
sessionToken,
|
|
2896
2905
|
desktop,
|
|
2906
|
+
mobile,
|
|
2897
2907
|
returnUrl,
|
|
2898
2908
|
flowId,
|
|
2899
2909
|
});
|
|
@@ -2909,6 +2919,7 @@ async function mountBetterAuthRoutes(app, options) {
|
|
|
2909
2919
|
logGoogleOAuthDebug(event, "callback-error", {
|
|
2910
2920
|
flowId: callbackFlowId,
|
|
2911
2921
|
desktop: callbackDesktop,
|
|
2922
|
+
mobile: callbackMobile,
|
|
2912
2923
|
message: msg,
|
|
2913
2924
|
});
|
|
2914
2925
|
return oauthErrorPage(AUTH_GOOGLE_FALLBACK);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { defineEventHandler, getHeader, getMethod, getQuery, setResponseHeader, } from "h3";
|
|
2
2
|
import { withCollapsedAgentSidebarParam } from "../shared/agent-sidebar-url.js";
|
|
3
3
|
import { EMBED_MODE_QUERY_PARAM, EMBED_START_PATH, EMBED_TOKEN_QUERY_PARAM, MCP_APP_CHAT_BRIDGE_QUERY_PARAM, } from "../shared/embed-auth.js";
|
|
4
|
-
import {
|
|
4
|
+
import { isMcpEmbedTransplantOrigin, MCP_EMBED_CORS_ALLOW_HEADERS, } from "../shared/mcp-embed-headers.js";
|
|
5
5
|
import { getConfiguredAppBasePath } from "./app-base-path.js";
|
|
6
|
+
import { readCorsAllowedOrigins } from "./cors-origins.js";
|
|
6
7
|
import { consumeEmbedSessionTicket, isEmbedCapabilityScope, normalizeEmbedTargetPath, setEmbedSessionCookie, signEmbedSessionToken, } from "./embed-session.js";
|
|
7
8
|
function withConfiguredBasePath(path) {
|
|
8
9
|
const base = getConfiguredAppBasePath();
|
|
@@ -48,7 +49,11 @@ function setEmbedStartResponseHeaders(event) {
|
|
|
48
49
|
}
|
|
49
50
|
function embedStartCorsOrigin(event) {
|
|
50
51
|
const origin = getHeader(event, "origin");
|
|
51
|
-
|
|
52
|
+
if (!origin)
|
|
53
|
+
return null;
|
|
54
|
+
if (isMcpEmbedTransplantOrigin(origin))
|
|
55
|
+
return origin;
|
|
56
|
+
return readCorsAllowedOrigins().includes(origin) ? origin : null;
|
|
52
57
|
}
|
|
53
58
|
function embedStartResponseHeaders(event, init = {}) {
|
|
54
59
|
const headers = new Headers({
|
|
@@ -138,6 +143,12 @@ function firstQueryValue(value) {
|
|
|
138
143
|
: "";
|
|
139
144
|
}
|
|
140
145
|
function wantsTransplantLocationResponse(event) {
|
|
146
|
+
const origin = getHeader(event, "origin");
|
|
147
|
+
const canReadLocation = !origin ||
|
|
148
|
+
embedStartCorsOrigin(event) !== null ||
|
|
149
|
+
isMcpEmbedTransplantOrigin(origin);
|
|
150
|
+
if (!canReadLocation)
|
|
151
|
+
return false;
|
|
141
152
|
if (getHeader(event, "x-agent-native-embed-transplant") === "1") {
|
|
142
153
|
return true;
|
|
143
154
|
}
|
|
@@ -88,6 +88,13 @@ export interface OAuthStatePayload {
|
|
|
88
88
|
owner?: string;
|
|
89
89
|
orgId?: string;
|
|
90
90
|
desktop?: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Explicit native-mobile intent from an embedded app. The callback may be
|
|
93
|
+
* served by a browser with a non-mobile user-agent, so relying on
|
|
94
|
+
* `isMobile(event)` alone can strand the native client on the web sign-in
|
|
95
|
+
* page.
|
|
96
|
+
*/
|
|
97
|
+
mobile?: boolean;
|
|
91
98
|
addAccount?: boolean;
|
|
92
99
|
app?: string;
|
|
93
100
|
/**
|
|
@@ -113,6 +120,7 @@ export interface EncodeOAuthStateOptions {
|
|
|
113
120
|
owner?: string;
|
|
114
121
|
orgId?: string;
|
|
115
122
|
desktop?: boolean;
|
|
123
|
+
mobile?: boolean;
|
|
116
124
|
addAccount?: boolean;
|
|
117
125
|
app?: string;
|
|
118
126
|
returnUrl?: string;
|
|
@@ -165,6 +173,7 @@ export interface OAuthSessionResult {
|
|
|
165
173
|
export declare function createOAuthSession(event: H3Event, email: string, opts: {
|
|
166
174
|
hasProductionSession: boolean;
|
|
167
175
|
desktop?: boolean;
|
|
176
|
+
mobile?: boolean;
|
|
168
177
|
trackSignup?: {
|
|
169
178
|
authProvider: string;
|
|
170
179
|
authUserId?: string;
|
|
@@ -182,6 +191,7 @@ export declare function createOAuthSession(event: H3Event, email: string, opts:
|
|
|
182
191
|
export declare function oauthCallbackResponse(event: H3Event, email: string, opts: {
|
|
183
192
|
sessionToken?: string;
|
|
184
193
|
desktop?: boolean;
|
|
194
|
+
mobile?: boolean;
|
|
185
195
|
addAccount?: boolean;
|
|
186
196
|
/**
|
|
187
197
|
* Same-origin path to return the viewer to after a successful web
|
|
@@ -453,6 +453,8 @@ export function encodeOAuthState(redirectUriOrOpts, owner, desktop, addAccount,
|
|
|
453
453
|
payload.g = opts.orgId;
|
|
454
454
|
if (opts.desktop)
|
|
455
455
|
payload.d = true;
|
|
456
|
+
if (opts.mobile)
|
|
457
|
+
payload.m = true;
|
|
456
458
|
if (opts.addAccount)
|
|
457
459
|
payload.a = true;
|
|
458
460
|
if (opts.app)
|
|
@@ -500,6 +502,7 @@ export function decodeOAuthState(stateParam, fallbackUri) {
|
|
|
500
502
|
owner: parsed.o || undefined,
|
|
501
503
|
orgId: typeof parsed.g === "string" ? parsed.g : undefined,
|
|
502
504
|
desktop: !!parsed.d,
|
|
505
|
+
mobile: !!parsed.m,
|
|
503
506
|
addAccount: !!parsed.a,
|
|
504
507
|
app: typeof parsed.app === "string" ? parsed.app : undefined,
|
|
505
508
|
// Pass returnUrl through as-is — same-origin validation runs at the
|
|
@@ -537,7 +540,10 @@ export async function resolveOAuthOwner(event, stateOwner) {
|
|
|
537
540
|
* app can inject it.
|
|
538
541
|
*/
|
|
539
542
|
export async function createOAuthSession(event, email, opts) {
|
|
540
|
-
|
|
543
|
+
// A native callback can arrive through a browser whose callback request
|
|
544
|
+
// user-agent does not identify as mobile. Prefer the signed flow intent and
|
|
545
|
+
// retain UA detection for ordinary mobile web sign-ins.
|
|
546
|
+
const mobile = opts.mobile || isMobile(event);
|
|
541
547
|
const needsDeepLink = opts.desktop || mobile;
|
|
542
548
|
const maxAge = getSessionMaxAge();
|
|
543
549
|
let sessionToken;
|
|
@@ -592,7 +598,9 @@ export async function createOAuthSession(event, email, opts) {
|
|
|
592
598
|
* pages, and plain web redirects — so templates don't have to.
|
|
593
599
|
*/
|
|
594
600
|
export function oauthCallbackResponse(event, email, opts) {
|
|
595
|
-
|
|
601
|
+
// The mobile flag is carried inside HMAC-signed OAuth state by native
|
|
602
|
+
// clients. UA detection remains the fallback for ordinary mobile browsers.
|
|
603
|
+
const mobile = opts.mobile || isMobile(event);
|
|
596
604
|
const query = getQuery(event);
|
|
597
605
|
const callbackState = typeof query.state === "string" && query.state.length > 0
|
|
598
606
|
? query.state
|
|
@@ -1299,6 +1299,16 @@ export function getOnboardingHtml(opts = {}) {
|
|
|
1299
1299
|
const googleAuthMode = resolveGoogleAuthMode(opts.googleAuthMode);
|
|
1300
1300
|
const builderPreviewLocalDevEnabled = isBuilderPreviewLocalDevEnabled();
|
|
1301
1301
|
const localeInitScript = getLocaleInitScript();
|
|
1302
|
+
const embeddedAuthInitScript = `(function() {
|
|
1303
|
+
try {
|
|
1304
|
+
var params = new URLSearchParams(window.location.search || "");
|
|
1305
|
+
if (params.get("embedded") === "1" || window.self !== window.top) {
|
|
1306
|
+
document.documentElement.setAttribute("data-agent-native-embedded", "1");
|
|
1307
|
+
}
|
|
1308
|
+
} catch (error) {
|
|
1309
|
+
void error;
|
|
1310
|
+
}
|
|
1311
|
+
})();`;
|
|
1302
1312
|
const marketing = opts.marketing ??
|
|
1303
1313
|
resolveBuiltInAuthMarketing({
|
|
1304
1314
|
requestHost: opts.requestHost,
|
|
@@ -1397,6 +1407,9 @@ ${localeMenuItemsHtml}
|
|
|
1397
1407
|
</div>`
|
|
1398
1408
|
: "";
|
|
1399
1409
|
const identitySsoHtml = identitySsoLoginButtonHtml();
|
|
1410
|
+
const embeddedAuthCss = identitySsoHtml
|
|
1411
|
+
? ' html[data-agent-native-embedded="1"] #identity-sso-btn { display: none !important; }\n'
|
|
1412
|
+
: "";
|
|
1400
1413
|
const localDevHtml = `
|
|
1401
1414
|
<div class="local-dev-signin" id="local-dev-signin" hidden>
|
|
1402
1415
|
<button type="button" class="btn-local-dev btn-primary" id="local-dev-btn" title="${esc(t("localDevDescription"))}"${i18nAttr("localDevButton")} data-i18n-title="localDevDescription" aria-describedby="local-dev-description">${esc(t("localDevButton"))}</button>
|
|
@@ -1758,6 +1771,7 @@ ${marketing.description ? ` <p class="app-desc" data-marketing-field="descr
|
|
|
1758
1771
|
<head>
|
|
1759
1772
|
<meta charset="UTF-8">
|
|
1760
1773
|
<script data-agent-native-locale-init>${localeInitScript}</script>
|
|
1774
|
+
<script data-agent-native-embedded-init>${embeddedAuthInitScript}</script>
|
|
1761
1775
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
|
1762
1776
|
<title>${hasMarketing ? esc(marketing.appName) + " — " + esc(t("pageTitleSignIn")) : esc(t("pageTitleWelcome"))}</title>
|
|
1763
1777
|
<link rel="icon" type="image/svg+xml" href="${withAppBasePath("/favicon.svg")}">
|
|
@@ -2305,6 +2319,7 @@ ${marketingStyles}
|
|
|
2305
2319
|
body.simplified-auth { background: #141414; }
|
|
2306
2320
|
body.simplified-auth .card { border-color: transparent; box-shadow: none; }
|
|
2307
2321
|
body.simplified-auth .local-note { display: none !important; }
|
|
2322
|
+
${embeddedAuthCss}
|
|
2308
2323
|
</style>
|
|
2309
2324
|
</head>
|
|
2310
2325
|
<body${simplifiedAuth ? ' class="simplified-auth"' : hasMarketing ? ' class="has-marketing"' : ""}>
|
|
@@ -7,6 +7,13 @@ export declare function isMcpProductHostOrigin(origin: string | null | undefined
|
|
|
7
7
|
export declare function isAgentNativeFirstPartyAppOrigin(origin: string | null | undefined): boolean;
|
|
8
8
|
export declare function isBuilderIoEmbedOrigin(origin: string | null | undefined): boolean;
|
|
9
9
|
export declare function isMcpEmbedCorsOrigin(origin: string | null | undefined): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Origins allowed to read the one-time embed location returned by a browser
|
|
12
|
+
* transplant request. Builder and localhost origins may use the broader MCP
|
|
13
|
+
* CORS surface, but must not receive a reusable session location unless they
|
|
14
|
+
* are explicitly configured through CORS_ALLOWED_ORIGINS.
|
|
15
|
+
*/
|
|
16
|
+
export declare function isMcpEmbedTransplantOrigin(origin: string | null | undefined): boolean;
|
|
10
17
|
export declare function shouldAllowMcpEmbedCredentials(origin: string | null | undefined): boolean;
|
|
11
18
|
export declare const MCP_EMBED_STATIC_ASSET_HEADERS: {
|
|
12
19
|
readonly "Access-Control-Allow-Origin": "*";
|
|
@@ -8,6 +8,14 @@ const MCP_PRODUCT_HOST_ORIGINS = new Set([
|
|
|
8
8
|
"https://chatgpt.com",
|
|
9
9
|
"https://claude.ai",
|
|
10
10
|
]);
|
|
11
|
+
const TRUSTED_NATIVE_APP_ORIGIN_RE = /^(?:tauri:\/\/localhost|https?:\/\/tauri\.localhost(?::\d+)?)$/;
|
|
12
|
+
function isExplicitCorsAllowedOrigin(origin) {
|
|
13
|
+
return (process.env.CORS_ALLOWED_ORIGINS ?? "")
|
|
14
|
+
.split(",")
|
|
15
|
+
.map((value) => value.trim())
|
|
16
|
+
.filter(Boolean)
|
|
17
|
+
.includes(origin);
|
|
18
|
+
}
|
|
11
19
|
export function isLocalMcpEmbedOrigin(origin) {
|
|
12
20
|
if (!origin)
|
|
13
21
|
return false;
|
|
@@ -97,12 +105,27 @@ export function isMcpEmbedCorsOrigin(origin) {
|
|
|
97
105
|
isAgentNativeFirstPartyAppOrigin(origin) ||
|
|
98
106
|
isBuilderIoEmbedOrigin(origin));
|
|
99
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Origins allowed to read the one-time embed location returned by a browser
|
|
110
|
+
* transplant request. Builder and localhost origins may use the broader MCP
|
|
111
|
+
* CORS surface, but must not receive a reusable session location unless they
|
|
112
|
+
* are explicitly configured through CORS_ALLOWED_ORIGINS.
|
|
113
|
+
*/
|
|
114
|
+
export function isMcpEmbedTransplantOrigin(origin) {
|
|
115
|
+
return (isClaudeMcpContentOrigin(origin) ||
|
|
116
|
+
isChatGptMcpSandboxOrigin(origin) ||
|
|
117
|
+
isMcpProductHostOrigin(origin) ||
|
|
118
|
+
isAgentNativeFirstPartyAppOrigin(origin));
|
|
119
|
+
}
|
|
100
120
|
export function shouldAllowMcpEmbedCredentials(origin) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
121
|
+
if (!origin || origin === "null")
|
|
122
|
+
return false;
|
|
123
|
+
// Credentialed CORS is an explicit deployment decision. MCP product,
|
|
124
|
+
// Builder, localhost, and arbitrary origins use bearer/embed credentials;
|
|
125
|
+
// only the configured browser allowlist and the framework's exact native
|
|
126
|
+
// app origins may receive cookies.
|
|
127
|
+
return (TRUSTED_NATIVE_APP_ORIGIN_RE.test(origin) ||
|
|
128
|
+
isExplicitCorsAllowedOrigin(origin));
|
|
106
129
|
}
|
|
107
130
|
// These paths are served straight off the CDN, so the security-headers h3
|
|
108
131
|
// middleware never runs for them — anything it sets that must also hold for
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.157.
|
|
3
|
+
"version": "0.157.19",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|