@verbatra/sdk 0.6.0 → 0.6.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/dist/index.d.cts CHANGED
@@ -246,23 +246,27 @@ declare const providerConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
246
246
  options: z.ZodObject<{
247
247
  model: z.ZodString;
248
248
  maxTokens: z.ZodNumber;
249
+ requestTimeoutMs: z.ZodOptional<z.ZodNumber>;
249
250
  }, z.core.$strict>;
250
251
  }, z.core.$strip>, z.ZodObject<{
251
252
  id: z.ZodLiteral<"openai">;
252
253
  options: z.ZodObject<{
253
254
  model: z.ZodString;
254
255
  maxOutputTokens: z.ZodNumber;
256
+ requestTimeoutMs: z.ZodOptional<z.ZodNumber>;
255
257
  }, z.core.$strict>;
256
258
  }, z.core.$strip>, z.ZodObject<{
257
259
  id: z.ZodLiteral<"gemini">;
258
260
  options: z.ZodObject<{
259
261
  model: z.ZodString;
260
262
  maxOutputTokens: z.ZodNumber;
263
+ requestTimeoutMs: z.ZodOptional<z.ZodNumber>;
261
264
  }, z.core.$strict>;
262
265
  }, z.core.$strip>, z.ZodObject<{
263
266
  id: z.ZodLiteral<"deepl">;
264
267
  options: z.ZodObject<{
265
268
  glossaryId: z.ZodOptional<z.ZodString>;
269
+ requestTimeoutMs: z.ZodOptional<z.ZodNumber>;
266
270
  }, z.core.$strict>;
267
271
  }, z.core.$strip>, z.ZodObject<{
268
272
  id: z.ZodLiteral<"openai-compatible">;
@@ -271,6 +275,7 @@ declare const providerConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
271
275
  model: z.ZodString;
272
276
  maxOutputTokens: z.ZodNumber;
273
277
  apiKeyEnvVar: z.ZodOptional<z.ZodString>;
278
+ requestTimeoutMs: z.ZodOptional<z.ZodNumber>;
274
279
  }, z.core.$strict>;
275
280
  }, z.core.$strip>], "id">;
276
281
  /** One validated provider section: the provider id plus that provider's own options. */
@@ -303,23 +308,27 @@ declare const verbatraConfigSchema: z.ZodObject<{
303
308
  options: z.ZodObject<{
304
309
  model: z.ZodString;
305
310
  maxTokens: z.ZodNumber;
311
+ requestTimeoutMs: z.ZodOptional<z.ZodNumber>;
306
312
  }, z.core.$strict>;
307
313
  }, z.core.$strip>, z.ZodObject<{
308
314
  id: z.ZodLiteral<"openai">;
309
315
  options: z.ZodObject<{
310
316
  model: z.ZodString;
311
317
  maxOutputTokens: z.ZodNumber;
318
+ requestTimeoutMs: z.ZodOptional<z.ZodNumber>;
312
319
  }, z.core.$strict>;
313
320
  }, z.core.$strip>, z.ZodObject<{
314
321
  id: z.ZodLiteral<"gemini">;
315
322
  options: z.ZodObject<{
316
323
  model: z.ZodString;
317
324
  maxOutputTokens: z.ZodNumber;
325
+ requestTimeoutMs: z.ZodOptional<z.ZodNumber>;
318
326
  }, z.core.$strict>;
319
327
  }, z.core.$strip>, z.ZodObject<{
320
328
  id: z.ZodLiteral<"deepl">;
321
329
  options: z.ZodObject<{
322
330
  glossaryId: z.ZodOptional<z.ZodString>;
331
+ requestTimeoutMs: z.ZodOptional<z.ZodNumber>;
323
332
  }, z.core.$strict>;
324
333
  }, z.core.$strip>, z.ZodObject<{
325
334
  id: z.ZodLiteral<"openai-compatible">;
@@ -328,6 +337,7 @@ declare const verbatraConfigSchema: z.ZodObject<{
328
337
  model: z.ZodString;
329
338
  maxOutputTokens: z.ZodNumber;
330
339
  apiKeyEnvVar: z.ZodOptional<z.ZodString>;
340
+ requestTimeoutMs: z.ZodOptional<z.ZodNumber>;
331
341
  }, z.core.$strict>;
332
342
  }, z.core.$strip>], "id">;
333
343
  glossary: z.ZodOptional<z.ZodUnion<readonly [z.ZodRecord<z.ZodString, z.ZodString>, z.ZodString]>>;
@@ -930,6 +940,9 @@ interface DiffDeps {
930
940
  */
931
941
  declare function diff(input: DiffInput, deps?: DiffDeps): Promise<DiffSummary>;
932
942
 
943
+ /** Why {@link gateCandidateValue} rejected a candidate value. */
944
+ type IntegrityGateReason = "placeholder" | "icu" | "degenerate";
945
+
933
946
  /** Input for {@link editEntry}: the validated config, exactly one target locale/key pair, and the human-typed replacement value. */
934
947
  interface EditEntryInput {
935
948
  /** The validated configuration (typically from {@link loadConfig}). */
@@ -959,7 +972,7 @@ type EditEntryResult = {
959
972
  readonly value: string;
960
973
  } | {
961
974
  readonly accepted: false;
962
- readonly reason: "placeholder" | "icu";
975
+ readonly reason: IntegrityGateReason;
963
976
  readonly value: string;
964
977
  };
965
978
  /**
@@ -1248,7 +1261,7 @@ type RetranslateEntryResult = {
1248
1261
  readonly reviewReasons: readonly ReviewReasonCode[];
1249
1262
  } | {
1250
1263
  readonly accepted: false;
1251
- readonly reason: "placeholder" | "icu";
1264
+ readonly reason: IntegrityGateReason;
1252
1265
  readonly value: string;
1253
1266
  };
1254
1267
  /**
@@ -1348,8 +1361,17 @@ interface NeedsReviewEntry {
1348
1361
  interface LocaleSummary {
1349
1362
  /** The target locale this summary is for. */
1350
1363
  readonly locale: string;
1351
- /** Whether this locale's run succeeded or failed (a failure does not abort the run). */
1352
- readonly status: "succeeded" | "failed";
1364
+ /**
1365
+ * This locale's honest run status (a non-success does not abort the run). `"succeeded"` when
1366
+ * nothing was withheld, including a genuine no-op with no candidate keys at all. `"partial"` when
1367
+ * at least one key was accepted and written (a {@link LocaleSummary.translated} key or a
1368
+ * {@link LocaleSummary.generated} plural form) but at least one was withheld (any of
1369
+ * {@link LocaleSummary.integrityMismatches}, {@link LocaleSummary.providerFailures}, or
1370
+ * {@link LocaleSummary.budgetWithheld} non-empty). `"failed"` when the locale had candidate keys
1371
+ * but accepted nothing at all (neither translated nor generated) while withholding at least one.
1372
+ * Withheld keys keep their prior lock hash and retry next run.
1373
+ */
1374
+ readonly status: "succeeded" | "partial" | "failed";
1353
1375
  /**
1354
1376
  * Keys translated and written this run. In dry-run, the keys that would be translated
1355
1377
  * (the provider is not called and nothing is written).
@@ -1419,9 +1441,13 @@ interface LocaleSummary {
1419
1441
  */
1420
1442
  readonly needsReview: readonly NeedsReviewEntry[];
1421
1443
  /**
1422
- * Present only when status is "failed": a structured, secret-free error. `code` is a preserved string
1423
- * (the underlying provider/adapter error's `code`, or `"LOCALE_FAILED"` as a fallback), intentionally
1424
- * wider than {@link SdkErrorCode}, so do not treat it as a closed set.
1444
+ * A structured, secret-free error for a locale that threw (an adapter/lock/provider-construction
1445
+ * failure isolated as data): present only on a "failed" locale, and only that throw path sets it.
1446
+ * A locale that is "failed" because every candidate key was withheld (all under `providerFailures`,
1447
+ * `integrityMismatches`, or `budgetWithheld`) carries those lists and any notices instead, and has
1448
+ * no `error`. `code` is a preserved string (the underlying provider/adapter error's `code`, or
1449
+ * `"LOCALE_FAILED"` as a fallback), intentionally wider than {@link SdkErrorCode}, so do not treat
1450
+ * it as a closed set.
1425
1451
  */
1426
1452
  readonly error?: {
1427
1453
  readonly code: string;
@@ -1434,9 +1460,15 @@ interface RunSummary {
1434
1460
  readonly dryRun: boolean;
1435
1461
  /** One {@link LocaleSummary} per target locale, in config order. */
1436
1462
  readonly locales: readonly LocaleSummary[];
1437
- /** Locales whose run succeeded. */
1463
+ /** Locales whose run succeeded with nothing withheld (status `"succeeded"`). */
1438
1464
  readonly succeeded: readonly string[];
1439
- /** Locales whose run failed (see each locale's `error`). */
1465
+ /**
1466
+ * Locales that wrote at least one translation but withheld at least one candidate key (status
1467
+ * `"partial"`). A partial locale still exits the CLI `0`: it made progress, and its withheld keys
1468
+ * retry next run. Never overlaps `succeeded` or `failed`.
1469
+ */
1470
+ readonly partial: readonly string[];
1471
+ /** Locales whose run failed, having accepted nothing (status `"failed"`; see each locale's `error`). */
1440
1472
  readonly failed: readonly string[];
1441
1473
  /**
1442
1474
  * Summed token usage across every locale's {@link LocaleSummary.usage}. Absent when no locale in the
@@ -1449,12 +1481,12 @@ interface RunSummary {
1449
1481
 
1450
1482
  /**
1451
1483
  * One target locale's snapshot inside the persisted run-status file. `status` is carried over from
1452
- * `LocaleSummary.status` unchanged, so an empty `needsReview` on a failed locale is never misread as
1453
- * "this locale ran clean with nothing to review".
1484
+ * `LocaleSummary.status` unchanged, so an empty `needsReview` on a failed or partial locale is never
1485
+ * misread as "this locale ran clean with nothing to review".
1454
1486
  */
1455
1487
  interface RunStatusLocale {
1456
1488
  readonly locale: string;
1457
- readonly status: "succeeded" | "failed";
1489
+ readonly status: "succeeded" | "partial" | "failed";
1458
1490
  /** The full, unmodified list from `LocaleSummary.needsReview`; already bounded and content-free. */
1459
1491
  readonly needsReview: readonly NeedsReviewEntry[];
1460
1492
  /** Absent exactly when the source `LocaleSummary.usage` was absent; never a fabricated zero. */
@@ -1510,6 +1542,28 @@ type RunStatusResult = {
1510
1542
  */
1511
1543
  declare function runStatus(input?: RunStatusInput, deps?: RunStatusDeps): Promise<RunStatusResult>;
1512
1544
 
1545
+ /** Diagnostic details, read leniently from a held lock file, about the process currently holding it. */
1546
+ interface LockHolder {
1547
+ /** The holder's process id, when the lock file could be read and carried a numeric `pid`. */
1548
+ readonly pid?: number;
1549
+ /** The holder's acquire time as an ISO-8601 string, when the lock file carried a string `acquiredAt`. */
1550
+ readonly acquiredAt?: string;
1551
+ }
1552
+ /** One wait-progress notification emitted while an acquire is blocked on a held lock. */
1553
+ interface LockWaitEvent {
1554
+ /** The on-disk path of the contended lock file, the exact path a person can delete if it is orphaned. */
1555
+ readonly lockPath: string;
1556
+ /** Milliseconds elapsed since this acquire started waiting, for a "still waiting" progress line. */
1557
+ readonly elapsedMs: number;
1558
+ /**
1559
+ * Diagnostics about the holding process, present only when the lock file could be read and parsed.
1560
+ * Purely for messaging; never consulted for any acquire decision.
1561
+ */
1562
+ readonly holder?: LockHolder;
1563
+ }
1564
+ /** Called while an acquire is blocked, to surface wait progress; the lock module itself writes no output. */
1565
+ type LockWaitListener = (event: LockWaitEvent) => void;
1566
+
1513
1567
  /** Everything the one-shot run needs: the validated config and where/how to run it. */
1514
1568
  interface TranslateInput {
1515
1569
  /** The validated configuration (typically from {@link loadConfig}). */
@@ -1531,6 +1585,19 @@ interface TranslateInput {
1531
1585
  * unset, the config's `generatePlurals` applies.
1532
1586
  */
1533
1587
  readonly generatePlurals?: boolean;
1588
+ /**
1589
+ * Called while a locale's write lock is blocked on another process holding it: once after the first
1590
+ * failed acquire (with the holder's pid/acquiredAt when the lock file is readable), then periodically
1591
+ * with the growing elapsed time. Never called for an uncontended run. The SDK writes no output; this
1592
+ * is the only wait-progress signal (the CLI renders its "still waiting" line from it).
1593
+ */
1594
+ readonly onLockWait?: LockWaitListener;
1595
+ /**
1596
+ * Override how long a locale's write lock keeps retrying before failing with `LOCK_CONTENDED`, in
1597
+ * milliseconds. Defaults to the lock's own 10-minute default when unset (surfaced to the CLI as
1598
+ * `--lock-timeout`).
1599
+ */
1600
+ readonly lockAcquireTimeoutMs?: number;
1534
1601
  }
1535
1602
  /** Composition seam: inject a registry, a provider builder, and a file system for tests. */
1536
1603
  interface TranslateDeps {
@@ -1568,7 +1635,7 @@ interface TranslateDeps {
1568
1635
  *
1569
1636
  * @param input - The validated config and run options (cwd, dryRun, prune, generatePlurals).
1570
1637
  * @param deps - Optional composition seams (registry, provider builder, file system) for tests.
1571
- * @returns A {@link RunSummary}: the per-locale {@link LocaleSummary}s and the succeeded/failed locale lists.
1638
+ * @returns A {@link RunSummary}: the per-locale {@link LocaleSummary}s and the succeeded/partial/failed locale lists.
1572
1639
  * @throws {@link SdkError} `UNKNOWN_FORMAT`: no adapter is registered for the configured format.
1573
1640
  * @throws {@link SdkError} `PROVIDER_CONSTRUCTION_FAILED`: the provider factory threw (this wraps the
1574
1641
  * provider's own error, including a missing `*_API_KEY` reported as `MISSING_API_KEY`); only on a
@@ -1789,6 +1856,14 @@ interface WatchInput {
1789
1856
  readonly debounceMs?: number;
1790
1857
  /** Called once per run with its result. The SDK does no logging; this is the only output. */
1791
1858
  readonly onRun: (result: WatchRunResult) => void;
1859
+ /**
1860
+ * Passed through to every run's {@link TranslateInput.onLockWait}: called while a locale's write lock
1861
+ * is blocked on another process, so a watch caller can surface the same "still waiting" progress a
1862
+ * one-shot run does.
1863
+ */
1864
+ readonly onLockWait?: LockWaitListener;
1865
+ /** Passed through to every run's {@link TranslateInput.lockAcquireTimeoutMs}; the lock's 10-minute default when unset. */
1866
+ readonly lockAcquireTimeoutMs?: number;
1792
1867
  }
1793
1868
  /** Composition seam: inject the watcher and the run for deterministic, offline tests. */
1794
1869
  interface WatchDeps {
@@ -1845,4 +1920,4 @@ interface WatchController {
1845
1920
  */
1846
1921
  declare function watch(input: WatchInput, deps?: WatchDeps): Promise<WatchController>;
1847
1922
 
1848
- export { type BudgetBehavior, type CheckDeps, type CheckInput, type CheckSummary, type ConfigSource, type CreateProvider, type CreateWatcher, DEFAULT_WORKBOOK_PATH, type DiffDeps, type DiffInput, type DiffSummary, type EditEntryDeps, type EditEntryInput, type EditEntryResult, type ExportWorkbookDeps, type ExportWorkbookInput, type ExportWorkbookResult, type GlossaryProvenance, type ImportWorkbookDeps, type ImportWorkbookInput, type KeyIntegrityDeps, type KeyIntegrityEntry, type KeyIntegrityInput, type KeyValueDeps, type KeyValueInput, type KeyValueResult, LOCK_FILE_NAME, type LoadConfigOptions, type LoadLockFileDeps, type LoadLockFileInput, type LoadedConfig, type LocaleCheckSummary, type LocaleDiff, type LocaleFileSnapshot, type LocaleKeyIntegrity, type LocaleNotice, type LocaleSnapshotDelta, type LocaleSummary, type LockFile, type LockLocaleState, type LockStateDeps, type LockStateInput, type LockStateResult, type NeedsReviewEntry, type ProviderConfig, type ProviderId, type ReadLocaleFileSnapshotDeps, type ReadLocaleFileSnapshotInput, type RetranslateEntryDeps, type RetranslateEntryInput, type RetranslateEntryResult, type ReviewReasonCode, type RunBudget, type RunStatusDeps, type RunStatusFile, type RunStatusInput, type RunStatusLocale, type RunStatusResult, type RunSummary, type RunTranslate, type ScaffoldableProviderId, SdkError, type SdkErrorCode, type SdkFs, type SdkNotice, type SdkNoticeCode, type SupportedFormat$1 as SupportedFormat, type TranslateDeps, type TranslateInput, type UsageSummary, type VerbatraConfig, type VerbatraConfigInput, type WatchController, type WatchDeps, type WatchInput, type WatchRunResult, type Watcher, check, defineConfig, diff, diffLocaleSnapshots, editEntry, exportWorkbook, importWorkbook, keyIntegrity, keyValue, loadConfig, loadConfigWithMeta, loadLockFile, lockState, readLocaleFileSnapshot, retranslateEntry, runStatus, scaffoldingMetadata, translate, verbatraConfigSchema, watch };
1923
+ export { type BudgetBehavior, type CheckDeps, type CheckInput, type CheckSummary, type ConfigSource, type CreateProvider, type CreateWatcher, DEFAULT_WORKBOOK_PATH, type DiffDeps, type DiffInput, type DiffSummary, type EditEntryDeps, type EditEntryInput, type EditEntryResult, type ExportWorkbookDeps, type ExportWorkbookInput, type ExportWorkbookResult, type GlossaryProvenance, type ImportWorkbookDeps, type ImportWorkbookInput, type IntegrityGateReason, type KeyIntegrityDeps, type KeyIntegrityEntry, type KeyIntegrityInput, type KeyValueDeps, type KeyValueInput, type KeyValueResult, LOCK_FILE_NAME, type LoadConfigOptions, type LoadLockFileDeps, type LoadLockFileInput, type LoadedConfig, type LocaleCheckSummary, type LocaleDiff, type LocaleFileSnapshot, type LocaleKeyIntegrity, type LocaleNotice, type LocaleSnapshotDelta, type LocaleSummary, type LockFile, type LockHolder, type LockLocaleState, type LockStateDeps, type LockStateInput, type LockStateResult, type LockWaitEvent, type LockWaitListener, type NeedsReviewEntry, type ProviderConfig, type ProviderId, type ReadLocaleFileSnapshotDeps, type ReadLocaleFileSnapshotInput, type RetranslateEntryDeps, type RetranslateEntryInput, type RetranslateEntryResult, type ReviewReasonCode, type RunBudget, type RunStatusDeps, type RunStatusFile, type RunStatusInput, type RunStatusLocale, type RunStatusResult, type RunSummary, type RunTranslate, type ScaffoldableProviderId, SdkError, type SdkErrorCode, type SdkFs, type SdkNotice, type SdkNoticeCode, type SupportedFormat$1 as SupportedFormat, type TranslateDeps, type TranslateInput, type UsageSummary, type VerbatraConfig, type VerbatraConfigInput, type WatchController, type WatchDeps, type WatchInput, type WatchRunResult, type Watcher, check, defineConfig, diff, diffLocaleSnapshots, editEntry, exportWorkbook, importWorkbook, keyIntegrity, keyValue, loadConfig, loadConfigWithMeta, loadLockFile, lockState, readLocaleFileSnapshot, retranslateEntry, runStatus, scaffoldingMetadata, translate, verbatraConfigSchema, watch };
package/dist/index.d.ts CHANGED
@@ -246,23 +246,27 @@ declare const providerConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
246
246
  options: z.ZodObject<{
247
247
  model: z.ZodString;
248
248
  maxTokens: z.ZodNumber;
249
+ requestTimeoutMs: z.ZodOptional<z.ZodNumber>;
249
250
  }, z.core.$strict>;
250
251
  }, z.core.$strip>, z.ZodObject<{
251
252
  id: z.ZodLiteral<"openai">;
252
253
  options: z.ZodObject<{
253
254
  model: z.ZodString;
254
255
  maxOutputTokens: z.ZodNumber;
256
+ requestTimeoutMs: z.ZodOptional<z.ZodNumber>;
255
257
  }, z.core.$strict>;
256
258
  }, z.core.$strip>, z.ZodObject<{
257
259
  id: z.ZodLiteral<"gemini">;
258
260
  options: z.ZodObject<{
259
261
  model: z.ZodString;
260
262
  maxOutputTokens: z.ZodNumber;
263
+ requestTimeoutMs: z.ZodOptional<z.ZodNumber>;
261
264
  }, z.core.$strict>;
262
265
  }, z.core.$strip>, z.ZodObject<{
263
266
  id: z.ZodLiteral<"deepl">;
264
267
  options: z.ZodObject<{
265
268
  glossaryId: z.ZodOptional<z.ZodString>;
269
+ requestTimeoutMs: z.ZodOptional<z.ZodNumber>;
266
270
  }, z.core.$strict>;
267
271
  }, z.core.$strip>, z.ZodObject<{
268
272
  id: z.ZodLiteral<"openai-compatible">;
@@ -271,6 +275,7 @@ declare const providerConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
271
275
  model: z.ZodString;
272
276
  maxOutputTokens: z.ZodNumber;
273
277
  apiKeyEnvVar: z.ZodOptional<z.ZodString>;
278
+ requestTimeoutMs: z.ZodOptional<z.ZodNumber>;
274
279
  }, z.core.$strict>;
275
280
  }, z.core.$strip>], "id">;
276
281
  /** One validated provider section: the provider id plus that provider's own options. */
@@ -303,23 +308,27 @@ declare const verbatraConfigSchema: z.ZodObject<{
303
308
  options: z.ZodObject<{
304
309
  model: z.ZodString;
305
310
  maxTokens: z.ZodNumber;
311
+ requestTimeoutMs: z.ZodOptional<z.ZodNumber>;
306
312
  }, z.core.$strict>;
307
313
  }, z.core.$strip>, z.ZodObject<{
308
314
  id: z.ZodLiteral<"openai">;
309
315
  options: z.ZodObject<{
310
316
  model: z.ZodString;
311
317
  maxOutputTokens: z.ZodNumber;
318
+ requestTimeoutMs: z.ZodOptional<z.ZodNumber>;
312
319
  }, z.core.$strict>;
313
320
  }, z.core.$strip>, z.ZodObject<{
314
321
  id: z.ZodLiteral<"gemini">;
315
322
  options: z.ZodObject<{
316
323
  model: z.ZodString;
317
324
  maxOutputTokens: z.ZodNumber;
325
+ requestTimeoutMs: z.ZodOptional<z.ZodNumber>;
318
326
  }, z.core.$strict>;
319
327
  }, z.core.$strip>, z.ZodObject<{
320
328
  id: z.ZodLiteral<"deepl">;
321
329
  options: z.ZodObject<{
322
330
  glossaryId: z.ZodOptional<z.ZodString>;
331
+ requestTimeoutMs: z.ZodOptional<z.ZodNumber>;
323
332
  }, z.core.$strict>;
324
333
  }, z.core.$strip>, z.ZodObject<{
325
334
  id: z.ZodLiteral<"openai-compatible">;
@@ -328,6 +337,7 @@ declare const verbatraConfigSchema: z.ZodObject<{
328
337
  model: z.ZodString;
329
338
  maxOutputTokens: z.ZodNumber;
330
339
  apiKeyEnvVar: z.ZodOptional<z.ZodString>;
340
+ requestTimeoutMs: z.ZodOptional<z.ZodNumber>;
331
341
  }, z.core.$strict>;
332
342
  }, z.core.$strip>], "id">;
333
343
  glossary: z.ZodOptional<z.ZodUnion<readonly [z.ZodRecord<z.ZodString, z.ZodString>, z.ZodString]>>;
@@ -930,6 +940,9 @@ interface DiffDeps {
930
940
  */
931
941
  declare function diff(input: DiffInput, deps?: DiffDeps): Promise<DiffSummary>;
932
942
 
943
+ /** Why {@link gateCandidateValue} rejected a candidate value. */
944
+ type IntegrityGateReason = "placeholder" | "icu" | "degenerate";
945
+
933
946
  /** Input for {@link editEntry}: the validated config, exactly one target locale/key pair, and the human-typed replacement value. */
934
947
  interface EditEntryInput {
935
948
  /** The validated configuration (typically from {@link loadConfig}). */
@@ -959,7 +972,7 @@ type EditEntryResult = {
959
972
  readonly value: string;
960
973
  } | {
961
974
  readonly accepted: false;
962
- readonly reason: "placeholder" | "icu";
975
+ readonly reason: IntegrityGateReason;
963
976
  readonly value: string;
964
977
  };
965
978
  /**
@@ -1248,7 +1261,7 @@ type RetranslateEntryResult = {
1248
1261
  readonly reviewReasons: readonly ReviewReasonCode[];
1249
1262
  } | {
1250
1263
  readonly accepted: false;
1251
- readonly reason: "placeholder" | "icu";
1264
+ readonly reason: IntegrityGateReason;
1252
1265
  readonly value: string;
1253
1266
  };
1254
1267
  /**
@@ -1348,8 +1361,17 @@ interface NeedsReviewEntry {
1348
1361
  interface LocaleSummary {
1349
1362
  /** The target locale this summary is for. */
1350
1363
  readonly locale: string;
1351
- /** Whether this locale's run succeeded or failed (a failure does not abort the run). */
1352
- readonly status: "succeeded" | "failed";
1364
+ /**
1365
+ * This locale's honest run status (a non-success does not abort the run). `"succeeded"` when
1366
+ * nothing was withheld, including a genuine no-op with no candidate keys at all. `"partial"` when
1367
+ * at least one key was accepted and written (a {@link LocaleSummary.translated} key or a
1368
+ * {@link LocaleSummary.generated} plural form) but at least one was withheld (any of
1369
+ * {@link LocaleSummary.integrityMismatches}, {@link LocaleSummary.providerFailures}, or
1370
+ * {@link LocaleSummary.budgetWithheld} non-empty). `"failed"` when the locale had candidate keys
1371
+ * but accepted nothing at all (neither translated nor generated) while withholding at least one.
1372
+ * Withheld keys keep their prior lock hash and retry next run.
1373
+ */
1374
+ readonly status: "succeeded" | "partial" | "failed";
1353
1375
  /**
1354
1376
  * Keys translated and written this run. In dry-run, the keys that would be translated
1355
1377
  * (the provider is not called and nothing is written).
@@ -1419,9 +1441,13 @@ interface LocaleSummary {
1419
1441
  */
1420
1442
  readonly needsReview: readonly NeedsReviewEntry[];
1421
1443
  /**
1422
- * Present only when status is "failed": a structured, secret-free error. `code` is a preserved string
1423
- * (the underlying provider/adapter error's `code`, or `"LOCALE_FAILED"` as a fallback), intentionally
1424
- * wider than {@link SdkErrorCode}, so do not treat it as a closed set.
1444
+ * A structured, secret-free error for a locale that threw (an adapter/lock/provider-construction
1445
+ * failure isolated as data): present only on a "failed" locale, and only that throw path sets it.
1446
+ * A locale that is "failed" because every candidate key was withheld (all under `providerFailures`,
1447
+ * `integrityMismatches`, or `budgetWithheld`) carries those lists and any notices instead, and has
1448
+ * no `error`. `code` is a preserved string (the underlying provider/adapter error's `code`, or
1449
+ * `"LOCALE_FAILED"` as a fallback), intentionally wider than {@link SdkErrorCode}, so do not treat
1450
+ * it as a closed set.
1425
1451
  */
1426
1452
  readonly error?: {
1427
1453
  readonly code: string;
@@ -1434,9 +1460,15 @@ interface RunSummary {
1434
1460
  readonly dryRun: boolean;
1435
1461
  /** One {@link LocaleSummary} per target locale, in config order. */
1436
1462
  readonly locales: readonly LocaleSummary[];
1437
- /** Locales whose run succeeded. */
1463
+ /** Locales whose run succeeded with nothing withheld (status `"succeeded"`). */
1438
1464
  readonly succeeded: readonly string[];
1439
- /** Locales whose run failed (see each locale's `error`). */
1465
+ /**
1466
+ * Locales that wrote at least one translation but withheld at least one candidate key (status
1467
+ * `"partial"`). A partial locale still exits the CLI `0`: it made progress, and its withheld keys
1468
+ * retry next run. Never overlaps `succeeded` or `failed`.
1469
+ */
1470
+ readonly partial: readonly string[];
1471
+ /** Locales whose run failed, having accepted nothing (status `"failed"`; see each locale's `error`). */
1440
1472
  readonly failed: readonly string[];
1441
1473
  /**
1442
1474
  * Summed token usage across every locale's {@link LocaleSummary.usage}. Absent when no locale in the
@@ -1449,12 +1481,12 @@ interface RunSummary {
1449
1481
 
1450
1482
  /**
1451
1483
  * One target locale's snapshot inside the persisted run-status file. `status` is carried over from
1452
- * `LocaleSummary.status` unchanged, so an empty `needsReview` on a failed locale is never misread as
1453
- * "this locale ran clean with nothing to review".
1484
+ * `LocaleSummary.status` unchanged, so an empty `needsReview` on a failed or partial locale is never
1485
+ * misread as "this locale ran clean with nothing to review".
1454
1486
  */
1455
1487
  interface RunStatusLocale {
1456
1488
  readonly locale: string;
1457
- readonly status: "succeeded" | "failed";
1489
+ readonly status: "succeeded" | "partial" | "failed";
1458
1490
  /** The full, unmodified list from `LocaleSummary.needsReview`; already bounded and content-free. */
1459
1491
  readonly needsReview: readonly NeedsReviewEntry[];
1460
1492
  /** Absent exactly when the source `LocaleSummary.usage` was absent; never a fabricated zero. */
@@ -1510,6 +1542,28 @@ type RunStatusResult = {
1510
1542
  */
1511
1543
  declare function runStatus(input?: RunStatusInput, deps?: RunStatusDeps): Promise<RunStatusResult>;
1512
1544
 
1545
+ /** Diagnostic details, read leniently from a held lock file, about the process currently holding it. */
1546
+ interface LockHolder {
1547
+ /** The holder's process id, when the lock file could be read and carried a numeric `pid`. */
1548
+ readonly pid?: number;
1549
+ /** The holder's acquire time as an ISO-8601 string, when the lock file carried a string `acquiredAt`. */
1550
+ readonly acquiredAt?: string;
1551
+ }
1552
+ /** One wait-progress notification emitted while an acquire is blocked on a held lock. */
1553
+ interface LockWaitEvent {
1554
+ /** The on-disk path of the contended lock file, the exact path a person can delete if it is orphaned. */
1555
+ readonly lockPath: string;
1556
+ /** Milliseconds elapsed since this acquire started waiting, for a "still waiting" progress line. */
1557
+ readonly elapsedMs: number;
1558
+ /**
1559
+ * Diagnostics about the holding process, present only when the lock file could be read and parsed.
1560
+ * Purely for messaging; never consulted for any acquire decision.
1561
+ */
1562
+ readonly holder?: LockHolder;
1563
+ }
1564
+ /** Called while an acquire is blocked, to surface wait progress; the lock module itself writes no output. */
1565
+ type LockWaitListener = (event: LockWaitEvent) => void;
1566
+
1513
1567
  /** Everything the one-shot run needs: the validated config and where/how to run it. */
1514
1568
  interface TranslateInput {
1515
1569
  /** The validated configuration (typically from {@link loadConfig}). */
@@ -1531,6 +1585,19 @@ interface TranslateInput {
1531
1585
  * unset, the config's `generatePlurals` applies.
1532
1586
  */
1533
1587
  readonly generatePlurals?: boolean;
1588
+ /**
1589
+ * Called while a locale's write lock is blocked on another process holding it: once after the first
1590
+ * failed acquire (with the holder's pid/acquiredAt when the lock file is readable), then periodically
1591
+ * with the growing elapsed time. Never called for an uncontended run. The SDK writes no output; this
1592
+ * is the only wait-progress signal (the CLI renders its "still waiting" line from it).
1593
+ */
1594
+ readonly onLockWait?: LockWaitListener;
1595
+ /**
1596
+ * Override how long a locale's write lock keeps retrying before failing with `LOCK_CONTENDED`, in
1597
+ * milliseconds. Defaults to the lock's own 10-minute default when unset (surfaced to the CLI as
1598
+ * `--lock-timeout`).
1599
+ */
1600
+ readonly lockAcquireTimeoutMs?: number;
1534
1601
  }
1535
1602
  /** Composition seam: inject a registry, a provider builder, and a file system for tests. */
1536
1603
  interface TranslateDeps {
@@ -1568,7 +1635,7 @@ interface TranslateDeps {
1568
1635
  *
1569
1636
  * @param input - The validated config and run options (cwd, dryRun, prune, generatePlurals).
1570
1637
  * @param deps - Optional composition seams (registry, provider builder, file system) for tests.
1571
- * @returns A {@link RunSummary}: the per-locale {@link LocaleSummary}s and the succeeded/failed locale lists.
1638
+ * @returns A {@link RunSummary}: the per-locale {@link LocaleSummary}s and the succeeded/partial/failed locale lists.
1572
1639
  * @throws {@link SdkError} `UNKNOWN_FORMAT`: no adapter is registered for the configured format.
1573
1640
  * @throws {@link SdkError} `PROVIDER_CONSTRUCTION_FAILED`: the provider factory threw (this wraps the
1574
1641
  * provider's own error, including a missing `*_API_KEY` reported as `MISSING_API_KEY`); only on a
@@ -1789,6 +1856,14 @@ interface WatchInput {
1789
1856
  readonly debounceMs?: number;
1790
1857
  /** Called once per run with its result. The SDK does no logging; this is the only output. */
1791
1858
  readonly onRun: (result: WatchRunResult) => void;
1859
+ /**
1860
+ * Passed through to every run's {@link TranslateInput.onLockWait}: called while a locale's write lock
1861
+ * is blocked on another process, so a watch caller can surface the same "still waiting" progress a
1862
+ * one-shot run does.
1863
+ */
1864
+ readonly onLockWait?: LockWaitListener;
1865
+ /** Passed through to every run's {@link TranslateInput.lockAcquireTimeoutMs}; the lock's 10-minute default when unset. */
1866
+ readonly lockAcquireTimeoutMs?: number;
1792
1867
  }
1793
1868
  /** Composition seam: inject the watcher and the run for deterministic, offline tests. */
1794
1869
  interface WatchDeps {
@@ -1845,4 +1920,4 @@ interface WatchController {
1845
1920
  */
1846
1921
  declare function watch(input: WatchInput, deps?: WatchDeps): Promise<WatchController>;
1847
1922
 
1848
- export { type BudgetBehavior, type CheckDeps, type CheckInput, type CheckSummary, type ConfigSource, type CreateProvider, type CreateWatcher, DEFAULT_WORKBOOK_PATH, type DiffDeps, type DiffInput, type DiffSummary, type EditEntryDeps, type EditEntryInput, type EditEntryResult, type ExportWorkbookDeps, type ExportWorkbookInput, type ExportWorkbookResult, type GlossaryProvenance, type ImportWorkbookDeps, type ImportWorkbookInput, type KeyIntegrityDeps, type KeyIntegrityEntry, type KeyIntegrityInput, type KeyValueDeps, type KeyValueInput, type KeyValueResult, LOCK_FILE_NAME, type LoadConfigOptions, type LoadLockFileDeps, type LoadLockFileInput, type LoadedConfig, type LocaleCheckSummary, type LocaleDiff, type LocaleFileSnapshot, type LocaleKeyIntegrity, type LocaleNotice, type LocaleSnapshotDelta, type LocaleSummary, type LockFile, type LockLocaleState, type LockStateDeps, type LockStateInput, type LockStateResult, type NeedsReviewEntry, type ProviderConfig, type ProviderId, type ReadLocaleFileSnapshotDeps, type ReadLocaleFileSnapshotInput, type RetranslateEntryDeps, type RetranslateEntryInput, type RetranslateEntryResult, type ReviewReasonCode, type RunBudget, type RunStatusDeps, type RunStatusFile, type RunStatusInput, type RunStatusLocale, type RunStatusResult, type RunSummary, type RunTranslate, type ScaffoldableProviderId, SdkError, type SdkErrorCode, type SdkFs, type SdkNotice, type SdkNoticeCode, type SupportedFormat$1 as SupportedFormat, type TranslateDeps, type TranslateInput, type UsageSummary, type VerbatraConfig, type VerbatraConfigInput, type WatchController, type WatchDeps, type WatchInput, type WatchRunResult, type Watcher, check, defineConfig, diff, diffLocaleSnapshots, editEntry, exportWorkbook, importWorkbook, keyIntegrity, keyValue, loadConfig, loadConfigWithMeta, loadLockFile, lockState, readLocaleFileSnapshot, retranslateEntry, runStatus, scaffoldingMetadata, translate, verbatraConfigSchema, watch };
1923
+ export { type BudgetBehavior, type CheckDeps, type CheckInput, type CheckSummary, type ConfigSource, type CreateProvider, type CreateWatcher, DEFAULT_WORKBOOK_PATH, type DiffDeps, type DiffInput, type DiffSummary, type EditEntryDeps, type EditEntryInput, type EditEntryResult, type ExportWorkbookDeps, type ExportWorkbookInput, type ExportWorkbookResult, type GlossaryProvenance, type ImportWorkbookDeps, type ImportWorkbookInput, type IntegrityGateReason, type KeyIntegrityDeps, type KeyIntegrityEntry, type KeyIntegrityInput, type KeyValueDeps, type KeyValueInput, type KeyValueResult, LOCK_FILE_NAME, type LoadConfigOptions, type LoadLockFileDeps, type LoadLockFileInput, type LoadedConfig, type LocaleCheckSummary, type LocaleDiff, type LocaleFileSnapshot, type LocaleKeyIntegrity, type LocaleNotice, type LocaleSnapshotDelta, type LocaleSummary, type LockFile, type LockHolder, type LockLocaleState, type LockStateDeps, type LockStateInput, type LockStateResult, type LockWaitEvent, type LockWaitListener, type NeedsReviewEntry, type ProviderConfig, type ProviderId, type ReadLocaleFileSnapshotDeps, type ReadLocaleFileSnapshotInput, type RetranslateEntryDeps, type RetranslateEntryInput, type RetranslateEntryResult, type ReviewReasonCode, type RunBudget, type RunStatusDeps, type RunStatusFile, type RunStatusInput, type RunStatusLocale, type RunStatusResult, type RunSummary, type RunTranslate, type ScaffoldableProviderId, SdkError, type SdkErrorCode, type SdkFs, type SdkNotice, type SdkNoticeCode, type SupportedFormat$1 as SupportedFormat, type TranslateDeps, type TranslateInput, type UsageSummary, type VerbatraConfig, type VerbatraConfigInput, type WatchController, type WatchDeps, type WatchInput, type WatchRunResult, type Watcher, check, defineConfig, diff, diffLocaleSnapshots, editEntry, exportWorkbook, importWorkbook, keyIntegrity, keyValue, loadConfig, loadConfigWithMeta, loadLockFile, lockState, readLocaleFileSnapshot, retranslateEntry, runStatus, scaffoldingMetadata, translate, verbatraConfigSchema, watch };