@8wave/ai-elements 0.114.0-beta.30 → 0.114.0-beta.43

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.
@@ -1,5 +1,11 @@
1
1
  /**
2
2
  * Adds a user as member of an organization if not already present.
3
- * Shared by external-auth and organization-auto-join flows.
3
+ * Shared by external-auth and organization-auto-join flows. Only auto-join
4
+ * passes `respectMemberLimit`: the widget's external login must never fail
5
+ * because the organization is full. An automatic join does not take a seat
6
+ * an open invitation holds either, and a skipped one is not retried at the
7
+ * next sign-in: the person joins through an invitation once a seat is free.
4
8
  */
5
- export declare function addMemberToOrganizationIfNotExists(userId: string, organizationId: string, role?: string): Promise<boolean>;
9
+ export declare function addMemberToOrganizationIfNotExists(userId: string, organizationId: string, role?: string, { respectMemberLimit }?: {
10
+ respectMemberLimit?: boolean;
11
+ }): Promise<boolean>;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Whether an organization already holds as many members as the limit the
3
+ * system panel set on it. An organization with no limit is never full.
4
+ * With `countPendingInvitations` an open invitation holds a seat until it is
5
+ * accepted or expires, so a new one cannot be sent for a seat another
6
+ * invitation already promised. The count runs outside the write that
7
+ * follows, like better-auth's own check: two joins racing for the last seat
8
+ * can both pass.
9
+ */
10
+ export declare function isOrganizationFull(organizationId: string, { countPendingInvitations, }?: {
11
+ countPendingInvitations?: boolean;
12
+ }): Promise<boolean>;
@@ -0,0 +1,2 @@
1
+ import { BetterAuthPlugin } from 'better-auth';
2
+ export declare function twoFactorAdmin(): BetterAuthPlugin;
@@ -1,4 +1,44 @@
1
+ import { SignInSecurityState } from '../../models/src';
1
2
  import { BetterFetch } from 'better-auth/client';
3
+ /**
4
+ * What `GET /two-factor-policy/user-security` answers to a system
5
+ * administrator, see docs/specs/two-factor-system-assistance.md (D3). Dates
6
+ * arrive as ISO strings.
7
+ */
8
+ export interface UserSignInSecurity {
9
+ state: SignInSecurityState;
10
+ factor: {
11
+ enabled: boolean;
12
+ method: 'totp' | 'otp' | null;
13
+ backupCodesRemaining: number | null;
14
+ /** Wrong codes in a row since the last right one. */
15
+ failedAttempts: number;
16
+ /** Set only while the pause after too many wrong codes lasts. */
17
+ lockedUntil: string | null;
18
+ };
19
+ passkeys: {
20
+ id: string;
21
+ name: string | null;
22
+ /** `multiDevice` when synced across the user's devices. */
23
+ deviceType: string;
24
+ backedUp: boolean;
25
+ createdAt: string | null;
26
+ }[];
27
+ trustedDevices: {
28
+ count: number;
29
+ lastExpiresAt: string | null;
30
+ };
31
+ hasPassword: boolean;
32
+ requiredBy: {
33
+ id: string;
34
+ name: string;
35
+ }[];
36
+ /** The latest changes, from the notices the user received. */
37
+ changes: {
38
+ action: string;
39
+ createdAt: string;
40
+ }[];
41
+ }
2
42
  /** What `GET /two-factor-policy/status` answers. */
3
43
  export interface TwoFactorStatus {
4
44
  enabled: boolean;
@@ -19,8 +59,9 @@ export interface TwoFactorStatus {
19
59
  passkeys: number;
20
60
  }
21
61
  /**
22
- * Client counterpart of the server `twoFactorPolicy()` plugin, see
23
- * docs/specs/two-factor-authentication.md.
62
+ * Client counterpart of the server `twoFactorPolicy()` and `twoFactorAdmin()`
63
+ * plugins, see docs/specs/two-factor-authentication.md and
64
+ * docs/specs/two-factor-system-assistance.md.
24
65
  */
25
66
  export declare function twoFactorPolicyClient(): {
26
67
  id: "two-factor-policy";
@@ -52,9 +93,71 @@ export declare function twoFactorPolicyClient(): {
52
93
  };
53
94
  error: null;
54
95
  }>;
96
+ userSecurity: (query: {
97
+ userId: string;
98
+ }) => Promise<{
99
+ data: null;
100
+ error: {
101
+ message?: string | undefined;
102
+ status: number;
103
+ statusText: string;
104
+ };
105
+ } | {
106
+ data: UserSignInSecurity;
107
+ error: null;
108
+ }>;
109
+ unlock: (data: {
110
+ userId: string;
111
+ }) => Promise<{
112
+ data: null;
113
+ error: {
114
+ message?: string | undefined;
115
+ status: number;
116
+ statusText: string;
117
+ };
118
+ } | {
119
+ data: {
120
+ status: boolean;
121
+ };
122
+ error: null;
123
+ }>;
124
+ removePasskey: (data: {
125
+ userId: string;
126
+ passkeyId: string;
127
+ }) => Promise<{
128
+ data: null;
129
+ error: {
130
+ message?: string | undefined;
131
+ status: number;
132
+ statusText: string;
133
+ };
134
+ } | {
135
+ data: {
136
+ status: boolean;
137
+ };
138
+ error: null;
139
+ }>;
140
+ forgetTrustedDevices: (data: {
141
+ userId: string;
142
+ }) => Promise<{
143
+ data: null;
144
+ error: {
145
+ message?: string | undefined;
146
+ status: number;
147
+ statusText: string;
148
+ };
149
+ } | {
150
+ data: {
151
+ status: boolean;
152
+ };
153
+ error: null;
154
+ }>;
55
155
  };
56
156
  };
57
157
  pathMethods: {
58
158
  '/two-factor-policy/reset': "POST";
159
+ '/two-factor-policy/unlock': "POST";
160
+ '/two-factor-policy/remove-passkey': "POST";
161
+ '/two-factor-policy/forget-trusted-devices': "POST";
59
162
  };
60
163
  };
@@ -1,5 +1,6 @@
1
1
  import { BetterAuthPlugin } from 'better-auth';
2
2
  import { createAuthMiddleware } from 'better-auth/api';
3
+ import { symmetricDecrypt } from 'better-auth/crypto';
3
4
  import { TwoFactorChange } from 'email';
4
5
  /**
5
6
  * Carries the challenge for a client that cannot hold the cookie: the widget,
@@ -9,6 +10,14 @@ import { TwoFactorChange } from 'email';
9
10
  export declare const TWO_FACTOR_TOKEN_HEADER = "x-two-factor-token";
10
11
  /** Time the code sent by email stays valid, in minutes. */
11
12
  export declare const TWO_FACTOR_OTP_PERIOD_MINUTES = 5;
13
+ export type TwoFactorRow = {
14
+ id: string;
15
+ userId: string;
16
+ backupCodes: string;
17
+ verified?: boolean | null;
18
+ failedVerificationCount?: number | null;
19
+ lockedUntil?: Date | string | null;
20
+ };
12
21
  type ChangedUser = {
13
22
  id: string;
14
23
  email: string;
@@ -21,6 +30,37 @@ export type MiddlewareContext = Parameters<Parameters<typeof createAuthMiddlewar
21
30
  * written.
22
31
  */
23
32
  export declare function announceTwoFactorChange(user: ChangedUser, change: TwoFactorChange): Promise<void>;
33
+ /**
34
+ * The verification rows that remember a device on which the user chose to
35
+ * skip the code: the `twoFactor` plugin names them `trust-device-<id>` and
36
+ * stores the user id as their value.
37
+ */
38
+ export declare function trustedDeviceWhere(userId: string): ({
39
+ field: string;
40
+ value: string;
41
+ operator?: undefined;
42
+ } | {
43
+ field: string;
44
+ operator: "starts_with";
45
+ value: string;
46
+ })[];
47
+ /**
48
+ * Forgets every device on which the user chose to skip the code. They are
49
+ * verification rows holding the user id: left in place, one would skip the
50
+ * code once the factor is on again, on a device that may be the lost one.
51
+ * The plugin itself forgets only the device the factor is turned off from.
52
+ */
53
+ export declare function forgetTrustedDevices(adapter: MiddlewareContext['context']['adapter'], userId: string): Promise<number>;
54
+ /**
55
+ * How many recovery codes the user has left, or null when it cannot be told.
56
+ * better-auth keeps them encrypted with its secret, so only the count leaves
57
+ * this function, never a code.
58
+ */
59
+ export declare function countBackupCodes(ctx: {
60
+ context: {
61
+ secretConfig: Parameters<typeof symmetricDecrypt>[0]['key'];
62
+ };
63
+ }, row: Pick<TwoFactorRow, 'backupCodes'>): Promise<number | null>;
24
64
  type PolicyOrganization = {
25
65
  requireTwoFactor?: boolean | null;
26
66
  deleted?: boolean | null;
@@ -54,8 +94,9 @@ export declare function isTwoFactorSetupRequiredForSession(input: {
54
94
  organizations: readonly PolicyOrganization[] | (() => Promise<readonly PolicyOrganization[]>);
55
95
  }): Promise<boolean>;
56
96
  /**
57
- * The status endpoint, the administrator reset, the widget bridge and the
58
- * announcements of docs/specs/two-factor-authentication.md.
97
+ * The status endpoint, the widget bridge and the announcements of
98
+ * docs/specs/two-factor-authentication.md. The administrator's endpoints are
99
+ * in two-factor-admin-plugin.ts.
59
100
  *
60
101
  * The endpoints live under `/two-factor-policy/` and not `/two-factor/` on
61
102
  * purpose: the `twoFactor` plugin rate limits its prefix to three requests
@@ -9,7 +9,7 @@ export declare const LeadSchema: z.ZodObject<{
9
9
  phone: z.ZodNullable<z.ZodString>;
10
10
  agentId: z.ZodString;
11
11
  organizationId: z.ZodString;
12
- chatId: z.ZodString;
12
+ chatId: z.ZodNullable<z.ZodString>;
13
13
  metadata: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
14
14
  createdAt: z.ZodCoercedDate<unknown>;
15
15
  createdBy: z.ZodString;
@@ -122,6 +122,13 @@ export declare const MemberSchema: z.ZodObject<{
122
122
  twoFactorEnabled: z.ZodOptional<z.ZodBoolean>;
123
123
  hasPassword: z.ZodOptional<z.ZodBoolean>;
124
124
  hasPasskey: z.ZodOptional<z.ZodBoolean>;
125
+ signInSecurity: z.ZodOptional<z.ZodEnum<{
126
+ required: "required";
127
+ none: "none";
128
+ totp: "totp";
129
+ otp: "otp";
130
+ passkey: "passkey";
131
+ }>>;
125
132
  }, z.core.$strip>;
126
133
  }, z.core.$strip>;
127
134
  export type Member = z.infer<typeof MemberSchema>;
@@ -17,6 +17,31 @@ export declare const NotificationType: {
17
17
  readonly Generic: "generic";
18
18
  };
19
19
  export type NotificationType = (typeof NotificationType)[keyof typeof NotificationType];
20
+ /**
21
+ * Group key of the admins' notification about a flagged conversation: one row
22
+ * per chat and admin, bumped by every later analysis of that chat.
23
+ */
24
+ export declare const chatAnalysisNotificationGroupKey: (chatId: string) => string;
25
+ /**
26
+ * Title of the admins' notification about a flagged conversation. It tells a
27
+ * block from a warning on a stored row better than `type` does: a group bump
28
+ * rewrites the title, the body and the metadata, never the type, so a chat
29
+ * warned first and blocked later keeps `type = chat_warning`.
30
+ */
31
+ export declare const chatAnalysisNotificationTitle: (blocked: boolean) => string;
32
+ /**
33
+ * Body of the admins' notification about a flagged conversation
34
+ * (`chat_warning` or `chat_blocked`). The writer quotes the chat's title; the
35
+ * GDPR erasure (`eraseConversationRows`) rebuilds the body without it, from the
36
+ * same sentences, so the notification and its link to the chat survive with
37
+ * nothing the user wrote. Migration `erase_chat_residuals` holds a SQL copy
38
+ * of these sentences for the rows erased before it.
39
+ */
40
+ export declare function chatAnalysisNotificationBody({ blocked, riskScore, chatTitle, }: {
41
+ blocked: boolean;
42
+ riskScore?: number;
43
+ chatTitle?: string;
44
+ }): string;
20
45
  export declare const NotificationSchema: z.ZodObject<{
21
46
  id: z.ZodString;
22
47
  organizationId: z.ZodNullable<z.ZodString>;
@@ -222,6 +222,16 @@ export declare const OrganizationRetentionPolicySchema: z.ZodObject<{
222
222
  anonymousRetentionDays: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
223
223
  }, z.core.$strip>;
224
224
  export type OrganizationRetentionPolicy = z.infer<typeof OrganizationRetentionPolicySchema>;
225
+ /**
226
+ * Per-organization limits an operator sets from the system panel, persisted
227
+ * inside the `organization.metadata` text column. `members` counts every
228
+ * member, the ones the widget's external login brings in included; absent or
229
+ * `null` means no limit.
230
+ */
231
+ export declare const OrganizationLimitsSchema: z.ZodObject<{
232
+ members: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
233
+ }, z.core.$strip>;
234
+ export type OrganizationLimits = z.infer<typeof OrganizationLimitsSchema>;
225
235
  /**
226
236
  * Product features an operator can switch on or off per organization from the
227
237
  * system panel. A feature is listed here when a contract can plausibly exclude
@@ -358,6 +368,9 @@ export declare const OrganizationMetadataSchema: z.ZodObject<{
358
368
  utilityModel: z.ZodOptional<z.ZodString>;
359
369
  disabledGatewayModels: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
360
370
  }, z.core.$strip>>>;
371
+ limits: z.ZodCatch<z.ZodOptional<z.ZodObject<{
372
+ members: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
373
+ }, z.core.$strip>>>;
361
374
  }, z.core.$loose>;
362
375
  export type OrganizationMetadata = z.infer<typeof OrganizationMetadataSchema>;
363
376
  /**
@@ -382,6 +395,12 @@ export declare function parseOrganizationMetadata(metadata: string | Record<stri
382
395
  * malformed metadata resolves to the platform defaults instead of failing.
383
396
  */
384
397
  export declare function resolveOrganizationFeatures(metadata: string | Record<string, unknown> | null | undefined): OrganizationFeatures;
398
+ /**
399
+ * The most members an organization may hold, from its raw `metadata` value,
400
+ * or `null` when it sets no limit. A malformed value reads as no limit, so a
401
+ * bad write can never lock an organization out of adding people.
402
+ */
403
+ export declare function resolveOrganizationMemberLimit(metadata: string | Record<string, unknown> | null | undefined): number | null;
385
404
  export declare const OrganizationPublicSchema: z.ZodObject<{
386
405
  id: z.ZodString;
387
406
  name: z.ZodString;
@@ -0,0 +1,168 @@
1
+ import { StoragePolicy } from './Media';
2
+ import * as z from 'zod';
3
+ /**
4
+ * Where a user stands on two-step verification and passkeys, as the system
5
+ * panel shows it (docs/specs/two-factor-system-assistance.md, D2). One state
6
+ * per user, decided in this order: the authenticator app, the email code, a
7
+ * passkey alone, then `required` for a member of an organization that asks
8
+ * for a second factor who signs in with a password and has none.
9
+ */
10
+ export declare const SignInSecurityStateSchema: z.ZodEnum<{
11
+ required: "required";
12
+ none: "none";
13
+ totp: "totp";
14
+ otp: "otp";
15
+ passkey: "passkey";
16
+ }>;
17
+ export type SignInSecurityState = z.infer<typeof SignInSecurityStateSchema>;
18
+ /** A row of the system users list. */
19
+ export declare const SignInSecurityUserSchema: z.ZodObject<{
20
+ id: z.ZodString;
21
+ name: z.ZodString;
22
+ email: z.ZodString;
23
+ emailVerified: z.ZodBoolean;
24
+ image: z.ZodNullable<z.ZodString>;
25
+ media: z.ZodNullable<z.ZodOptional<z.ZodObject<{
26
+ name: z.ZodString;
27
+ src: z.ZodObject<{
28
+ type: z.ZodString;
29
+ name: z.ZodString;
30
+ mimeType: z.ZodString;
31
+ bytesCount: z.ZodNumber;
32
+ extension: z.ZodString;
33
+ metadata: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
34
+ tags: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
35
+ storageHandle: z.ZodDiscriminatedUnion<[z.ZodObject<{
36
+ type: z.ZodLiteral<"azure-blob">;
37
+ blobUri: z.ZodURL;
38
+ containerName: z.ZodString;
39
+ pathRelativeToContainer: z.ZodString;
40
+ }, z.core.$strip>, z.ZodObject<{
41
+ type: z.ZodLiteral<"s3">;
42
+ bucket: z.ZodString;
43
+ key: z.ZodString;
44
+ region: z.ZodString;
45
+ }, z.core.$strip>], "type">;
46
+ storagePolicy: z.ZodEnum<typeof StoragePolicy>;
47
+ signData: z.ZodOptional<z.ZodString>;
48
+ storedFileName: z.ZodString;
49
+ }, z.core.$strip>;
50
+ srcSet: z.ZodDefault<z.ZodArray<z.ZodObject<{
51
+ type: z.ZodString;
52
+ name: z.ZodString;
53
+ mimeType: z.ZodString;
54
+ bytesCount: z.ZodNumber;
55
+ extension: z.ZodString;
56
+ metadata: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
57
+ tags: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
58
+ storageHandle: z.ZodDiscriminatedUnion<[z.ZodObject<{
59
+ type: z.ZodLiteral<"azure-blob">;
60
+ blobUri: z.ZodURL;
61
+ containerName: z.ZodString;
62
+ pathRelativeToContainer: z.ZodString;
63
+ }, z.core.$strip>, z.ZodObject<{
64
+ type: z.ZodLiteral<"s3">;
65
+ bucket: z.ZodString;
66
+ key: z.ZodString;
67
+ region: z.ZodString;
68
+ }, z.core.$strip>], "type">;
69
+ storagePolicy: z.ZodEnum<typeof StoragePolicy>;
70
+ signData: z.ZodOptional<z.ZodString>;
71
+ storedFileName: z.ZodString;
72
+ }, z.core.$strip>>>;
73
+ rawOptions: z.ZodOptional<z.ZodCustom<{
74
+ type: import('./Media').MediaType.picture;
75
+ width: number;
76
+ height: number;
77
+ blurhash?: string | undefined;
78
+ } | {
79
+ type: import('./Media').MediaType.video;
80
+ width: number;
81
+ height: number;
82
+ duration: number;
83
+ } | {
84
+ type: import('./Media').MediaType.audio;
85
+ duration: number;
86
+ } | {
87
+ type: import('./Media').MediaType.office;
88
+ } | {
89
+ type: import('./Media').MediaType.archive;
90
+ } | {
91
+ type: import('./Media').MediaType.binary;
92
+ } | {
93
+ type: import('./Media').MediaType.executable;
94
+ }, {
95
+ type: import('./Media').MediaType.picture;
96
+ width: number;
97
+ height: number;
98
+ blurhash?: string | undefined;
99
+ } | {
100
+ type: import('./Media').MediaType.video;
101
+ width: number;
102
+ height: number;
103
+ duration: number;
104
+ } | {
105
+ type: import('./Media').MediaType.audio;
106
+ duration: number;
107
+ } | {
108
+ type: import('./Media').MediaType.office;
109
+ } | {
110
+ type: import('./Media').MediaType.archive;
111
+ } | {
112
+ type: import('./Media').MediaType.binary;
113
+ } | {
114
+ type: import('./Media').MediaType.executable;
115
+ }>>;
116
+ }, z.core.$strip>>>;
117
+ createdAt: z.ZodCoercedDate<unknown>;
118
+ updatedAt: z.ZodCoercedDate<unknown>;
119
+ banned: z.ZodNullable<z.ZodBoolean>;
120
+ banReason: z.ZodNullable<z.ZodString>;
121
+ banExpires: z.ZodNullable<z.ZodCoercedDate<unknown>>;
122
+ isAnonymous: z.ZodNullable<z.ZodBoolean>;
123
+ role: z.ZodEnum<{
124
+ user: "user";
125
+ admin: "admin";
126
+ }>;
127
+ twoFactorEnabled: z.ZodOptional<z.ZodBoolean>;
128
+ signInSecurity: z.ZodEnum<{
129
+ required: "required";
130
+ none: "none";
131
+ totp: "totp";
132
+ otp: "otp";
133
+ passkey: "passkey";
134
+ }>;
135
+ }, z.core.$strip>;
136
+ export type SignInSecurityUser = z.infer<typeof SignInSecurityUserSchema>;
137
+ export declare const SignInSecurityUsersQuerystringSchema: z.ZodObject<{
138
+ signInSecurity: z.ZodOptional<z.ZodEnum<{
139
+ required: "required";
140
+ none: "none";
141
+ totp: "totp";
142
+ otp: "otp";
143
+ passkey: "passkey";
144
+ }>>;
145
+ "filter[fullText]": z.ZodOptional<z.ZodString>;
146
+ sort: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
147
+ name: "name";
148
+ createdAt: "createdAt";
149
+ updatedAt: "updatedAt";
150
+ "-name": "-name";
151
+ "-createdAt": "-createdAt";
152
+ "-updatedAt": "-updatedAt";
153
+ email: "email";
154
+ "-email": "-email";
155
+ }>>>;
156
+ 'page[number]': z.ZodDefault<z.ZodPipe<z.ZodUnion<[z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
157
+ 'page[size]': z.ZodDefault<z.ZodPipe<z.ZodUnion<[z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
158
+ }, z.core.$strip>;
159
+ export type SignInSecurityUsersQuerystring = z.infer<typeof SignInSecurityUsersQuerystringSchema>;
160
+ /** How many users are in each state. */
161
+ export declare const SignInSecuritySummarySchema: z.ZodRecord<z.ZodEnum<{
162
+ required: "required";
163
+ none: "none";
164
+ totp: "totp";
165
+ otp: "otp";
166
+ passkey: "passkey";
167
+ }>, z.ZodNumber>;
168
+ export type SignInSecuritySummary = z.infer<typeof SignInSecuritySummarySchema>;
@@ -33,6 +33,7 @@ export * from './Integration';
33
33
  export * from './Platform';
34
34
  export * from './ReasoningChat';
35
35
  export * from './RevisedAnswer';
36
+ export * from './SignInSecurity';
36
37
  export * from './StreamError';
37
38
  export * from './SubAgent';
38
39
  export * from './TimingLog';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@8wave/ai-elements",
3
3
  "private": false,
4
- "version": "0.114.0-beta.30",
4
+ "version": "0.114.0-beta.43",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "vite --port 8082 --open --host",