@agent-native/core 0.170.0 → 0.171.1
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/analytics/server/lib/workspace-feature-flags.ts +84 -20
- package/dist/action.d.ts +4 -1
- package/dist/action.js +2 -0
- package/dist/app-config/auth.d.ts +1 -0
- package/dist/app-config/auth.js +7 -0
- package/dist/app-config/schema.d.ts +1 -0
- package/dist/client/use-action.js +8 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/server/action-routes.js +6 -2
- package/dist/server/better-auth-instance.js +14 -0
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/vite/client.js +9 -2
- package/docs/content/authentication.mdx +40 -21
- package/docs/content/environment-variables.mdx +1 -0
- package/package.json +1 -1
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
|
|
3
3
|
import { signA2AToken } from "@agent-native/core/a2a";
|
|
4
|
+
import {
|
|
5
|
+
ActionContractError,
|
|
6
|
+
AgentActionStopError,
|
|
7
|
+
} from "@agent-native/core/action";
|
|
4
8
|
import { isFeatureFlagEnabled } from "@agent-native/core/feature-flags";
|
|
5
9
|
import { fetchOrgApps, type OrgApp } from "@agent-native/core/mcp";
|
|
6
10
|
import { getOrgDomain } from "@agent-native/core/org";
|
|
@@ -9,6 +13,7 @@ import { VERIFIED_FLEET_FLAG_MUTATIONS } from "../../shared/feature-flags.js";
|
|
|
9
13
|
import type { AnalyticsAdminContext } from "./db-admin-connections.js";
|
|
10
14
|
|
|
11
15
|
const TARGET_TIMEOUT_MS = 3_000;
|
|
16
|
+
const VERIFICATION_ATTEMPTS = 2;
|
|
12
17
|
const CONCURRENCY = 4;
|
|
13
18
|
|
|
14
19
|
export type FleetFlagState =
|
|
@@ -197,6 +202,8 @@ export type WorkspaceFeatureFlagFailurePhase =
|
|
|
197
202
|
| "unsupported-target"
|
|
198
203
|
| "target-action"
|
|
199
204
|
| "persistence"
|
|
205
|
+
| "verification-timeout"
|
|
206
|
+
| "verification-network"
|
|
200
207
|
| "verification";
|
|
201
208
|
|
|
202
209
|
const FAILURE_MESSAGES: Record<WorkspaceFeatureFlagFailurePhase, string> = {
|
|
@@ -211,16 +218,42 @@ const FAILURE_MESSAGES: Record<WorkspaceFeatureFlagFailurePhase, string> = {
|
|
|
211
218
|
"target-action": "The target app could not complete the feature flag action.",
|
|
212
219
|
persistence:
|
|
213
220
|
"The target app did not persist the requested feature flag rules.",
|
|
221
|
+
"verification-timeout":
|
|
222
|
+
"The feature flag change persisted, but the target app timed out during verification.",
|
|
223
|
+
"verification-network":
|
|
224
|
+
"The feature flag change persisted, but Analytics could not reach the target app during verification.",
|
|
214
225
|
verification: "Analytics could not verify the persisted feature flag change.",
|
|
215
226
|
};
|
|
216
227
|
|
|
217
|
-
export class WorkspaceFeatureFlagFailure extends
|
|
228
|
+
export class WorkspaceFeatureFlagFailure extends AgentActionStopError {
|
|
218
229
|
constructor(readonly phase: WorkspaceFeatureFlagFailurePhase) {
|
|
219
|
-
|
|
230
|
+
const message = `[${phase}] ${FAILURE_MESSAGES[phase]}`;
|
|
231
|
+
const errorCode = `workspace_feature_flag_${phase.replace("-", "_")}`;
|
|
232
|
+
super(message, {
|
|
233
|
+
errorCode,
|
|
234
|
+
details: { phase },
|
|
235
|
+
toolResult: JSON.stringify({ error: errorCode, phase, message }),
|
|
236
|
+
});
|
|
220
237
|
this.name = "WorkspaceFeatureFlagFailure";
|
|
221
238
|
}
|
|
222
239
|
}
|
|
223
240
|
|
|
241
|
+
class WorkspaceFeatureFlagSetupFailure extends ActionContractError {
|
|
242
|
+
readonly phase: "directory" | "token-generation";
|
|
243
|
+
|
|
244
|
+
constructor(phase: "directory" | "token-generation") {
|
|
245
|
+
const message = `[${phase}] ${FAILURE_MESSAGES[phase]}`;
|
|
246
|
+
const errorCode = `workspace_feature_flag_${phase.replace("-", "_")}`;
|
|
247
|
+
super(message, {
|
|
248
|
+
errorCode,
|
|
249
|
+
details: { phase },
|
|
250
|
+
statusCode: 503,
|
|
251
|
+
});
|
|
252
|
+
this.name = "WorkspaceFeatureFlagSetupFailure";
|
|
253
|
+
this.phase = phase;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
224
257
|
type TargetFailureReason = "token-generation" | "timeout" | "network";
|
|
225
258
|
|
|
226
259
|
class TargetCallFailure extends Error {
|
|
@@ -242,6 +275,16 @@ export function classifyWorkspaceFeatureFlagTargetFailure(
|
|
|
242
275
|
return "network";
|
|
243
276
|
}
|
|
244
277
|
|
|
278
|
+
function isSyntaxError(error: unknown): boolean {
|
|
279
|
+
return (
|
|
280
|
+
error instanceof SyntaxError ||
|
|
281
|
+
(!!error &&
|
|
282
|
+
typeof error === "object" &&
|
|
283
|
+
"name" in error &&
|
|
284
|
+
error.name === "SyntaxError")
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
245
288
|
function targetFailure(error: unknown): WorkspaceFeatureFlagFailure {
|
|
246
289
|
const reason = classifyWorkspaceFeatureFlagTargetFailure(error);
|
|
247
290
|
return new WorkspaceFeatureFlagFailure(reason);
|
|
@@ -259,10 +302,10 @@ async function resolveTargetApp(
|
|
|
259
302
|
serviceOrgId: admin.orgId,
|
|
260
303
|
});
|
|
261
304
|
} catch {
|
|
262
|
-
throw new
|
|
305
|
+
throw new WorkspaceFeatureFlagSetupFailure("directory");
|
|
263
306
|
}
|
|
264
307
|
const app = apps.find((candidate) => candidate.id === appId);
|
|
265
|
-
if (!app) throw new
|
|
308
|
+
if (!app) throw new WorkspaceFeatureFlagSetupFailure("directory");
|
|
266
309
|
return app;
|
|
267
310
|
}
|
|
268
311
|
|
|
@@ -302,12 +345,41 @@ async function callTarget(
|
|
|
302
345
|
let parsed: unknown = null;
|
|
303
346
|
try {
|
|
304
347
|
parsed = await response.json();
|
|
305
|
-
} catch {
|
|
306
|
-
|
|
348
|
+
} catch (error) {
|
|
349
|
+
const successfulResponse = response.status >= 200 && response.status < 300;
|
|
350
|
+
if (successfulResponse && !isSyntaxError(error))
|
|
351
|
+
throw new TargetCallFailure(
|
|
352
|
+
classifyWorkspaceFeatureFlagTargetFailure(error),
|
|
353
|
+
);
|
|
354
|
+
// Invalid legacy payloads and non-success statuses are classified below.
|
|
307
355
|
}
|
|
308
356
|
return { status: response.status, body: parsed };
|
|
309
357
|
}
|
|
310
358
|
|
|
359
|
+
async function readBackTarget(
|
|
360
|
+
app: OrgApp,
|
|
361
|
+
admin: AnalyticsAdminContext,
|
|
362
|
+
orgDomain: string,
|
|
363
|
+
): Promise<{ status: number; body: unknown }> {
|
|
364
|
+
for (let attempt = 1; attempt <= VERIFICATION_ATTEMPTS; attempt += 1) {
|
|
365
|
+
try {
|
|
366
|
+
return await callTarget(app, admin, "list-feature-flags", {}, orgDomain);
|
|
367
|
+
} catch (error) {
|
|
368
|
+
const reason = classifyWorkspaceFeatureFlagTargetFailure(error);
|
|
369
|
+
const retryable = reason === "timeout" || reason === "network";
|
|
370
|
+
if (retryable && attempt < VERIFICATION_ATTEMPTS) continue;
|
|
371
|
+
throw new WorkspaceFeatureFlagFailure(
|
|
372
|
+
reason === "timeout"
|
|
373
|
+
? "verification-timeout"
|
|
374
|
+
: reason === "network"
|
|
375
|
+
? "verification-network"
|
|
376
|
+
: reason,
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
throw new WorkspaceFeatureFlagFailure("verification");
|
|
381
|
+
}
|
|
382
|
+
|
|
311
383
|
export function classifyWorkspaceFeatureFlagList(
|
|
312
384
|
app: OrgApp,
|
|
313
385
|
result: { status: number; body: unknown },
|
|
@@ -404,9 +476,10 @@ export async function setWorkspaceFeatureFlag(
|
|
|
404
476
|
try {
|
|
405
477
|
orgDomain = (await getOrgDomain(admin.orgId))?.trim().toLowerCase();
|
|
406
478
|
} catch {
|
|
407
|
-
throw new
|
|
479
|
+
throw new WorkspaceFeatureFlagSetupFailure("token-generation");
|
|
408
480
|
}
|
|
409
|
-
if (!orgDomain)
|
|
481
|
+
if (!orgDomain)
|
|
482
|
+
throw new WorkspaceFeatureFlagSetupFailure("token-generation");
|
|
410
483
|
let result: Awaited<ReturnType<typeof callTarget>>;
|
|
411
484
|
try {
|
|
412
485
|
result = await callTarget(
|
|
@@ -417,6 +490,8 @@ export async function setWorkspaceFeatureFlag(
|
|
|
417
490
|
orgDomain,
|
|
418
491
|
);
|
|
419
492
|
} catch (error) {
|
|
493
|
+
if (classifyWorkspaceFeatureFlagTargetFailure(error) === "token-generation")
|
|
494
|
+
throw new WorkspaceFeatureFlagSetupFailure("token-generation");
|
|
420
495
|
throw targetFailure(error);
|
|
421
496
|
}
|
|
422
497
|
if (result.status === 401 || result.status === 403)
|
|
@@ -465,18 +540,7 @@ export async function setWorkspaceFeatureFlag(
|
|
|
465
540
|
return mutation;
|
|
466
541
|
}
|
|
467
542
|
|
|
468
|
-
|
|
469
|
-
try {
|
|
470
|
-
readBack = await callTarget(
|
|
471
|
-
app,
|
|
472
|
-
admin,
|
|
473
|
-
"list-feature-flags",
|
|
474
|
-
{},
|
|
475
|
-
orgDomain,
|
|
476
|
-
);
|
|
477
|
-
} catch (error) {
|
|
478
|
-
throw targetFailure(error);
|
|
479
|
-
}
|
|
543
|
+
const readBack = await readBackTarget(app, admin, orgDomain);
|
|
480
544
|
if (readBack.status === 401 || readBack.status === 403)
|
|
481
545
|
throw new WorkspaceFeatureFlagFailure("authorization");
|
|
482
546
|
if (readBack.status === 404 || readBack.status === 405)
|
package/dist/action.d.ts
CHANGED
|
@@ -130,8 +130,10 @@ export interface ActionRunContext {
|
|
|
130
130
|
*/
|
|
131
131
|
export type ActionAuthorize<TArgs> = (args: TArgs, ctx?: ActionRunContext) => void | boolean | Promise<void | boolean>;
|
|
132
132
|
export interface AgentActionStopOptions {
|
|
133
|
-
/** Optional stable code
|
|
133
|
+
/** Optional stable code safe to surface in run metadata and action transports. */
|
|
134
134
|
errorCode?: string;
|
|
135
|
+
/** Safe structured context for callers. Never include secrets or raw driver errors. */
|
|
136
|
+
details?: Record<string, unknown>;
|
|
135
137
|
/** Optional short tool-result text. Defaults to the user-facing message. */
|
|
136
138
|
toolResult?: string;
|
|
137
139
|
}
|
|
@@ -164,6 +166,7 @@ export declare function isActionContractError(error: unknown): error is ActionCo
|
|
|
164
166
|
export declare class AgentActionStopError extends Error {
|
|
165
167
|
readonly agentNativeStop = true;
|
|
166
168
|
readonly errorCode?: string;
|
|
169
|
+
readonly details?: Record<string, unknown>;
|
|
167
170
|
readonly toolResult?: string;
|
|
168
171
|
constructor(message: string, options?: AgentActionStopOptions);
|
|
169
172
|
}
|
package/dist/action.js
CHANGED
|
@@ -34,11 +34,13 @@ export function isActionContractError(error) {
|
|
|
34
34
|
export class AgentActionStopError extends Error {
|
|
35
35
|
agentNativeStop = true;
|
|
36
36
|
errorCode;
|
|
37
|
+
details;
|
|
37
38
|
toolResult;
|
|
38
39
|
constructor(message, options = {}) {
|
|
39
40
|
super(message);
|
|
40
41
|
this.name = "AgentActionStopError";
|
|
41
42
|
this.errorCode = options.errorCode;
|
|
43
|
+
this.details = options.details;
|
|
42
44
|
this.toolResult = options.toolResult;
|
|
43
45
|
}
|
|
44
46
|
}
|
package/dist/app-config/auth.js
CHANGED
|
@@ -4,4 +4,11 @@ export const authConfig = z.object({
|
|
|
4
4
|
env: "AGENT_NATIVE_DISABLE_DESKTOP_SSO_FALLBACK",
|
|
5
5
|
doc: "Disable the loopback Desktop SSO fallback in development so isolated acceptance runs can use their configured local identity. Ignored in production.",
|
|
6
6
|
}),
|
|
7
|
+
// Deliberately optional rather than defaulted: unset means "derive from the
|
|
8
|
+
// deployment", which is not a boolean. A default here would erase the
|
|
9
|
+
// difference between an operator who chose a policy and one who never spoke.
|
|
10
|
+
requireEmailVerification: z.boolean().optional().meta({
|
|
11
|
+
env: "AUTH_REQUIRE_EMAIL_VERIFICATION",
|
|
12
|
+
doc: "Whether password signup must verify the email address before it gets a session. Unset derives it: hosted deployments require it, local development skips it. Setting it false accepts an unverified email as a login credential, including in production.",
|
|
13
|
+
}),
|
|
7
14
|
});
|
|
@@ -38,6 +38,7 @@ export declare const appConfigSchema: z.ZodObject<{
|
|
|
38
38
|
}, z.core.$strip>>;
|
|
39
39
|
auth: z.ZodPrefault<z.ZodObject<{
|
|
40
40
|
disableDesktopSsoFallbackInDevelopment: z.ZodDefault<z.ZodBoolean>;
|
|
41
|
+
requireEmailVerification: z.ZodOptional<z.ZodBoolean>;
|
|
41
42
|
}, z.core.$strip>>;
|
|
42
43
|
integrations: z.ZodPrefault<z.ZodObject<{
|
|
43
44
|
allowUnverifiedWebhooks: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -366,6 +366,14 @@ async function performActionFetch(name, method, params, options) {
|
|
|
366
366
|
}
|
|
367
367
|
const error = new Error(`Action ${name} failed: ${message}`);
|
|
368
368
|
error.status = res.status;
|
|
369
|
+
if (typeof data?.errorCode === "string") {
|
|
370
|
+
error.errorCode = data.errorCode;
|
|
371
|
+
}
|
|
372
|
+
if (data?.details &&
|
|
373
|
+
typeof data.details === "object" &&
|
|
374
|
+
!Array.isArray(data.details)) {
|
|
375
|
+
error.details = data.details;
|
|
376
|
+
}
|
|
369
377
|
throw error;
|
|
370
378
|
}
|
|
371
379
|
// 2xx but the body couldn't even be read (mid-stream abort, decode failure,
|
|
@@ -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
|
}>>;
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* Body: { json: any, fieldName?: string, type?: "map"|"array", requestSource?: string }
|
|
14
14
|
*/
|
|
15
15
|
export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
16
|
-
error: string;
|
|
17
16
|
ok?: undefined;
|
|
17
|
+
error: string;
|
|
18
18
|
} | {
|
|
19
19
|
error?: undefined;
|
|
20
20
|
ok: boolean;
|
|
@@ -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;
|
|
@@ -543,19 +543,23 @@ export function mountActionRoutes(nitroApp, actions, options) {
|
|
|
543
543
|
setResponseStatus(event, status);
|
|
544
544
|
// Only echo the raw message for known-safe cases:
|
|
545
545
|
// - validation errors (deterministic, parameter-shape only)
|
|
546
|
+
// - action contract errors (explicitly safe on every transport)
|
|
546
547
|
// - explicit user-facing errors (AgentActionStopError / fail())
|
|
547
548
|
// - errors with an explicit statusCode < 500 (client errors)
|
|
548
549
|
// For uncategorized 500s, return a generic message and keep the
|
|
549
550
|
// real detail server-side only — it can contain DB/driver/
|
|
550
551
|
// upstream text we must not leak to HTTP callers.
|
|
551
552
|
const isUserFacing = isValidationError ||
|
|
553
|
+
isActionContractError(err) ||
|
|
552
554
|
isAgentActionStopError(err) ||
|
|
553
555
|
(explicitStatus !== undefined && explicitStatus < 500);
|
|
554
556
|
if (isUserFacing) {
|
|
555
|
-
return isActionContractError(err)
|
|
557
|
+
return isActionContractError(err) || isAgentActionStopError(err)
|
|
556
558
|
? {
|
|
557
559
|
error: msg,
|
|
558
|
-
|
|
560
|
+
...(typeof err.errorCode === "string"
|
|
561
|
+
? { errorCode: err.errorCode }
|
|
562
|
+
: {}),
|
|
559
563
|
...(err.details === undefined
|
|
560
564
|
? {}
|
|
561
565
|
: { details: err.details }),
|
|
@@ -14,6 +14,7 @@ import { jwt } from "better-auth/plugins/jwt";
|
|
|
14
14
|
import { magicLink } from "better-auth/plugins/magic-link";
|
|
15
15
|
import { pgTable, text as pgText, timestamp as pgTimestamp, boolean as pgBoolean, } from "drizzle-orm/pg-core";
|
|
16
16
|
import { sqliteTable, text as sqliteText, integer as sqliteInteger, } from "drizzle-orm/sqlite-core";
|
|
17
|
+
import { getAppConfig } from "../app-config/index.js";
|
|
17
18
|
import { TEMPLATES } from "../cli/templates-meta.js";
|
|
18
19
|
import { getDbExec, isPostgres } from "../db/client.js";
|
|
19
20
|
import { getDialect, getCloudflareD1Binding, getDatabaseUrl, getDatabaseAuthToken, closePgliteClients, getPgliteClient, isPgliteUrl, loadPgliteDrizzle, pgPoolOptions, neonPoolOptions, guardNeonPool, sharedDbPool, onSharedDbPoolsClosed, onSharedDbPoolReplaced, prepareLocalSqliteUrl, sqliteFilenameFromUrl, retrySqliteBusy, } from "../db/client.js";
|
|
@@ -266,6 +267,19 @@ export function isDeployPreview() {
|
|
|
266
267
|
return deployContext === "deploy-preview";
|
|
267
268
|
}
|
|
268
269
|
export function resolveEmailPasswordAuthPolicy(emailConfigured) {
|
|
270
|
+
const declared = getAppConfig().auth.requireEmailVerification;
|
|
271
|
+
if (declared !== undefined) {
|
|
272
|
+
// A declared policy is the whole policy — it outranks both the hosted
|
|
273
|
+
// derivation below and AUTH_SKIP_EMAIL_VERIFICATION. Choosing `false` is
|
|
274
|
+
// choosing to accept an unverified email as a login credential, so the
|
|
275
|
+
// signup lock that exists to prevent exactly that comes off with it.
|
|
276
|
+
return {
|
|
277
|
+
requireEmailVerification: declared && emailConfigured,
|
|
278
|
+
// Verification that no provider can deliver would strand every new
|
|
279
|
+
// account on an unverifiable signup, so refuse the signup instead.
|
|
280
|
+
disableSignUp: declared && !emailConfigured,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
269
283
|
const hosted = process.env.NODE_ENV === "production" || isDeployPreview();
|
|
270
284
|
return {
|
|
271
285
|
requireEmailVerification: emailConfigured && (hosted || !shouldSkipEmailVerification()),
|
|
@@ -26,8 +26,8 @@ export declare function createRealtimeTokenHandler(): import("h3").EventHandlerW
|
|
|
26
26
|
expiresAt?: undefined;
|
|
27
27
|
ttlSeconds?: undefined;
|
|
28
28
|
} | {
|
|
29
|
+
error?: undefined;
|
|
29
30
|
token: string;
|
|
30
31
|
expiresAt: string;
|
|
31
32
|
ttlSeconds: number;
|
|
32
|
-
error?: undefined;
|
|
33
33
|
}>>;
|
package/dist/vite/client.js
CHANGED
|
@@ -2924,6 +2924,13 @@ function createAgentNativeConfig(options = {}, command, userConfig = {}, mode =
|
|
|
2924
2924
|
const forcePollingWatch = process.env.CHOKIDAR_USEPOLLING === "1";
|
|
2925
2925
|
const pollingWatchInterval = Number(process.env.CHOKIDAR_INTERVAL ?? 1000);
|
|
2926
2926
|
const userWatch = userConfig.server?.watch ?? {};
|
|
2927
|
+
// Vite 8 defines `rollupOptions` on `build`/`optimizeDeps` as a getter alias
|
|
2928
|
+
// of `rolldownOptions`. Spreading the section copies the alias as a plain own
|
|
2929
|
+
// property, so returning our own `rolldownOptions` alongside it makes the two
|
|
2930
|
+
// diverge and Vite warns that this plugin set both — then ignores the
|
|
2931
|
+
// `rollupOptions` half regardless. Drop the alias from what we spread back.
|
|
2932
|
+
const { rollupOptions: _buildRollupOptionsAlias, ...userBuild } = userConfig.build ?? {};
|
|
2933
|
+
const { rollupOptions: _depsRollupOptionsAlias, ...userOptimizeDeps } = userConfig.optimizeDeps ?? {};
|
|
2927
2934
|
return {
|
|
2928
2935
|
logLevel: options.logLevel ??
|
|
2929
2936
|
userConfig.logLevel ??
|
|
@@ -3013,7 +3020,7 @@ function createAgentNativeConfig(options = {}, command, userConfig = {}, mode =
|
|
|
3013
3020
|
},
|
|
3014
3021
|
},
|
|
3015
3022
|
build: {
|
|
3016
|
-
...
|
|
3023
|
+
...userBuild,
|
|
3017
3024
|
outDir: options.outDir ?? userConfig.build?.outDir ?? "dist/spa",
|
|
3018
3025
|
// Vite 8 defaults CSS minification to Lightning CSS, which collapses a
|
|
3019
3026
|
// `backdrop-filter` + `-webkit-backdrop-filter` pair down to only the
|
|
@@ -3096,7 +3103,7 @@ function createAgentNativeConfig(options = {}, command, userConfig = {}, mode =
|
|
|
3096
3103
|
],
|
|
3097
3104
|
},
|
|
3098
3105
|
optimizeDeps: {
|
|
3099
|
-
...
|
|
3106
|
+
...userOptimizeDeps,
|
|
3100
3107
|
include: [
|
|
3101
3108
|
...getDefaultOptimizeDeps(cwd),
|
|
3102
3109
|
...(hasDep("@agent-native/pinpoint", cwd)
|
|
@@ -210,6 +210,24 @@ verification and the signup verification email is not sent. It does not change
|
|
|
210
210
|
magic-link delivery. Use it only for QA or preview environments, and name test accounts with a `+qa` address
|
|
211
211
|
(`name+qa@example.com`) so they are easy to identify.
|
|
212
212
|
|
|
213
|
+
## Email Verification Policy {#email-verification-policy}
|
|
214
|
+
|
|
215
|
+
`AUTH_SKIP_EMAIL_VERIFICATION` is a local and QA convenience — a hosted
|
|
216
|
+
deployment ignores it. To state the policy for any environment, production
|
|
217
|
+
included, declare it in config:
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
defineAppConfig({ auth: { requireEmailVerification: false } });
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
The deployment alias is `AUTH_REQUIRE_EMAIL_VERIFICATION=0`. A declared value
|
|
224
|
+
outranks both `AUTH_SKIP_EMAIL_VERIFICATION` and the per-environment default.
|
|
225
|
+
|
|
226
|
+
`false` accepts an unverified address as a login credential — anyone can claim
|
|
227
|
+
any email — so use it only where signup is already restricted another way.
|
|
228
|
+
`true` on a deployment with no email provider disables password signup instead,
|
|
229
|
+
because the verification could never be delivered.
|
|
230
|
+
|
|
213
231
|
## Social Providers {#social-providers}
|
|
214
232
|
|
|
215
233
|
Set environment variables to enable social login. Better Auth auto-detects them:
|
|
@@ -477,27 +495,28 @@ The default `/_agent-native/google/auth-url` route does this automatically — o
|
|
|
477
495
|
|
|
478
496
|
## Environment Variables {#environment-variables}
|
|
479
497
|
|
|
480
|
-
| Variable | Purpose
|
|
481
|
-
| --------------------------------------- |
|
|
482
|
-
| `BETTER_AUTH_SECRET` | Signing key for Better Auth (auto-generated if not set)
|
|
483
|
-
| `AUTH_SKIP_EMAIL_VERIFICATION` | Set to `1` in QA/preview environments to let password-fallback signups proceed without verification; local dev/test skips by default
|
|
484
|
-
| `
|
|
485
|
-
| `
|
|
486
|
-
| `
|
|
487
|
-
| `
|
|
488
|
-
| `
|
|
489
|
-
| `
|
|
490
|
-
| `
|
|
491
|
-
| `
|
|
492
|
-
| `
|
|
493
|
-
| `
|
|
494
|
-
| `
|
|
495
|
-
| `
|
|
496
|
-
| `
|
|
497
|
-
| `
|
|
498
|
-
| `
|
|
499
|
-
| `
|
|
500
|
-
| `
|
|
498
|
+
| Variable | Purpose |
|
|
499
|
+
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
500
|
+
| `BETTER_AUTH_SECRET` | Signing key for Better Auth (auto-generated if not set) |
|
|
501
|
+
| `AUTH_SKIP_EMAIL_VERIFICATION` | Set to `1` in QA/preview environments to let password-fallback signups proceed without verification; local dev/test skips by default |
|
|
502
|
+
| `AUTH_REQUIRE_EMAIL_VERIFICATION` | Set to `0` or `1` to declare the password-signup verification policy for any environment, production included; outranks `AUTH_SKIP_EMAIL_VERIFICATION` |
|
|
503
|
+
| `AUTH_MAGIC_LINK` | Set to `0`, `false`, or `off` to force the email/password fallback even when outbound email is ready |
|
|
504
|
+
| `AUTH_DISABLED` | Set to `true` or `1` to skip login/signup; all requests run as one shared user (local dev/preview only — not for production with real users) |
|
|
505
|
+
| `AGENT_NATIVE_DISABLE_AUTO_DEV_ACCOUNT` | Set to `1` to disable localhost auto-sign-in on a fresh dev database |
|
|
506
|
+
| `AUTH_MODE` | `local` resolves CLI/agent identity only (which dev user `pnpm action` runs as); never a browser login bypass |
|
|
507
|
+
| `COOKIE_DOMAIN` | Opt into shared session cookies across same-database subdomains (see [Cookie Realms](#cookie-realms)) |
|
|
508
|
+
| `AGENT_NATIVE_WORKSPACE` | `1` runs in workspace mode — one shared session realm across workspace apps |
|
|
509
|
+
| `AGENT_NATIVE_SHARE_COOKIE_DOMAIN` | Set with `COOKIE_DOMAIN` to share one auth database across first-party subdomains |
|
|
510
|
+
| `OAUTH_STATE_SECRET` | Dedicated HMAC key for OAuth state envelopes (see [Security — OAuth State Signing](/docs/security#oauth-state)) |
|
|
511
|
+
| `GOOGLE_SIGN_IN_CLIENT_ID` | Preferred low-scope Google OAuth client ID for app login |
|
|
512
|
+
| `GOOGLE_SIGN_IN_CLIENT_SECRET` | Preferred low-scope Google OAuth secret for app login |
|
|
513
|
+
| `GOOGLE_CLIENT_ID` | Legacy Google login fallback, and provider OAuth client ID for Google API integrations |
|
|
514
|
+
| `GOOGLE_CLIENT_SECRET` | Legacy Google login fallback, and provider OAuth secret for Google API integrations |
|
|
515
|
+
| `GITHUB_CLIENT_ID` | Enable GitHub OAuth |
|
|
516
|
+
| `GITHUB_CLIENT_SECRET` | GitHub OAuth secret |
|
|
517
|
+
| `ACCESS_TOKEN` | Static bearer fallback for MCP/connect clients; not browser auth |
|
|
518
|
+
| `ACCESS_TOKENS` | Comma-separated static bearer fallbacks for MCP/connect clients; not browser auth |
|
|
519
|
+
| `A2A_SECRET` | Shared secret for JWT-signed A2A cross-app identity verification and, when present, MCP OAuth access-token signing |
|
|
501
520
|
|
|
502
521
|
## What's next
|
|
503
522
|
|
|
@@ -87,6 +87,7 @@ public app behavior, use the config paths above instead.
|
|
|
87
87
|
| `AUTH_DISABLED` | Disables auth for local or preview-only work. Unset already means false. | `true` or `1`; never use on a public production app. |
|
|
88
88
|
| `AUTH_MAGIC_LINK` | Controls the password-first versus magic-link sign-in path. | `0` keeps password-first sign-in; otherwise a configured email transport can enable magic links. |
|
|
89
89
|
| `AUTH_SKIP_EMAIL_VERIFICATION` | Skips signup email verification for local QA or previews. | `1`; never use it to weaken production auth. |
|
|
90
|
+
| `AUTH_REQUIRE_EMAIL_VERIFICATION` | Declares whether password signup must verify the email address, in any environment. | `0` or `1`; outranks `AUTH_SKIP_EMAIL_VERIFICATION`. `0` accepts an unverified address as a login credential. |
|
|
90
91
|
| `ACCESS_TOKEN` / `ACCESS_TOKENS` | Static bearer fallback for MCP and Connect clients. | One token or a delimited token list; these are not browser auth. |
|
|
91
92
|
| `AGENT_PROD_CODE_EXECUTION` | Production code-execution policy. | `off`, `sandboxed`, or `trusted`. See [Production Code Execution](/docs/actions-agent-tools#production-code-execution). |
|
|
92
93
|
| `AGENT_NATIVE_SSR_CACHE` | Deployment-wide public SSR shell cache policy. | Unset/`on`, `off`, or a duration such as `30s` or `5m`. See [SSR Caching](/docs/ssr-caching). |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.171.1",
|
|
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": {
|