@agent-native/core 0.161.2 → 0.161.4

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.
@@ -36,6 +36,8 @@ export declare class EngineError extends Error {
36
36
  * one-shot trim-and-retry recovery.
37
37
  */
38
38
  readonly contextOverflow?: boolean;
39
+ /** Sizes and counts of the failed request; see {@link EngineRequestShape}. */
40
+ readonly requestShape?: EngineRequestShape;
39
41
  constructor(message: string, opts?: {
40
42
  errorCode?: string;
41
43
  upgradeUrl?: string;
@@ -43,6 +45,7 @@ export declare class EngineError extends Error {
43
45
  providerRetryable?: boolean;
44
46
  requestId?: string;
45
47
  contextOverflow?: boolean;
48
+ requestShape?: EngineRequestShape;
46
49
  });
47
50
  }
48
51
  /**
@@ -213,7 +216,23 @@ export type EngineEvent = {
213
216
  * the time the agent decides whether to trim and retry.
214
217
  */
215
218
  contextOverflow?: boolean;
219
+ /**
220
+ * Sizes and counts of the request that failed. Never prompt or user
221
+ * content — the point is to make "what did we send" answerable from a
222
+ * capture, which an opaque gateway 500 otherwise leaves unanswerable.
223
+ */
224
+ requestShape?: EngineRequestShape;
216
225
  };
226
+ /**
227
+ * Shape-only description of what an engine put on the wire. Every field is a
228
+ * size, a count, or a model id, so it is safe to attach to an error capture.
229
+ */
230
+ export interface EngineRequestShape {
231
+ model: string;
232
+ payloadBytes: number;
233
+ toolCount: number;
234
+ messageCount: number;
235
+ }
217
236
  export interface EngineCapabilities {
218
237
  /** Extended / adaptive thinking support */
219
238
  thinking: boolean;
@@ -35,6 +35,8 @@ export class EngineError extends Error {
35
35
  * one-shot trim-and-retry recovery.
36
36
  */
37
37
  contextOverflow;
38
+ /** Sizes and counts of the failed request; see {@link EngineRequestShape}. */
39
+ requestShape;
38
40
  constructor(message, opts) {
39
41
  super(message);
40
42
  this.name = "EngineError";
@@ -44,5 +46,6 @@ export class EngineError extends Error {
44
46
  this.providerRetryable = opts?.providerRetryable;
45
47
  this.requestId = opts?.requestId;
46
48
  this.contextOverflow = opts?.contextOverflow;
49
+ this.requestShape = opts?.requestShape;
47
50
  }
48
51
  }
@@ -3753,6 +3753,7 @@ export async function runAgentLoop(opts) {
3753
3753
  providerRetryable: event.providerRetryable,
3754
3754
  contextOverflow: event.contextOverflow,
3755
3755
  requestId: event.requestId,
3756
+ requestShape: event.requestShape,
3756
3757
  });
3757
3758
  }
3758
3759
  }
@@ -1,3 +1,4 @@
1
+ import type { EngineRequestShape } from "./engine/types.js";
1
2
  import type { AgentChatEvent, RunEvent, RunStatus } from "./types.js";
2
3
  export interface ActiveRun {
3
4
  runId: string;
@@ -233,6 +234,11 @@ export declare function resolveSqlSubscriptionPollMs(now: number, activePollUnti
233
234
  */
234
235
  export declare function nextSqlSubscriptionEmptyPolls(current: number, hadEvents: boolean, now: number, activePollUntil: number): number;
235
236
  export declare function resolveSqlSubscriptionRetryMs(consecutiveFailures: number): number;
237
+ /**
238
+ * Sentry tags are strings, and an absent shape must stay absent: a run that
239
+ * failed before the request was built did not send a zero-byte payload.
240
+ */
241
+ export declare function engineRequestShapeTags(shape: EngineRequestShape | undefined): Record<string, string>;
236
242
  export interface StartRunOptions {
237
243
  /** Keep a request-scoped serverless invocation alive for this run. */
238
244
  waitUntil?: (promise: Promise<unknown>) => void;
@@ -299,6 +299,20 @@ function getRunErrorCode(err) {
299
299
  // only when the run row is persisted.
300
300
  return classifyTerminalErrorCode(describeErrorWithCauses(err));
301
301
  }
302
+ /**
303
+ * Sentry tags are strings, and an absent shape must stay absent: a run that
304
+ * failed before the request was built did not send a zero-byte payload.
305
+ */
306
+ export function engineRequestShapeTags(shape) {
307
+ if (!shape)
308
+ return {};
309
+ return {
310
+ engineModel: shape.model,
311
+ enginePayloadBytes: String(shape.payloadBytes),
312
+ engineToolCount: String(shape.toolCount),
313
+ engineMessageCount: String(shape.messageCount),
314
+ };
315
+ }
302
316
  function getEngineRunErrorDetails(err) {
303
317
  if (err.statusCode === 429)
304
318
  return err.message;
@@ -1078,6 +1092,11 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
1078
1092
  statusCode: engineError?.statusCode != null
1079
1093
  ? String(engineError.statusCode)
1080
1094
  : undefined,
1095
+ // What we sent, in sizes and counts only. A gateway rejection describes
1096
+ // nothing about the request behind it, so without these an oversized
1097
+ // payload and an upstream outage produce the same capture — which is
1098
+ // how one gateway 500 cost a night of guessing.
1099
+ ...engineRequestShapeTags(engineError?.requestShape),
1081
1100
  },
1082
1101
  extra: {
1083
1102
  runId,
@@ -715,11 +715,11 @@ export declare function getRunOutcomeCounters(options?: {
715
715
  terminalReason: string;
716
716
  count: number;
717
717
  }>>;
718
- /** Delete old runs and expire stale "running" rows that haven't had activity
719
- * (e.g. worker crashed before updating status). Genuinely completed runs are
720
- * pruned at `olderThanMs`; errored/aborted/truncated runs are kept until
721
- * `erroredOlderThanMs` (a longer window, falling back to `olderThanMs`) so
722
- * their event log survives for cut-off pattern analysis via listErroredRuns. */
718
+ /**
719
+ * Run cleanup is scheduled after every completed run, including completions
720
+ * from several concurrent requests in one isolate. Share one sweep locally;
721
+ * Postgres additionally serializes the durable prune across isolates.
722
+ */
723
723
  export declare function cleanupOldRuns(olderThanMs: number, erroredOlderThanMs?: number): Promise<void>;
724
724
  /**
725
725
  * List recent unsuccessful runs (errored, aborted, and truncated) for cut-off
@@ -2357,6 +2357,8 @@ const RUN_OUTCOME_DAY_MS = 86_400_000;
2357
2357
  * every run id a user pasted into a bug report was gone before anyone looked.
2358
2358
  */
2359
2359
  const UNSUCCESSFUL_STATUS_SQL_LIST = `('errored', 'aborted', 'truncated')`;
2360
+ const RUN_OUTCOME_PRUNE_BATCH_LIMIT = 200;
2361
+ const RUN_OUTCOME_PRUNE_LOCK_KEY = "agent-native:run-outcome-prune";
2360
2362
  /**
2361
2363
  * Fold the terminal outcomes of the rows `cleanupOldRuns` is about to delete
2362
2364
  * into `agent_run_outcome_daily`, so success/failure RATES survive pruning even
@@ -2364,29 +2366,46 @@ const UNSUCCESSFUL_STATUS_SQL_LIST = `('errored', 'aborted', 'truncated')`;
2364
2366
  * covers exactly the unpruned ones, so a rate over any window is
2365
2367
  * `getRunOutcomeCounters()` plus the live rows — no gap, no double count.
2366
2368
  *
2367
- * The DELETE ... RETURNING is the claim: concurrent cleanup calls can both
2368
- * observe a row, but only the caller that deletes it receives it to roll up.
2369
- * Grouping the returned rows in TypeScript avoids dialect-specific date SQL.
2369
+ * Postgres callers take a transaction-scoped advisory lease before the claim.
2370
+ * The bounded DELETE ... RETURNING is still the source of truth for which rows
2371
+ * this invocation owns. Grouping the returned rows in TypeScript avoids
2372
+ * dialect-specific date SQL.
2370
2373
  * Counter upserts run in the same transaction as the delete; a failed upsert
2371
2374
  * rolls back the claim so the source rows remain available for a retry.
2372
2375
  */
2373
2376
  async function pruneAndRollUpPrunedRunOutcomes(client, cutoff, erroredCutoff) {
2374
2377
  const prune = async (tx) => {
2375
- await tx.execute({
2376
- sql: `DELETE FROM agent_run_events WHERE run_id IN (
2377
- SELECT id FROM agent_runs
2378
- WHERE (status = 'completed' AND completed_at < ?)
2379
- OR (status IN ${UNSUCCESSFUL_STATUS_SQL_LIST} AND completed_at < ?)
2380
- )`,
2381
- args: [cutoff, erroredCutoff],
2382
- });
2378
+ if (isPostgres()) {
2379
+ const lockResult = await tx.execute({
2380
+ sql: "SELECT pg_try_advisory_xact_lock(hashtextextended(?, 0::bigint)) AS acquired",
2381
+ args: [RUN_OUTCOME_PRUNE_LOCK_KEY],
2382
+ });
2383
+ const acquired = lockResult.rows[0]?.acquired;
2384
+ if (acquired !== true && acquired !== "t")
2385
+ return;
2386
+ }
2383
2387
  const { rows } = await tx.execute({
2384
2388
  sql: `DELETE FROM agent_runs
2385
- WHERE (status = 'completed' AND completed_at < ?)
2386
- OR (status IN ${UNSUCCESSFUL_STATUS_SQL_LIST} AND completed_at < ?)
2387
- RETURNING status, completed_at, terminal_reason`,
2389
+ WHERE id IN (
2390
+ SELECT id FROM agent_runs
2391
+ WHERE (status = 'completed' AND completed_at < ?)
2392
+ OR (status IN ${UNSUCCESSFUL_STATUS_SQL_LIST} AND completed_at < ?)
2393
+ ORDER BY completed_at ASC, id ASC
2394
+ LIMIT ${RUN_OUTCOME_PRUNE_BATCH_LIMIT}
2395
+ )
2396
+ RETURNING id, status, completed_at, terminal_reason`,
2388
2397
  args: [cutoff, erroredCutoff],
2389
2398
  });
2399
+ const runIds = rows
2400
+ .map((row) => row.id)
2401
+ .filter((id) => typeof id === "string");
2402
+ if (runIds.length > 0) {
2403
+ const placeholders = runIds.map(() => "?").join(", ");
2404
+ await tx.execute({
2405
+ sql: `DELETE FROM agent_run_events WHERE run_id IN (${placeholders})`,
2406
+ args: runIds,
2407
+ });
2408
+ }
2390
2409
  const groups = new Map();
2391
2410
  for (const row of rows) {
2392
2411
  const outcome = row;
@@ -2470,7 +2489,8 @@ export async function getRunOutcomeCounters(options) {
2470
2489
  * pruned at `olderThanMs`; errored/aborted/truncated runs are kept until
2471
2490
  * `erroredOlderThanMs` (a longer window, falling back to `olderThanMs`) so
2472
2491
  * their event log survives for cut-off pattern analysis via listErroredRuns. */
2473
- export async function cleanupOldRuns(olderThanMs, erroredOlderThanMs) {
2492
+ let cleanupOldRunsInFlight;
2493
+ async function cleanupOldRunsInternal(olderThanMs, erroredOlderThanMs) {
2474
2494
  await ensureRunTables();
2475
2495
  const client = getDbExec();
2476
2496
  const cutoff = Date.now() - olderThanMs;
@@ -2567,6 +2587,23 @@ export async function cleanupOldRuns(olderThanMs, erroredOlderThanMs) {
2567
2587
  // counting the same source rows.
2568
2588
  await pruneAndRollUpPrunedRunOutcomes(client, cutoff, erroredCutoff);
2569
2589
  }
2590
+ /**
2591
+ * Run cleanup is scheduled after every completed run, including completions
2592
+ * from several concurrent requests in one isolate. Share one sweep locally;
2593
+ * Postgres additionally serializes the durable prune across isolates.
2594
+ */
2595
+ export function cleanupOldRuns(olderThanMs, erroredOlderThanMs) {
2596
+ if (cleanupOldRunsInFlight)
2597
+ return cleanupOldRunsInFlight;
2598
+ const current = cleanupOldRunsInternal(olderThanMs, erroredOlderThanMs);
2599
+ let settled;
2600
+ settled = current.finally(() => {
2601
+ if (cleanupOldRunsInFlight === settled)
2602
+ cleanupOldRunsInFlight = undefined;
2603
+ });
2604
+ cleanupOldRunsInFlight = settled;
2605
+ return settled;
2606
+ }
2570
2607
  /**
2571
2608
  * List recent unsuccessful runs (errored, aborted, and truncated) for cut-off
2572
2609
  * pattern analysis. Read-only, bounded, and ordered newest-first. Surfaced via
@@ -14,6 +14,7 @@ import { type ContentPart } from "./sse-event-processor.js";
14
14
  import type { ChatThreadScope, ChatThreadSnapshot } from "./use-chat-threads.js";
15
15
  export { AssistantMessageListErrorBoundary, AssistantUiStaleIndexErrorBoundary, assistantUiRecoverableRenderErrorKind, isAssistantUiRecoverableRenderError, isAssistantUiStaleIndexError, } from "./assistant-ui-recovery.js";
16
16
  export { displayableUserMessageText } from "./chat/message-components.js";
17
+ export declare function shouldSuppressUnauthenticatedDesktopThreadRestore(surface: AgentChatSurfaceKind, status: number): boolean;
17
18
  type AssistantUiMessageResourceShape = {
18
19
  id: string;
19
20
  content: readonly unknown[];
@@ -44,6 +44,11 @@ import { useRunStuckDetection } from "./use-run-stuck-detection.js";
44
44
  import { cn } from "./utils.js";
45
45
  export { AssistantMessageListErrorBoundary, AssistantUiStaleIndexErrorBoundary, assistantUiRecoverableRenderErrorKind, isAssistantUiRecoverableRenderError, isAssistantUiStaleIndexError, } from "./assistant-ui-recovery.js";
46
46
  export { displayableUserMessageText } from "./chat/message-components.js";
47
+ // Desktop chat mounts beside the parent identity gate, so an unauthenticated
48
+ // relay is an expected empty state until that gate establishes a session.
49
+ export function shouldSuppressUnauthenticatedDesktopThreadRestore(surface, status) {
50
+ return surface === "desktop" && (status === 401 || status === 403);
51
+ }
47
52
  const useBrowserLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
48
53
  export function assistantUiMessageListStructureKey(messages) {
49
54
  return JSON.stringify(messages.map((message) => [
@@ -2654,7 +2659,11 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2654
2659
  const res = await fetch(`${apiUrl}/threads/${encodeURIComponent(threadId)}`);
2655
2660
  if (!res.ok) {
2656
2661
  if (!cancelled) {
2657
- setThreadRestoreError(res.status === 404 ? "not-found" : "unavailable");
2662
+ setThreadRestoreError(shouldSuppressUnauthenticatedDesktopThreadRestore(agentChatSurface, res.status)
2663
+ ? null
2664
+ : res.status === 404
2665
+ ? "not-found"
2666
+ : "unavailable");
2658
2667
  }
2659
2668
  return;
2660
2669
  }
@@ -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
- ok?: undefined;
17
16
  error: string;
17
+ ok?: undefined;
18
18
  } | {
19
19
  error?: undefined;
20
20
  ok: boolean;
package/dist/db/client.js CHANGED
@@ -1258,7 +1258,12 @@ async function createDbExecInternal(config = {}, trackSingletonResources = false
1258
1258
  const { rawSql, args } = sqlAndArgs(sql);
1259
1259
  const { timeoutMs } = dbExecQueryBudget(sql);
1260
1260
  const pgSql = sqliteToPostgresParams(rawSql);
1261
- const result = await withDbTimeout("query", () => client.query(pgSql, args), timeoutOverrideMs ?? timeoutMs);
1261
+ // Neon only accepts multiple SQL commands through its simple protocol;
1262
+ // the transaction start has no parameters, so use that overload.
1263
+ const runQuery = () => args.length === 0 && rawSql.includes(";")
1264
+ ? client.query(pgSql)
1265
+ : client.query(pgSql, args);
1266
+ const result = await withDbTimeout("query", () => runQuery(), timeoutOverrideMs ?? timeoutMs);
1262
1267
  return {
1263
1268
  rows: result.rows,
1264
1269
  rowsAffected: result.rowCount ?? 0,
@@ -1404,7 +1409,10 @@ async function createDbExecInternal(config = {}, trackSingletonResources = false
1404
1409
  },
1405
1410
  };
1406
1411
  try {
1407
- await queryNeonClient(client, "BEGIN");
1412
+ // Send the transaction start and idle reaper together. Neon
1413
+ // transaction pooling can ignore startup parameters, and a
1414
+ // worker can die between separate BEGIN and SET LOCAL calls.
1415
+ await queryNeonClient(client, "BEGIN; SET LOCAL idle_in_transaction_session_timeout = 30000");
1408
1416
  const result = await fn(tx);
1409
1417
  await queryNeonClient(client, "COMMIT");
1410
1418
  releaseClient();
@@ -40,6 +40,43 @@ function getNeonServerlessDrizzle() {
40
40
  export function isSqlRead(sql) {
41
41
  return /^\s*(SELECT|WITH\s)/i.test(sql);
42
42
  }
43
+ const NEON_IDLE_IN_TRANSACTION_TIMEOUT_SQL = "SET LOCAL idle_in_transaction_session_timeout = 30000";
44
+ function queryText(sql) {
45
+ if (typeof sql === "string")
46
+ return sql;
47
+ if (sql && typeof sql === "object" && "text" in sql) {
48
+ const text = sql.text;
49
+ return typeof text === "string" ? text : "";
50
+ }
51
+ return "";
52
+ }
53
+ function isBeginQuery(sql) {
54
+ return /^\s*BEGIN(?:\s|$)/i.test(queryText(sql));
55
+ }
56
+ /**
57
+ * Drizzle sends BEGIN through the client returned by pool.connect(), so a
58
+ * pool startup parameter alone is not enough protection when Neon routes the
59
+ * connection through a transaction pooler. Put the idle timeout in the same
60
+ * simple-protocol message as BEGIN; a worker killed before its next query
61
+ * still leaves a backend that will reap itself.
62
+ */
63
+ function guardNeonTransactionClient(client) {
64
+ return new Proxy(client, {
65
+ get(target, prop) {
66
+ if (prop !== "query") {
67
+ const value = target[prop];
68
+ return typeof value === "function" ? value.bind(target) : value;
69
+ }
70
+ return (...args) => {
71
+ const sql = args[0];
72
+ if (!isBeginQuery(sql))
73
+ return target.query(...args);
74
+ const text = queryText(sql).replace(/;\s*$/, "");
75
+ return target.query(`${text}; ${NEON_IDLE_IN_TRANSACTION_TIMEOUT_SQL}`);
76
+ };
77
+ },
78
+ });
79
+ }
43
80
  /**
44
81
  * Wraps a @neondatabase/serverless Pool so every query goes through
45
82
  * the same withDbTimeout + retryOnConnectionError resilience that the
@@ -129,6 +166,11 @@ export function buildResilientNeonPool(pool) {
129
166
  get(target, prop) {
130
167
  if (prop === "query")
131
168
  return resilientQuery;
169
+ if (prop === "connect") {
170
+ return (...args) => target
171
+ .connect(...args)
172
+ .then((client) => guardNeonTransactionClient(client));
173
+ }
132
174
  const val = target[prop];
133
175
  return typeof val === "function" ? val.bind(target) : val;
134
176
  },
@@ -17,11 +17,11 @@ declare const _default: import("../../action.js").ActionDefinition<{
17
17
  id?: undefined;
18
18
  provider?: undefined;
19
19
  } | {
20
- error?: undefined;
21
20
  configured?: undefined;
22
21
  connectPath?: undefined;
23
22
  url: string;
24
23
  id: string;
25
24
  provider: string;
25
+ error?: undefined;
26
26
  }>;
27
27
  export default _default;
@@ -80,8 +80,8 @@ const EN_AUTH_COPY = {
80
80
  createAccount: "Create account",
81
81
  passwordMinPlaceholder: `At least ${PASSWORD_MIN_LENGTH} characters`,
82
82
  confirmPasswordPlaceholder: "Confirm password",
83
- magicLinkTitle: "Welcome",
84
- magicLinkSubtitle: "Create an account or sign in",
83
+ magicLinkTitle: NATIVE_AUTH_COPY["en-US"].welcomeTitle,
84
+ magicLinkSubtitle: NATIVE_AUTH_COPY["en-US"].welcomeSubtitle,
85
85
  signupProgress: "Signup progress",
86
86
  progressAccount: "Account",
87
87
  progressVerify: "Verify",
@@ -107,11 +107,6 @@ const EN_AUTH_COPY = {
107
107
  copyCommand: "Copy command",
108
108
  copied: "Copied",
109
109
  closeGoogleChoices: "Close Google sign-in choices",
110
- legalPrefix: "By signing up, you accept our",
111
- legalTerms: "Terms",
112
- legalConnector: "and",
113
- legalPrivacy: "Privacy Policy",
114
- legalSuffix: ".",
115
110
  signInToContinue: "Sign in to continue.",
116
111
  finishSignInFailed: "We couldn't finish signing you in. Please sign in manually.",
117
112
  enterPasswordAfterVerification: "Enter your password after verifying your email.",
@@ -159,8 +154,8 @@ const AUTH_LOCALE_COPY = {
159
154
  createAccount: "创建账户",
160
155
  passwordMinPlaceholder: `至少 ${PASSWORD_MIN_LENGTH} 个字符`,
161
156
  confirmPasswordPlaceholder: "确认密码",
162
- magicLinkTitle: "欢迎",
163
- magicLinkSubtitle: "创建账户或登录",
157
+ magicLinkTitle: NATIVE_AUTH_COPY["zh-CN"].welcomeTitle,
158
+ magicLinkSubtitle: NATIVE_AUTH_COPY["zh-CN"].welcomeSubtitle,
164
159
  signupProgress: "注册进度",
165
160
  progressAccount: "账户",
166
161
  progressVerify: "验证",
@@ -186,11 +181,6 @@ const AUTH_LOCALE_COPY = {
186
181
  copyCommand: "复制命令",
187
182
  copied: "已复制",
188
183
  closeGoogleChoices: "关闭 Google 登录选项",
189
- legalPrefix: "注册即表示你接受我们的",
190
- legalTerms: "条款",
191
- legalConnector: "和",
192
- legalPrivacy: "隐私政策",
193
- legalSuffix: "。",
194
184
  signInToContinue: "登录以继续。",
195
185
  finishSignInFailed: "无法自动完成登录。",
196
186
  enterPasswordAfterVerification: "验证邮箱后请输入密码。",
@@ -236,8 +226,8 @@ const AUTH_LOCALE_COPY = {
236
226
  createAccount: "建立帳號",
237
227
  passwordMinPlaceholder: `至少 ${PASSWORD_MIN_LENGTH} 個字元`,
238
228
  confirmPasswordPlaceholder: "確認密碼",
239
- magicLinkTitle: "歡迎",
240
- magicLinkSubtitle: "建立帳戶或登入",
229
+ magicLinkTitle: NATIVE_AUTH_COPY["zh-TW"].welcomeTitle,
230
+ magicLinkSubtitle: NATIVE_AUTH_COPY["zh-TW"].welcomeSubtitle,
241
231
  signupProgress: "註冊進度",
242
232
  progressAccount: "帳號",
243
233
  progressVerify: "驗證",
@@ -263,11 +253,6 @@ const AUTH_LOCALE_COPY = {
263
253
  copyCommand: "複製指令",
264
254
  copied: "已複製",
265
255
  closeGoogleChoices: "關閉 Google 登入選項",
266
- legalPrefix: "註冊即表示你接受我們的",
267
- legalTerms: "條款",
268
- legalConnector: "和",
269
- legalPrivacy: "隱私權政策",
270
- legalSuffix: "。",
271
256
  signInToContinue: "登入以繼續。",
272
257
  finishSignInFailed: "無法自動完成登入。",
273
258
  enterPasswordAfterVerification: "驗證電子郵件後請輸入密碼。",
@@ -313,8 +298,8 @@ const AUTH_LOCALE_COPY = {
313
298
  createAccount: "Crear cuenta",
314
299
  passwordMinPlaceholder: `Al menos ${PASSWORD_MIN_LENGTH} caracteres`,
315
300
  confirmPasswordPlaceholder: "Confirmar contraseña",
316
- magicLinkTitle: "Bienvenido",
317
- magicLinkSubtitle: "Crea una cuenta o inicia sesión",
301
+ magicLinkTitle: NATIVE_AUTH_COPY["es-ES"].welcomeTitle,
302
+ magicLinkSubtitle: NATIVE_AUTH_COPY["es-ES"].welcomeSubtitle,
318
303
  signupProgress: "Progreso de registro",
319
304
  progressAccount: "Cuenta",
320
305
  progressVerify: "Verificar",
@@ -340,11 +325,6 @@ const AUTH_LOCALE_COPY = {
340
325
  copyCommand: "Copiar comando",
341
326
  copied: "Copiado",
342
327
  closeGoogleChoices: "Cerrar opciones de inicio con Google",
343
- legalPrefix: "Al registrarte, aceptas nuestros",
344
- legalTerms: "Términos",
345
- legalConnector: "y",
346
- legalPrivacy: "Política de privacidad",
347
- legalSuffix: ".",
348
328
  signInToContinue: "Inicia sesión para continuar.",
349
329
  finishSignInFailed: "No se pudo completar el inicio automáticamente.",
350
330
  enterPasswordAfterVerification: "Introduce tu contraseña después de verificar tu email.",
@@ -390,8 +370,8 @@ const AUTH_LOCALE_COPY = {
390
370
  createAccount: "Créer un compte",
391
371
  passwordMinPlaceholder: `Au moins ${PASSWORD_MIN_LENGTH} caractères`,
392
372
  confirmPasswordPlaceholder: "Confirmer le mot de passe",
393
- magicLinkTitle: "Bienvenue",
394
- magicLinkSubtitle: "Créez un compte ou connectez-vous",
373
+ magicLinkTitle: NATIVE_AUTH_COPY["fr-FR"].welcomeTitle,
374
+ magicLinkSubtitle: NATIVE_AUTH_COPY["fr-FR"].welcomeSubtitle,
395
375
  signupProgress: "Progression de l'inscription",
396
376
  progressAccount: "Compte",
397
377
  progressVerify: "Vérifier",
@@ -417,11 +397,6 @@ const AUTH_LOCALE_COPY = {
417
397
  copyCommand: "Copier la commande",
418
398
  copied: "Copié",
419
399
  closeGoogleChoices: "Fermer les choix de connexion Google",
420
- legalPrefix: "En vous inscrivant, vous acceptez nos",
421
- legalTerms: "Conditions",
422
- legalConnector: "et",
423
- legalPrivacy: "Politique de confidentialité",
424
- legalSuffix: ".",
425
400
  signInToContinue: "Connectez-vous pour continuer.",
426
401
  finishSignInFailed: "Impossible de terminer la connexion automatiquement.",
427
402
  enterPasswordAfterVerification: "Saisissez votre mot de passe après avoir vérifié votre e-mail.",
@@ -467,8 +442,8 @@ const AUTH_LOCALE_COPY = {
467
442
  createAccount: "Konto erstellen",
468
443
  passwordMinPlaceholder: `Mindestens ${PASSWORD_MIN_LENGTH} Zeichen`,
469
444
  confirmPasswordPlaceholder: "Passwort bestätigen",
470
- magicLinkTitle: "Willkommen",
471
- magicLinkSubtitle: "Konto erstellen oder anmelden",
445
+ magicLinkTitle: NATIVE_AUTH_COPY["de-DE"].welcomeTitle,
446
+ magicLinkSubtitle: NATIVE_AUTH_COPY["de-DE"].welcomeSubtitle,
472
447
  signupProgress: "Registrierungsfortschritt",
473
448
  progressAccount: "Konto",
474
449
  progressVerify: "Prüfen",
@@ -494,11 +469,6 @@ const AUTH_LOCALE_COPY = {
494
469
  copyCommand: "Befehl kopieren",
495
470
  copied: "Kopiert",
496
471
  closeGoogleChoices: "Google-Anmeldeoptionen schließen",
497
- legalPrefix: "Mit der Registrierung akzeptierst du unsere",
498
- legalTerms: "Bedingungen",
499
- legalConnector: "und",
500
- legalPrivacy: "Datenschutzrichtlinie",
501
- legalSuffix: ".",
502
472
  signInToContinue: "Melde dich an, um fortzufahren.",
503
473
  finishSignInFailed: "Die Anmeldung konnte nicht automatisch abgeschlossen werden.",
504
474
  enterPasswordAfterVerification: "Gib dein Passwort ein, nachdem du deine E-Mail bestätigt hast.",
@@ -544,8 +514,8 @@ const AUTH_LOCALE_COPY = {
544
514
  createAccount: "アカウントを作成",
545
515
  passwordMinPlaceholder: `${PASSWORD_MIN_LENGTH} 文字以上`,
546
516
  confirmPasswordPlaceholder: "パスワードを確認",
547
- magicLinkTitle: "ようこそ",
548
- magicLinkSubtitle: "アカウントを作成するかサインインしてください",
517
+ magicLinkTitle: NATIVE_AUTH_COPY["ja-JP"].welcomeTitle,
518
+ magicLinkSubtitle: NATIVE_AUTH_COPY["ja-JP"].welcomeSubtitle,
549
519
  signupProgress: "登録の進行状況",
550
520
  progressAccount: "アカウント",
551
521
  progressVerify: "確認",
@@ -571,11 +541,6 @@ const AUTH_LOCALE_COPY = {
571
541
  copyCommand: "コマンドをコピー",
572
542
  copied: "コピーしました",
573
543
  closeGoogleChoices: "Google サインインの選択肢を閉じる",
574
- legalPrefix: "登録すると、以下に同意したものとみなされます:",
575
- legalTerms: "利用規約",
576
- legalConnector: "および",
577
- legalPrivacy: "プライバシーポリシー",
578
- legalSuffix: "。",
579
544
  signInToContinue: "続行するにはサインインしてください。",
580
545
  finishSignInFailed: "サインインを自動で完了できませんでした。",
581
546
  enterPasswordAfterVerification: "メールを確認した後、パスワードを入力してください。",
@@ -621,8 +586,8 @@ const AUTH_LOCALE_COPY = {
621
586
  createAccount: "계정 만들기",
622
587
  passwordMinPlaceholder: `${PASSWORD_MIN_LENGTH}자 이상`,
623
588
  confirmPasswordPlaceholder: "비밀번호 확인",
624
- magicLinkTitle: "환영합니다",
625
- magicLinkSubtitle: "계정을 만들거나 로그인하세요",
589
+ magicLinkTitle: NATIVE_AUTH_COPY["ko-KR"].welcomeTitle,
590
+ magicLinkSubtitle: NATIVE_AUTH_COPY["ko-KR"].welcomeSubtitle,
626
591
  signupProgress: "가입 진행 상황",
627
592
  progressAccount: "계정",
628
593
  progressVerify: "확인",
@@ -648,11 +613,6 @@ const AUTH_LOCALE_COPY = {
648
613
  copyCommand: "명령 복사",
649
614
  copied: "복사됨",
650
615
  closeGoogleChoices: "Google 로그인 선택 닫기",
651
- legalPrefix: "가입하면 다음에 동의하게 됩니다:",
652
- legalTerms: "약관",
653
- legalConnector: "및",
654
- legalPrivacy: "개인정보 처리방침",
655
- legalSuffix: ".",
656
616
  signInToContinue: "계속하려면 로그인하세요.",
657
617
  finishSignInFailed: "자동으로 로그인을 완료할 수 없습니다.",
658
618
  enterPasswordAfterVerification: "이메일을 확인한 후 비밀번호를 입력하세요.",
@@ -698,8 +658,8 @@ const AUTH_LOCALE_COPY = {
698
658
  createAccount: "Criar conta",
699
659
  passwordMinPlaceholder: `Pelo menos ${PASSWORD_MIN_LENGTH} caracteres`,
700
660
  confirmPasswordPlaceholder: "Confirmar senha",
701
- magicLinkTitle: "Bem-vindo",
702
- magicLinkSubtitle: "Crie uma conta ou entre",
661
+ magicLinkTitle: NATIVE_AUTH_COPY["pt-BR"].welcomeTitle,
662
+ magicLinkSubtitle: NATIVE_AUTH_COPY["pt-BR"].welcomeSubtitle,
703
663
  signupProgress: "Progresso do cadastro",
704
664
  progressAccount: "Conta",
705
665
  progressVerify: "Verificar",
@@ -725,11 +685,6 @@ const AUTH_LOCALE_COPY = {
725
685
  copyCommand: "Copiar comando",
726
686
  copied: "Copiado",
727
687
  closeGoogleChoices: "Fechar opções de login com Google",
728
- legalPrefix: "Ao se cadastrar, você aceita nossos",
729
- legalTerms: "Termos",
730
- legalConnector: "e",
731
- legalPrivacy: "Política de Privacidade",
732
- legalSuffix: ".",
733
688
  signInToContinue: "Entre para continuar.",
734
689
  finishSignInFailed: "Não foi possível concluir o login automaticamente.",
735
690
  enterPasswordAfterVerification: "Digite sua senha depois de verificar seu email.",
@@ -775,8 +730,8 @@ const AUTH_LOCALE_COPY = {
775
730
  createAccount: "खाता बनाएं",
776
731
  passwordMinPlaceholder: `कम से कम ${PASSWORD_MIN_LENGTH} अक्षर`,
777
732
  confirmPasswordPlaceholder: "पासवर्ड की पुष्टि करें",
778
- magicLinkTitle: "स्वागत है",
779
- magicLinkSubtitle: "खाता बनाएं या साइन इन करें",
733
+ magicLinkTitle: NATIVE_AUTH_COPY["hi-IN"].welcomeTitle,
734
+ magicLinkSubtitle: NATIVE_AUTH_COPY["hi-IN"].welcomeSubtitle,
780
735
  signupProgress: "साइनअप प्रगति",
781
736
  progressAccount: "खाता",
782
737
  progressVerify: "सत्यापित करें",
@@ -802,11 +757,6 @@ const AUTH_LOCALE_COPY = {
802
757
  copyCommand: "कमांड कॉपी करें",
803
758
  copied: "कॉपी हो गया",
804
759
  closeGoogleChoices: "Google साइन-इन विकल्प बंद करें",
805
- legalPrefix: "साइन अप करके, आप हमारी",
806
- legalTerms: "शर्तें",
807
- legalConnector: "और",
808
- legalPrivacy: "गोपनीयता नीति",
809
- legalSuffix: "स्वीकार करते हैं।",
810
760
  signInToContinue: "जारी रखने के लिए साइन इन करें।",
811
761
  finishSignInFailed: "साइन इन अपने आप पूरा नहीं हो सका।",
812
762
  enterPasswordAfterVerification: "ईमेल सत्यापित करने के बाद अपना पासवर्ड दर्ज करें।",
@@ -852,8 +802,8 @@ const AUTH_LOCALE_COPY = {
852
802
  createAccount: "إنشاء حساب",
853
803
  passwordMinPlaceholder: `${PASSWORD_MIN_LENGTH} أحرف على الأقل`,
854
804
  confirmPasswordPlaceholder: "تأكيد كلمة المرور",
855
- magicLinkTitle: "مرحبًا",
856
- magicLinkSubtitle: "أنشئ حسابًا أو سجّل الدخول",
805
+ magicLinkTitle: NATIVE_AUTH_COPY["ar-SA"].welcomeTitle,
806
+ magicLinkSubtitle: NATIVE_AUTH_COPY["ar-SA"].welcomeSubtitle,
857
807
  signupProgress: "تقدم التسجيل",
858
808
  progressAccount: "الحساب",
859
809
  progressVerify: "التحقق",
@@ -879,11 +829,6 @@ const AUTH_LOCALE_COPY = {
879
829
  copyCommand: "نسخ الأمر",
880
830
  copied: "تم النسخ",
881
831
  closeGoogleChoices: "إغلاق خيارات تسجيل الدخول عبر Google",
882
- legalPrefix: "بالتسجيل، فإنك توافق على",
883
- legalTerms: "الشروط",
884
- legalConnector: "و",
885
- legalPrivacy: "سياسة الخصوصية",
886
- legalSuffix: ".",
887
832
  signInToContinue: "سجّل الدخول للمتابعة.",
888
833
  finishSignInFailed: "تعذر إكمال تسجيل الدخول تلقائيًا.",
889
834
  enterPasswordAfterVerification: "أدخل كلمة المرور بعد التحقق من بريدك الإلكتروني.",