@camstack/system 1.2.452 → 1.2.454

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.
Files changed (96) hide show
  1. package/dist/{agent-https-CMz4S56_.js → agent-https-B7IjRRzT.js} +2 -2
  2. package/dist/{agent-https-D6lRxHma.mjs → agent-https-oXsR24R2.mjs} +2 -2
  3. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  6. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  7. package/dist/builtins/alerts/alert-episode-policy.d.ts +39 -0
  8. package/dist/builtins/alerts/alerts.addon.d.ts +25 -38
  9. package/dist/builtins/alerts/alerts.addon.js +178 -423
  10. package/dist/builtins/alerts/alerts.addon.mjs +178 -423
  11. package/dist/builtins/alerts/emit-alert-policy.d.ts +3 -16
  12. package/dist/builtins/alerts/runner-alert.d.ts +0 -13
  13. package/dist/builtins/autotrack/index.js +1 -1
  14. package/dist/builtins/autotrack/index.mjs +1 -1
  15. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
  16. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2 -2
  17. package/dist/builtins/calendars/calendars.addon.js +1 -1
  18. package/dist/builtins/calendars/calendars.addon.mjs +1 -1
  19. package/dist/builtins/camera-grid/index.js +1 -1
  20. package/dist/builtins/camera-grid/index.mjs +1 -1
  21. package/dist/builtins/composer/composer.addon.js +2 -2
  22. package/dist/builtins/composer/composer.addon.mjs +2 -2
  23. package/dist/builtins/console-logging/index.js +1 -1
  24. package/dist/builtins/console-logging/index.mjs +1 -1
  25. package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
  26. package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
  27. package/dist/builtins/device-history/device-history.addon.js +1 -1
  28. package/dist/builtins/device-history/device-history.addon.mjs +1 -1
  29. package/dist/builtins/device-manager/device-manager.addon.js +1 -1
  30. package/dist/builtins/device-manager/device-manager.addon.mjs +1 -1
  31. package/dist/builtins/hub-forwarder/index.js +1 -1
  32. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  33. package/dist/builtins/keychain/index.js +4 -4
  34. package/dist/builtins/keychain/index.mjs +4 -4
  35. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  36. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  37. package/dist/builtins/local-auth/local-auth.addon.js +3 -3
  38. package/dist/builtins/local-auth/local-auth.addon.mjs +3 -3
  39. package/dist/builtins/local-network/local-network.addon.js +2 -2
  40. package/dist/builtins/local-network/local-network.addon.mjs +2 -2
  41. package/dist/builtins/loki-logging/index.js +1 -1
  42. package/dist/builtins/loki-logging/index.mjs +1 -1
  43. package/dist/builtins/native-metrics/native-metrics.addon.js +15 -2
  44. package/dist/builtins/native-metrics/native-metrics.addon.mjs +15 -2
  45. package/dist/builtins/platform-probe/index.js +1 -1
  46. package/dist/builtins/platform-probe/index.mjs +1 -1
  47. package/dist/builtins/record-entries/index.js +22 -2
  48. package/dist/builtins/record-entries/index.mjs +22 -2
  49. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  50. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  51. package/dist/builtins/snapshot/index.js +1 -1
  52. package/dist/builtins/snapshot/index.mjs +1 -1
  53. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  54. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  55. package/dist/builtins/sqlite-storage/index.js +1 -1
  56. package/dist/builtins/sqlite-storage/index.mjs +1 -1
  57. package/dist/builtins/sqlite-storage/records/records-engine.d.ts +13 -0
  58. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +2 -2
  59. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +2 -2
  60. package/dist/builtins/storage-orchestrator/location-class-paths.d.ts +13 -0
  61. package/dist/builtins/storage-orchestrator/location-repoint-move.d.ts +6 -1
  62. package/dist/builtins/storage-orchestrator/location-repointer.d.ts +97 -0
  63. package/dist/builtins/storage-orchestrator/location-store.d.ts +13 -2
  64. package/dist/builtins/storage-orchestrator/record-kinds.d.ts +5 -0
  65. package/dist/builtins/storage-orchestrator/storage-location-band-monitor.d.ts +38 -4
  66. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +8 -2
  67. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +486 -143
  68. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +486 -143
  69. package/dist/builtins/storage-orchestrator/storage-orchestrator.service.d.ts +26 -1
  70. package/dist/builtins/system-config/system-config.addon.js +3 -3
  71. package/dist/builtins/system-config/system-config.addon.mjs +3 -3
  72. package/dist/builtins/winston-logging/index.js +1 -1
  73. package/dist/builtins/winston-logging/index.mjs +1 -1
  74. package/dist/{composition-sources-BQmhc3se.js → composition-sources-Cukc78eu.js} +1 -1
  75. package/dist/{composition-sources-DzuruDVY.mjs → composition-sources-bt6IQDLu.mjs} +1 -1
  76. package/dist/{constants-BPWA-r-Y.js → constants-DBGG6Zu5.js} +1 -1
  77. package/dist/{constants-gzGZbnwO.mjs → constants-DQcVrhVI.mjs} +1 -1
  78. package/dist/{device-ca-75rt3U_U.js → device-ca-B3hUVfYk.js} +1 -1
  79. package/dist/{device-ca-D05nqNXE.mjs → device-ca-Utfvsx5x.mjs} +1 -1
  80. package/dist/{dist-DV2ll9Hi.mjs → dist-7ZciHCIp.mjs} +394 -38
  81. package/dist/{dist-Ct88catN.js → dist-Bi4Ioxop.js} +417 -37
  82. package/dist/file-host-worker.js +1 -1
  83. package/dist/file-host-worker.mjs +1 -1
  84. package/dist/{hub-mdns-ciao-DAU1WttG.js → hub-mdns-ciao-2FzLWfbD.js} +2 -2
  85. package/dist/{hub-mdns-ciao-BPOg2iyc.mjs → hub-mdns-ciao-BRs3DUT-.mjs} +2 -2
  86. package/dist/index.js +17 -9
  87. package/dist/index.mjs +17 -9
  88. package/dist/kernel/cap-router-builder.d.ts +9 -0
  89. package/dist/{phase6-declare-gate-BJplW6tX.mjs → phase6-declare-gate-D-2LMw9o.mjs} +1 -1
  90. package/dist/{phase6-declare-gate-C6WqagkC.js → phase6-declare-gate-DwYAep7y.js} +1 -1
  91. package/dist/{restore-archive-Cprdh3P9.js → restore-archive-C0ymYQk7.js} +1 -1
  92. package/dist/{restore-archive-Jiz63mVB.mjs → restore-archive-UVZv_hmr.mjs} +1 -1
  93. package/dist/{wal-maintenance-BspX29jz.js → wal-maintenance-Bo_lo3Dh.js} +0 -0
  94. package/dist/{wal-maintenance-BNIaf3QP.mjs → wal-maintenance-Df89xxrK.mjs} +0 -0
  95. package/package.json +1 -1
  96. package/dist/builtins/alerts/critical-push.d.ts +0 -90
@@ -1,4 +1,4 @@
1
- import { An as memoizePinnedFfmpeg, Br as DeviceType, Hr as BaseAddon, Rr as DeviceFeature, Vr as errMsg, Vt as deriveBatteryPresence, _r as streamQualityLabel, _t as bareAddonId, lr as snapshotCapability, ma as EventCategory, qi as nodePin, s as BatteryStatusSchema, vt as batteryCapability } from "../../dist-DV2ll9Hi.mjs";
1
+ import { Gr as errMsg, Hr as DeviceFeature, Kr as BaseAddon, Pn as memoizePinnedFfmpeg, Ut as deriveBatteryPresence, Wr as DeviceType, Zi as nodePin, bt as batteryCapability, pr as snapshotCapability, s as BatteryStatusSchema, va as EventCategory, xr as streamQualityLabel, yt as bareAddonId } from "../../dist-7ZciHCIp.mjs";
2
2
  import { i as renderCourtesyOverlayJpeg, n as courtesyCacheKey, r as renderCourtesyJpeg } from "../../snapshot-courtesy-CyFs1M3I.mjs";
3
3
  import { z } from "zod";
4
4
  import { randomUUID } from "node:crypto";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Ct88catN.js");
6
+ const require_dist = require("../../dist-Bi4Ioxop.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let node_fs_promises = require("node:fs/promises");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Hr as BaseAddon, hr as storageProviderCapability, in as filesystemBrowseCapability } from "../../dist-DV2ll9Hi.mjs";
1
+ import { Kr as BaseAddon, on as filesystemBrowseCapability, yr as storageProviderCapability } from "../../dist-7ZciHCIp.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { mkdir, readdir, realpath, statfs } from "node:fs/promises";
4
4
  import * as path$1 from "node:path";
@@ -4,7 +4,7 @@ Object.defineProperties(exports, {
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
6
  const require_builtins_sqlite_storage_filesystem_storage_addon = require("./filesystem-storage.addon.js");
7
- const require_wal_maintenance = require("../../wal-maintenance-BspX29jz.js");
7
+ const require_wal_maintenance = require("../../wal-maintenance-Bo_lo3Dh.js");
8
8
  const require_builtins_sqlite_storage_sqlite_settings_addon = require("./sqlite-settings.addon.js");
9
9
  //#region src/builtins/sqlite-storage/config-store.ts
10
10
  var ConfigStore = class {
@@ -1,5 +1,5 @@
1
1
  import { FilesystemStorageAddon, t as FilesystemStorageProvider } from "./filesystem-storage.addon.mjs";
2
- import { A as SQLITE_MMAP_CEILING_BYTES, B as SQLITE_SLOW_CALL_MS, F as sqlitePageCacheKibFor, H as sqliteEngineStorm, I as SQLITE_ENGINE_STORM_WALL_SHARE, L as SQLITE_PROFILE_REPORT_FLOOR_MS, M as SQLITE_PAGE_CACHE_CEILING_KIB, N as SQLITE_PAGE_CACHE_FLOOR_KIB, P as sqliteMmapSizeFor, R as SQLITE_PROFILE_REPORT_INTERVAL_MS, V as SqliteOpProfiler, a as WalMaintenance, i as WAL_MAINTENANCE_INTERVAL_MS, j as SQLITE_MMAP_FLOOR_BYTES, k as SQLITE_JOURNAL_SIZE_LIMIT_BYTES, l as SqliteSettingsBackend, n as WAL_HIGH_WATER_BYTES, r as WAL_IDLE_QUIET_MS, t as WAL_CHECKPOINT_BUSY_TIMEOUT_MS, z as SQLITE_PROFILE_TOP_N } from "../../wal-maintenance-BNIaf3QP.mjs";
2
+ import { A as SQLITE_MMAP_CEILING_BYTES, B as SQLITE_SLOW_CALL_MS, F as sqlitePageCacheKibFor, H as sqliteEngineStorm, I as SQLITE_ENGINE_STORM_WALL_SHARE, L as SQLITE_PROFILE_REPORT_FLOOR_MS, M as SQLITE_PAGE_CACHE_CEILING_KIB, N as SQLITE_PAGE_CACHE_FLOOR_KIB, P as sqliteMmapSizeFor, R as SQLITE_PROFILE_REPORT_INTERVAL_MS, V as SqliteOpProfiler, a as WalMaintenance, i as WAL_MAINTENANCE_INTERVAL_MS, j as SQLITE_MMAP_FLOOR_BYTES, k as SQLITE_JOURNAL_SIZE_LIMIT_BYTES, l as SqliteSettingsBackend, n as WAL_HIGH_WATER_BYTES, r as WAL_IDLE_QUIET_MS, t as WAL_CHECKPOINT_BUSY_TIMEOUT_MS, z as SQLITE_PROFILE_TOP_N } from "../../wal-maintenance-Df89xxrK.mjs";
3
3
  import { SqliteSettingsAddon } from "./sqlite-settings.addon.mjs";
4
4
  //#region src/builtins/sqlite-storage/config-store.ts
5
5
  var ConfigStore = class {
@@ -27,6 +27,19 @@ export declare class RecordsEngine implements RecordsEngineOps {
27
27
  private readonly now;
28
28
  private readonly writesSinceTrim;
29
29
  constructor(db: Database.Database, options: RecordsEngineOptions);
30
+ /**
31
+ * Boot correction: every kind that ever allocated an id has its counter raised
32
+ * above the highest numeric id it holds. Adopted legacy rows (or any caller
33
+ * chosen numeric id) kept ids far above a counter that started at 1, and the
34
+ * upsert would have overwritten another device's row on the first collision.
35
+ * Runs on the engine's own connection, over the (kind_id, id) unique index.
36
+ */
37
+ private correctSequences;
38
+ /** Ids handed out by allocateId and not yet written: their first write is insert-only. */
39
+ private readonly pendingAllocated;
40
+ private static readonly PENDING_ALLOCATED_MAX;
41
+ /** Raise a kind's counter above the numeric ids of a caller-supplied batch (adoption). */
42
+ private raiseSequenceFor;
30
43
  private schemaReady;
31
44
  private kindOf;
32
45
  declareKinds(input: OwnedInput<"declareKinds">): Promise<RecordsOutput<"declareKinds">>;
@@ -3,9 +3,9 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Ct88catN.js");
6
+ const require_dist = require("../../dist-Bi4Ioxop.js");
7
7
  const require_retired_settings_keys = require("../../retired-settings-keys-D_VwZiDt.js");
8
- const require_wal_maintenance = require("../../wal-maintenance-BspX29jz.js");
8
+ const require_wal_maintenance = require("../../wal-maintenance-Bo_lo3Dh.js");
9
9
  const require_sqlite_snapshot_protocol = require("../../sqlite-snapshot-protocol-B9OndqZv.js");
10
10
  const require_wal_checkpoint_policy = require("../../wal-checkpoint-policy-d2z2qPqx.js");
11
11
  let node_fs_promises = require("node:fs/promises");
@@ -1,6 +1,6 @@
1
- import { $ as STORAGE_ADMIN_SCOPED_BASES, $i as parseSettingsArrayAdoptsFrom, $r as NoCallerError, Gn as recordsCapability, Hr as BaseAddon, It as dataStoreProviderCapability, Jr as DeclarationRefusedError, Lt as databaseSnapshotCapability, Q as STORAGE_ADMIN_MOVED_COLLECTIONS, S as DATA_STORE_DATABASES, Vr as errMsg, Yi as normalizeSecretOwner, _i as UnknownRecordKindError, _t as bareAddonId, b as DATABASE_SNAPSHOT_SUFFIX, dr as storageAdminCapability, ea as parseSettingsBrokersAdoptsFrom, ii as RECORD_SWEEP_INTERVAL_MS, si as RUNTIME_DEFAULTS, ui as RecordKindDeclarationSchema, wr as vectorStoreCapability, x as DATABASE_SNAPSHOT_TIMEOUT_MS, y as DATABASE_SNAPSHOT_PARTIAL_SUFFIX } from "../../dist-DV2ll9Hi.mjs";
1
+ import { $ as STORAGE_ADMIN_SCOPED_BASES, $i as normalizeSecretOwner, Gr as errMsg, Kr as BaseAddon, Or as vectorStoreCapability, Q as STORAGE_ADMIN_MOVED_COLLECTIONS, Qr as DeclarationRefusedError, Rt as dataStoreProviderCapability, S as DATA_STORE_DATABASES, Yn as recordsCapability, b as DATABASE_SNAPSHOT_SUFFIX, ci as RECORD_SWEEP_INTERVAL_MS, di as RUNTIME_DEFAULTS, hr as storageAdminCapability, ia as parseSettingsBrokersAdoptsFrom, mi as RecordKindDeclarationSchema, ra as parseSettingsArrayAdoptsFrom, ri as NoCallerError, x as DATABASE_SNAPSHOT_TIMEOUT_MS, xi as UnknownRecordKindError, y as DATABASE_SNAPSHOT_PARTIAL_SUFFIX, yt as bareAddonId, zt as databaseSnapshotCapability } from "../../dist-7ZciHCIp.mjs";
2
2
  import { i as retiredKeyStoreOf, n as purgeRetiredSettingsKeys } from "../../retired-settings-keys-Bhwe4ymb.mjs";
3
- import { C as RecordsEngine, D as callVector, E as callRecords, O as controlTargetOf, S as trustResult, T as callData, _ as collectTransferables, a as WalMaintenance, c as vectorProviderOfOwnedFile, d as CrossDatabaseTransactError, f as DatabaseBusyError, l as SqliteSettingsBackend, m as rehydrateError, o as createVectorIndex, p as DuplicatePlacementError, s as vectorProviderOf, u as CollectionPlacementError, v as isFileHostMessage, w as callControl } from "../../wal-maintenance-BNIaf3QP.mjs";
3
+ import { C as RecordsEngine, D as callVector, E as callRecords, O as controlTargetOf, S as trustResult, T as callData, _ as collectTransferables, a as WalMaintenance, c as vectorProviderOfOwnedFile, d as CrossDatabaseTransactError, f as DatabaseBusyError, l as SqliteSettingsBackend, m as rehydrateError, o as createVectorIndex, p as DuplicatePlacementError, s as vectorProviderOf, u as CollectionPlacementError, v as isFileHostMessage, w as callControl } from "../../wal-maintenance-Df89xxrK.mjs";
4
4
  import { r as isSnapshotWorkerMessage } from "../../sqlite-snapshot-protocol-2QaTouWV.mjs";
5
5
  import { a as isWorkerMessage, t as WAL_CHECKPOINT_LARGE_PASS_FRAMES } from "../../wal-checkpoint-policy-BahrTtyD.mjs";
6
6
  import { createRequire } from "node:module";
@@ -0,0 +1,13 @@
1
+ /** Class → top-level directories directly under the root (no camera segment). */
2
+ export declare const STORAGE_CLASS_ROOT_SUBTREES: Readonly<Record<string, readonly string[]>>;
3
+ /**
4
+ * - `own` — at or under the class subtree: copy it, delete it.
5
+ * - `descend` — an ancestor of the subtree (the camera directory): walk into
6
+ * it, but it is not itself a file to move.
7
+ * - `foreign` — another class's data: never read, never copied, never deleted.
8
+ */
9
+ export type PathVerdict = "own" | "descend" | "foreign";
10
+ /** Does this class own only part of its root? */
11
+ export declare function isSubtreeScoped(type: string): boolean;
12
+ /** Classify a root-relative path (`/` separators) for a location of `type`. */
13
+ export declare function classifyPath(type: string, relativePath: string): PathVerdict;
@@ -28,6 +28,7 @@
28
28
  * I/O is injected: the walk and the ordering are what matter and they are
29
29
  * testable without a filesystem.
30
30
  */
31
+ import type { PathVerdict } from "./location-class-paths.js";
31
32
  /** The four operations a move needs, in the caller's own terms. */
32
33
  export interface RepointMoveIo {
33
34
  /** Entries directly under `prefix` (undefined = the root), as relative paths. */
@@ -45,8 +46,12 @@ export interface RepointMoveResult {
45
46
  readonly files: number;
46
47
  readonly bytes: number;
47
48
  }
49
+ /** Decides, per root-relative path, whether it belongs to the moving location. */
50
+ export type RepointScope = (relativePath: string) => PathVerdict;
51
+ /** The whole root — only correct for a class that owns all of it. */
52
+ export declare const WHOLE_ROOT: RepointScope;
48
53
  /**
49
54
  * Copy every file from the source root to the destination, verify each, then
50
55
  * delete the originals. Throws with NOTHING deleted if any copy fails.
51
56
  */
52
- export declare function moveLocationTree(io: RepointMoveIo): Promise<RepointMoveResult>;
57
+ export declare function moveLocationTree(io: RepointMoveIo, scope?: RepointScope): Promise<RepointMoveResult>;
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Re-pointing a SINGLE location, as one guarded operation.
3
+ *
4
+ * ## What went wrong (2026-10-20, ~8.6k event-media files lost)
5
+ *
6
+ * `homekitClips:default` shared `/recordings/recordings` with
7
+ * `eventMedia:default`. Repointing it ran a tree move over the WHOLE shared root
8
+ * and deleted the originals — every camera's `<cam>/events` included — while a
9
+ * `pipelineAnalytics.relocateMedia` job of the other location was moving the very
10
+ * same files. Nothing arbitrated between the two writers of one disk.
11
+ *
12
+ * ## The two rules
13
+ *
14
+ * 1. **Scope** — a repoint copies and deletes only its class's own subtree
15
+ * ({@link classifyPath}, D327). A foreign file is never even read.
16
+ * 2. **One claim per physical root, owned here.** A repoint holds a claim on the
17
+ * realpath of the root it reads and the root it writes. It starts only when no
18
+ * mover (`relocateMedia`, `relocateFootage`, a migration drain) touches an
19
+ * overlapping root, and a mover asks {@link clearance} before it runs and
20
+ * refuses while a repoint holds the root. No timers and no retries (D3): the
21
+ * answer is "now" and a refusal is typed.
22
+ *
23
+ * ## Why it cannot race
24
+ *
25
+ * Both sides ANNOUNCE first, then look: the repoint takes its claim and then
26
+ * lists the movers; a mover registers its job (so it appears in the list) and
27
+ * then asks {@link clearance}. Whichever announces second sees the other. The
28
+ * mover list is read fail-CLOSED here: this is the destructive side, and an
29
+ * unreadable list is not "no movers" (the opposite of D49's rule for a one-shot
30
+ * event, which this is not — a refusal costs the operator one retry).
31
+ *
32
+ * The claim is in memory and dies with the process. A crash in the middle of a
33
+ * repoint leaves the bytes in the old root or a verified copy in the new one (see
34
+ * `location-repoint-move.ts`), never a stale lock.
35
+ */
36
+ import type { IScopedLogger, StorageLocation, StorageMigrationMover } from "@camstack/types";
37
+ import { type RepointMoveIo } from "./location-repoint-move.js";
38
+ /** Why a repoint, or a mover, was turned away. Typed so callers can branch. */
39
+ export type RepointRefusalReason = "mover-active" | "repoint-active" | "movers-unreadable";
40
+ export declare class RepointRefusedError extends Error {
41
+ readonly reason: RepointRefusalReason;
42
+ override readonly name = "RepointRefusedError";
43
+ constructor(reason: RepointRefusalReason, message: string);
44
+ }
45
+ /** The answer a mover gets before it touches a root. */
46
+ export type RootClearance = {
47
+ readonly clear: true;
48
+ } | {
49
+ readonly clear: false;
50
+ readonly reason: "repoint-active";
51
+ /** The location whose repoint holds the root. */
52
+ readonly repointLocationId: string;
53
+ };
54
+ /** The row fields a repoint reads — `next` is the incoming upsert, not a stored row. */
55
+ export interface RepointTarget {
56
+ readonly id: string;
57
+ readonly type: string;
58
+ readonly config: Readonly<Record<string, unknown>>;
59
+ }
60
+ export interface LocationRepointerDeps {
61
+ readonly logger: IScopedLogger;
62
+ /** The physical key of a path (realpath, or the resolved path when it does not exist yet). */
63
+ readonly resolveKey: (basePath: string) => Promise<string>;
64
+ /** The CURRENT basePath of a stored location, or null when it has none / is unknown. */
65
+ readonly locationBasePath: (locationId: string) => string | null;
66
+ readonly locationIdsOfType: (type: string) => readonly string[];
67
+ /** Every mover in both lanes. A throw refuses the repoint. */
68
+ readonly listMovers: () => Promise<readonly StorageMigrationMover[]>;
69
+ /** I/O over the source (stored) and destination (next) roots. */
70
+ readonly ioFor: (stored: StorageLocation, next: RepointTarget) => Promise<RepointMoveIo>;
71
+ }
72
+ /** Does `a` equal `b`, or sit inside it, or contain it? Segment-wise, never by raw prefix. */
73
+ export declare function rootsOverlap(a: string, b: string): boolean;
74
+ export declare class LocationRepointer {
75
+ private readonly deps;
76
+ private readonly claims;
77
+ constructor(deps: LocationRepointerDeps);
78
+ /**
79
+ * Move `stored`'s own subtree to `next`'s root, then run `commit` (the row
80
+ * save) while the claim is still held. Throws {@link RepointRefusedError}
81
+ * with NOTHING copied or deleted when a mover is on the root.
82
+ */
83
+ run<T>(stored: StorageLocation, next: RepointTarget, commit: () => T | Promise<T>): Promise<T>;
84
+ /**
85
+ * May a mover that touches `locationIds` run? Refuses while a repoint holds
86
+ * an overlapping root. Called by the owning addon AFTER its job is
87
+ * registered, so the repoint's mover listing and this check cannot both miss.
88
+ */
89
+ clearance(locationIds: readonly string[]): Promise<RootClearance>;
90
+ private claim;
91
+ private refuseWhileMovers;
92
+ private locationsTouchedBy;
93
+ /** `*` is the media mover's "from wherever the rows are": every eventMedia location. */
94
+ private keysOf;
95
+ private refuse;
96
+ private logMoved;
97
+ }
@@ -8,7 +8,7 @@
8
8
  * pattern as `IntegrationRegistry` in `sqlite-storage/`.
9
9
  */
10
10
  import type { ISettingsStoreProvider, LegacyStorageLocationDefault, RecordKindHandle, RecordsAccess, StorageLocation } from "@camstack/types";
11
- import { type StorageLocationBody } from "./record-kinds.js";
11
+ import { type StorageLocationBody, type StorageSeedMarkerBody } from "./record-kinds.js";
12
12
  /**
13
13
  * A location as it comes back OFF DISK: the live record plus the retired
14
14
  * `isDefault` column, which exists only so the orchestrator's boot migration
@@ -31,6 +31,13 @@ export interface ILocationStore {
31
31
  upsert(loc: StorageLocation): Promise<void>;
32
32
  /** Remove a location by id. */
33
33
  delete(id: string): Promise<void>;
34
+ /**
35
+ * The location types a default was ever seeded for (or that already existed
36
+ * when this memory began). Absent on a store that cannot remember — such a
37
+ * store seeds on "type has no row", as before.
38
+ */
39
+ loadSeededTypes?(): Promise<readonly string[]>;
40
+ markTypeSeeded?(type: string): Promise<void>;
34
41
  }
35
42
  /**
36
43
  * @durable class=config owner=storage-orchestrator
@@ -49,7 +56,11 @@ export declare class SqliteLocationStore implements ILocationStore {
49
56
  private readonly store;
50
57
  private declared;
51
58
  private readonly locationsKind;
52
- constructor(store: ISettingsStoreProvider, locationsKind?: RecordKindHandle<StorageLocationBody> | null);
59
+ constructor(store: ISettingsStoreProvider, locationsKind?: RecordKindHandle<StorageLocationBody> | null, seedMarkersKind?: RecordKindHandle<StorageSeedMarkerBody> | null);
60
+ private readonly seedMarkersKind;
61
+ static seedMarkersOf(records: Pick<RecordsAccess, "kind">): RecordKindHandle<StorageSeedMarkerBody>;
62
+ loadSeededTypes(): Promise<readonly string[]>;
63
+ markTypeSeeded(type: string): Promise<void>;
53
64
  static recordStoresOf(records: Pick<RecordsAccess, "kind">): RecordKindHandle<StorageLocationBody>;
54
65
  /**
55
66
  * Lazy `declareCollection` — keeps the constructor cheap and async-free.
@@ -18,6 +18,10 @@ export type StorageLocationBody = {
18
18
  readonly isDefault?: boolean;
19
19
  };
20
20
  export declare const StorageLocationBodySchema: z.ZodType<StorageLocationBody>;
21
+ export type StorageSeedMarkerBody = {
22
+ readonly seededAt: number;
23
+ };
24
+ export declare const StorageSeedMarkerBodySchema: z.ZodType<StorageSeedMarkerBody>;
21
25
  export type StorageDrainStateBody = {
22
26
  readonly locationId: string;
23
27
  readonly startedAtMs: number;
@@ -31,6 +35,7 @@ export type StorageDrainStateBody = {
31
35
  export declare const StorageDrainStateBodySchema: z.ZodType<StorageDrainStateBody>;
32
36
  export type StorageOrchestratorRecordKindsMap = {
33
37
  readonly locations: DefinedRecordKind<z.ZodType<StorageLocationBody>>;
38
+ readonly "seed-markers": DefinedRecordKind<z.ZodType<StorageSeedMarkerBody>>;
34
39
  readonly "drain-state": DefinedRecordKind<z.ZodType<StorageDrainStateBody>>;
35
40
  };
36
41
  export declare const storageOrchestratorRecordKinds: StorageOrchestratorRecordKindsMap;
@@ -1,9 +1,28 @@
1
1
  /**
2
2
  * StorageLocationBandMonitor — free-space bands for Notification Center.
3
3
  *
4
- * Capacity that failed to measure is `null` and becomes CRITICAL (D393) —
5
- * never folded into 0% or 100%. Disabled / non-managed locations are absent
6
- * from the sweep input and clear any open episode without an `ok` event.
4
+ * Two rules, picked per sample:
5
+ *
6
+ * - UNMANAGED (no `floorPercent`): the fixed percentage bands (low <= 15 %,
7
+ * critical <= 5 %), now with hysteresis so a value hovering at an edge
8
+ * emits ONE edge.
9
+ * - MANAGED BY EVICTION (`floorPercent` = the free-space floor the evictor
10
+ * holds): sitting AT the floor is the evictor doing its job, not an
11
+ * incident. A recordings volume kept at 150 GB free of 3.7 TB is ~4 % free
12
+ * for ever; the fixed 5 % critical bar flapped it low <-> critical every few
13
+ * minutes. Bands are relative to the floor instead:
14
+ * - `low`: free below 80 % of the floor for the whole grace window
15
+ * (the evictor is falling behind). Exit at 90 %.
16
+ * - `critical`: free below 50 % of the floor, no grace (writes are about to
17
+ * fail; the evictor is losing). Exit at 60 %.
18
+ * "Half the floor" was chosen over an absolute byte minimum because it scales
19
+ * with the operator's own setting and needs no second knob; it is far above
20
+ * the few MB a segment needs, so it fires before writes fail.
21
+ *
22
+ * A measurement that FAILED (`freePercent === null`) never changes the band and
23
+ * never opens an episode (D393): "could not read" is not evidence about space.
24
+ * Disabled / non-managed locations are absent from the sweep input and clear
25
+ * any open episode without an `ok` event.
7
26
  *
8
27
  * Pure, injected clock, one transition per band change.
9
28
  */
@@ -21,6 +40,12 @@ export interface StorageLocationBandSample {
21
40
  readonly locationName?: string;
22
41
  /** Free percent, or `null` when the probe failed. */
23
42
  readonly freePercent: number | null;
43
+ /** Free-space floor (percent) an evictor holds this location at; absent when
44
+ * the location is not managed by eviction. */
45
+ readonly floorPercent?: number;
46
+ /** Absolute figures, carried only so an edge can be logged with them. */
47
+ readonly freeBytes?: number;
48
+ readonly floorBytes?: number;
24
49
  }
25
50
  export interface StorageLocationBandEvent {
26
51
  readonly locationId: string;
@@ -28,13 +53,22 @@ export interface StorageLocationBandEvent {
28
53
  readonly locationName?: string;
29
54
  readonly band: StorageLocationBand;
30
55
  readonly freePercent: number | null;
56
+ readonly floorPercent?: number;
57
+ readonly freeBytes?: number;
58
+ readonly floorBytes?: number;
31
59
  readonly reason: string;
32
60
  readonly previous: StorageLocationBand | null;
33
61
  }
62
+ /** How long the evictor may sit below the low margin before it counts as behind. */
63
+ export declare const MANAGED_LOW_GRACE_MS: number;
34
64
  export declare class StorageLocationBandMonitor {
35
65
  private readonly config;
66
+ private readonly now;
36
67
  private readonly band;
37
- constructor(config?: StorageLocationBandConfig);
68
+ /** When free first went under `lowEnter` and has stayed there. */
69
+ private readonly belowLowSince;
70
+ constructor(config?: StorageLocationBandConfig, now?: () => number);
71
+ private nextBand;
38
72
  /**
39
73
  * Observe one sweep of managed locations. Locations missing from `samples`
40
74
  * drop their episode without emitting `ok` (disabled / removed).
@@ -3,6 +3,7 @@ import { BaseAddon } from "@camstack/types";
3
3
  interface StorageOrchestratorConfig extends Record<string, unknown> {}
4
4
  export declare class StorageOrchestratorAddon extends BaseAddon<StorageOrchestratorConfig> {
5
5
  private locationsKind;
6
+ private seedMarkersKind;
6
7
  private drainStateKind;
7
8
  private service;
8
9
  private pressureManager;
@@ -19,6 +20,8 @@ export declare class StorageOrchestratorAddon extends BaseAddon<StorageOrchestra
19
20
  private pressureTimer;
20
21
  private pressureSweepInFlight;
21
22
  private migration;
23
+ /** Guards every single-location repoint and arbitrates movers per physical root. */
24
+ private repointer;
22
25
  /**
23
26
  * Cached `providerId → nodeLocal` snapshot (SP1). Backs the orchestrator's
24
27
  * synchronous `NodeLocalResolver` — `getProviderInfo()` is async, so we
@@ -98,13 +101,15 @@ export declare class StorageOrchestratorAddon extends BaseAddon<StorageOrchestra
98
101
  * restart — the service never hydrates from / persists to SQLite.
99
102
  */
100
103
  private storeWireInFlight;
104
+ /** True while the locations kind is empty and its legacy table still holds rows. */
105
+ private adoptionIsPending;
101
106
  private ensureStoreWired;
102
107
  private storageProviders;
103
108
  /** Single-flighted pressure sweep — never overlaps a slow eviction round. */
104
109
  private runPressureSweep;
105
110
  /**
106
111
  * Sample free space for every managed location and emit NC band edges.
107
- * A failed capacity read stays `null` (critical) — never folded to 0 (D393).
112
+ * A failed capacity read stays `null` and changes no band — never folded to 0 (D393).
108
113
  */
109
114
  private emitLocationBandEvents;
110
115
  /**
@@ -153,7 +158,8 @@ export declare class StorageOrchestratorAddon extends BaseAddon<StorageOrchestra
153
158
  * A no-op for everything else — a rename, a mode change, a `multi` kind, or a
154
159
  * location being created for the first time.
155
160
  */
156
- private carryDataOnRepoint;
161
+ private upsertCarryingData;
162
+ private requireRepointer;
157
163
  private secretKeysFor;
158
164
  /**
159
165
  * Ring the doorbell: the SET of storage locations changed (D387).