@camstack/addon-pipeline 1.2.76 → 1.2.77

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 (31) hide show
  1. package/dist/{addon-utils-CTpQfjSR.js → addon-utils-bZcwLaVW.js} +1 -1
  2. package/dist/audio-analyzer/index.js +2 -2
  3. package/dist/audio-analyzer/index.mjs +1 -1
  4. package/dist/detection-pipeline/index.js +4 -4
  5. package/dist/detection-pipeline/index.mjs +2 -2
  6. package/dist/{dist-CZLjObZZ.js → dist-DQZzL0Ri.js} +355 -39
  7. package/dist/{dist-zksfWnEA.mjs → dist-HhcoV-Ky.mjs} +355 -39
  8. package/dist/{event-loop-stall-monitor-CcBQAI28.js → event-loop-stall-monitor-CBOo1oh_.js} +1 -1
  9. package/dist/{event-loop-stall-monitor-BuZA3loB.mjs → event-loop-stall-monitor-CqBEWGSZ.mjs} +1 -1
  10. package/dist/{lazy-sharp-SWR_D1Um.js → lazy-sharp-DIC1rpea.js} +1 -1
  11. package/dist/motion-wasm/index.js +2 -2
  12. package/dist/motion-wasm/index.mjs +1 -1
  13. package/dist/pipeline-runner/index.js +4 -4
  14. package/dist/pipeline-runner/index.mjs +3 -3
  15. package/dist/recorder/index.js +368 -18
  16. package/dist/recorder/index.mjs +367 -17
  17. package/dist/session-decode/decode-worker-child.js +2 -2
  18. package/dist/session-decode/decode-worker-child.mjs +1 -1
  19. package/dist/stream-broker/_stub.js +2 -2
  20. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-Dd6XOtVn.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-De_xzvjU.mjs} +3 -3
  21. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DHJSxHH1.mjs +26 -0
  22. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-Dg08SxUW.mjs +26 -0
  23. package/dist/stream-broker/{hostInit-COc_Q_xX.mjs → hostInit-CDS5frmk.mjs} +3 -3
  24. package/dist/stream-broker/index.js +306 -71
  25. package/dist/stream-broker/index.mjs +306 -71
  26. package/dist/stream-broker/remoteEntry.js +1 -1
  27. package/dist/{worker-protocol-DyX_HbaJ.mjs → worker-protocol-BNlLGLBV.mjs} +1 -1
  28. package/dist/{worker-protocol-DAQ1iZFK.js → worker-protocol-DZVGVAPU.js} +1 -1
  29. package/package.json +1 -1
  30. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-RYTOS6B-.mjs +0 -26
  31. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DfsLsn2e.mjs +0 -26
@@ -13645,6 +13645,17 @@ var LlmImageSchema = object({
13645
13645
  bytes: _instanceof(Uint8Array),
13646
13646
  mimeType: string()
13647
13647
  });
13648
+ /**
13649
+ * Retry policy. `enabled: false` is NOT the same as `maxAttempts: 1` in intent —
13650
+ * the flag is what a consumer table flips, the count is what the operator tunes.
13651
+ * A retry doubles the wall time of a call, so the two gates that run inside a
13652
+ * notification's budget keep it off (see `CONSUMER_RETRY_POLICY` in addon-ai).
13653
+ */
13654
+ var LlmRetryPolicySchema = object({
13655
+ enabled: boolean().default(false),
13656
+ /** Total attempts INCLUDING the first. 1 = no retry. */
13657
+ maxAttempts: number().int().min(1).max(5).default(1)
13658
+ });
13648
13659
  var LlmGenerateBaseInputSchema = object({
13649
13660
  /** Collection routing (the notification-output posture). */
13650
13661
  addonId: string().optional(),
@@ -13659,7 +13670,28 @@ var LlmGenerateBaseInputSchema = object({
13659
13670
  jsonSchema: record(string(), unknown()).optional(),
13660
13671
  /** Per-call override of the profile default. */
13661
13672
  maxTokens: number().int().positive().optional(),
13662
- temperature: number().optional()
13673
+ temperature: number().optional(),
13674
+ /** Per-call override of the profile default (nucleus sampling). */
13675
+ topP: number().min(0).max(1).optional(),
13676
+ /** Per-call override of the profile default (top-k sampling). */
13677
+ topK: number().int().positive().optional(),
13678
+ /** Per-call override of `profile.timeoutMs` — the total generation bound. */
13679
+ timeoutMs: number().int().positive().optional(),
13680
+ /** Per-call override; beats both the consumer table and the profile. */
13681
+ retry: LlmRetryPolicySchema.optional(),
13682
+ /**
13683
+ * Caller-minted id that makes this generation CANCELLABLE.
13684
+ *
13685
+ * Without it a caller that stops waiting cannot stop the work: the gates race
13686
+ * the call against 8 s and free their own slot when the timer wins, while the
13687
+ * generation upstream keeps running to `profile.timeoutMs` — 60 s by default,
13688
+ * on a single-threaded local model. The per-camera bound then counts WAITS,
13689
+ * not generations, and the real load is unbounded.
13690
+ *
13691
+ * `AbortSignal` cannot cross a process boundary; an id can. Pass one here and
13692
+ * `llm.cancel({ requestId })` tears the socket down.
13693
+ */
13694
+ requestId: string().optional()
13663
13695
  });
13664
13696
  /**
13665
13697
  * `llm-runtime` — node-side managed llama.cpp executor (spec §4). Registered
@@ -13698,8 +13730,49 @@ var ManagedRuntimeConfigSchema = object({
13698
13730
  gpuLayers: number().int().default(0),
13699
13731
  /** Default: cpus-2, clamped ≥1 (resolved node-side). */
13700
13732
  threads: number().int().optional(),
13701
- /** Concurrent slots. */
13733
+ /** Concurrent slots (`--parallel`). */
13702
13734
  parallel: number().int().default(1),
13735
+ /** Logical batch size (`-b`). Larger = faster prompt ingest, more RAM. */
13736
+ batchSize: number().int().positive().optional(),
13737
+ /** Physical batch / micro-batch (`-ub`). */
13738
+ ubatchSize: number().int().positive().optional(),
13739
+ /**
13740
+ * `--flash-attn`. Cuts KV-cache memory on the backends that implement it and
13741
+ * is a no-op elsewhere, so it is offered rather than assumed.
13742
+ */
13743
+ flashAttention: boolean().default(false),
13744
+ /**
13745
+ * `--mlock`. Pins the weights in RAM so the OS cannot page them out mid
13746
+ * inference. Costs the full model size in resident memory — which is exactly
13747
+ * what the RAM budget is counting.
13748
+ */
13749
+ mlock: boolean().default(false),
13750
+ /**
13751
+ * `--no-mmap`. Reads the whole GGUF up front instead of mapping it. Slower to
13752
+ * start, but avoids the page-fault stalls a network or spinning-disk model
13753
+ * store produces on every first token.
13754
+ */
13755
+ noMmap: boolean().default(false),
13756
+ /** `--cache-type-k` / `--cache-type-v` — quantising the KV cache is the
13757
+ * cheapest way to fit a longer context in the same RAM. */
13758
+ cacheTypeK: _enum([
13759
+ "f32",
13760
+ "f16",
13761
+ "q8_0",
13762
+ "q5_1",
13763
+ "q5_0",
13764
+ "q4_1",
13765
+ "q4_0"
13766
+ ]).optional(),
13767
+ cacheTypeV: _enum([
13768
+ "f32",
13769
+ "f16",
13770
+ "q8_0",
13771
+ "q5_1",
13772
+ "q5_0",
13773
+ "q4_1",
13774
+ "q4_0"
13775
+ ]).optional(),
13703
13776
  /** Else lazy: first generate boots it. */
13704
13777
  autoStart: boolean().default(false),
13705
13778
  /** 0 = never; frees RAM after quiet periods. */
@@ -13787,10 +13860,44 @@ var LlmProfileSchema = object({
13787
13860
  baseUrl: string().optional(),
13788
13861
  /** ConfigUISchema type:'password' — never round-trips (spec §5). */
13789
13862
  apiKey: string().optional(),
13863
+ /** Vision on/off. A vision call against a `false` profile is REFUSED, never
13864
+ * degraded to text — that shipped once and produced a confident answer to a
13865
+ * question about a picture nobody sent. */
13790
13866
  supportsVision: boolean(),
13791
13867
  temperature: number().min(0).max(2).optional(),
13868
+ /** Nucleus sampling. Every wire we speak has it. */
13869
+ topP: number().min(0).max(1).optional(),
13870
+ /** Top-k sampling. Carried only by the wires that have it — NEITHER OpenAI
13871
+ * wire does, and the client drops it there (measured: the request body gets
13872
+ * `top_p` and no `top_k`). The profile editor hides the field wherever it
13873
+ * would change nothing; `KINDS_WITH_TOP_K` is the single owner of that list. */
13874
+ topK: number().int().positive().optional(),
13792
13875
  maxTokens: number().int().positive().optional(),
13876
+ /** Prompt context window. Advisory for cloud kinds (they enforce their own);
13877
+ * for `managed-local` it is the llama.cpp `--ctx-size` the runtime starts
13878
+ * the model with, so it is the one field that changes a PROCESS. */
13879
+ contextLength: number().int().positive().optional(),
13880
+ /** Default system prompt. A caller's `system` REPLACES it (never appends —
13881
+ * two system prompts fighting is worse than either alone). */
13882
+ systemPrompt: string().optional(),
13883
+ /** Total generation bound — the only one a unary call has. */
13793
13884
  timeoutMs: number().int().positive().default(6e4),
13885
+ /** Wait for response headers only. */
13886
+ connectTimeoutMs: number().int().positive().default(1e4),
13887
+ /** Accepted, but no output yet — a cold GPU load lives here. */
13888
+ firstTokenTimeoutMs: number().int().positive().default(12e4),
13889
+ /** Output started then stopped. */
13890
+ idleTimeoutMs: number().int().positive().default(6e4),
13891
+ /** Profile-level default. The per-consumer table and a per-call override
13892
+ * both beat it — see `resolveRetryPolicy`. */
13893
+ retry: LlmRetryPolicySchema.default({
13894
+ enabled: false,
13895
+ maxAttempts: 1
13896
+ }),
13897
+ /** Whether this profile may use tools. The tool-call plumbing rides the
13898
+ * library; the REGISTRY of callable tools is ours and is empty in v1, so a
13899
+ * `true` here buys the wiring, not behaviour, until tools are registered. */
13900
+ toolsEnabled: boolean().default(false),
13794
13901
  extraHeaders: record(string(), string()).optional(),
13795
13902
  /** kind === 'managed-local' only (spec §4). */
13796
13903
  runtime: ManagedRuntimeConfigSchema.optional()
@@ -13852,7 +13959,10 @@ var ProfileRefInputSchema = object({
13852
13959
  addonId: string(),
13853
13960
  profileId: string()
13854
13961
  });
13855
- method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
13962
+ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(object({
13963
+ addonId: string().optional(),
13964
+ requestId: string()
13965
+ }), _void(), { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
13856
13966
  kind: "mutation",
13857
13967
  auth: "admin"
13858
13968
  }), method(ProfileRefInputSchema, _void(), {
@@ -15931,28 +16041,36 @@ var NcOccupancyConditionSchema = object({
15931
16041
  /**
15932
16042
  * Audio condition (IMMEDIATE trigger) — a rule on SOUND, not on a picture.
15933
16043
  *
15934
- * Operator-approved vocabulary (2026-08-12, option A — the same one the
15935
- * reference notifier uses, so an operator moving between them re-uses what
15936
- * they already know): a rule matches when, over a sampling window of
15937
- * `samplingSeconds`, at least `hitPercent`% of the audio samples in that
15938
- * window are HITS. A sample is a hit when it satisfies BOTH present filters:
15939
- *
15940
- * - `dbThreshold` its level is at or above this many dBFS (see
15941
- * {@link NC_AUDIO_DBFS_FLOOR}: negative-going, `0` = full scale);
15942
- * - `labels` the classifier put at least one of these labels on it.
15943
- *
15944
- * Both are OPTIONAL and independent, which is the point of the shape: a
15945
- * loudness rule ("something loud at 3am") needs no model to be right, and a
15946
- * label rule ("a dog barked") needs no threshold. **Fail-closed when NEITHER
15947
- * is given** a window in which every sample is trivially a hit would fire on
15948
- * silence, so the engine refuses such a condition rather than notifying on
15949
- * nothing (the schema cannot express "at least one of" without becoming a
15950
- * ZodEffects the cap path would have to special-case).
15951
- *
15952
- * `hitPercent` is over the samples the window actually HOLDS, and the window
15953
- * must be FULL before it can match a window that has been open for two
15954
- * seconds of its ten is 100% of nothing, and firing on it would make
15955
- * `samplingSeconds` decorative.
16044
+ * **TWO EXCLUSIVE MODES** (operator decision 2026-08-14, D157). Which one a
16045
+ * rule is in is not a stored field it is WHICH FILTER the rule carries, so
16046
+ * there is no second switch that can disagree with the first and every rule
16047
+ * authored before the decision migrates for free (`audioModeOf`):
16048
+ *
16049
+ * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
16050
+ * classifier labels with one of them. No window, no percentage:
16051
+ * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
16052
+ * `throttle` cooldown is the only brake. The per-label confidence floor is
16053
+ * the analyzer's (`classificationMinScore`, per device) — a label only
16054
+ * reaches this condition if the classifier was already confident enough.
16055
+ * - **LEVEL mode `dbThreshold` present, no labels.** The sampling window IS
16056
+ * the condition: at least `hitPercent`% of the samples over
16057
+ * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
16058
+ * {@link NC_AUDIO_DBFS_FLOOR}: negative-going, `0` = full scale). The window
16059
+ * must be FULL before it can match a window open for two of its ten
16060
+ * seconds is 100% of nothing.
16061
+ *
16062
+ * **Why label mode has no window.** It had one, and it never fired: the
16063
+ * analyzer emits ~1 audio frame per second but YAMNet only LABELS one to three
16064
+ * of them per episode, even through continuous crying. The measured maximum
16065
+ * `hitPercent` over the whole live history was 40 — under the shipped default
16066
+ * of 60, so a label rule could not fire at all, ever. A percentage of frames is
16067
+ * the wrong question to ask of a sparse classifier.
16068
+ *
16069
+ * **Fail-closed when NEITHER is given** — every sample would be a trivial hit
16070
+ * and the rule would fire on silence. The schema cannot express "exactly one
16071
+ * of" without becoming a ZodEffects the cap path would have to special-case, so
16072
+ * the exclusivity is enforced where every editor writes (`patchAudio`) and a
16073
+ * legacy rule carrying both resolves to LABEL (the mode that fires).
15956
16074
  *
15957
16075
  * Labels are the audio macro classes (`AUDIO_MACRO_LABELS` / the NC taxonomy's
15958
16076
  * `audio-*` ids). Both spellings are accepted — the matcher normalizes the
@@ -15960,13 +16078,13 @@ var NcOccupancyConditionSchema = object({
15960
16078
  * an operator who typed `dog` mean the same thing.
15961
16079
  */
15962
16080
  var NcAudioConditionSchema = object({
15963
- /** Audio macro labels; absent = any sound (level-only rule). */
16081
+ /** LABEL MODE: audio macro labels. Present fires on the first labelled frame. */
15964
16082
  labels: array(string().min(1)).min(1).optional(),
15965
- /** Level floor in dBFS (negative-going, `0` = full scale); absent = any level. */
16083
+ /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
15966
16084
  dbThreshold: number().min(-96).max(0).optional(),
15967
- /** Percentage of the window's samples that must be hits (1–100). */
16085
+ /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
15968
16086
  hitPercent: number().int().min(1).max(100).default(60),
15969
- /** Length of the sampling window in seconds. */
16087
+ /** LEVEL MODE ONLY: length of the sampling window in seconds. */
15970
16088
  samplingSeconds: number().int().min(1).max(300).default(10)
15971
16089
  });
15972
16090
  /**
@@ -26463,14 +26581,50 @@ var recordingExportCapability = {
26463
26581
  * thing except the comparator: `similarity` (CLIP cosine at the same ROI coords
26464
26582
  * vs condition-tagged references) and `llm` (vision-LLM judgment over the crop).
26465
26583
  *
26466
- * D14 device-config archetype (`deviceConfig.ui.kind:'widget'`) the framework
26467
- * derives the device-detail contribution; the provider carries NO hand-written
26468
- * settings-contribution methods. `status.kind:'push'` the engine pushes on
26469
- * every hysteresis flip / availability change; consumers never poll.
26470
- */
26471
- /** Extensible condition tag. Seeded 'day' | 'night'; open by design so more can
26472
- * be added without a wire break (matching falls back to any-condition refs). */
26584
+ * **No `deviceConfig`, deliberately.** This shipped as the D14 widget archetype,
26585
+ * which put a "Scenes" tab on one camera's detail page. That is the wrong shape
26586
+ * for the thing: a scene is a standing question about the property ("is the bin
26587
+ * still out"), and the operator's question is "which of my scenes have tripped",
26588
+ * across every camera at once — not "what does camera 617 think". Buried one
26589
+ * camera deep it also could not be found. The surface is now a top-level admin
26590
+ * page (`/scenes`, `pages/Scenes.tsx`) that lists every scene on every camera and
26591
+ * picks the camera inside the create flow, the same shape Events and Faces have.
26592
+ *
26593
+ * The consequence to keep in mind: `host/scene-monitor-editor` is gone from
26594
+ * `HOST_WIDGETS` too. `scripts/check-host-widget-resolves.ts` asserts BOTH
26595
+ * directions, so a registration nobody declares fails exactly as loudly as a
26596
+ * declaration nobody registers. The editor is imported directly by the page.
26597
+ *
26598
+ * `status.kind:'push'` — the engine pushes on every hysteresis flip /
26599
+ * availability change; consumers never poll.
26600
+ */
26601
+ /** Extensible condition tag. Seeded 'day' | 'ir' (the two variants the operator
26602
+ * captures) plus 'night' | 'dawn' | 'dusk' from the resolver's sun-times band.
26603
+ * Open by design so more can be added without a wire break.
26604
+ *
26605
+ * Matching does NOT fall back across conditions: cross-condition cosines are
26606
+ * not comparable, so "I have never seen this scene in this light" is reported
26607
+ * as `unknown`, never guessed. A day reference scored against an IR frame
26608
+ * collapses the cosine and would latch a false alarm every single night. */
26473
26609
  var SceneConditionSchema = string();
26610
+ /** `matched` = the baseline is what we see; `diverged` = it demonstrably is not;
26611
+ * `unknown` = we cannot judge (no reference for this condition, encoder model
26612
+ * changed, view shifted, no snapshot). `unknown` is a real value, not a null,
26613
+ * and never counts toward hysteresis in either direction. */
26614
+ var SceneVerdictSchema = _enum([
26615
+ "matched",
26616
+ "diverged",
26617
+ "unknown"
26618
+ ]);
26619
+ /** Why a scene cannot judge. Named, because this feature's failure mode is
26620
+ * silence that reads as "nothing has happened". */
26621
+ var SceneUnavailableSchema = _enum([
26622
+ "no-reference-for-condition",
26623
+ "view-shifted",
26624
+ "no-vision-profile",
26625
+ "encoder-model-changed",
26626
+ "no-snapshot"
26627
+ ]);
26474
26628
  /** One captured reference — condition-tagged, model-version-gated. `embedding`
26475
26629
  * is `number[]` (Float32Array does NOT survive MsgPack/UDS). */
26476
26630
  var SceneReferenceSchema = object({
@@ -26478,7 +26632,14 @@ var SceneReferenceSchema = object({
26478
26632
  modelId: string(),
26479
26633
  condition: SceneConditionSchema,
26480
26634
  capturedAt: number(),
26481
- thumbnailMediaId: string().optional()
26635
+ thumbnailMediaId: string().optional(),
26636
+ /** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
26637
+ * anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
26638
+ * normalized rect frame a different piece of world, and the scene would
26639
+ * diverge forever with a perfectly plausible cosine. Checked LAZILY, only
26640
+ * when hysteresis is about to flip — one extra encode per candidate
26641
+ * transition, not per poll. */
26642
+ anchorEmbedding: array(number()).optional()
26482
26643
  });
26483
26644
  var SceneMonitorStateSchema = object({
26484
26645
  id: string(),
@@ -26500,6 +26661,25 @@ var SceneCheckSchema = discriminatedUnion("mode", [object({
26500
26661
  profileId: string().optional(),
26501
26662
  hysteresisCount: number().int().positive()
26502
26663
  })]);
26664
+ var SCENE_DEFAULT_ANCHOR_THRESHOLD = .85;
26665
+ /**
26666
+ * Vision-model adjudication of a candidate flip. Field names deliberately
26667
+ * mirror `NcConfirmSchema` so an operator meets one vocabulary, not two.
26668
+ *
26669
+ * `onTimeout` defaults to **'hold'**, the OPPOSITE of `NcConfirmGate`'s
26670
+ * fail-open: a notification suppressed is the worse error there, but a vision
26671
+ * model that timed out has not told us the bin is gone, and a latch is a
26672
+ * stateful claim that costs the operator a trip to reset.
26673
+ */
26674
+ var SceneConfirmSchema = object({
26675
+ enabled: boolean().default(false),
26676
+ prompt: string().min(1).max(1e3),
26677
+ profileId: string().optional(),
26678
+ timeoutMs: number().int().min(1e3).max(2e4).default(8e3),
26679
+ maxImagePx: number().int().min(64).max(2048).default(448),
26680
+ /** What a timeout / unavailable model means for the PENDING flip. */
26681
+ onTimeout: _enum(["flip", "hold"]).default("hold")
26682
+ });
26503
26683
  var SceneMonitorSchema = object({
26504
26684
  id: string(),
26505
26685
  label: string(),
@@ -26518,7 +26698,41 @@ var SceneMonitorSchema = object({
26518
26698
  lastConfidence: number().nullable(),
26519
26699
  currentCondition: SceneConditionSchema.nullable(),
26520
26700
  availability: _enum(["ok", "unavailable"]),
26521
- unavailableReason: string().nullable()
26701
+ unavailableReason: string().nullable(),
26702
+ /** Which state is "the initial screen". `null` until the first capture. */
26703
+ baselineStateId: string().nullable(),
26704
+ /** Which boolean drives notification rules and any export. */
26705
+ emit: _enum(["latched", "live"]).default("latched"),
26706
+ /** Live: does the region match the baseline RIGHT NOW. */
26707
+ verdict: SceneVerdictSchema,
26708
+ /** Has it been `diverged` at least once since `armedAt` — the operator's boolean. */
26709
+ latched: boolean(),
26710
+ /** Last reset (or creation). */
26711
+ armedAt: number(),
26712
+ divergedAt: number().nullable(),
26713
+ restoredAt: number().nullable(),
26714
+ /** A check is only COUNTED when the device has been quiet this long. Motion
26715
+ * during the window DISCARDS the observation — a car pulling up in front of
26716
+ * the bin must not be able to spend hysteresis credit. */
26717
+ quietSeconds: number().int().min(0).max(3600).default(60),
26718
+ /** An observation only advances the pending count when it is at least this
26719
+ * far from the previously counted one, so N agreeing checks span real time
26720
+ * rather than N adjacent polls inside one occlusion. */
26721
+ minObservationSpacingSec: number().int().min(0).max(3600).default(120),
26722
+ /** Vision-model adjudication of a candidate flip. Similarity primary only. */
26723
+ confirm: SceneConfirmSchema.optional(),
26724
+ /** Whole-frame anchor cosine below which a flip is REFUSED as `view-shifted`. */
26725
+ anchorThreshold: number().min(0).max(1).default(SCENE_DEFAULT_ANCHOR_THRESHOLD),
26726
+ /** Clear the latch on its own when the scene matches again? Default false —
26727
+ * `restoredAt` and the `scene-restored` edge are recorded regardless, so an
26728
+ * automation can react to the bin coming back without the operator's own
26729
+ * alarm silently clearing itself. */
26730
+ autoRestore: boolean().default(false),
26731
+ /** Named cause when `verdict === 'unknown'`. */
26732
+ unavailable: SceneUnavailableSchema.nullable(),
26733
+ /** Conditions that have at least one comparable reference — the coverage line
26734
+ * ("day ✓ · ir ✓ · dusk ✗") that turns a silent fallback into a visible fact. */
26735
+ coveredConditions: array(SceneConditionSchema)
26522
26736
  });
26523
26737
  var SceneMonitorStatusSchema = object({
26524
26738
  monitors: array(SceneMonitorSchema),
@@ -26551,7 +26765,14 @@ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSche
26551
26765
  "both"
26552
26766
  ]).optional(),
26553
26767
  checkIntervalSec: number().optional(),
26554
- check: SceneCheckSchema.optional()
26768
+ check: SceneCheckSchema.optional(),
26769
+ emit: _enum(["latched", "live"]).optional(),
26770
+ quietSeconds: number().int().min(0).max(3600).optional(),
26771
+ minObservationSpacingSec: number().int().min(0).max(3600).optional(),
26772
+ anchorThreshold: number().min(0).max(1).optional(),
26773
+ autoRestore: boolean().optional(),
26774
+ /** `null` clears the vision-model adjudicator. */
26775
+ confirm: SceneConfirmSchema.nullable().optional()
26555
26776
  })
26556
26777
  }), _void(), {
26557
26778
  kind: "mutation",
@@ -26588,6 +26809,14 @@ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSche
26588
26809
  }), _void(), {
26589
26810
  kind: "mutation",
26590
26811
  auth: "admin"
26812
+ }), method(object({
26813
+ deviceId: number(),
26814
+ monitorId: string(),
26815
+ /** Defaults to TRUE at the provider seam — see `SCENE_RESET_RECAPTURES`. */
26816
+ recapture: boolean().optional()
26817
+ }), _void(), {
26818
+ kind: "mutation",
26819
+ auth: "admin"
26591
26820
  });
26592
26821
  /**
26593
26822
  * Per-stage gating mode applied to the zones a rule references.
@@ -26896,12 +27125,64 @@ var NetworkAddressSchema = object({
26896
27125
  family: string(),
26897
27126
  internal: boolean()
26898
27127
  });
27128
+ /**
27129
+ * Provenance of the site coordinates, and the whole reason this is not just two
27130
+ * numbers.
27131
+ *
27132
+ * - `operator-set` — a human typed it, or accepted a detection. Authoritative;
27133
+ * nothing overwrites it.
27134
+ * - `derived-from-ip` — the hub geolocated its own public IP once, because a
27135
+ * default that is right to a few kilometres beats the coarse UTC clock split
27136
+ * the sun-times consumers otherwise fall back to.
27137
+ *
27138
+ * The UI shows which one it is. An operator who cannot tell a guess from their
27139
+ * own input will eventually trust the guess.
27140
+ */
27141
+ var SiteLocationSourceSchema = _enum(["operator-set", "derived-from-ip"]);
27142
+ /**
27143
+ * The read shape: the location plus the honest state of the one-shot derivation.
27144
+ *
27145
+ * `derivationAttemptedAt` is what makes the "one call, ever" contract
27146
+ * inspectable. When it is set and `location` is null, the geo-IP lookup ran and
27147
+ * failed; the hub will NOT try again on its own — the fallback is declared
27148
+ * (consumers degrade to their own last resort) and the operator either types the
27149
+ * coordinates or presses detect.
27150
+ */
27151
+ var SiteLocationStatusSchema = object({
27152
+ location: object({
27153
+ /** WGS84 decimal degrees. */
27154
+ latitude: number().min(-90).max(90),
27155
+ longitude: number().min(-180).max(180),
27156
+ source: SiteLocationSourceSchema,
27157
+ /** Epoch ms the value was last written. */
27158
+ updatedAt: number(),
27159
+ /**
27160
+ * Human-readable place the geo-IP service reported ("Napoli, IT"). Display
27161
+ * only — never parsed, never matched on. Absent for an operator-typed value.
27162
+ */
27163
+ label: string().optional()
27164
+ }).nullable(),
27165
+ derivationAttemptedAt: number().nullable(),
27166
+ /** Why the last derivation failed, for the UI to show instead of a shrug. */
27167
+ derivationError: string().nullable()
27168
+ });
27169
+ /** `null` clears the location and re-arms nothing — the derivation stays spent. */
27170
+ var SetSiteLocationInputSchema = object({
27171
+ latitude: number().min(-90).max(90),
27172
+ longitude: number().min(-180).max(180)
27173
+ }).nullable();
26899
27174
  method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), method(_void(), FeatureManifestSchema), method(_void(), array(NetworkAddressSchema).readonly()), method(_void(), unknown().nullable(), { auth: "admin" }), method(record(string(), unknown()), _null(), {
26900
27175
  kind: "mutation",
26901
27176
  auth: "admin"
26902
27177
  }), method(_void(), _void(), {
26903
27178
  kind: "mutation",
26904
27179
  auth: "admin"
27180
+ }), method(_void(), SiteLocationStatusSchema), method(SetSiteLocationInputSchema, SiteLocationStatusSchema, {
27181
+ kind: "mutation",
27182
+ auth: "admin"
27183
+ }), method(_void(), SiteLocationStatusSchema, {
27184
+ kind: "mutation",
27185
+ auth: "admin"
26905
27186
  });
26906
27187
  object({
26907
27188
  /** True when the device's tamper switch / case-open contact is
@@ -29632,6 +29913,12 @@ Object.freeze({
29632
29913
  addonId: null,
29633
29914
  access: "create"
29634
29915
  },
29916
+ "llm.cancel": {
29917
+ capName: "llm",
29918
+ capScope: "system",
29919
+ addonId: null,
29920
+ access: "create"
29921
+ },
29635
29922
  "llm.deleteModel": {
29636
29923
  capName: "llm",
29637
29924
  capScope: "system",
@@ -31882,6 +32169,12 @@ Object.freeze({
31882
32169
  addonId: null,
31883
32170
  access: "create"
31884
32171
  },
32172
+ "sceneMonitor.resetScene": {
32173
+ capName: "scene-monitor",
32174
+ capScope: "device",
32175
+ addonId: null,
32176
+ access: "delete"
32177
+ },
31885
32178
  "sceneMonitor.updateScene": {
31886
32179
  capName: "scene-monitor",
31887
32180
  capScope: "device",
@@ -32560,6 +32853,12 @@ Object.freeze({
32560
32853
  addonId: null,
32561
32854
  access: "create"
32562
32855
  },
32856
+ "system.detectSiteLocation": {
32857
+ capName: "system",
32858
+ capScope: "system",
32859
+ addonId: null,
32860
+ access: "create"
32861
+ },
32563
32862
  "system.featureFlags": {
32564
32863
  capName: "system",
32565
32864
  capScope: "system",
@@ -32578,6 +32877,12 @@ Object.freeze({
32578
32877
  addonId: null,
32579
32878
  access: "view"
32580
32879
  },
32880
+ "system.getSiteLocation": {
32881
+ capName: "system",
32882
+ capScope: "system",
32883
+ addonId: null,
32884
+ access: "view"
32885
+ },
32581
32886
  "system.health": {
32582
32887
  capName: "system",
32583
32888
  capScope: "system",
@@ -32602,6 +32907,12 @@ Object.freeze({
32602
32907
  addonId: null,
32603
32908
  access: "create"
32604
32909
  },
32910
+ "system.setSiteLocation": {
32911
+ capName: "system",
32912
+ capScope: "system",
32913
+ addonId: null,
32914
+ access: "create"
32915
+ },
32605
32916
  "terminalSession.adoptLegacyMonitor": {
32606
32917
  capName: "terminal-session",
32607
32918
  capScope: "system",
@@ -34564,6 +34875,11 @@ Object.freeze({
34564
34875
  form: "single",
34565
34876
  optional: false
34566
34877
  }],
34878
+ "sceneMonitor.resetScene": [{
34879
+ name: "deviceId",
34880
+ form: "single",
34881
+ optional: false
34882
+ }],
34567
34883
  "sceneMonitor.updateScene": [{
34568
34884
  name: "deviceId",
34569
34885
  form: "single",
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-CZLjObZZ.js");
1
+ const require_dist = require("./dist-DQZzL0Ri.js");
2
2
  let node_perf_hooks = require("node:perf_hooks");
3
3
  //#region src/detection-pipeline/registry/model-catalogs.ts
4
4
  var HF_REPO = "camstack/camstack-models";
@@ -1,4 +1,4 @@
1
- import { a as COCO_TO_MACRO, i as COCO_80_LABELS, r as AUDIO_MACRO_LABELS, z as hfModelUrl } from "./dist-zksfWnEA.mjs";
1
+ import { a as COCO_TO_MACRO, i as COCO_80_LABELS, r as AUDIO_MACRO_LABELS, z as hfModelUrl } from "./dist-HhcoV-Ky.mjs";
2
2
  import { PerformanceObserver } from "node:perf_hooks";
3
3
  //#region src/detection-pipeline/registry/model-catalogs.ts
4
4
  var HF_REPO = "camstack/camstack-models";
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-CZLjObZZ.js");
1
+ const require_dist = require("./dist-DQZzL0Ri.js");
2
2
  let node_url = require("node:url");
3
3
  let node_module = require("node:module");
4
4
  let node_path = require("node:path");
@@ -2,8 +2,8 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-CZLjObZZ.js");
6
- const require_lazy_sharp = require("../lazy-sharp-SWR_D1Um.js");
5
+ const require_dist = require("../dist-DQZzL0Ri.js");
6
+ const require_lazy_sharp = require("../lazy-sharp-DIC1rpea.js");
7
7
  let node_path = require("node:path");
8
8
  let node_fs = require("node:fs");
9
9
  //#region src/motion-wasm/wasm-motion-detector.ts
@@ -1,4 +1,4 @@
1
- import { H as motionDetectionCapability, R as evaluateZoneRules, ft as hydrateSchema, st as BaseAddon, ut as DeviceType } from "../dist-zksfWnEA.mjs";
1
+ import { H as motionDetectionCapability, R as evaluateZoneRules, ft as hydrateSchema, st as BaseAddon, ut as DeviceType } from "../dist-HhcoV-Ky.mjs";
2
2
  import { t as getSharp } from "../lazy-sharp-6oymT_yf.mjs";
3
3
  import { join } from "node:path";
4
4
  import { readFileSync } from "node:fs";
@@ -2,11 +2,11 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-CZLjObZZ.js");
6
- const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-CcBQAI28.js");
7
- const require_worker_protocol = require("../worker-protocol-DAQ1iZFK.js");
5
+ const require_dist = require("../dist-DQZzL0Ri.js");
6
+ const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-CBOo1oh_.js");
7
+ const require_worker_protocol = require("../worker-protocol-DZVGVAPU.js");
8
8
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
9
- const require_lazy_sharp = require("../lazy-sharp-SWR_D1Um.js");
9
+ const require_lazy_sharp = require("../lazy-sharp-DIC1rpea.js");
10
10
  const require_remote_restream = require("../remote-restream-BYbAsgUf.js");
11
11
  let sharp = require("sharp");
12
12
  sharp = require_dist.__toESM(sharp);
@@ -1,6 +1,6 @@
1
- import { At as string, Ct as boolean, Dt as number, Et as literal, K as pipelineRunnerCapability, M as deriveDetailCropRect, Mt as EventCategory, Ot as object, St as array, Tt as lazy, U as pickDetailCropConvention, W as pickNativeLeaseOverride, dt as createEvent, gt as nodePin, j as defineCustomActions, k as customAction, s as DEFAULT_DETAIL_CROP_CONVENTION, st as BaseAddon, tt as errMsg, u as DEVICE_BACKEND_TO_FORMAT, xt as _enum } from "../dist-zksfWnEA.mjs";
2
- import { c as getStepDefinition, t as attributeStall } from "../event-loop-stall-monitor-BuZA3loB.mjs";
3
- import { a as nativeLeaseSettingEnv, t as isWorkerReply } from "../worker-protocol-DyX_HbaJ.mjs";
1
+ import { At as string, Ct as boolean, Dt as number, Et as literal, K as pipelineRunnerCapability, M as deriveDetailCropRect, Mt as EventCategory, Ot as object, St as array, Tt as lazy, U as pickDetailCropConvention, W as pickNativeLeaseOverride, dt as createEvent, gt as nodePin, j as defineCustomActions, k as customAction, s as DEFAULT_DETAIL_CROP_CONVENTION, st as BaseAddon, tt as errMsg, u as DEVICE_BACKEND_TO_FORMAT, xt as _enum } from "../dist-HhcoV-Ky.mjs";
2
+ import { c as getStepDefinition, t as attributeStall } from "../event-loop-stall-monitor-CqBEWGSZ.mjs";
3
+ import { a as nativeLeaseSettingEnv, t as isWorkerReply } from "../worker-protocol-BNlLGLBV.mjs";
4
4
  import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
5
5
  import { t as getSharp } from "../lazy-sharp-6oymT_yf.mjs";
6
6
  import { i as withDetectionIntent, n as profileForStreamId, t as isRemoteRestream } from "../remote-restream-Ci7RXNGb.mjs";