camstack 1.2.44 → 1.2.46

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.
@@ -23633,9 +23633,9 @@ var require_zod = __commonJS({
23633
23633
  }
23634
23634
  });
23635
23635
 
23636
- // ../system/dist/dist-D5Jhh8Vk.js
23637
- var require_dist_D5Jhh8Vk = __commonJS({
23638
- "../system/dist/dist-D5Jhh8Vk.js"(exports) {
23636
+ // ../system/dist/dist-jRSrzoXr.js
23637
+ var require_dist_jRSrzoXr = __commonJS({
23638
+ "../system/dist/dist-jRSrzoXr.js"(exports) {
23639
23639
  "use strict";
23640
23640
  var zod = require_zod();
23641
23641
  var EventCategory = /* @__PURE__ */ (function(EventCategory2) {
@@ -34136,7 +34136,23 @@ var require_dist_D5Jhh8Vk = __commonJS({
34136
34136
  updatedAt: zod.z.number(),
34137
34137
  /** Failure detail — present on a `dead` row. */
34138
34138
  error: zod.z.string().optional(),
34139
- subject: NcHistorySubjectSchema
34139
+ subject: NcHistorySubjectSchema,
34140
+ /**
34141
+ * Ids of the artefacts (still, then gif, then clip) this row's successful
34142
+ * delivery indexed in the artefact library — a REFERENCE, never the bytes
34143
+ * (an artefact is often megabytes; this row is durable JSON rewritten on
34144
+ * every delivery attempt). Absent on a row still pending/dead, a row
34145
+ * delivered before this field shipped, or a wiring with no artefact index.
34146
+ *
34147
+ * Resolve one to a fetchable URL with `resolveArtifactUrl` — an id
34148
+ * outlives any one URL's TTL, so a caller mints a fresh link on demand
34149
+ * rather than trusting one frozen at delivery time. `resolveArtifactUrl`
34150
+ * also answers `null` for an id whose artefact has since expired past the
34151
+ * retained shelf's own age bound — the degrade a caller (the Home
34152
+ * Assistant export) must render as "no image right now", never as a
34153
+ * broken link.
34154
+ */
34155
+ artifactIds: zod.z.array(zod.z.string().min(1)).optional()
34140
34156
  });
34141
34157
  var NcHistoryFilterSchema = zod.z.object({
34142
34158
  ruleId: zod.z.string().optional(),
@@ -34382,6 +34398,20 @@ var require_dist_D5Jhh8Vk = __commonJS({
34382
34398
  */
34383
34399
  getHistory: method(zod.z.object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), zod.z.object({ entries: zod.z.array(NcHistoryEntrySchema) }), { auth: "admin" }),
34384
34400
  /**
34401
+ * Mint a fresh, externally-reachable URL for one artefact a history row
34402
+ * named in {@link NcHistoryEntrySchema.shape.artifactIds} — the SAME
34403
+ * signed-link mechanism the dispatcher uses to attach media to an
34404
+ * outgoing notification (one derivation; this never re-implements it).
34405
+ *
34406
+ * `url: null` on THREE causes a caller must treat identically ("no image
34407
+ * right now", never a broken link): the id is unknown, its artefact has
34408
+ * expired past the retained shelf's own age bound, or this install has no
34409
+ * externally-reachable base URL. A caller that received a URL on a
34410
+ * previous call and now gets `null` must stop showing it — a link that
34411
+ * worked five minutes ago is not proof it works now.
34412
+ */
34413
+ resolveArtifactUrl: method(zod.z.object({ artifactId: zod.z.string().min(1) }), zod.z.object({ url: zod.z.string().nullable() }), { auth: "admin" }),
34414
+ /**
34385
34415
  * Snooze windows currently in effect, plus any whose digest has not yet
34386
34416
  * gone out. `caller: 'required'`: a user sees their OWN windows and the
34387
34417
  * global ones that silence them, never another person's private silence.
@@ -34621,6 +34651,726 @@ var require_dist_D5Jhh8Vk = __commonJS({
34621
34651
  getDescriptor: method(zod.z.void(), OauthIntegrationDescriptorSchema, { auth: "admin" })
34622
34652
  }
34623
34653
  };
34654
+ var NativeCropRefSchema = zod.z.object({
34655
+ /** Handle keying the retained native surface (node-pinned to its owner). */
34656
+ handle: FrameHandleSchema,
34657
+ /** The parent crop's padded/clamped rectangle in FRAME-space pixels. */
34658
+ cropFrameSpace: zod.z.object({
34659
+ x: zod.z.number(),
34660
+ y: zod.z.number(),
34661
+ w: zod.z.number(),
34662
+ h: zod.z.number()
34663
+ })
34664
+ });
34665
+ zod.z.object({
34666
+ crop: zod.z.object({
34667
+ left: zod.z.number(),
34668
+ top: zod.z.number(),
34669
+ width: zod.z.number().positive(),
34670
+ height: zod.z.number().positive()
34671
+ }).optional(),
34672
+ content: zod.z.object({
34673
+ width: zod.z.number().int().positive(),
34674
+ height: zod.z.number().int().positive()
34675
+ }),
34676
+ fit: zod.z.enum(["stretch", "contain"]),
34677
+ format: zod.z.enum([
34678
+ "rgb",
34679
+ "gray",
34680
+ "jpeg"
34681
+ ])
34682
+ });
34683
+ var FrameRefSchema = zod.z.object({
34684
+ registryId: zod.z.string().min(1),
34685
+ id: zod.z.string().min(1),
34686
+ width: zod.z.number().int().positive(),
34687
+ height: zod.z.number().int().positive(),
34688
+ format: zod.z.enum(["rgb", "gray"]),
34689
+ timestamp: zod.z.number(),
34690
+ capturedAt: zod.z.number().optional()
34691
+ });
34692
+ var ModelFormatSchema$1 = zod.z.enum([
34693
+ "onnx",
34694
+ "coreml",
34695
+ "openvino",
34696
+ "tflite",
34697
+ "pt",
34698
+ "gguf"
34699
+ ]);
34700
+ var PipelineSlotSchema = zod.z.enum([
34701
+ "detector",
34702
+ "cropper",
34703
+ "classifier",
34704
+ "refiner",
34705
+ "audio-classifier"
34706
+ ]);
34707
+ var PipelineEngineChoiceSchema = zod.z.object({
34708
+ runtime: zod.z.enum(["node", "python"]),
34709
+ backend: zod.z.string(),
34710
+ format: ModelFormatSchema$1,
34711
+ device: zod.z.string().optional()
34712
+ });
34713
+ var EngineDeviceInfoSchema = zod.z.object({
34714
+ id: zod.z.string(),
34715
+ label: zod.z.string(),
34716
+ description: zod.z.string().optional()
34717
+ });
34718
+ var AvailableEngineSchema = zod.z.object({
34719
+ engine: PipelineEngineChoiceSchema,
34720
+ devices: zod.z.array(EngineDeviceInfoSchema).readonly(),
34721
+ defaultDevice: zod.z.string()
34722
+ });
34723
+ var PipelineDefaultStepSchema = zod.z.lazy(() => zod.z.object({
34724
+ addonId: zod.z.string(),
34725
+ addonName: zod.z.string(),
34726
+ slot: PipelineSlotSchema,
34727
+ inputClasses: zod.z.array(zod.z.string()).readonly(),
34728
+ outputClasses: zod.z.array(zod.z.string()).readonly(),
34729
+ enabled: zod.z.boolean(),
34730
+ modelId: zod.z.string(),
34731
+ children: zod.z.array(PipelineDefaultStepSchema).readonly(),
34732
+ group: zod.z.string().optional(),
34733
+ settings: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
34734
+ }));
34735
+ var PipelineTemplateStepSchema = zod.z.lazy(() => zod.z.object({
34736
+ addonId: zod.z.string(),
34737
+ enabled: zod.z.boolean(),
34738
+ modelId: zod.z.string(),
34739
+ children: zod.z.array(PipelineTemplateStepSchema).readonly(),
34740
+ settings: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
34741
+ }));
34742
+ var PipelineTemplateSchema$1 = zod.z.object({
34743
+ id: zod.z.string(),
34744
+ name: zod.z.string(),
34745
+ createdAt: zod.z.string(),
34746
+ updatedAt: zod.z.string(),
34747
+ engine: PipelineEngineChoiceSchema,
34748
+ steps: zod.z.array(PipelineTemplateStepSchema).readonly()
34749
+ });
34750
+ var PipelineModelOptionSchema = zod.z.object({
34751
+ id: zod.z.string(),
34752
+ name: zod.z.string(),
34753
+ formats: zod.z.record(zod.z.string(), zod.z.object({
34754
+ downloaded: zod.z.boolean(),
34755
+ sizeMB: zod.z.number()
34756
+ })),
34757
+ group: ModelVariantGroupSchema.optional(),
34758
+ legacy: zod.z.boolean().optional(),
34759
+ provider: ModelProviderIdSchema.optional()
34760
+ });
34761
+ var ConfigFieldBridge = zod.z.custom();
34762
+ var PipelineAddonSchemaSchema = zod.z.object({
34763
+ id: zod.z.string(),
34764
+ name: zod.z.string(),
34765
+ slot: PipelineSlotSchema,
34766
+ inputClasses: zod.z.array(zod.z.string()).readonly(),
34767
+ outputClasses: zod.z.array(zod.z.string()).readonly(),
34768
+ childSlots: zod.z.array(PipelineSlotSchema).readonly(),
34769
+ models: zod.z.array(PipelineModelOptionSchema).readonly(),
34770
+ defaultModelId: zod.z.string(),
34771
+ defaultModelIdByFormat: zod.z.record(zod.z.string(), zod.z.string()).optional(),
34772
+ enabledByDefault: zod.z.boolean().optional(),
34773
+ backfillIntoExistingOverrides: zod.z.boolean().optional(),
34774
+ defaultConfidence: zod.z.number(),
34775
+ group: zod.z.string().optional(),
34776
+ configSchema: zod.z.array(ConfigFieldBridge).readonly().optional()
34777
+ });
34778
+ var PipelineSlotSchemaSchema = zod.z.object({
34779
+ id: PipelineSlotSchema,
34780
+ label: zod.z.string(),
34781
+ priority: zod.z.number(),
34782
+ parentSlot: PipelineSlotSchema.nullable(),
34783
+ addons: zod.z.array(PipelineAddonSchemaSchema).readonly()
34784
+ });
34785
+ var PipelineSchemaSchema = zod.z.object({
34786
+ availableEngines: zod.z.array(AvailableEngineSchema).readonly(),
34787
+ selectedEngine: PipelineEngineChoiceSchema,
34788
+ slots: zod.z.array(PipelineSlotSchemaSchema).readonly()
34789
+ });
34790
+ var EngineProvisioningSchema = zod.z.object({
34791
+ runtimeId: zod.z.enum([
34792
+ "onnx",
34793
+ "openvino",
34794
+ "coreml",
34795
+ "edgetpu"
34796
+ ]).nullable(),
34797
+ device: zod.z.string().nullable(),
34798
+ state: zod.z.enum([
34799
+ "idle",
34800
+ "installing",
34801
+ "verifying",
34802
+ "ready",
34803
+ "failed"
34804
+ ]),
34805
+ progress: zod.z.number().optional(),
34806
+ error: zod.z.string().optional(),
34807
+ nextRetryAt: zod.z.number().optional(),
34808
+ /**
34809
+ * Gate A (config-correctness gate at engine change): human-readable
34810
+ * config issues surfaced EAGERLY when the node's engine changes — model
34811
+ * substitutions ("chose X, running Y") and zero-build steps ("no model
34812
+ * has a <format> build"). Additive/optional: informational only, never
34813
+ * enforced here — `assertEngineReady` (readiness) still gates inference.
34814
+ * Absent/empty when the node-default tree resolves cleanly.
34815
+ */
34816
+ configIssues: zod.z.array(zod.z.string()).optional()
34817
+ });
34818
+ var PipelineStepInputSchema = zod.z.lazy(() => zod.z.object({
34819
+ addonId: zod.z.string(),
34820
+ modelId: zod.z.string().optional(),
34821
+ enabled: zod.z.boolean().default(true),
34822
+ children: zod.z.array(PipelineStepInputSchema).optional(),
34823
+ settings: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
34824
+ jumpDeviceKey: zod.z.string().optional()
34825
+ }));
34826
+ var ModelSubstitutionSchema = zod.z.object({
34827
+ addonId: zod.z.string(),
34828
+ chosen: zod.z.string(),
34829
+ running: zod.z.string(),
34830
+ format: zod.z.string()
34831
+ });
34832
+ var PipelineValidationIssueSchema = zod.z.object({
34833
+ addonId: zod.z.string(),
34834
+ kind: zod.z.enum(["unknown-addon", "no-format-build"]),
34835
+ detail: zod.z.string()
34836
+ });
34837
+ var PipelineValidationResultSchema = zod.z.object({
34838
+ ok: zod.z.boolean(),
34839
+ issues: zod.z.array(PipelineValidationIssueSchema).readonly(),
34840
+ substitutions: zod.z.array(ModelSubstitutionSchema).readonly(),
34841
+ /** The node's `currentEngine.format` this validation ran against. */
34842
+ format: zod.z.string()
34843
+ });
34844
+ var ReferenceImageEntrySchema = zod.z.object({
34845
+ filename: zod.z.string(),
34846
+ stepIds: zod.z.array(zod.z.string()).readonly().optional()
34847
+ });
34848
+ var ReferenceImageBodySchema = zod.z.object({
34849
+ base64: zod.z.string(),
34850
+ filename: zod.z.string()
34851
+ });
34852
+ var ReferenceAudioEntrySchema = zod.z.object({
34853
+ filename: zod.z.string(),
34854
+ sizeKb: zod.z.number()
34855
+ });
34856
+ var ReferenceAudioBodySchema = zod.z.object({ base64: zod.z.string() });
34857
+ var AudioBackendSchema = zod.z.object({
34858
+ id: zod.z.string(),
34859
+ name: zod.z.string(),
34860
+ description: zod.z.string(),
34861
+ available: zod.z.boolean(),
34862
+ /**
34863
+ * Raw classifier labels this backend can emit (e.g. YAMNet's
34864
+ * 521-class set or Apple SoundAnalysis's 303-class set). Used by
34865
+ * the benchmark UI to populate the `enabledMicroClasses` filter
34866
+ * specific to the selected backend without a separate fetch.
34867
+ */
34868
+ rawLabels: zod.z.array(zod.z.string()).readonly().optional()
34869
+ });
34870
+ var AudioCapabilitiesSchema = zod.z.object({
34871
+ activeBackend: zod.z.string(),
34872
+ availableBackends: zod.z.array(AudioBackendSchema).readonly(),
34873
+ sampleRate: zod.z.number(),
34874
+ chunkDurationMs: zod.z.number()
34875
+ });
34876
+ var DownloadModelResultSchema = zod.z.object({
34877
+ filePath: zod.z.string(),
34878
+ sizeMB: zod.z.number(),
34879
+ durationMs: zod.z.number()
34880
+ });
34881
+ var AudioTestResultSchema = zod.z.object({
34882
+ success: zod.z.boolean(),
34883
+ error: zod.z.string().optional(),
34884
+ frame: zod.z.custom().optional()
34885
+ });
34886
+ var PipelineConfigBridge = zod.z.custom();
34887
+ var ConfigUISchemaBridge = zod.z.custom();
34888
+ var ConfigUISchemaNullableBridge = zod.z.custom();
34889
+ var InferenceCapabilitiesBridge = zod.z.custom();
34890
+ var ModelAvailabilityListBridge = zod.z.custom();
34891
+ var PipelineRunResultBridge = zod.z.custom();
34892
+ var pipelineExecutorCapability = {
34893
+ name: "pipeline-executor",
34894
+ scope: "system",
34895
+ mode: "singleton",
34896
+ methods: {
34897
+ getAvailableEngines: method(zod.z.void(), zod.z.array(PipelineEngineChoiceSchema)),
34898
+ getSelectedEngine: method(zod.z.void(), PipelineEngineChoiceSchema),
34899
+ getDefaultSteps: method(PipelineEngineChoiceSchema, zod.z.array(PipelineDefaultStepSchema)),
34900
+ /**
34901
+ * Per-node detection-engine provisioning snapshot. Returns the live
34902
+ * state of the lazy runtime-provisioning machine on `nodeId`
34903
+ * (idle / installing / verifying / ready / failed). The UI pairs this
34904
+ * one-shot query with the `pipeline.engine-provisioning` live event
34905
+ * (emitted on every transition) to drive a per-node "engine ready?"
34906
+ * indicator without polling. Phase 2.
34907
+ */
34908
+ getEngineProvisioning: method(zod.z.object({ nodeId: zod.z.string() }), EngineProvisioningSchema),
34909
+ getVideoPipelineSteps: method(zod.z.void(), zod.z.record(zod.z.string(), zod.z.object({
34910
+ modelId: zod.z.string(),
34911
+ settings: zod.z.record(zod.z.string(), zod.z.unknown()).readonly()
34912
+ }))),
34913
+ setVideoPipelineSteps: method(zod.z.object({ steps: zod.z.record(zod.z.string(), zod.z.object({
34914
+ modelId: zod.z.string(),
34915
+ settings: zod.z.record(zod.z.string(), zod.z.unknown()).readonly()
34916
+ })) }), zod.z.object({ success: zod.z.literal(true) }), {
34917
+ kind: "mutation",
34918
+ auth: "admin"
34919
+ }),
34920
+ /**
34921
+ * Clear THIS node's executor-side PER-DEVICE settings stores (the
34922
+ * per-camera step overrides the object-detection root reads via
34923
+ * `applyDeviceOverridesToTree`). `nodeId` is the ROUTING key — the
34924
+ * generated cap-router strips it (default `nodeIdMode: 'routing'`) and
34925
+ * dispatches to that node, so the provider method runs ON the target
34926
+ * node and receives no `nodeId`.
34927
+ *
34928
+ * This is the slimmed executor leg of the orchestrator's
34929
+ * `resetNodePipelineDefaults` flow (which owns the real reset: node
34930
+ * addonDefaults pins + per-camera orchestrator overrides). The legacy
34931
+ * `resetToDefault` — which reset a persisted global step-tree seed
34932
+ * nothing in the live per-camera path read — was removed together with
34933
+ * that seed.
34934
+ */
34935
+ clearDeviceOverrides: method(zod.z.object({ nodeId: zod.z.string() }), zod.z.object({
34936
+ success: zod.z.literal(true),
34937
+ clearedDevices: zod.z.number()
34938
+ }), {
34939
+ kind: "mutation",
34940
+ auth: "admin"
34941
+ }),
34942
+ /**
34943
+ * Which of THIS node's inference devices the executor currently refuses,
34944
+ * and why. `nodeId` is the ROUTING key (stripped by the generated router).
34945
+ *
34946
+ * The channel that did not exist. Pool health was known only inside the
34947
+ * detection addon and was an input to no routing decision anywhere: the
34948
+ * per-dispatch capability gate is keyed on model FORMAT and so can never
34949
+ * separate `openvino:gpu` from `openvino:npu`, and the orchestrator's live
34950
+ * eligibility probe (`platformProbe.getCapabilities`) answers about
34951
+ * HARDWARE — which was present throughout. So when the hub's `openvino:gpu`
34952
+ * Python worker was SIGABRT'd by the Intel GPU plugin on 2026-08-25, the
34953
+ * balancer went on handing that dead pool cameras by rotation for 31 hours:
34954
+ * ~370 000 `PoolWorker[w0]: not initialized` lines, every frame lost.
34955
+ *
34956
+ * Read semantics the caller depends on, and which the provider guarantees:
34957
+ * this is a synchronous read of in-memory state. It never probes hardware,
34958
+ * never spawns a pool and never throws — an EMPTY `unhealthy` means "asked,
34959
+ * nothing is refused", which is what re-admits a device. A read that FAILS
34960
+ * (node offline, version skew) must therefore be distinguishable from an
34961
+ * empty answer, and it is: it rejects.
34962
+ */
34963
+ getInferenceDeviceHealth: method(zod.z.object({ nodeId: zod.z.string() }), zod.z.object({ unhealthy: zod.z.array(zod.z.object({
34964
+ /** `<backend>:<device>`, e.g. `openvino:gpu`. */
34965
+ deviceKey: zod.z.string(),
34966
+ /**
34967
+ * `failed` — the per-device restart budget is exhausted; no pool
34968
+ * will be spawned until an operator re-arms it or the runner
34969
+ * respawns. `backoff` — under budget, waiting out the backoff (or
34970
+ * a cached pool observed dead and not yet condemned).
34971
+ */
34972
+ state: zod.z.enum(["failed", "backoff"]),
34973
+ /** Epoch ms of the death that produced this state. */
34974
+ since: zod.z.number(),
34975
+ /** Pool deaths inside the current window. */
34976
+ deaths: zod.z.number(),
34977
+ /** The last death's message. */
34978
+ lastError: zod.z.string()
34979
+ })).readonly() })),
34980
+ /**
34981
+ * Re-arm a terminally `failed` inference device on `nodeId`: forget its
34982
+ * restart budget so the next dispatch builds a fresh pool.
34983
+ *
34984
+ * The terminal state is deliberate (the abort it bounds is deterministic —
34985
+ * an automatic probation would just respawn Python forever, more slowly),
34986
+ * and a terminal state an operator cannot leave is a silent fault. This is
34987
+ * the way out. `rearmed:false` means there was nothing to forget.
34988
+ */
34989
+ rearmInferenceDevice: method(zod.z.object({
34990
+ nodeId: zod.z.string(),
34991
+ deviceKey: zod.z.string()
34992
+ }), zod.z.object({ rearmed: zod.z.boolean() }), {
34993
+ kind: "mutation",
34994
+ auth: "admin"
34995
+ }),
34996
+ getSchema: method(zod.z.void(), PipelineSchemaSchema),
34997
+ getGlobalSteps: method(zod.z.void(), zod.z.array(PipelineDefaultStepSchema).readonly().nullable()),
34998
+ getGlobalPipelineConfig: method(zod.z.void(), PipelineConfigBridge),
34999
+ getOrchestratorConfigSchema: method(zod.z.void(), ConfigUISchemaBridge),
35000
+ /**
35001
+ * Gate B (pre-init config-correctness gate). PURE COMPUTE against this
35002
+ * node's `currentEngine.format` — resolves `steps` the same way the
35003
+ * runtime dispatch path would, and reports what WOULD happen without
35004
+ * touching any node-global state. Called by the orchestrator at attach
35005
+ * time (`attachOn`), node-pinned to the TARGET node, so config problems
35006
+ * surface BEFORE `pipelineRunner.attachCamera` rather than at the first
35007
+ * per-frame resolve. `ok` is false iff `issues` is non-empty (both
35008
+ * `unknown-addon` and `no-format-build` are HARD issues); `substitutions`
35009
+ * is informational (a degraded-but-loadable model swap) and never
35010
+ * affects `ok`. Never throws.
35011
+ */
35012
+ validatePipeline: method(zod.z.object({ steps: zod.z.array(PipelineStepInputSchema) }), PipelineValidationResultSchema),
35013
+ listTemplates: method(zod.z.void(), zod.z.array(PipelineTemplateSchema$1).readonly()),
35014
+ saveTemplate: method(zod.z.object({
35015
+ name: zod.z.string(),
35016
+ steps: zod.z.array(PipelineTemplateStepSchema).readonly(),
35017
+ engine: PipelineEngineChoiceSchema
35018
+ }), PipelineTemplateSchema$1, { kind: "mutation" }),
35019
+ updateTemplate: method(zod.z.object({
35020
+ id: zod.z.string(),
35021
+ name: zod.z.string().optional(),
35022
+ steps: zod.z.array(PipelineTemplateStepSchema).readonly().optional()
35023
+ }), PipelineTemplateSchema$1, { kind: "mutation" }),
35024
+ deleteTemplate: method(zod.z.object({ id: zod.z.string() }), zod.z.void(), { kind: "mutation" }),
35025
+ getCapabilities: method(zod.z.void(), InferenceCapabilitiesBridge),
35026
+ getAddonModels: method(zod.z.object({ addonId: zod.z.string() }), ModelAvailabilityListBridge),
35027
+ downloadModel: method(zod.z.object({
35028
+ addonId: zod.z.string(),
35029
+ modelId: zod.z.string(),
35030
+ format: ModelFormatSchema$1
35031
+ }), DownloadModelResultSchema, { kind: "mutation" }),
35032
+ deleteModel: method(zod.z.object({
35033
+ addonId: zod.z.string(),
35034
+ modelId: zod.z.string(),
35035
+ format: ModelFormatSchema$1
35036
+ }), zod.z.object({ success: zod.z.literal(true) }), { kind: "mutation" }),
35037
+ /**
35038
+ * Stateless single-frame execution. Callers (runner, benchmark) pass
35039
+ * the complete `engine` + `steps` tree; the executor holds no state
35040
+ * about cameras or saved pipelines.
35041
+ *
35042
+ * `engine` is optional during the migration window to preserve the
35043
+ * legacy call shape used by existing benchmark code; once all
35044
+ * callers pass it explicitly we make it required.
35045
+ *
35046
+ * Exactly one of `frame`, `frameRef`, `frameHandle`, `imageBase64`,
35047
+ * `referenceImage` must be provided:
35048
+ * - `frame`: runtime dispatch path (runner → decoded broker frame).
35049
+ * Carries the raw buffer, dimensions, and format; the executor
35050
+ * uses it directly without base64 round-tripping.
35051
+ * - `frameHandle` (CB5): a zero-pixel shm `FrameHandle` for the SAME
35052
+ * decoded frame. Both runner and executor are hub-local processes
35053
+ * sharing `/dev/shm`, so the executor maps the named segment and
35054
+ * reads the pixels back zero-copy — eliminating the ~1.2MB
35055
+ * re-serialisation over UDS/MsgPack the `frame` path pays per call.
35056
+ * High-risk: the FrameRing is a latest-wins seqlock with no
35057
+ * refcount, so a recycled slot yields a null read; the executor
35058
+ * then degrades to an empty result and the runner ships pixels via
35059
+ * `frame` as the fallback (queue-depth gated on the runner side).
35060
+ * - `imageBase64`: one-shot test path (benchmark ImageTab).
35061
+ * - `referenceImage`: named file from the reference-image store.
35062
+ */
35063
+ runPipeline: method(zod.z.object({
35064
+ engine: PipelineEngineChoiceSchema.optional(),
35065
+ steps: zod.z.array(PipelineStepInputSchema).min(1),
35066
+ frame: FrameInputSchema.optional(),
35067
+ /**
35068
+ * Process-local lazy frame. Valid only when caller and provider resolve
35069
+ * in the same execution-group process; split/cross-node callers use
35070
+ * `frame`/`image` inline compatibility instead.
35071
+ */
35072
+ frameRef: FrameRefSchema.optional(),
35073
+ /**
35074
+ * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
35075
+ * the decoded pixels live in. One more member of the one-of
35076
+ * frame/frameHandle/image/imageBase64/referenceImage group.
35077
+ */
35078
+ frameHandle: FrameHandleSchema.optional(),
35079
+ imageBase64: zod.z.string().optional(),
35080
+ /**
35081
+ * Binary JPEG bytes — preferred over `imageBase64` on internal
35082
+ * hops (hub → forked worker via Moleculer MsgPack) because it
35083
+ * skips the 33% base64 overhead + the per-call base64 decode on
35084
+ * the detection-pipeline worker. Callers can pass either; exactly
35085
+ * one of `frame`/`image`/`imageBase64`/`referenceImage` is required.
35086
+ */
35087
+ image: zod.z.instanceof(Uint8Array).optional(),
35088
+ referenceImage: zod.z.string().optional(),
35089
+ deviceId: zod.z.number().optional(),
35090
+ sessionId: zod.z.string().optional(),
35091
+ /**
35092
+ * Execution plane. 'full' (default) runs the whole tree — benchmark,
35093
+ * reference-image, and detail-subtree calls. 'frame' is the live
35094
+ * per-frame dispatch: ONLY root-plane steps run; crop children
35095
+ * (inputClasses ≠ null) are skipped and served per-track via
35096
+ * pipelineRunner.runDetailSubtree (two-plane design).
35097
+ */
35098
+ plane: zod.z.enum(["full", "frame"]).optional(),
35099
+ /**
35100
+ * Inference-device selector (Phase 2 multi-device). Format
35101
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
35102
+ * Omitted ⇒ the runner's default device (current single-engine
35103
+ * behaviour). Selects WHICH device pool of the node runs the call.
35104
+ */
35105
+ deviceKey: zod.z.string().optional(),
35106
+ /**
35107
+ * Two-plane NATIVE child-crop reference. Set by `runDetailSubtree` ONLY
35108
+ * when the parent crop was resolved from the frame's retained NATIVE
35109
+ * surface (a frameHandle HIT). Lets the executor re-cut a LEAF crop
35110
+ * child's ROI (plate-ocr, face-embedding, leaf classifiers) at native
35111
+ * resolution from that surface — the SAME quality path faces already
35112
+ * had — instead of the downscaled parent tile. `handle` keys the native
35113
+ * surface (node-pinned to its owner); `cropFrameSpace` is the parent
35114
+ * crop's padded/clamped rectangle in FRAME-space pixels, used to compose
35115
+ * the executor's crop-normalized child ROI back into frame-normalized
35116
+ * coordinates. Auxiliary to the image source (`image`/`frame`/…), NOT one
35117
+ * of the mutually-exclusive image inputs. Absent ⇒ tile-crop children
35118
+ * (today's behaviour on the fallback path).
35119
+ */
35120
+ nativeCropRef: NativeCropRefSchema.optional()
35121
+ }), PipelineRunResultBridge, { kind: "mutation" }),
35122
+ /**
35123
+ * Batched run — N raw frames packed into one cap call. The provider
35124
+ * routes the batch through `SharedInferencePool.inferBatch`
35125
+ * (`MSG_INFER_BATCH = 0x03`) so the IPC framing and JSON response
35126
+ * envelope cost is amortised N:1 vs N concurrent `runPipeline`
35127
+ * calls. Single root step + uniform model assumed; trees with crop
35128
+ * children fall back to sequential execution.
35129
+ *
35130
+ * Used by `scripts/bench-batch-style.mts` for batch benchmarking —
35131
+ * N frames in one call to amortise per-call IPC overhead.
35132
+ */
35133
+ runPipelineBatch: method(zod.z.object({
35134
+ engine: PipelineEngineChoiceSchema.optional(),
35135
+ steps: zod.z.array(PipelineStepInputSchema).min(1),
35136
+ frames: zod.z.array(FrameInputSchema).min(1).max(255),
35137
+ deviceId: zod.z.number().optional(),
35138
+ sessionId: zod.z.string().optional(),
35139
+ /**
35140
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
35141
+ * the batch to the Python pool's bench preprocess cache
35142
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
35143
+ * preprocessed ONCE and every later inference is a pure-inference cache
35144
+ * hit — the sustained-throughput run measures inference, not
35145
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
35146
+ * full preprocess every call, correct). Fresh per sustained run;
35147
+ * released via `uncacheFrame`.
35148
+ */
35149
+ frameId: zod.z.number().int().nonnegative().optional(),
35150
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
35151
+ deviceKey: zod.z.string().optional()
35152
+ }), zod.z.object({ results: zod.z.array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }),
35153
+ /**
35154
+ * Cache a raw frame inside the Python inference pool's memory.
35155
+ * Returns a numeric `frameId` that `inferCached` references —
35156
+ * subsequent calls send only 5 bytes through the pipe instead of
35157
+ * 1.2MB raw data, eliminating the pipe transfer bottleneck.
35158
+ */
35159
+ cacheFrameInPool: method(zod.z.object({
35160
+ data: zod.z.instanceof(Uint8Array),
35161
+ width: zod.z.number().int().positive(),
35162
+ height: zod.z.number().int().positive(),
35163
+ format: zod.z.enum([
35164
+ "rgb",
35165
+ "bgr",
35166
+ "gray"
35167
+ ])
35168
+ }), zod.z.object({
35169
+ frameId: zod.z.number(),
35170
+ width: zod.z.number(),
35171
+ height: zod.z.number()
35172
+ }), { kind: "mutation" }),
35173
+ /**
35174
+ * Run inference on a previously cached frame. Sends only 5 bytes
35175
+ * (model_idx + frameId) through the IPC pipe — eliminates the
35176
+ * ~35ms per-call overhead of transferring 1.2MB raw data.
35177
+ */
35178
+ inferCached: method(zod.z.object({
35179
+ stepId: zod.z.string(),
35180
+ frameId: zod.z.number().int()
35181
+ }), zod.z.record(zod.z.string(), zod.z.unknown()), { kind: "mutation" }),
35182
+ /**
35183
+ * Release a cached frame from the Python pool's memory.
35184
+ */
35185
+ uncacheFrame: method(zod.z.object({ frameId: zod.z.number().int() }), zod.z.void(), { kind: "mutation" }),
35186
+ /** Returns the effective pool tuning (resolved from user overrides + backend defaults). */
35187
+ getEffectiveTuning: method(zod.z.void(), zod.z.object({
35188
+ batchMode: zod.z.string(),
35189
+ windowMs: zod.z.number(),
35190
+ maxBatchSize: zod.z.number(),
35191
+ concurrency: zod.z.number()
35192
+ })),
35193
+ /**
35194
+ * List every EngineFactory currently loaded in this executor's RAM,
35195
+ * with the models resident and a coarse "in use" marker derived from
35196
+ * ongoing inference activity. Used by the Pipeline page Engines tab.
35197
+ */
35198
+ listLoadedEngines: method(zod.z.void(), zod.z.array(zod.z.object({
35199
+ engineKey: zod.z.string(),
35200
+ engine: PipelineEngineChoiceSchema,
35201
+ modelsLoaded: zod.z.array(zod.z.string()).readonly(),
35202
+ inUseByCameras: zod.z.array(zod.z.number()).readonly(),
35203
+ /**
35204
+ * Origin of this resident factory.
35205
+ * - `runtime` — main camera-serving engine (no idle TTL).
35206
+ * - `warm-override` — benchmark/test override held in the warm
35207
+ * cache; auto-disposed after the idle TTL.
35208
+ * - `device-pool` — a concurrent per-device pool (Phase 2
35209
+ * multi-device, keyed by `deviceKey`) resolved
35210
+ * via `resolveDeviceFactory`. Runs alongside the
35211
+ * `runtime` engine on a DIFFERENT accelerator
35212
+ * (NPU / iGPU / Coral) — this is how the
35213
+ * Engines tab shows all pools running at once.
35214
+ */
35215
+ kind: zod.z.enum([
35216
+ "runtime",
35217
+ "warm-override",
35218
+ "device-pool"
35219
+ ]),
35220
+ /** Native pid of the underlying Python pool (null when no pool). */
35221
+ poolPid: zod.z.number().nullable(),
35222
+ /** ms since this factory was last used (null when not warm-tracked). */
35223
+ idleMs: zod.z.number().nullable(),
35224
+ /** Idle TTL after which `warm-override` factories self-evict (null when not applicable). */
35225
+ idleTtlMs: zod.z.number().nullable()
35226
+ })).readonly()),
35227
+ /** Warm up an engine without running a frame. No-op if already loaded. */
35228
+ spinEngine: method(zod.z.object({ engine: PipelineEngineChoiceSchema }), zod.z.object({ success: zod.z.literal(true) }), {
35229
+ kind: "mutation",
35230
+ auth: "admin"
35231
+ }),
35232
+ /**
35233
+ * Unload an engine from RAM. `force:true` unloads even when cameras
35234
+ * are actively using it (they re-spin on next frame). Default is
35235
+ * gated — returns `{success:false, reason}` when in use.
35236
+ */
35237
+ killEngine: method(zod.z.object({
35238
+ engine: PipelineEngineChoiceSchema,
35239
+ force: zod.z.boolean().optional()
35240
+ }), zod.z.object({
35241
+ success: zod.z.boolean(),
35242
+ reason: zod.z.string().optional()
35243
+ }), {
35244
+ kind: "mutation",
35245
+ auth: "admin"
35246
+ }),
35247
+ listReferenceImages: method(zod.z.void(), zod.z.array(ReferenceImageEntrySchema).readonly()),
35248
+ getReferenceImage: method(zod.z.object({ filename: zod.z.string() }), ReferenceImageBodySchema.nullable()),
35249
+ getReferenceAudioFiles: method(zod.z.void(), zod.z.array(ReferenceAudioEntrySchema).readonly()),
35250
+ getReferenceAudio: method(zod.z.object({ filename: zod.z.string() }), ReferenceAudioBodySchema.nullable()),
35251
+ getAudioCapabilities: method(zod.z.void(), AudioCapabilitiesSchema),
35252
+ runAudioTest: method(zod.z.object({
35253
+ addonId: zod.z.string(),
35254
+ modelId: zod.z.string(),
35255
+ filename: zod.z.string().optional(),
35256
+ settings: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
35257
+ }), AudioTestResultSchema, { kind: "mutation" }),
35258
+ getDetectionConfigSchema: method(zod.z.void(), ConfigUISchemaNullableBridge)
35259
+ }
35260
+ };
35261
+ var ZoneRuleModeEnum = zod.z.enum(["include", "exclude"]);
35262
+ var ZoneRuleSchema = zod.z.object({
35263
+ /** Stable rule id — survives edits, used by the UI for diffing. */
35264
+ id: zod.z.string(),
35265
+ /** Optional human-readable label rendered in the rule editor. */
35266
+ name: zod.z.string().optional(),
35267
+ /** Zones this rule targets. The rule's `mode` applies to ALL
35268
+ * listed zones (OR-set: a detection in any one of them counts).
35269
+ * At least one zone id required — a rule with no targets is a
35270
+ * configuration mistake and the form validator rejects it. */
35271
+ zoneIds: zod.z.array(zod.z.string()).min(1).readonly(),
35272
+ mode: ZoneRuleModeEnum,
35273
+ /**
35274
+ * Class names this rule applies to. Empty / undefined ⇒ rule
35275
+ * applies to every class. Class strings match the `macroClass`
35276
+ * field on detections (e.g. `person`, `car`, `dog`).
35277
+ */
35278
+ classFilter: zod.z.array(zod.z.string()).readonly().optional(),
35279
+ /**
35280
+ * Minimum bbox/mask overlap (0–1) with any of the rule's zones
35281
+ * required to consider an entity "in the zone". Defaults to the
35282
+ * consumer's stage default when omitted. Kept for back-compat with
35283
+ * existing per-rule overrides; new operators pick the value via
35284
+ * `bboxInclusionPct` (operator-friendly 0–100). Whichever field is
35285
+ * set, the lower-level engine reads it as a 0–1 fraction.
35286
+ */
35287
+ overlapThreshold: zod.z.number().min(0).max(1).optional(),
35288
+ /**
35289
+ * Operator-friendly version of `overlapThreshold` — the percentage
35290
+ * of the detection's bbox that must lie inside the zone for the
35291
+ * rule to match. Documented default is 85%; the engine substitutes
35292
+ * that when the field is omitted (kept optional so existing rules
35293
+ * stored without it stay valid).
35294
+ *
35295
+ * When BOTH `overlapThreshold` and `bboxInclusionPct` are set on a
35296
+ * rule, the engine prefers `bboxInclusionPct` because it's the
35297
+ * field exposed in the UI. Internally both feed the same gate.
35298
+ */
35299
+ bboxInclusionPct: zod.z.number().min(0).max(100).optional(),
35300
+ /**
35301
+ * When `true` and a detection has a segmentation mask, use the
35302
+ * mask for overlap instead of the bbox. Detection-stage only;
35303
+ * motion rules ignore this field.
35304
+ */
35305
+ preferMask: zod.z.boolean().optional(),
35306
+ /**
35307
+ * Soft-toggle: `false` disables the rule without deleting it.
35308
+ * Defaults to `true` so operators creating a rule via the UI
35309
+ * see it active immediately.
35310
+ */
35311
+ enabled: zod.z.boolean().default(true)
35312
+ });
35313
+ zod.z.array(ZoneRuleSchema).readonly();
35314
+ var ZoneKindEnum = zod.z.enum(["polygon", "tripwire"]);
35315
+ var PolygonPointSchema = zod.z.object({
35316
+ x: zod.z.number(),
35317
+ y: zod.z.number()
35318
+ });
35319
+ var ZoneSchema = zod.z.object({
35320
+ id: zod.z.string(),
35321
+ name: zod.z.string(),
35322
+ kind: ZoneKindEnum.default("polygon"),
35323
+ /** Polygon vertices, fraction of frame (0–1). */
35324
+ polygon: zod.z.array(PolygonPointSchema).readonly(),
35325
+ /** Visual color for UI rendering. */
35326
+ color: zod.z.string().default("#3b82f6")
35327
+ });
35328
+ var zonesCapability = {
35329
+ name: "zones",
35330
+ scope: "device",
35331
+ mode: "singleton",
35332
+ deviceTypes: [DeviceType.Camera],
35333
+ methods: {
35334
+ listZones: method(zod.z.object({ deviceId: zod.z.number() }), zod.z.array(ZoneSchema).readonly()),
35335
+ addZone: method(zod.z.object({
35336
+ deviceId: zod.z.number(),
35337
+ zone: ZoneSchema
35338
+ }), zod.z.void(), {
35339
+ kind: "mutation",
35340
+ auth: "admin"
35341
+ }),
35342
+ removeZone: method(zod.z.object({
35343
+ deviceId: zod.z.number(),
35344
+ zoneId: zod.z.string()
35345
+ }), zod.z.void(), {
35346
+ kind: "mutation",
35347
+ auth: "admin"
35348
+ }),
35349
+ updateZone: method(zod.z.object({
35350
+ deviceId: zod.z.number(),
35351
+ zone: ZoneSchema
35352
+ }), zod.z.void(), {
35353
+ kind: "mutation",
35354
+ auth: "admin"
35355
+ })
35356
+ },
35357
+ /**
35358
+ * Runtime-state slice — the live zone catalogue mirrored by the
35359
+ * orchestrator on every CRUD mutation. Consumers read via
35360
+ * `device.state.zones.value` / `.watch(...)` without round-tripping
35361
+ * the cap, and the codegen DeviceProxy auto-wires the reactive
35362
+ * handle. Slice shape is `{ zones: Zone[] }` so future extensions
35363
+ * (e.g. zone groupings) can sit alongside the polygon list.
35364
+ */
35365
+ runtimeState: zod.z.object({ zones: zod.z.array(ZoneSchema).readonly() }),
35366
+ /**
35367
+ * Runtime-state durability: **restored** — written only on operator mutation, so a camera that never had one has nothing to re-derive from. This is the slice `zone-mirror-hydration.ts` exists to paper over.
35368
+ *
35369
+ * See `RuntimeStateDurability`. Enforced by
35370
+ * `scripts/check-runtime-state-durability.ts`.
35371
+ */
35372
+ durability: "restored"
35373
+ };
34624
35374
  var TrackStateSchema = zod.z.enum([
34625
35375
  "new",
34626
35376
  "entered",
@@ -35441,6 +36191,25 @@ var require_dist_D5Jhh8Vk = __commonJS({
35441
36191
  /** Present when the pass ended by throwing. */
35442
36192
  error: zod.z.string().nullable()
35443
36193
  });
36194
+ var ReplayFrameInputSchema = zod.z.object({
36195
+ timestamp: zod.z.number(),
36196
+ frame: PipelineRunResultBridge
36197
+ });
36198
+ var ReplayTrackSchema = zod.z.object({
36199
+ className: zod.z.string(),
36200
+ firstSeenMs: zod.z.number(),
36201
+ lastSeenMs: zod.z.number(),
36202
+ /** Bbox of the track's FIRST matched detection, pixel-space in the clip's
36203
+ * frame — a representative box for the diff's `(className, window, IoU)`
36204
+ * pairing (`replay-diff.ts`). A replay does not need the full per-frame
36205
+ * trajectory production's `Track.positions` keeps. */
36206
+ bbox: BoundingBoxSchema,
36207
+ /** How many of the input frames this track matched a real detection on
36208
+ * (never a coasted/extrapolated frame) — the replay's own signal for "how
36209
+ * solid is this track", cheaper than re-deriving it from a trajectory. */
36210
+ framesMatched: zod.z.number().int()
36211
+ });
36212
+ var RunReplayFrameProcessorResultSchema = zod.z.object({ tracks: zod.z.array(ReplayTrackSchema).readonly() });
35444
36213
  var pipelineAnalyticsCapability = {
35445
36214
  name: "pipeline-analytics",
35446
36215
  scope: "device",
@@ -35931,6 +36700,32 @@ var require_dist_D5Jhh8Vk = __commonJS({
35931
36700
  kind: "mutation",
35932
36701
  auth: "admin"
35933
36702
  }),
36703
+ /**
36704
+ * The FrameProcessor pass of a replay run — see the `Replay` section
36705
+ * above this capability's definition for why this is not the
36706
+ * `processFrame` method this file's header says pipeline-analytics does
36707
+ * not have.
36708
+ *
36709
+ * Constructs a FRESH `FrameProcessor` for `(deviceId, source)`, feeds it
36710
+ * `frames` IN THE ORDER GIVEN (the caller is responsible for time
36711
+ * ordering — this method does not sort), and returns the tracks it
36712
+ * produced. Zero persistence: no `TrackStore`, no event bus, no media
36713
+ * capture. `zones` / `detectionRules` are the run's OWN zone set —
36714
+ * typically the camera's real zones plus an ephemeral overlay
36715
+ * (`addon-benchmark`'s `replay-plan.ts`), never read from or written to
36716
+ * the `zones` capability by this method itself.
36717
+ */
36718
+ runReplayFrameProcessor: method(zod.z.object({
36719
+ deviceId: zod.z.number(),
36720
+ source: DetectionSourceSchema,
36721
+ zones: zod.z.array(ZoneSchema).readonly().optional(),
36722
+ detectionRules: zod.z.array(ZoneRuleSchema).readonly().optional(),
36723
+ zoneMembershipMinOverlap: zod.z.number().min(0).max(1).optional(),
36724
+ frames: zod.z.array(ReplayFrameInputSchema).min(1)
36725
+ }), RunReplayFrameProcessorResultSchema, {
36726
+ kind: "mutation",
36727
+ auth: "admin"
36728
+ }),
35934
36729
  /** Every annotation on a track, oldest first. */
35935
36730
  listRetrainAnnotations: method(zod.z.object({ trackId: zod.z.string() }), zod.z.array(RetrainAnnotationSchema).readonly(), {
35936
36731
  kind: "query",
@@ -36097,559 +36892,6 @@ var require_dist_D5Jhh8Vk = __commonJS({
36097
36892
  }) }
36098
36893
  }
36099
36894
  };
36100
- var NativeCropRefSchema = zod.z.object({
36101
- /** Handle keying the retained native surface (node-pinned to its owner). */
36102
- handle: FrameHandleSchema,
36103
- /** The parent crop's padded/clamped rectangle in FRAME-space pixels. */
36104
- cropFrameSpace: zod.z.object({
36105
- x: zod.z.number(),
36106
- y: zod.z.number(),
36107
- w: zod.z.number(),
36108
- h: zod.z.number()
36109
- })
36110
- });
36111
- zod.z.object({
36112
- crop: zod.z.object({
36113
- left: zod.z.number(),
36114
- top: zod.z.number(),
36115
- width: zod.z.number().positive(),
36116
- height: zod.z.number().positive()
36117
- }).optional(),
36118
- content: zod.z.object({
36119
- width: zod.z.number().int().positive(),
36120
- height: zod.z.number().int().positive()
36121
- }),
36122
- fit: zod.z.enum(["stretch", "contain"]),
36123
- format: zod.z.enum([
36124
- "rgb",
36125
- "gray",
36126
- "jpeg"
36127
- ])
36128
- });
36129
- var FrameRefSchema = zod.z.object({
36130
- registryId: zod.z.string().min(1),
36131
- id: zod.z.string().min(1),
36132
- width: zod.z.number().int().positive(),
36133
- height: zod.z.number().int().positive(),
36134
- format: zod.z.enum(["rgb", "gray"]),
36135
- timestamp: zod.z.number(),
36136
- capturedAt: zod.z.number().optional()
36137
- });
36138
- var ModelFormatSchema$1 = zod.z.enum([
36139
- "onnx",
36140
- "coreml",
36141
- "openvino",
36142
- "tflite",
36143
- "pt",
36144
- "gguf"
36145
- ]);
36146
- var PipelineSlotSchema = zod.z.enum([
36147
- "detector",
36148
- "cropper",
36149
- "classifier",
36150
- "refiner",
36151
- "audio-classifier"
36152
- ]);
36153
- var PipelineEngineChoiceSchema = zod.z.object({
36154
- runtime: zod.z.enum(["node", "python"]),
36155
- backend: zod.z.string(),
36156
- format: ModelFormatSchema$1,
36157
- device: zod.z.string().optional()
36158
- });
36159
- var EngineDeviceInfoSchema = zod.z.object({
36160
- id: zod.z.string(),
36161
- label: zod.z.string(),
36162
- description: zod.z.string().optional()
36163
- });
36164
- var AvailableEngineSchema = zod.z.object({
36165
- engine: PipelineEngineChoiceSchema,
36166
- devices: zod.z.array(EngineDeviceInfoSchema).readonly(),
36167
- defaultDevice: zod.z.string()
36168
- });
36169
- var PipelineDefaultStepSchema = zod.z.lazy(() => zod.z.object({
36170
- addonId: zod.z.string(),
36171
- addonName: zod.z.string(),
36172
- slot: PipelineSlotSchema,
36173
- inputClasses: zod.z.array(zod.z.string()).readonly(),
36174
- outputClasses: zod.z.array(zod.z.string()).readonly(),
36175
- enabled: zod.z.boolean(),
36176
- modelId: zod.z.string(),
36177
- children: zod.z.array(PipelineDefaultStepSchema).readonly(),
36178
- group: zod.z.string().optional(),
36179
- settings: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
36180
- }));
36181
- var PipelineTemplateStepSchema = zod.z.lazy(() => zod.z.object({
36182
- addonId: zod.z.string(),
36183
- enabled: zod.z.boolean(),
36184
- modelId: zod.z.string(),
36185
- children: zod.z.array(PipelineTemplateStepSchema).readonly(),
36186
- settings: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
36187
- }));
36188
- var PipelineTemplateSchema$1 = zod.z.object({
36189
- id: zod.z.string(),
36190
- name: zod.z.string(),
36191
- createdAt: zod.z.string(),
36192
- updatedAt: zod.z.string(),
36193
- engine: PipelineEngineChoiceSchema,
36194
- steps: zod.z.array(PipelineTemplateStepSchema).readonly()
36195
- });
36196
- var PipelineModelOptionSchema = zod.z.object({
36197
- id: zod.z.string(),
36198
- name: zod.z.string(),
36199
- formats: zod.z.record(zod.z.string(), zod.z.object({
36200
- downloaded: zod.z.boolean(),
36201
- sizeMB: zod.z.number()
36202
- })),
36203
- group: ModelVariantGroupSchema.optional(),
36204
- legacy: zod.z.boolean().optional(),
36205
- provider: ModelProviderIdSchema.optional()
36206
- });
36207
- var ConfigFieldBridge = zod.z.custom();
36208
- var PipelineAddonSchemaSchema = zod.z.object({
36209
- id: zod.z.string(),
36210
- name: zod.z.string(),
36211
- slot: PipelineSlotSchema,
36212
- inputClasses: zod.z.array(zod.z.string()).readonly(),
36213
- outputClasses: zod.z.array(zod.z.string()).readonly(),
36214
- childSlots: zod.z.array(PipelineSlotSchema).readonly(),
36215
- models: zod.z.array(PipelineModelOptionSchema).readonly(),
36216
- defaultModelId: zod.z.string(),
36217
- defaultModelIdByFormat: zod.z.record(zod.z.string(), zod.z.string()).optional(),
36218
- enabledByDefault: zod.z.boolean().optional(),
36219
- backfillIntoExistingOverrides: zod.z.boolean().optional(),
36220
- defaultConfidence: zod.z.number(),
36221
- group: zod.z.string().optional(),
36222
- configSchema: zod.z.array(ConfigFieldBridge).readonly().optional()
36223
- });
36224
- var PipelineSlotSchemaSchema = zod.z.object({
36225
- id: PipelineSlotSchema,
36226
- label: zod.z.string(),
36227
- priority: zod.z.number(),
36228
- parentSlot: PipelineSlotSchema.nullable(),
36229
- addons: zod.z.array(PipelineAddonSchemaSchema).readonly()
36230
- });
36231
- var PipelineSchemaSchema = zod.z.object({
36232
- availableEngines: zod.z.array(AvailableEngineSchema).readonly(),
36233
- selectedEngine: PipelineEngineChoiceSchema,
36234
- slots: zod.z.array(PipelineSlotSchemaSchema).readonly()
36235
- });
36236
- var EngineProvisioningSchema = zod.z.object({
36237
- runtimeId: zod.z.enum([
36238
- "onnx",
36239
- "openvino",
36240
- "coreml",
36241
- "edgetpu"
36242
- ]).nullable(),
36243
- device: zod.z.string().nullable(),
36244
- state: zod.z.enum([
36245
- "idle",
36246
- "installing",
36247
- "verifying",
36248
- "ready",
36249
- "failed"
36250
- ]),
36251
- progress: zod.z.number().optional(),
36252
- error: zod.z.string().optional(),
36253
- nextRetryAt: zod.z.number().optional(),
36254
- /**
36255
- * Gate A (config-correctness gate at engine change): human-readable
36256
- * config issues surfaced EAGERLY when the node's engine changes — model
36257
- * substitutions ("chose X, running Y") and zero-build steps ("no model
36258
- * has a <format> build"). Additive/optional: informational only, never
36259
- * enforced here — `assertEngineReady` (readiness) still gates inference.
36260
- * Absent/empty when the node-default tree resolves cleanly.
36261
- */
36262
- configIssues: zod.z.array(zod.z.string()).optional()
36263
- });
36264
- var PipelineStepInputSchema = zod.z.lazy(() => zod.z.object({
36265
- addonId: zod.z.string(),
36266
- modelId: zod.z.string().optional(),
36267
- enabled: zod.z.boolean().default(true),
36268
- children: zod.z.array(PipelineStepInputSchema).optional(),
36269
- settings: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
36270
- jumpDeviceKey: zod.z.string().optional()
36271
- }));
36272
- var ModelSubstitutionSchema = zod.z.object({
36273
- addonId: zod.z.string(),
36274
- chosen: zod.z.string(),
36275
- running: zod.z.string(),
36276
- format: zod.z.string()
36277
- });
36278
- var PipelineValidationIssueSchema = zod.z.object({
36279
- addonId: zod.z.string(),
36280
- kind: zod.z.enum(["unknown-addon", "no-format-build"]),
36281
- detail: zod.z.string()
36282
- });
36283
- var PipelineValidationResultSchema = zod.z.object({
36284
- ok: zod.z.boolean(),
36285
- issues: zod.z.array(PipelineValidationIssueSchema).readonly(),
36286
- substitutions: zod.z.array(ModelSubstitutionSchema).readonly(),
36287
- /** The node's `currentEngine.format` this validation ran against. */
36288
- format: zod.z.string()
36289
- });
36290
- var ReferenceImageEntrySchema = zod.z.object({
36291
- filename: zod.z.string(),
36292
- stepIds: zod.z.array(zod.z.string()).readonly().optional()
36293
- });
36294
- var ReferenceImageBodySchema = zod.z.object({
36295
- base64: zod.z.string(),
36296
- filename: zod.z.string()
36297
- });
36298
- var ReferenceAudioEntrySchema = zod.z.object({
36299
- filename: zod.z.string(),
36300
- sizeKb: zod.z.number()
36301
- });
36302
- var ReferenceAudioBodySchema = zod.z.object({ base64: zod.z.string() });
36303
- var AudioBackendSchema = zod.z.object({
36304
- id: zod.z.string(),
36305
- name: zod.z.string(),
36306
- description: zod.z.string(),
36307
- available: zod.z.boolean(),
36308
- /**
36309
- * Raw classifier labels this backend can emit (e.g. YAMNet's
36310
- * 521-class set or Apple SoundAnalysis's 303-class set). Used by
36311
- * the benchmark UI to populate the `enabledMicroClasses` filter
36312
- * specific to the selected backend without a separate fetch.
36313
- */
36314
- rawLabels: zod.z.array(zod.z.string()).readonly().optional()
36315
- });
36316
- var AudioCapabilitiesSchema = zod.z.object({
36317
- activeBackend: zod.z.string(),
36318
- availableBackends: zod.z.array(AudioBackendSchema).readonly(),
36319
- sampleRate: zod.z.number(),
36320
- chunkDurationMs: zod.z.number()
36321
- });
36322
- var DownloadModelResultSchema = zod.z.object({
36323
- filePath: zod.z.string(),
36324
- sizeMB: zod.z.number(),
36325
- durationMs: zod.z.number()
36326
- });
36327
- var AudioTestResultSchema = zod.z.object({
36328
- success: zod.z.boolean(),
36329
- error: zod.z.string().optional(),
36330
- frame: zod.z.custom().optional()
36331
- });
36332
- var PipelineConfigBridge = zod.z.custom();
36333
- var ConfigUISchemaBridge = zod.z.custom();
36334
- var ConfigUISchemaNullableBridge = zod.z.custom();
36335
- var InferenceCapabilitiesBridge = zod.z.custom();
36336
- var ModelAvailabilityListBridge = zod.z.custom();
36337
- var PipelineRunResultBridge = zod.z.custom();
36338
- var pipelineExecutorCapability = {
36339
- name: "pipeline-executor",
36340
- scope: "system",
36341
- mode: "singleton",
36342
- methods: {
36343
- getAvailableEngines: method(zod.z.void(), zod.z.array(PipelineEngineChoiceSchema)),
36344
- getSelectedEngine: method(zod.z.void(), PipelineEngineChoiceSchema),
36345
- getDefaultSteps: method(PipelineEngineChoiceSchema, zod.z.array(PipelineDefaultStepSchema)),
36346
- /**
36347
- * Per-node detection-engine provisioning snapshot. Returns the live
36348
- * state of the lazy runtime-provisioning machine on `nodeId`
36349
- * (idle / installing / verifying / ready / failed). The UI pairs this
36350
- * one-shot query with the `pipeline.engine-provisioning` live event
36351
- * (emitted on every transition) to drive a per-node "engine ready?"
36352
- * indicator without polling. Phase 2.
36353
- */
36354
- getEngineProvisioning: method(zod.z.object({ nodeId: zod.z.string() }), EngineProvisioningSchema),
36355
- getVideoPipelineSteps: method(zod.z.void(), zod.z.record(zod.z.string(), zod.z.object({
36356
- modelId: zod.z.string(),
36357
- settings: zod.z.record(zod.z.string(), zod.z.unknown()).readonly()
36358
- }))),
36359
- setVideoPipelineSteps: method(zod.z.object({ steps: zod.z.record(zod.z.string(), zod.z.object({
36360
- modelId: zod.z.string(),
36361
- settings: zod.z.record(zod.z.string(), zod.z.unknown()).readonly()
36362
- })) }), zod.z.object({ success: zod.z.literal(true) }), {
36363
- kind: "mutation",
36364
- auth: "admin"
36365
- }),
36366
- /**
36367
- * Clear THIS node's executor-side PER-DEVICE settings stores (the
36368
- * per-camera step overrides the object-detection root reads via
36369
- * `applyDeviceOverridesToTree`). `nodeId` is the ROUTING key — the
36370
- * generated cap-router strips it (default `nodeIdMode: 'routing'`) and
36371
- * dispatches to that node, so the provider method runs ON the target
36372
- * node and receives no `nodeId`.
36373
- *
36374
- * This is the slimmed executor leg of the orchestrator's
36375
- * `resetNodePipelineDefaults` flow (which owns the real reset: node
36376
- * addonDefaults pins + per-camera orchestrator overrides). The legacy
36377
- * `resetToDefault` — which reset a persisted global step-tree seed
36378
- * nothing in the live per-camera path read — was removed together with
36379
- * that seed.
36380
- */
36381
- clearDeviceOverrides: method(zod.z.object({ nodeId: zod.z.string() }), zod.z.object({
36382
- success: zod.z.literal(true),
36383
- clearedDevices: zod.z.number()
36384
- }), {
36385
- kind: "mutation",
36386
- auth: "admin"
36387
- }),
36388
- getSchema: method(zod.z.void(), PipelineSchemaSchema),
36389
- getGlobalSteps: method(zod.z.void(), zod.z.array(PipelineDefaultStepSchema).readonly().nullable()),
36390
- getGlobalPipelineConfig: method(zod.z.void(), PipelineConfigBridge),
36391
- getOrchestratorConfigSchema: method(zod.z.void(), ConfigUISchemaBridge),
36392
- /**
36393
- * Gate B (pre-init config-correctness gate). PURE COMPUTE against this
36394
- * node's `currentEngine.format` — resolves `steps` the same way the
36395
- * runtime dispatch path would, and reports what WOULD happen without
36396
- * touching any node-global state. Called by the orchestrator at attach
36397
- * time (`attachOn`), node-pinned to the TARGET node, so config problems
36398
- * surface BEFORE `pipelineRunner.attachCamera` rather than at the first
36399
- * per-frame resolve. `ok` is false iff `issues` is non-empty (both
36400
- * `unknown-addon` and `no-format-build` are HARD issues); `substitutions`
36401
- * is informational (a degraded-but-loadable model swap) and never
36402
- * affects `ok`. Never throws.
36403
- */
36404
- validatePipeline: method(zod.z.object({ steps: zod.z.array(PipelineStepInputSchema) }), PipelineValidationResultSchema),
36405
- listTemplates: method(zod.z.void(), zod.z.array(PipelineTemplateSchema$1).readonly()),
36406
- saveTemplate: method(zod.z.object({
36407
- name: zod.z.string(),
36408
- steps: zod.z.array(PipelineTemplateStepSchema).readonly(),
36409
- engine: PipelineEngineChoiceSchema
36410
- }), PipelineTemplateSchema$1, { kind: "mutation" }),
36411
- updateTemplate: method(zod.z.object({
36412
- id: zod.z.string(),
36413
- name: zod.z.string().optional(),
36414
- steps: zod.z.array(PipelineTemplateStepSchema).readonly().optional()
36415
- }), PipelineTemplateSchema$1, { kind: "mutation" }),
36416
- deleteTemplate: method(zod.z.object({ id: zod.z.string() }), zod.z.void(), { kind: "mutation" }),
36417
- getCapabilities: method(zod.z.void(), InferenceCapabilitiesBridge),
36418
- getAddonModels: method(zod.z.object({ addonId: zod.z.string() }), ModelAvailabilityListBridge),
36419
- downloadModel: method(zod.z.object({
36420
- addonId: zod.z.string(),
36421
- modelId: zod.z.string(),
36422
- format: ModelFormatSchema$1
36423
- }), DownloadModelResultSchema, { kind: "mutation" }),
36424
- deleteModel: method(zod.z.object({
36425
- addonId: zod.z.string(),
36426
- modelId: zod.z.string(),
36427
- format: ModelFormatSchema$1
36428
- }), zod.z.object({ success: zod.z.literal(true) }), { kind: "mutation" }),
36429
- /**
36430
- * Stateless single-frame execution. Callers (runner, benchmark) pass
36431
- * the complete `engine` + `steps` tree; the executor holds no state
36432
- * about cameras or saved pipelines.
36433
- *
36434
- * `engine` is optional during the migration window to preserve the
36435
- * legacy call shape used by existing benchmark code; once all
36436
- * callers pass it explicitly we make it required.
36437
- *
36438
- * Exactly one of `frame`, `frameRef`, `frameHandle`, `imageBase64`,
36439
- * `referenceImage` must be provided:
36440
- * - `frame`: runtime dispatch path (runner → decoded broker frame).
36441
- * Carries the raw buffer, dimensions, and format; the executor
36442
- * uses it directly without base64 round-tripping.
36443
- * - `frameHandle` (CB5): a zero-pixel shm `FrameHandle` for the SAME
36444
- * decoded frame. Both runner and executor are hub-local processes
36445
- * sharing `/dev/shm`, so the executor maps the named segment and
36446
- * reads the pixels back zero-copy — eliminating the ~1.2MB
36447
- * re-serialisation over UDS/MsgPack the `frame` path pays per call.
36448
- * High-risk: the FrameRing is a latest-wins seqlock with no
36449
- * refcount, so a recycled slot yields a null read; the executor
36450
- * then degrades to an empty result and the runner ships pixels via
36451
- * `frame` as the fallback (queue-depth gated on the runner side).
36452
- * - `imageBase64`: one-shot test path (benchmark ImageTab).
36453
- * - `referenceImage`: named file from the reference-image store.
36454
- */
36455
- runPipeline: method(zod.z.object({
36456
- engine: PipelineEngineChoiceSchema.optional(),
36457
- steps: zod.z.array(PipelineStepInputSchema).min(1),
36458
- frame: FrameInputSchema.optional(),
36459
- /**
36460
- * Process-local lazy frame. Valid only when caller and provider resolve
36461
- * in the same execution-group process; split/cross-node callers use
36462
- * `frame`/`image` inline compatibility instead.
36463
- */
36464
- frameRef: FrameRefSchema.optional(),
36465
- /**
36466
- * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
36467
- * the decoded pixels live in. One more member of the one-of
36468
- * frame/frameHandle/image/imageBase64/referenceImage group.
36469
- */
36470
- frameHandle: FrameHandleSchema.optional(),
36471
- imageBase64: zod.z.string().optional(),
36472
- /**
36473
- * Binary JPEG bytes — preferred over `imageBase64` on internal
36474
- * hops (hub → forked worker via Moleculer MsgPack) because it
36475
- * skips the 33% base64 overhead + the per-call base64 decode on
36476
- * the detection-pipeline worker. Callers can pass either; exactly
36477
- * one of `frame`/`image`/`imageBase64`/`referenceImage` is required.
36478
- */
36479
- image: zod.z.instanceof(Uint8Array).optional(),
36480
- referenceImage: zod.z.string().optional(),
36481
- deviceId: zod.z.number().optional(),
36482
- sessionId: zod.z.string().optional(),
36483
- /**
36484
- * Execution plane. 'full' (default) runs the whole tree — benchmark,
36485
- * reference-image, and detail-subtree calls. 'frame' is the live
36486
- * per-frame dispatch: ONLY root-plane steps run; crop children
36487
- * (inputClasses ≠ null) are skipped and served per-track via
36488
- * pipelineRunner.runDetailSubtree (two-plane design).
36489
- */
36490
- plane: zod.z.enum(["full", "frame"]).optional(),
36491
- /**
36492
- * Inference-device selector (Phase 2 multi-device). Format
36493
- * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
36494
- * Omitted ⇒ the runner's default device (current single-engine
36495
- * behaviour). Selects WHICH device pool of the node runs the call.
36496
- */
36497
- deviceKey: zod.z.string().optional(),
36498
- /**
36499
- * Two-plane NATIVE child-crop reference. Set by `runDetailSubtree` ONLY
36500
- * when the parent crop was resolved from the frame's retained NATIVE
36501
- * surface (a frameHandle HIT). Lets the executor re-cut a LEAF crop
36502
- * child's ROI (plate-ocr, face-embedding, leaf classifiers) at native
36503
- * resolution from that surface — the SAME quality path faces already
36504
- * had — instead of the downscaled parent tile. `handle` keys the native
36505
- * surface (node-pinned to its owner); `cropFrameSpace` is the parent
36506
- * crop's padded/clamped rectangle in FRAME-space pixels, used to compose
36507
- * the executor's crop-normalized child ROI back into frame-normalized
36508
- * coordinates. Auxiliary to the image source (`image`/`frame`/…), NOT one
36509
- * of the mutually-exclusive image inputs. Absent ⇒ tile-crop children
36510
- * (today's behaviour on the fallback path).
36511
- */
36512
- nativeCropRef: NativeCropRefSchema.optional()
36513
- }), PipelineRunResultBridge, { kind: "mutation" }),
36514
- /**
36515
- * Batched run — N raw frames packed into one cap call. The provider
36516
- * routes the batch through `SharedInferencePool.inferBatch`
36517
- * (`MSG_INFER_BATCH = 0x03`) so the IPC framing and JSON response
36518
- * envelope cost is amortised N:1 vs N concurrent `runPipeline`
36519
- * calls. Single root step + uniform model assumed; trees with crop
36520
- * children fall back to sequential execution.
36521
- *
36522
- * Used by `scripts/bench-batch-style.mts` for batch benchmarking —
36523
- * N frames in one call to amortise per-call IPC overhead.
36524
- */
36525
- runPipelineBatch: method(zod.z.object({
36526
- engine: PipelineEngineChoiceSchema.optional(),
36527
- steps: zod.z.array(PipelineStepInputSchema).min(1),
36528
- frames: zod.z.array(FrameInputSchema).min(1).max(255),
36529
- deviceId: zod.z.number().optional(),
36530
- sessionId: zod.z.string().optional(),
36531
- /**
36532
- * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
36533
- * the batch to the Python pool's bench preprocess cache
36534
- * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
36535
- * preprocessed ONCE and every later inference is a pure-inference cache
36536
- * hit — the sustained-throughput run measures inference, not
36537
- * decode+preprocess+infer. Omitted/0 for live frames (all different →
36538
- * full preprocess every call, correct). Fresh per sustained run;
36539
- * released via `uncacheFrame`.
36540
- */
36541
- frameId: zod.z.number().int().nonnegative().optional(),
36542
- /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
36543
- deviceKey: zod.z.string().optional()
36544
- }), zod.z.object({ results: zod.z.array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }),
36545
- /**
36546
- * Cache a raw frame inside the Python inference pool's memory.
36547
- * Returns a numeric `frameId` that `inferCached` references —
36548
- * subsequent calls send only 5 bytes through the pipe instead of
36549
- * 1.2MB raw data, eliminating the pipe transfer bottleneck.
36550
- */
36551
- cacheFrameInPool: method(zod.z.object({
36552
- data: zod.z.instanceof(Uint8Array),
36553
- width: zod.z.number().int().positive(),
36554
- height: zod.z.number().int().positive(),
36555
- format: zod.z.enum([
36556
- "rgb",
36557
- "bgr",
36558
- "gray"
36559
- ])
36560
- }), zod.z.object({
36561
- frameId: zod.z.number(),
36562
- width: zod.z.number(),
36563
- height: zod.z.number()
36564
- }), { kind: "mutation" }),
36565
- /**
36566
- * Run inference on a previously cached frame. Sends only 5 bytes
36567
- * (model_idx + frameId) through the IPC pipe — eliminates the
36568
- * ~35ms per-call overhead of transferring 1.2MB raw data.
36569
- */
36570
- inferCached: method(zod.z.object({
36571
- stepId: zod.z.string(),
36572
- frameId: zod.z.number().int()
36573
- }), zod.z.record(zod.z.string(), zod.z.unknown()), { kind: "mutation" }),
36574
- /**
36575
- * Release a cached frame from the Python pool's memory.
36576
- */
36577
- uncacheFrame: method(zod.z.object({ frameId: zod.z.number().int() }), zod.z.void(), { kind: "mutation" }),
36578
- /** Returns the effective pool tuning (resolved from user overrides + backend defaults). */
36579
- getEffectiveTuning: method(zod.z.void(), zod.z.object({
36580
- batchMode: zod.z.string(),
36581
- windowMs: zod.z.number(),
36582
- maxBatchSize: zod.z.number(),
36583
- concurrency: zod.z.number()
36584
- })),
36585
- /**
36586
- * List every EngineFactory currently loaded in this executor's RAM,
36587
- * with the models resident and a coarse "in use" marker derived from
36588
- * ongoing inference activity. Used by the Pipeline page Engines tab.
36589
- */
36590
- listLoadedEngines: method(zod.z.void(), zod.z.array(zod.z.object({
36591
- engineKey: zod.z.string(),
36592
- engine: PipelineEngineChoiceSchema,
36593
- modelsLoaded: zod.z.array(zod.z.string()).readonly(),
36594
- inUseByCameras: zod.z.array(zod.z.number()).readonly(),
36595
- /**
36596
- * Origin of this resident factory.
36597
- * - `runtime` — main camera-serving engine (no idle TTL).
36598
- * - `warm-override` — benchmark/test override held in the warm
36599
- * cache; auto-disposed after the idle TTL.
36600
- * - `device-pool` — a concurrent per-device pool (Phase 2
36601
- * multi-device, keyed by `deviceKey`) resolved
36602
- * via `resolveDeviceFactory`. Runs alongside the
36603
- * `runtime` engine on a DIFFERENT accelerator
36604
- * (NPU / iGPU / Coral) — this is how the
36605
- * Engines tab shows all pools running at once.
36606
- */
36607
- kind: zod.z.enum([
36608
- "runtime",
36609
- "warm-override",
36610
- "device-pool"
36611
- ]),
36612
- /** Native pid of the underlying Python pool (null when no pool). */
36613
- poolPid: zod.z.number().nullable(),
36614
- /** ms since this factory was last used (null when not warm-tracked). */
36615
- idleMs: zod.z.number().nullable(),
36616
- /** Idle TTL after which `warm-override` factories self-evict (null when not applicable). */
36617
- idleTtlMs: zod.z.number().nullable()
36618
- })).readonly()),
36619
- /** Warm up an engine without running a frame. No-op if already loaded. */
36620
- spinEngine: method(zod.z.object({ engine: PipelineEngineChoiceSchema }), zod.z.object({ success: zod.z.literal(true) }), {
36621
- kind: "mutation",
36622
- auth: "admin"
36623
- }),
36624
- /**
36625
- * Unload an engine from RAM. `force:true` unloads even when cameras
36626
- * are actively using it (they re-spin on next frame). Default is
36627
- * gated — returns `{success:false, reason}` when in use.
36628
- */
36629
- killEngine: method(zod.z.object({
36630
- engine: PipelineEngineChoiceSchema,
36631
- force: zod.z.boolean().optional()
36632
- }), zod.z.object({
36633
- success: zod.z.boolean(),
36634
- reason: zod.z.string().optional()
36635
- }), {
36636
- kind: "mutation",
36637
- auth: "admin"
36638
- }),
36639
- listReferenceImages: method(zod.z.void(), zod.z.array(ReferenceImageEntrySchema).readonly()),
36640
- getReferenceImage: method(zod.z.object({ filename: zod.z.string() }), ReferenceImageBodySchema.nullable()),
36641
- getReferenceAudioFiles: method(zod.z.void(), zod.z.array(ReferenceAudioEntrySchema).readonly()),
36642
- getReferenceAudio: method(zod.z.object({ filename: zod.z.string() }), ReferenceAudioBodySchema.nullable()),
36643
- getAudioCapabilities: method(zod.z.void(), AudioCapabilitiesSchema),
36644
- runAudioTest: method(zod.z.object({
36645
- addonId: zod.z.string(),
36646
- modelId: zod.z.string(),
36647
- filename: zod.z.string().optional(),
36648
- settings: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
36649
- }), AudioTestResultSchema, { kind: "mutation" }),
36650
- getDetectionConfigSchema: method(zod.z.void(), ConfigUISchemaNullableBridge)
36651
- }
36652
- };
36653
36895
  zod.z.object({
36654
36896
  activeCameras: zod.z.number(),
36655
36897
  throttledCameras: zod.z.number(),
@@ -36674,66 +36916,6 @@ var require_dist_D5Jhh8Vk = __commonJS({
36674
36916
  ])
36675
36917
  });
36676
36918
  var CameraMetricsWithDeviceIdSchema = CameraMetricsSchema.extend({ deviceId: zod.z.number() });
36677
- var ZoneKindEnum = zod.z.enum(["polygon", "tripwire"]);
36678
- var PolygonPointSchema = zod.z.object({
36679
- x: zod.z.number(),
36680
- y: zod.z.number()
36681
- });
36682
- var ZoneSchema = zod.z.object({
36683
- id: zod.z.string(),
36684
- name: zod.z.string(),
36685
- kind: ZoneKindEnum.default("polygon"),
36686
- /** Polygon vertices, fraction of frame (0–1). */
36687
- polygon: zod.z.array(PolygonPointSchema).readonly(),
36688
- /** Visual color for UI rendering. */
36689
- color: zod.z.string().default("#3b82f6")
36690
- });
36691
- var zonesCapability = {
36692
- name: "zones",
36693
- scope: "device",
36694
- mode: "singleton",
36695
- deviceTypes: [DeviceType.Camera],
36696
- methods: {
36697
- listZones: method(zod.z.object({ deviceId: zod.z.number() }), zod.z.array(ZoneSchema).readonly()),
36698
- addZone: method(zod.z.object({
36699
- deviceId: zod.z.number(),
36700
- zone: ZoneSchema
36701
- }), zod.z.void(), {
36702
- kind: "mutation",
36703
- auth: "admin"
36704
- }),
36705
- removeZone: method(zod.z.object({
36706
- deviceId: zod.z.number(),
36707
- zoneId: zod.z.string()
36708
- }), zod.z.void(), {
36709
- kind: "mutation",
36710
- auth: "admin"
36711
- }),
36712
- updateZone: method(zod.z.object({
36713
- deviceId: zod.z.number(),
36714
- zone: ZoneSchema
36715
- }), zod.z.void(), {
36716
- kind: "mutation",
36717
- auth: "admin"
36718
- })
36719
- },
36720
- /**
36721
- * Runtime-state slice — the live zone catalogue mirrored by the
36722
- * orchestrator on every CRUD mutation. Consumers read via
36723
- * `device.state.zones.value` / `.watch(...)` without round-tripping
36724
- * the cap, and the codegen DeviceProxy auto-wires the reactive
36725
- * handle. Slice shape is `{ zones: Zone[] }` so future extensions
36726
- * (e.g. zone groupings) can sit alongside the polygon list.
36727
- */
36728
- runtimeState: zod.z.object({ zones: zod.z.array(ZoneSchema).readonly() }),
36729
- /**
36730
- * Runtime-state durability: **restored** — written only on operator mutation, so a camera that never had one has nothing to re-derive from. This is the slice `zone-mirror-hydration.ts` exists to paper over.
36731
- *
36732
- * See `RuntimeStateDurability`. Enforced by
36733
- * `scripts/check-runtime-state-durability.ts`.
36734
- */
36735
- durability: "restored"
36736
- };
36737
36919
  var NativeCropBboxSchema = zod.z.object({
36738
36920
  x: zod.z.number(),
36739
36921
  y: zod.z.number(),
@@ -45526,6 +45708,23 @@ var require_dist_D5Jhh8Vk = __commonJS({
45526
45708
  /** Media ms the returned fragment covers. */
45527
45709
  gopDurMs: zod.z.number()
45528
45710
  });
45711
+ var ReadWindowBytesResultSchema = zod.z.discriminatedUnion("kind", [zod.z.object({
45712
+ kind: zod.z.literal("ok"),
45713
+ data: zod.z.instanceof(Uint8Array),
45714
+ /** Absolute epoch ms of the returned bytes' first sample — at or before
45715
+ * the requested `fromMs` (anchored on the nearest keyframe). */
45716
+ gopStartMs: zod.z.number(),
45717
+ /** Media ms the returned bytes cover, from `gopStartMs`. */
45718
+ gopDurMs: zod.z.number(),
45719
+ /** `false` ⇒ the safety byte cap cut the read short before it reached
45720
+ * the requested `toMs`; the caller got fewer frames than asked for. */
45721
+ reachesRequestedEnd: zod.z.boolean()
45722
+ }), zod.z.object({
45723
+ kind: zod.z.literal("spans-multiple-segments"),
45724
+ /** Where the covering segment's own footage runs out — informational,
45725
+ * not a retry hint (retrying the same window would refuse again). */
45726
+ segmentEndMs: zod.z.number()
45727
+ })]);
45529
45728
  var recordingCapability = {
45530
45729
  name: "recording",
45531
45730
  scope: "system",
@@ -45605,6 +45804,21 @@ var require_dist_D5Jhh8Vk = __commonJS({
45605
45804
  kind: "query",
45606
45805
  auth: "admin"
45607
45806
  }),
45807
+ /** Read a WINDOW `[fromMs, toMs)` of segment `startMs`, by mfra byte
45808
+ * range — the multi-GOP twin of `readGopBytes`. Refuses (does not
45809
+ * degrade) when the window runs past the covering segment. Used by the
45810
+ * replay clip's `recording` source to fetch several seconds of native
45811
+ * footage for its single ffmpeg decode pass. */
45812
+ readWindowBytes: method(zod.z.object({
45813
+ deviceId: zod.z.number(),
45814
+ profile: zod.z.string(),
45815
+ startMs: zod.z.number(),
45816
+ fromMs: zod.z.number(),
45817
+ toMs: zod.z.number()
45818
+ }), ReadWindowBytesResultSchema, {
45819
+ kind: "query",
45820
+ auth: "admin"
45821
+ }),
45608
45822
  setDeviceConfig: method(zod.z.object({
45609
45823
  deviceId: zod.z.number(),
45610
45824
  config: RecordingConfigSchema
@@ -46167,59 +46381,6 @@ var require_dist_D5Jhh8Vk = __commonJS({
46167
46381
  */
46168
46382
  durability: "session"
46169
46383
  };
46170
- var ZoneRuleModeEnum = zod.z.enum(["include", "exclude"]);
46171
- var ZoneRuleSchema = zod.z.object({
46172
- /** Stable rule id — survives edits, used by the UI for diffing. */
46173
- id: zod.z.string(),
46174
- /** Optional human-readable label rendered in the rule editor. */
46175
- name: zod.z.string().optional(),
46176
- /** Zones this rule targets. The rule's `mode` applies to ALL
46177
- * listed zones (OR-set: a detection in any one of them counts).
46178
- * At least one zone id required — a rule with no targets is a
46179
- * configuration mistake and the form validator rejects it. */
46180
- zoneIds: zod.z.array(zod.z.string()).min(1).readonly(),
46181
- mode: ZoneRuleModeEnum,
46182
- /**
46183
- * Class names this rule applies to. Empty / undefined ⇒ rule
46184
- * applies to every class. Class strings match the `macroClass`
46185
- * field on detections (e.g. `person`, `car`, `dog`).
46186
- */
46187
- classFilter: zod.z.array(zod.z.string()).readonly().optional(),
46188
- /**
46189
- * Minimum bbox/mask overlap (0–1) with any of the rule's zones
46190
- * required to consider an entity "in the zone". Defaults to the
46191
- * consumer's stage default when omitted. Kept for back-compat with
46192
- * existing per-rule overrides; new operators pick the value via
46193
- * `bboxInclusionPct` (operator-friendly 0–100). Whichever field is
46194
- * set, the lower-level engine reads it as a 0–1 fraction.
46195
- */
46196
- overlapThreshold: zod.z.number().min(0).max(1).optional(),
46197
- /**
46198
- * Operator-friendly version of `overlapThreshold` — the percentage
46199
- * of the detection's bbox that must lie inside the zone for the
46200
- * rule to match. Documented default is 85%; the engine substitutes
46201
- * that when the field is omitted (kept optional so existing rules
46202
- * stored without it stay valid).
46203
- *
46204
- * When BOTH `overlapThreshold` and `bboxInclusionPct` are set on a
46205
- * rule, the engine prefers `bboxInclusionPct` because it's the
46206
- * field exposed in the UI. Internally both feed the same gate.
46207
- */
46208
- bboxInclusionPct: zod.z.number().min(0).max(100).optional(),
46209
- /**
46210
- * When `true` and a detection has a segmentation mask, use the
46211
- * mask for overlap instead of the bbox. Detection-stage only;
46212
- * motion rules ignore this field.
46213
- */
46214
- preferMask: zod.z.boolean().optional(),
46215
- /**
46216
- * Soft-toggle: `false` disables the rule without deleting it.
46217
- * Defaults to `true` so operators creating a rule via the UI
46218
- * see it active immediately.
46219
- */
46220
- enabled: zod.z.boolean().default(true)
46221
- });
46222
- zod.z.array(ZoneRuleSchema).readonly();
46223
46384
  var ScriptRunnerStatusSchema = zod.z.object({
46224
46385
  /** Whether the script is currently executing. */
46225
46386
  isRunning: zod.z.boolean(),
@@ -51166,6 +51327,12 @@ var require_dist_D5Jhh8Vk = __commonJS({
51166
51327
  addonId: null,
51167
51328
  access: "view"
51168
51329
  },
51330
+ "notificationRules.resolveArtifactUrl": {
51331
+ capName: "notification-rules",
51332
+ capScope: "system",
51333
+ addonId: null,
51334
+ access: "view"
51335
+ },
51169
51336
  "notificationRules.setAlarmConfig": {
51170
51337
  capName: "notification-rules",
51171
51338
  capScope: "system",
@@ -51610,6 +51777,12 @@ var require_dist_D5Jhh8Vk = __commonJS({
51610
51777
  addonId: null,
51611
51778
  access: "create"
51612
51779
  },
51780
+ "pipelineAnalytics.runReplayFrameProcessor": {
51781
+ capName: "pipeline-analytics",
51782
+ capScope: "device",
51783
+ addonId: null,
51784
+ access: "create"
51785
+ },
51613
51786
  "pipelineAnalytics.saveRetrainAnnotations": {
51614
51787
  capName: "pipeline-analytics",
51615
51788
  capScope: "device",
@@ -51742,6 +51915,12 @@ var require_dist_D5Jhh8Vk = __commonJS({
51742
51915
  addonId: null,
51743
51916
  access: "view"
51744
51917
  },
51918
+ "pipelineExecutor.getInferenceDeviceHealth": {
51919
+ capName: "pipeline-executor",
51920
+ capScope: "system",
51921
+ addonId: null,
51922
+ access: "view"
51923
+ },
51745
51924
  "pipelineExecutor.getOrchestratorConfigSchema": {
51746
51925
  capName: "pipeline-executor",
51747
51926
  capScope: "system",
@@ -51814,6 +51993,12 @@ var require_dist_D5Jhh8Vk = __commonJS({
51814
51993
  addonId: null,
51815
51994
  access: "view"
51816
51995
  },
51996
+ "pipelineExecutor.rearmInferenceDevice": {
51997
+ capName: "pipeline-executor",
51998
+ capScope: "system",
51999
+ addonId: null,
52000
+ access: "create"
52001
+ },
51817
52002
  "pipelineExecutor.runAudioTest": {
51818
52003
  capName: "pipeline-executor",
51819
52004
  capScope: "system",
@@ -52552,6 +52737,12 @@ var require_dist_D5Jhh8Vk = __commonJS({
52552
52737
  addonId: null,
52553
52738
  access: "view"
52554
52739
  },
52740
+ "recording.readWindowBytes": {
52741
+ capName: "recording",
52742
+ capScope: "system",
52743
+ addonId: null,
52744
+ access: "view"
52745
+ },
52555
52746
  "recording.refreshStorageLocationsForMigration": {
52556
52747
  capName: "recording",
52557
52748
  capScope: "system",
@@ -55062,6 +55253,11 @@ var require_dist_D5Jhh8Vk = __commonJS({
55062
55253
  form: "single",
55063
55254
  optional: false
55064
55255
  }],
55256
+ "pipelineAnalytics.runReplayFrameProcessor": [{
55257
+ name: "deviceId",
55258
+ form: "single",
55259
+ optional: false
55260
+ }],
55065
55261
  "pipelineAnalytics.saveRetrainAnnotations": [{
55066
55262
  name: "deviceId",
55067
55263
  form: "single",
@@ -55377,6 +55573,11 @@ var require_dist_D5Jhh8Vk = __commonJS({
55377
55573
  form: "single",
55378
55574
  optional: false
55379
55575
  }],
55576
+ "recording.readWindowBytes": [{
55577
+ name: "deviceId",
55578
+ form: "single",
55579
+ optional: false
55580
+ }],
55380
55581
  "recording.relocateFootage": [{
55381
55582
  name: "deviceId",
55382
55583
  form: "single",
@@ -57031,7 +57232,7 @@ var require_alerts_addon = __commonJS({
57031
57232
  [Symbol.toStringTag]: { value: "Module" }
57032
57233
  });
57033
57234
  require_chunk_Cek0wNdY();
57034
- var require_dist10 = require_dist_D5Jhh8Vk();
57235
+ var require_dist10 = require_dist_jRSrzoXr();
57035
57236
  function selectExpired(alerts, cutoffMs) {
57036
57237
  return alerts.filter((a) => a.createdAt < cutoffMs).sort((a, b) => a.createdAt - b.createdAt).map((a) => a.id);
57037
57238
  }
@@ -57850,7 +58051,7 @@ var require_console_logging = __commonJS({
57850
58051
  [Symbol.toStringTag]: { value: "Module" }
57851
58052
  });
57852
58053
  require_chunk_Cek0wNdY();
57853
- var require_dist10 = require_dist_D5Jhh8Vk();
58054
+ var require_dist10 = require_dist_jRSrzoXr();
57854
58055
  var require_formatter = require_formatter_DqAKDlvN();
57855
58056
  var LEVEL_RANK = {
57856
58057
  debug: 0,
@@ -57944,7 +58145,7 @@ var require_core_blocks_addon = __commonJS({
57944
58145
  "use strict";
57945
58146
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
57946
58147
  var require_chunk = require_chunk_Cek0wNdY();
57947
- var require_dist10 = require_dist_D5Jhh8Vk();
58148
+ var require_dist10 = require_dist_jRSrzoXr();
57948
58149
  var node_crypto = __require("crypto");
57949
58150
  var node_fs_promises = __require("fs/promises");
57950
58151
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -58841,11 +59042,11 @@ var require_core_blocks = __commonJS({
58841
59042
  }
58842
59043
  });
58843
59044
 
58844
- // ../system/dist/retired-settings-keys-Dflres-F.js
58845
- var require_retired_settings_keys_Dflres_F = __commonJS({
58846
- "../system/dist/retired-settings-keys-Dflres-F.js"(exports) {
59045
+ // ../system/dist/retired-settings-keys-ovd_DQzc.js
59046
+ var require_retired_settings_keys_ovd_DQzc = __commonJS({
59047
+ "../system/dist/retired-settings-keys-ovd_DQzc.js"(exports) {
58847
59048
  "use strict";
58848
- var require_dist10 = require_dist_D5Jhh8Vk();
59049
+ var require_dist10 = require_dist_jRSrzoXr();
58849
59050
  function settingsStoreIsAuthoritativeHere(env) {
58850
59051
  const raw = (env["CAMSTACK_ROLE"] ?? "").trim().toLowerCase();
58851
59052
  return raw === "" || raw === "hub";
@@ -60888,8 +61089,8 @@ var require_device_manager_addon = __commonJS({
60888
61089
  [Symbol.toStringTag]: { value: "Module" }
60889
61090
  });
60890
61091
  require_chunk_Cek0wNdY();
60891
- var require_dist10 = require_dist_D5Jhh8Vk();
60892
- var require_retired_settings_keys = require_retired_settings_keys_Dflres_F();
61092
+ var require_dist10 = require_dist_jRSrzoXr();
61093
+ var require_retired_settings_keys = require_retired_settings_keys_ovd_DQzc();
60893
61094
  var node_crypto = __require("crypto");
60894
61095
  var _camstack_types_node = require_node();
60895
61096
  var JOB_HISTORY = 20;
@@ -61908,6 +62109,10 @@ var require_device_manager_addon = __commonJS({
61908
62109
  device
61909
62110
  };
61910
62111
  }
62112
+ function extractCallerAddonId(input) {
62113
+ const raw = Reflect.get(input, "callerAddonId");
62114
+ return typeof raw === "string" && raw.length > 0 ? raw : void 0;
62115
+ }
61911
62116
  async function listPersistedByAddon(pctx, input) {
61912
62117
  const { addonId } = input;
61913
62118
  return (await pctx.metaStore.rows.listByAddon(addonId)).map(({ meta: m }) => ({
@@ -61935,6 +62140,7 @@ var require_device_manager_addon = __commonJS({
61935
62140
  async function listAll(pctx, input) {
61936
62141
  const ownerFilter = Reflect.get(input, "addonId");
61937
62142
  if (ownerFilter !== void 0 && (typeof ownerFilter !== "string" || ownerFilter.length === 0)) throw new Error(`deviceManager.listAll: addonId must be a non-empty string or be omitted \u2014 got ${JSON.stringify(ownerFilter)}. An owner filter that is present but empty is never widened to every device. On an addon context the id is \`ctx.id\`; there is no \`ctx.addonId\`.`);
62143
+ const callerAddonId = pctx.metaStore.rows.censusArmed ? extractCallerAddonId(input) : void 0;
61938
62144
  const { addonId } = input;
61939
62145
  const slim = input.projection === "slim";
61940
62146
  const camerasOnly = input.isCamera === true;
@@ -61944,7 +62150,7 @@ var require_device_manager_addon = __commonJS({
61944
62150
  const fleet = addonId ? await pctx.metaStore.rows.listByAddon(addonId) : await pctx.metaStore.rows.listFleet({
61945
62151
  ...input.deviceIds !== void 0 ? { deviceIds: input.deviceIds } : {},
61946
62152
  slim
61947
- });
62153
+ }, callerAddonId);
61948
62154
  const rowById = /* @__PURE__ */ new Map();
61949
62155
  for (const row of fleet) rowById.set(row.meta.id, row);
61950
62156
  if (pctx.registry) {
@@ -62446,18 +62652,38 @@ var require_device_manager_addon = __commonJS({
62446
62652
  manualIds
62447
62653
  };
62448
62654
  }
62655
+ function expandContainer(id, byId, byParent, visited, out) {
62656
+ if (visited.has(id)) return;
62657
+ const candidate = byId.get(id);
62658
+ if (!candidate) return;
62659
+ if (candidate.type !== require_dist10.DeviceType.Container) {
62660
+ out.add(id);
62661
+ return;
62662
+ }
62663
+ const nextVisited = new Set(visited);
62664
+ nextVisited.add(id);
62665
+ const children = byParent.get(id) ?? [];
62666
+ for (const child of children) expandContainer(child.id, byId, byParent, nextVisited, out);
62667
+ }
62449
62668
  function resolveLinkedDeviceIds(params) {
62450
62669
  const { selfId, selfLocation, config, candidates } = params;
62670
+ const byId = new Map(candidates.map((c) => [c.id, c]));
62671
+ const byParent = /* @__PURE__ */ new Map();
62672
+ for (const c of candidates) {
62673
+ if (c.parentDeviceId === null) continue;
62674
+ const siblings = byParent.get(c.parentDeviceId);
62675
+ if (siblings) siblings.push(c);
62676
+ else byParent.set(c.parentDeviceId, [c]);
62677
+ }
62451
62678
  const ids = /* @__PURE__ */ new Set();
62452
- for (const c of candidates) if (c.parentDeviceId === selfId) ids.add(c.id);
62679
+ const expand = (id) => expandContainer(id, byId, byParent, /* @__PURE__ */ new Set(), ids);
62680
+ for (const c of candidates) if (c.parentDeviceId === selfId) expand(c.id);
62453
62681
  if (config.mode === "auto") {
62454
- if (selfLocation !== null && selfLocation.length > 0) {
62455
- for (const c of candidates) if (c.location === selfLocation) ids.add(c.id);
62682
+ if (selfLocation !== null && selfLocation.length > 0) for (const c of candidates) {
62683
+ if (c.type === require_dist10.DeviceType.Container) continue;
62684
+ if (c.location === selfLocation) ids.add(c.id);
62456
62685
  }
62457
- } else {
62458
- const known = new Set(candidates.map((c) => c.id));
62459
- for (const id of config.manualIds) if (known.has(id)) ids.add(id);
62460
- }
62686
+ } else for (const id of config.manualIds) if (byId.has(id)) expand(id);
62461
62687
  ids.delete(selfId);
62462
62688
  return [...ids];
62463
62689
  }
@@ -62524,7 +62750,7 @@ var require_device_manager_addon = __commonJS({
62524
62750
  if (!persisted || persisted.meta.type !== require_dist10.DeviceType.Camera) return null;
62525
62751
  const [all, blob] = await Promise.all([listAll(pctx, LINK_CANDIDATE_PROJECTION), pctx.settings.readDeviceStore(deviceId)]);
62526
62752
  const config = parseLinkedDevicesConfig(blob);
62527
- const options = all.filter((d) => d.id !== deviceId).map((d) => ({
62753
+ const options = all.filter((d) => d.id !== deviceId && d.type !== require_dist10.DeviceType.Container).map((d) => ({
62528
62754
  value: String(d.id),
62529
62755
  label: d.location !== null ? `${d.name} (${d.location})` : d.name
62530
62756
  }));
@@ -63528,11 +63754,42 @@ var require_device_manager_addon = __commonJS({
63528
63754
  }
63529
63755
  });
63530
63756
  }
63757
+ async function collectDescendants(metaStore, rootId) {
63758
+ const out = [];
63759
+ const visited = /* @__PURE__ */ new Set([rootId]);
63760
+ const queue = [rootId];
63761
+ while (queue.length > 0) {
63762
+ const parentId = queue.shift();
63763
+ if (parentId === void 0) break;
63764
+ for (const row of await metaStore.rows.listByParent(parentId)) {
63765
+ if (visited.has(row.meta.id)) continue;
63766
+ visited.add(row.meta.id);
63767
+ out.push(row.meta);
63768
+ queue.push(row.meta.id);
63769
+ }
63770
+ }
63771
+ return out;
63772
+ }
63531
63773
  async function setLocation(pctx, input) {
63532
63774
  const { deviceId, location } = input;
63775
+ const cascaded = [];
63776
+ const failed = [];
63533
63777
  await pctx.metaStore.withMetaWriteLock(async () => {
63534
- if (!await pctx.metaStore.resolvePersistedById(deviceId)) throw new Error(`[device-manager] setLocation: unknown device id=${deviceId}`);
63778
+ const persisted = await pctx.metaStore.resolvePersistedById(deviceId);
63779
+ if (!persisted) throw new Error(`[device-manager] setLocation: unknown device id=${deviceId}`);
63535
63780
  await pctx.metaStore.rows.patch(deviceId, { location });
63781
+ if (persisted.meta.type === require_dist10.DeviceType.Container) {
63782
+ const descendants = await collectDescendants(pctx.metaStore, deviceId);
63783
+ for (const descendant of descendants) try {
63784
+ await pctx.metaStore.rows.patch(descendant.id, { location });
63785
+ cascaded.push(descendant.id);
63786
+ } catch (err) {
63787
+ failed.push({
63788
+ id: descendant.id,
63789
+ error: err
63790
+ });
63791
+ }
63792
+ }
63536
63793
  });
63537
63794
  pctx.host.ctx.eventBus.emit({
63538
63795
  id: (0, node_crypto.randomUUID)(),
@@ -63548,6 +63805,36 @@ var require_device_manager_addon = __commonJS({
63548
63805
  value: location
63549
63806
  }
63550
63807
  });
63808
+ for (const childId of cascaded) pctx.host.ctx.eventBus.emit({
63809
+ id: (0, node_crypto.randomUUID)(),
63810
+ timestamp: /* @__PURE__ */ new Date(),
63811
+ source: {
63812
+ type: "device",
63813
+ id: childId
63814
+ },
63815
+ category: require_dist10.EventCategory.DeviceMetaChanged,
63816
+ data: {
63817
+ deviceId: childId,
63818
+ field: "location",
63819
+ value: location
63820
+ }
63821
+ });
63822
+ if (cascaded.length > 0 || failed.length > 0) pctx.host.ctx.logger.info("setLocation: cascaded location to container descendants", {
63823
+ tags: { deviceId },
63824
+ meta: {
63825
+ location,
63826
+ updated: cascaded.length,
63827
+ failed: failed.length
63828
+ }
63829
+ });
63830
+ if (failed.length > 0) pctx.host.ctx.logger.error("setLocation: cascade partially failed \u2014 some descendants NOT aligned to the new location", {
63831
+ tags: { deviceId },
63832
+ meta: {
63833
+ location,
63834
+ failedIds: failed.map((f) => f.id),
63835
+ errors: failed.map((f) => f.error instanceof Error ? f.error.message : String(f.error))
63836
+ }
63837
+ });
63551
63838
  }
63552
63839
  async function setType(pctx, input) {
63553
63840
  const { deviceId, type } = input;
@@ -64039,9 +64326,29 @@ var require_device_manager_addon = __commonJS({
64039
64326
  * to it even if it lands after the close — otherwise the slow reads at the
64040
64327
  * edge would be attributed to nobody, and the slow ones are the interesting
64041
64328
  * ones.
64042
- */
64043
- siteOf(nowMs = Date.now()) {
64329
+ *
64330
+ * `callerAddonId`, when given, is formatted as the site VERBATIM instead of
64331
+ * walking the local stack. A call that arrived over the cap channel — a
64332
+ * forked addon's `ctx.api.deviceManager.listAll.query()`, routed through
64333
+ * `LocalChildRegistry` → `onUnownedCall` → `CapRouteResolver` — carries its
64334
+ * origin as DATA (the registered `childId` the parent captured off the UDS
64335
+ * connection), and no amount of stack depth would ever recover it: every
64336
+ * frame between the socket read and this call is the SAME generic
64337
+ * dispatcher code (`onUnownedCall`, `dispatch`, `invoke`) regardless of
64338
+ * which addon called — the 2026-08-25 boot census measured this directly,
64339
+ * five thousand calls landing on `listAll` with nothing nameable above it.
64340
+ *
64341
+ * `isOpen` is checked FIRST, before the id is even looked at — a caller
64342
+ * threading a (cheap, already-known) `callerAddonId` through on every
64343
+ * request, armed or not, must not pay for the template string when
64344
+ * disarmed. This is also why formatting lives HERE rather than at the call
64345
+ * site: `DeviceRowStore` passes the raw id through unconditionally, and
64346
+ * only this method — which already knows whether armed — decides whether
64347
+ * to spend anything on it.
64348
+ */
64349
+ siteOf(callerAddonId, nowMs = Date.now()) {
64044
64350
  if (!this.isOpen(nowMs)) return null;
64351
+ if (callerAddonId !== void 0) return `addon:${callerAddonId}`;
64045
64352
  const holder = {};
64046
64353
  Error.captureStackTrace(holder, this.siteOf);
64047
64354
  return siteFromStack(holder.stack);
@@ -64329,6 +64636,16 @@ var require_device_manager_addon = __commonJS({
64329
64636
  /** Call-site attribution for fleet reads. Armed only by
64330
64637
  * `CAMSTACK_FLEET_READ_CENSUS_MS`; see `fleet-read-census.ts`. */
64331
64638
  census;
64639
+ /**
64640
+ * Is the fleet-read census armed right now? Exposed so a caller ABOVE this
64641
+ * store (`device-queries.ts`) can skip extracting a caller identity off its
64642
+ * input entirely when nobody is measuring — "the boolean field read first"
64643
+ * pattern this repo already uses elsewhere to keep a diagnostic seam at
64644
+ * zero cost while disarmed.
64645
+ */
64646
+ get censusArmed() {
64647
+ return this.census.armed;
64648
+ }
64332
64649
  constructor(backend, logger, census) {
64333
64650
  this.backend = backend;
64334
64651
  this.logger = logger;
@@ -64377,15 +64694,23 @@ var require_device_manager_addon = __commonJS({
64377
64694
  }
64378
64695
  return decoded;
64379
64696
  }
64380
- /** Every device, ordered by numeric id. */
64381
- async listAll() {
64697
+ /**
64698
+ * Every device, ordered by numeric id.
64699
+ *
64700
+ * `callerAddonId`, when known, is the addon that ORIGINATED this call —
64701
+ * captured off the cap-channel envelope (`CapCallInput.callerAddonId`, set
64702
+ * by `LocalChildRegistry` from the registered `childId`), never guessed.
64703
+ * Passed straight to the census; a stack walk can never name it (see
64704
+ * `fleet-read-census.ts#siteOf`).
64705
+ */
64706
+ async listAll(callerAddonId) {
64382
64707
  return this.list({
64383
64708
  orderBy: {
64384
64709
  field: "deviceId",
64385
64710
  direction: "asc"
64386
64711
  },
64387
64712
  limit: DEVICE_ROWS_FLEET_LIMIT
64388
- });
64713
+ }, void 0, callerAddonId);
64389
64714
  }
64390
64715
  /**
64391
64716
  * The fleet, read as narrowly as the question allows.
@@ -64402,8 +64727,10 @@ var require_device_manager_addon = __commonJS({
64402
64727
  *
64403
64728
  * `isCamera` is deliberately NOT one of them — see that constant's note and
64404
64729
  * `device-row-store-filtered.spec.ts`.
64730
+ *
64731
+ * `callerAddonId` — see {@link listAll}'s doc; same meaning here.
64405
64732
  */
64406
- async listFleet(opts) {
64733
+ async listFleet(opts, callerAddonId) {
64407
64734
  if (opts.deviceIds !== void 0 && opts.deviceIds.length === 0) return [];
64408
64735
  return this.list({
64409
64736
  ...opts.deviceIds !== void 0 ? { whereIn: { deviceId: [...new Set(opts.deviceIds)] } } : {},
@@ -64412,7 +64739,7 @@ var require_device_manager_addon = __commonJS({
64412
64739
  direction: "asc"
64413
64740
  },
64414
64741
  limit: DEVICE_ROWS_FLEET_LIMIT
64415
- }, opts.slim === true ? DEVICE_ROWS_SLIM_COLUMNS : void 0);
64742
+ }, opts.slim === true ? DEVICE_ROWS_SLIM_COLUMNS : void 0, callerAddonId);
64416
64743
  }
64417
64744
  /**
64418
64745
  * Many devices, by numeric id, in ONE query.
@@ -64503,8 +64830,8 @@ var require_device_manager_addon = __commonJS({
64503
64830
  await this.declare();
64504
64831
  return this.backend.count({ collection: DEVICE_ROWS_COLLECTION });
64505
64832
  }
64506
- async list(filter, columns) {
64507
- const site = this.census.siteOf();
64833
+ async list(filter, columns, callerAddonId) {
64834
+ const site = this.census.siteOf(callerAddonId);
64508
64835
  await this.declare();
64509
64836
  const records = await this.backend.query({
64510
64837
  collection: DEVICE_ROWS_COLLECTION,
@@ -65432,7 +65759,7 @@ var require_hub_forwarder = __commonJS({
65432
65759
  [Symbol.toStringTag]: { value: "Module" }
65433
65760
  });
65434
65761
  require_chunk_Cek0wNdY();
65435
- var require_dist10 = require_dist_D5Jhh8Vk();
65762
+ var require_dist10 = require_dist_jRSrzoXr();
65436
65763
  var require_formatter = require_formatter_DqAKDlvN();
65437
65764
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
65438
65765
  var HubForwarderDestination = class {
@@ -65569,7 +65896,7 @@ var require_liveness_monitor_addon = __commonJS({
65569
65896
  "use strict";
65570
65897
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
65571
65898
  require_chunk_Cek0wNdY();
65572
- var require_dist10 = require_dist_D5Jhh8Vk();
65899
+ var require_dist10 = require_dist_jRSrzoXr();
65573
65900
  var NO_DEVICES = "liveness:no-devices";
65574
65901
  var ALL_OFFLINE = "liveness:all-devices-offline";
65575
65902
  var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
@@ -65759,7 +66086,7 @@ var require_local_auth_addon = __commonJS({
65759
66086
  [Symbol.toStringTag]: { value: "Module" }
65760
66087
  });
65761
66088
  var require_chunk = require_chunk_Cek0wNdY();
65762
- var require_dist10 = require_dist_D5Jhh8Vk();
66089
+ var require_dist10 = require_dist_jRSrzoXr();
65763
66090
  var node_crypto = __require("crypto");
65764
66091
  node_crypto = require_chunk.__toESM(node_crypto);
65765
66092
  var crypto$1 = __require("crypto");
@@ -73443,7 +73770,7 @@ var require_loki_logging = __commonJS({
73443
73770
  [Symbol.toStringTag]: { value: "Module" }
73444
73771
  });
73445
73772
  require_chunk_Cek0wNdY();
73446
- var require_dist10 = require_dist_D5Jhh8Vk();
73773
+ var require_dist10 = require_dist_jRSrzoXr();
73447
73774
  function sanitizeLabelName(raw) {
73448
73775
  const replaced = raw.replaceAll(/[^a-zA-Z0-9_]/g, "_");
73449
73776
  return /^[a-zA-Z_]/.test(replaced) ? replaced : `_${replaced}`;
@@ -74008,7 +74335,7 @@ var require_native_metrics_addon = __commonJS({
74008
74335
  [Symbol.toStringTag]: { value: "Module" }
74009
74336
  });
74010
74337
  var require_chunk = require_chunk_Cek0wNdY();
74011
- var require_dist10 = require_dist_D5Jhh8Vk();
74338
+ var require_dist10 = require_dist_jRSrzoXr();
74012
74339
  var node_child_process = __require("child_process");
74013
74340
  var node_util = __require("util");
74014
74341
  var node_os = __require("os");
@@ -74950,7 +75277,7 @@ var require_filesystem_storage_addon = __commonJS({
74950
75277
  [Symbol.toStringTag]: { value: "Module" }
74951
75278
  });
74952
75279
  var require_chunk = require_chunk_Cek0wNdY();
74953
- var require_dist10 = require_dist_D5Jhh8Vk();
75280
+ var require_dist10 = require_dist_jRSrzoXr();
74954
75281
  var node_crypto = __require("crypto");
74955
75282
  var node_fs_promises = __require("fs/promises");
74956
75283
  var node_path = __require("path");
@@ -76066,8 +76393,8 @@ var require_sqlite_settings_addon = __commonJS({
76066
76393
  [Symbol.toStringTag]: { value: "Module" }
76067
76394
  });
76068
76395
  var require_chunk = require_chunk_Cek0wNdY();
76069
- var require_dist10 = require_dist_D5Jhh8Vk();
76070
- var require_retired_settings_keys = require_retired_settings_keys_Dflres_F();
76396
+ var require_dist10 = require_dist_jRSrzoXr();
76397
+ var require_retired_settings_keys = require_retired_settings_keys_ovd_DQzc();
76071
76398
  var node_crypto = __require("crypto");
76072
76399
  var node_fs = __require("fs");
76073
76400
  var node_module = __require("module");
@@ -78261,7 +78588,7 @@ var require_storage_orchestrator_addon = __commonJS({
78261
78588
  [Symbol.toStringTag]: { value: "Module" }
78262
78589
  });
78263
78590
  var require_chunk = require_chunk_Cek0wNdY();
78264
- var require_dist10 = require_dist_D5Jhh8Vk();
78591
+ var require_dist10 = require_dist_jRSrzoXr();
78265
78592
  var node_crypto = __require("crypto");
78266
78593
  var node_fs_promises = __require("fs/promises");
78267
78594
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -80140,7 +80467,7 @@ var require_system_config_addon = __commonJS({
80140
80467
  [Symbol.toStringTag]: { value: "Module" }
80141
80468
  });
80142
80469
  require_chunk_Cek0wNdY();
80143
- var require_dist10 = require_dist_D5Jhh8Vk();
80470
+ var require_dist10 = require_dist_jRSrzoXr();
80144
80471
  var SECTION_TITLES = {
80145
80472
  server: "Server",
80146
80473
  auth: "Authentication"
@@ -98201,7 +98528,7 @@ var require_winston_logging = __commonJS({
98201
98528
  [Symbol.toStringTag]: { value: "Module" }
98202
98529
  });
98203
98530
  var require_chunk = require_chunk_Cek0wNdY();
98204
- var require_dist10 = require_dist_D5Jhh8Vk();
98531
+ var require_dist10 = require_dist_jRSrzoXr();
98205
98532
  var require_formatter = require_formatter_DqAKDlvN();
98206
98533
  var node_path = __require("path");
98207
98534
  node_path = require_chunk.__toESM(node_path);
@@ -100144,9 +100471,9 @@ var require_event_category_EY0GNjV9 = __commonJS({
100144
100471
  }
100145
100472
  });
100146
100473
 
100147
- // ../types/dist/sleep-DnbNfEhn.js
100148
- var require_sleep_DnbNfEhn = __commonJS({
100149
- "../types/dist/sleep-DnbNfEhn.js"(exports) {
100474
+ // ../types/dist/sleep-CSodb2vQ.js
100475
+ var require_sleep_CSodb2vQ = __commonJS({
100476
+ "../types/dist/sleep-CSodb2vQ.js"(exports) {
100150
100477
  "use strict";
100151
100478
  var require_event_category = require_event_category_EY0GNjV9();
100152
100479
  var zod = require_zod();
@@ -102674,6 +103001,7 @@ var require_sleep_DnbNfEhn = __commonJS({
102674
103001
  testRule: (input) => dispatch("notification-rules", "notificationRules", "testRule", "mutation", input),
102675
103002
  getConditionCatalog: (input) => dispatch("notification-rules", "notificationRules", "getConditionCatalog", "query", input),
102676
103003
  getHistory: (input) => dispatch("notification-rules", "notificationRules", "getHistory", "query", input),
103004
+ resolveArtifactUrl: (input) => dispatch("notification-rules", "notificationRules", "resolveArtifactUrl", "query", input),
102677
103005
  listSnoozes: (input) => dispatch("notification-rules", "notificationRules", "listSnoozes", "query", input),
102678
103006
  createSnooze: (input) => dispatch("notification-rules", "notificationRules", "createSnooze", "mutation", input),
102679
103007
  cancelSnooze: (input) => dispatch("notification-rules", "notificationRules", "cancelSnooze", "mutation", input),
@@ -102746,6 +103074,7 @@ var require_sleep_DnbNfEhn = __commonJS({
102746
103074
  deselectRetrainFrame: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "deselectRetrainFrame", "mutation", input),
102747
103075
  getRetrainFrameImage: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getRetrainFrameImage", "query", input),
102748
103076
  proposeRetrainAnnotations: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "proposeRetrainAnnotations", "mutation", input),
103077
+ runReplayFrameProcessor: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "runReplayFrameProcessor", "mutation", input),
102749
103078
  listRetrainAnnotations: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listRetrainAnnotations", "query", input),
102750
103079
  saveRetrainAnnotations: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "saveRetrainAnnotations", "mutation", input),
102751
103080
  completeRetrainTrack: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "completeRetrainTrack", "mutation", input),
@@ -103016,6 +103345,7 @@ var require_sleep_DnbNfEhn = __commonJS({
103016
103345
  locateSegment: (input) => dispatchSystem("recording", "locateSegment", "query", input),
103017
103346
  readSegmentBytes: (input) => dispatchSystem("recording", "readSegmentBytes", "query", input),
103018
103347
  readGopBytes: (input) => dispatchSystem("recording", "readGopBytes", "query", input),
103348
+ readWindowBytes: (input) => dispatchSystem("recording", "readWindowBytes", "query", input),
103019
103349
  setDeviceConfig: (input) => dispatchSystem("recording", "setDeviceConfig", "mutation", input),
103020
103350
  rescanStorage: (input) => dispatchSystem("recording", "rescanStorage", "mutation", input),
103021
103351
  pruneFootage: (input) => dispatchSystem("recording", "pruneFootage", "mutation", input),
@@ -103697,7 +104027,7 @@ var require_addon = __commonJS({
103697
104027
  "use strict";
103698
104028
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
103699
104029
  var require_event_category = require_event_category_EY0GNjV9();
103700
- var require_sleep = require_sleep_DnbNfEhn();
104030
+ var require_sleep = require_sleep_CSodb2vQ();
103701
104031
  var require_err_msg = require_err_msg_COpsHMw2();
103702
104032
  var CAP_INPUT_DEFAULTS = Object.freeze({
103703
104033
  "addons": { "getLogs": { "limit": 100 } },
@@ -110573,9 +110903,9 @@ var require_dist2 = __commonJS({
110573
110903
  }
110574
110904
  });
110575
110905
 
110576
- // ../system/dist/manifest-python-deps-Bu8H1_rg.js
110577
- var require_manifest_python_deps_Bu8H1_rg = __commonJS({
110578
- "../system/dist/manifest-python-deps-Bu8H1_rg.js"(exports) {
110906
+ // ../system/dist/manifest-python-deps-DBQarwp9.js
110907
+ var require_manifest_python_deps_DBQarwp9 = __commonJS({
110908
+ "../system/dist/manifest-python-deps-DBQarwp9.js"(exports) {
110579
110909
  "use strict";
110580
110910
  var require_chunk = require_chunk_Cek0wNdY();
110581
110911
  var node_crypto = __require("crypto");
@@ -114426,6 +114756,14 @@ var require_manifest_python_deps_Bu8H1_rg = __commonJS({
114426
114756
  const raw = Reflect.get(args, "nodeId");
114427
114757
  return typeof raw === "string" && raw.length > 0 ? raw : void 0;
114428
114758
  }
114759
+ function withCallerAddonId(args, callerAddonId) {
114760
+ if (callerAddonId === void 0) return args;
114761
+ if (args === null || typeof args !== "object" || Array.isArray(args)) return args;
114762
+ return {
114763
+ ...args,
114764
+ callerAddonId
114765
+ };
114766
+ }
114429
114767
  var CapRouteResolver = class {
114430
114768
  hubNodeId;
114431
114769
  broker;
@@ -114493,10 +114831,20 @@ var require_manifest_python_deps_Bu8H1_rg = __commonJS({
114493
114831
  * `hub-local-uds` caller that already knows the addonId (`buildCapCallFn`)
114494
114832
  * talks to `LocalChildRegistry.callCapOnChild` directly rather than through
114495
114833
  * `dispatch`.
114496
- */
114497
- async dispatch(route, method, args, addonId) {
114834
+ * @param callerAddonId Optional out-of-band CALLER-identity hint — the
114835
+ * addon that ORIGINATED the call, distinct from `addonId` above (which
114836
+ * names the resolved PROVIDER, not the caller). Set by
114837
+ * `onUnownedCall` from `CapCallInput.callerAddonId`
114838
+ * (`LocalChildRegistry` stamped it off the registered `childId`). Only
114839
+ * the `hub-in-process` branch consumes it — merged into `args` so a
114840
+ * diagnostic tool deep in that provider (e.g. device-manager's
114841
+ * fleet-read census) can read it without every provider method changing
114842
+ * signature, the same "extra field the provider destructures or
114843
+ * ignores" convention `nodeId` already uses on this path.
114844
+ */
114845
+ async dispatch(route, method, args, addonId, callerAddonId) {
114498
114846
  try {
114499
- return await this.dispatchInner(route, method, args, addonId);
114847
+ return await this.dispatchInner(route, method, args, addonId, callerAddonId);
114500
114848
  } catch (err) {
114501
114849
  if (err instanceof CapRouteError) throw err;
114502
114850
  const nodeId = this.routeNodeId(route);
@@ -114515,10 +114863,10 @@ var require_manifest_python_deps_Bu8H1_rg = __commonJS({
114515
114863
  * Inner dispatch: may throw CapRouteError (validation failures) or arbitrary
114516
114864
  * transport errors. The outer `dispatch` wraps non-CapRouteErrors.
114517
114865
  */
114518
- async dispatchInner(route, method, args, addonId) {
114866
+ async dispatchInner(route, method, args, addonId, callerAddonId) {
114519
114867
  switch (route.kind) {
114520
114868
  case "hub-in-process":
114521
- return route.ref.invoke(method, args);
114869
+ return route.ref.invoke(method, withCallerAddonId(args, callerAddonId));
114522
114870
  case "hub-local-uds": {
114523
114871
  const registry = this.hubLocalRegistry;
114524
114872
  if (registry === null) throw new CapRouteError(route.capName, method, {
@@ -115052,7 +115400,8 @@ var require_manifest_python_deps_Bu8H1_rg = __commonJS({
115052
115400
  args: out.args,
115053
115401
  ...out.deviceId !== void 0 ? { deviceId: out.deviceId } : {},
115054
115402
  ...out.nodeId !== void 0 ? { nodeId: out.nodeId } : {},
115055
- ...out.native === true ? { native: true } : {}
115403
+ ...out.native === true ? { native: true } : {},
115404
+ ...childId !== null ? { callerAddonId: childId } : {}
115056
115405
  };
115057
115406
  const pinnedNodeId = out.nodeId ?? extractNodeId(out.args);
115058
115407
  const pinTargetsThisNode = pinnedNodeId !== void 0 && this.ownNodeId !== void 0 && pinnedNodeId === this.ownNodeId;
@@ -115546,7 +115895,7 @@ var require_manifest_python_deps_Bu8H1_rg = __commonJS({
115546
115895
  ...nodeId !== void 0 ? { nodeId } : {},
115547
115896
  ...deviceId !== void 0 ? { deviceId } : {}
115548
115897
  });
115549
- return await resolver.dispatch(route, input.method, input.args);
115898
+ return input.callerAddonId !== void 0 ? await resolver.dispatch(route, input.method, input.args, void 0, input.callerAddonId) : await resolver.dispatch(route, input.method, input.args);
115550
115899
  } catch (err) {
115551
115900
  if (!(err instanceof CapRouteError) || err.reason !== "no-provider") throw err;
115552
115901
  }
@@ -121417,7 +121766,7 @@ var require_dist3 = __commonJS({
121417
121766
  "use strict";
121418
121767
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
121419
121768
  var require_chunk = require_chunk_Cek0wNdY();
121420
- var require_dist10 = require_dist_D5Jhh8Vk();
121769
+ var require_dist10 = require_dist_jRSrzoXr();
121421
121770
  var require_builtins_alerts_alerts_addon = require_alerts_addon();
121422
121771
  require_alerts();
121423
121772
  var require_formatter = require_formatter_DqAKDlvN();
@@ -121443,7 +121792,7 @@ var require_dist3 = __commonJS({
121443
121792
  var require_builtins_winston_logging_index = require_winston_logging();
121444
121793
  var require_file_data_plane = require_file_data_plane_DO8KbxCe();
121445
121794
  var require_tls$1 = require_tls_u8QCJCFE();
121446
- var require_manifest_python_deps = require_manifest_python_deps_Bu8H1_rg();
121795
+ var require_manifest_python_deps = require_manifest_python_deps_DBQarwp9();
121447
121796
  var require_resource_monitor = require_resource_monitor_CdnzxBLP();
121448
121797
  var require_custom_action_registry = require_custom_action_registry_jY0NOZK8();
121449
121798
  var zod = require_zod();
@@ -202065,7 +202414,7 @@ var require_dist4 = __commonJS({
202065
202414
  "use strict";
202066
202415
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
202067
202416
  var require_event_category = require_event_category_EY0GNjV9();
202068
- var require_sleep = require_sleep_DnbNfEhn();
202417
+ var require_sleep = require_sleep_CSodb2vQ();
202069
202418
  var require_canonical_hash = require_canonical_hash_DNV8S5ET();
202070
202419
  var require_enums2 = require_enums();
202071
202420
  var require_err_msg = require_err_msg_COpsHMw2();
@@ -213052,7 +213401,23 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
213052
213401
  updatedAt: zod.z.number(),
213053
213402
  /** Failure detail — present on a `dead` row. */
213054
213403
  error: zod.z.string().optional(),
213055
- subject: NcHistorySubjectSchema
213404
+ subject: NcHistorySubjectSchema,
213405
+ /**
213406
+ * Ids of the artefacts (still, then gif, then clip) this row's successful
213407
+ * delivery indexed in the artefact library — a REFERENCE, never the bytes
213408
+ * (an artefact is often megabytes; this row is durable JSON rewritten on
213409
+ * every delivery attempt). Absent on a row still pending/dead, a row
213410
+ * delivered before this field shipped, or a wiring with no artefact index.
213411
+ *
213412
+ * Resolve one to a fetchable URL with `resolveArtifactUrl` — an id
213413
+ * outlives any one URL's TTL, so a caller mints a fresh link on demand
213414
+ * rather than trusting one frozen at delivery time. `resolveArtifactUrl`
213415
+ * also answers `null` for an id whose artefact has since expired past the
213416
+ * retained shelf's own age bound — the degrade a caller (the Home
213417
+ * Assistant export) must render as "no image right now", never as a
213418
+ * broken link.
213419
+ */
213420
+ artifactIds: zod.z.array(zod.z.string().min(1)).optional()
213056
213421
  });
213057
213422
  var NC_HISTORY_LIMIT_DEFAULT = 100;
213058
213423
  var NC_HISTORY_LIMIT_MAX = 500;
@@ -213300,6 +213665,20 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
213300
213665
  */
213301
213666
  getHistory: require_sleep.method(zod.z.object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), zod.z.object({ entries: zod.z.array(NcHistoryEntrySchema) }), { auth: "admin" }),
213302
213667
  /**
213668
+ * Mint a fresh, externally-reachable URL for one artefact a history row
213669
+ * named in {@link NcHistoryEntrySchema.shape.artifactIds} — the SAME
213670
+ * signed-link mechanism the dispatcher uses to attach media to an
213671
+ * outgoing notification (one derivation; this never re-implements it).
213672
+ *
213673
+ * `url: null` on THREE causes a caller must treat identically ("no image
213674
+ * right now", never a broken link): the id is unknown, its artefact has
213675
+ * expired past the retained shelf's own age bound, or this install has no
213676
+ * externally-reachable base URL. A caller that received a URL on a
213677
+ * previous call and now gets `null` must stop showing it — a link that
213678
+ * worked five minutes ago is not proof it works now.
213679
+ */
213680
+ resolveArtifactUrl: require_sleep.method(zod.z.object({ artifactId: zod.z.string().min(1) }), zod.z.object({ url: zod.z.string().nullable() }), { auth: "admin" }),
213681
+ /**
213303
213682
  * Snooze windows currently in effect, plus any whose digest has not yet
213304
213683
  * gone out. `caller: 'required'`: a user sees their OWN windows and the
213305
213684
  * global ones that silence them, never another person's private silence.
@@ -213539,6 +213918,726 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
213539
213918
  getDescriptor: require_sleep.method(zod.z.void(), OauthIntegrationDescriptorSchema, { auth: "admin" })
213540
213919
  }
213541
213920
  };
213921
+ var NativeCropRefSchema = zod.z.object({
213922
+ /** Handle keying the retained native surface (node-pinned to its owner). */
213923
+ handle: require_sleep.FrameHandleSchema,
213924
+ /** The parent crop's padded/clamped rectangle in FRAME-space pixels. */
213925
+ cropFrameSpace: zod.z.object({
213926
+ x: zod.z.number(),
213927
+ y: zod.z.number(),
213928
+ w: zod.z.number(),
213929
+ h: zod.z.number()
213930
+ })
213931
+ });
213932
+ zod.z.object({
213933
+ crop: zod.z.object({
213934
+ left: zod.z.number(),
213935
+ top: zod.z.number(),
213936
+ width: zod.z.number().positive(),
213937
+ height: zod.z.number().positive()
213938
+ }).optional(),
213939
+ content: zod.z.object({
213940
+ width: zod.z.number().int().positive(),
213941
+ height: zod.z.number().int().positive()
213942
+ }),
213943
+ fit: zod.z.enum(["stretch", "contain"]),
213944
+ format: zod.z.enum([
213945
+ "rgb",
213946
+ "gray",
213947
+ "jpeg"
213948
+ ])
213949
+ });
213950
+ var FrameRefSchema = zod.z.object({
213951
+ registryId: zod.z.string().min(1),
213952
+ id: zod.z.string().min(1),
213953
+ width: zod.z.number().int().positive(),
213954
+ height: zod.z.number().int().positive(),
213955
+ format: zod.z.enum(["rgb", "gray"]),
213956
+ timestamp: zod.z.number(),
213957
+ capturedAt: zod.z.number().optional()
213958
+ });
213959
+ var ModelFormatSchema$1 = zod.z.enum([
213960
+ "onnx",
213961
+ "coreml",
213962
+ "openvino",
213963
+ "tflite",
213964
+ "pt",
213965
+ "gguf"
213966
+ ]);
213967
+ var PipelineSlotSchema = zod.z.enum([
213968
+ "detector",
213969
+ "cropper",
213970
+ "classifier",
213971
+ "refiner",
213972
+ "audio-classifier"
213973
+ ]);
213974
+ var PipelineEngineChoiceSchema = zod.z.object({
213975
+ runtime: zod.z.enum(["node", "python"]),
213976
+ backend: zod.z.string(),
213977
+ format: ModelFormatSchema$1,
213978
+ device: zod.z.string().optional()
213979
+ });
213980
+ var EngineDeviceInfoSchema = zod.z.object({
213981
+ id: zod.z.string(),
213982
+ label: zod.z.string(),
213983
+ description: zod.z.string().optional()
213984
+ });
213985
+ var AvailableEngineSchema = zod.z.object({
213986
+ engine: PipelineEngineChoiceSchema,
213987
+ devices: zod.z.array(EngineDeviceInfoSchema).readonly(),
213988
+ defaultDevice: zod.z.string()
213989
+ });
213990
+ var PipelineDefaultStepSchema = zod.z.lazy(() => zod.z.object({
213991
+ addonId: zod.z.string(),
213992
+ addonName: zod.z.string(),
213993
+ slot: PipelineSlotSchema,
213994
+ inputClasses: zod.z.array(zod.z.string()).readonly(),
213995
+ outputClasses: zod.z.array(zod.z.string()).readonly(),
213996
+ enabled: zod.z.boolean(),
213997
+ modelId: zod.z.string(),
213998
+ children: zod.z.array(PipelineDefaultStepSchema).readonly(),
213999
+ group: zod.z.string().optional(),
214000
+ settings: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
214001
+ }));
214002
+ var PipelineTemplateStepSchema = zod.z.lazy(() => zod.z.object({
214003
+ addonId: zod.z.string(),
214004
+ enabled: zod.z.boolean(),
214005
+ modelId: zod.z.string(),
214006
+ children: zod.z.array(PipelineTemplateStepSchema).readonly(),
214007
+ settings: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
214008
+ }));
214009
+ var PipelineTemplateSchema$1 = zod.z.object({
214010
+ id: zod.z.string(),
214011
+ name: zod.z.string(),
214012
+ createdAt: zod.z.string(),
214013
+ updatedAt: zod.z.string(),
214014
+ engine: PipelineEngineChoiceSchema,
214015
+ steps: zod.z.array(PipelineTemplateStepSchema).readonly()
214016
+ });
214017
+ var PipelineModelOptionSchema = zod.z.object({
214018
+ id: zod.z.string(),
214019
+ name: zod.z.string(),
214020
+ formats: zod.z.record(zod.z.string(), zod.z.object({
214021
+ downloaded: zod.z.boolean(),
214022
+ sizeMB: zod.z.number()
214023
+ })),
214024
+ group: ModelVariantGroupSchema.optional(),
214025
+ legacy: zod.z.boolean().optional(),
214026
+ provider: ModelProviderIdSchema.optional()
214027
+ });
214028
+ var ConfigFieldBridge = zod.z.custom();
214029
+ var PipelineAddonSchemaSchema = zod.z.object({
214030
+ id: zod.z.string(),
214031
+ name: zod.z.string(),
214032
+ slot: PipelineSlotSchema,
214033
+ inputClasses: zod.z.array(zod.z.string()).readonly(),
214034
+ outputClasses: zod.z.array(zod.z.string()).readonly(),
214035
+ childSlots: zod.z.array(PipelineSlotSchema).readonly(),
214036
+ models: zod.z.array(PipelineModelOptionSchema).readonly(),
214037
+ defaultModelId: zod.z.string(),
214038
+ defaultModelIdByFormat: zod.z.record(zod.z.string(), zod.z.string()).optional(),
214039
+ enabledByDefault: zod.z.boolean().optional(),
214040
+ backfillIntoExistingOverrides: zod.z.boolean().optional(),
214041
+ defaultConfidence: zod.z.number(),
214042
+ group: zod.z.string().optional(),
214043
+ configSchema: zod.z.array(ConfigFieldBridge).readonly().optional()
214044
+ });
214045
+ var PipelineSlotSchemaSchema = zod.z.object({
214046
+ id: PipelineSlotSchema,
214047
+ label: zod.z.string(),
214048
+ priority: zod.z.number(),
214049
+ parentSlot: PipelineSlotSchema.nullable(),
214050
+ addons: zod.z.array(PipelineAddonSchemaSchema).readonly()
214051
+ });
214052
+ var PipelineSchemaSchema = zod.z.object({
214053
+ availableEngines: zod.z.array(AvailableEngineSchema).readonly(),
214054
+ selectedEngine: PipelineEngineChoiceSchema,
214055
+ slots: zod.z.array(PipelineSlotSchemaSchema).readonly()
214056
+ });
214057
+ var EngineProvisioningSchema = zod.z.object({
214058
+ runtimeId: zod.z.enum([
214059
+ "onnx",
214060
+ "openvino",
214061
+ "coreml",
214062
+ "edgetpu"
214063
+ ]).nullable(),
214064
+ device: zod.z.string().nullable(),
214065
+ state: zod.z.enum([
214066
+ "idle",
214067
+ "installing",
214068
+ "verifying",
214069
+ "ready",
214070
+ "failed"
214071
+ ]),
214072
+ progress: zod.z.number().optional(),
214073
+ error: zod.z.string().optional(),
214074
+ nextRetryAt: zod.z.number().optional(),
214075
+ /**
214076
+ * Gate A (config-correctness gate at engine change): human-readable
214077
+ * config issues surfaced EAGERLY when the node's engine changes — model
214078
+ * substitutions ("chose X, running Y") and zero-build steps ("no model
214079
+ * has a <format> build"). Additive/optional: informational only, never
214080
+ * enforced here — `assertEngineReady` (readiness) still gates inference.
214081
+ * Absent/empty when the node-default tree resolves cleanly.
214082
+ */
214083
+ configIssues: zod.z.array(zod.z.string()).optional()
214084
+ });
214085
+ var PipelineStepInputSchema = zod.z.lazy(() => zod.z.object({
214086
+ addonId: zod.z.string(),
214087
+ modelId: zod.z.string().optional(),
214088
+ enabled: zod.z.boolean().default(true),
214089
+ children: zod.z.array(PipelineStepInputSchema).optional(),
214090
+ settings: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
214091
+ jumpDeviceKey: zod.z.string().optional()
214092
+ }));
214093
+ var ModelSubstitutionSchema = zod.z.object({
214094
+ addonId: zod.z.string(),
214095
+ chosen: zod.z.string(),
214096
+ running: zod.z.string(),
214097
+ format: zod.z.string()
214098
+ });
214099
+ var PipelineValidationIssueSchema = zod.z.object({
214100
+ addonId: zod.z.string(),
214101
+ kind: zod.z.enum(["unknown-addon", "no-format-build"]),
214102
+ detail: zod.z.string()
214103
+ });
214104
+ var PipelineValidationResultSchema = zod.z.object({
214105
+ ok: zod.z.boolean(),
214106
+ issues: zod.z.array(PipelineValidationIssueSchema).readonly(),
214107
+ substitutions: zod.z.array(ModelSubstitutionSchema).readonly(),
214108
+ /** The node's `currentEngine.format` this validation ran against. */
214109
+ format: zod.z.string()
214110
+ });
214111
+ var ReferenceImageEntrySchema = zod.z.object({
214112
+ filename: zod.z.string(),
214113
+ stepIds: zod.z.array(zod.z.string()).readonly().optional()
214114
+ });
214115
+ var ReferenceImageBodySchema = zod.z.object({
214116
+ base64: zod.z.string(),
214117
+ filename: zod.z.string()
214118
+ });
214119
+ var ReferenceAudioEntrySchema = zod.z.object({
214120
+ filename: zod.z.string(),
214121
+ sizeKb: zod.z.number()
214122
+ });
214123
+ var ReferenceAudioBodySchema = zod.z.object({ base64: zod.z.string() });
214124
+ var AudioBackendSchema = zod.z.object({
214125
+ id: zod.z.string(),
214126
+ name: zod.z.string(),
214127
+ description: zod.z.string(),
214128
+ available: zod.z.boolean(),
214129
+ /**
214130
+ * Raw classifier labels this backend can emit (e.g. YAMNet's
214131
+ * 521-class set or Apple SoundAnalysis's 303-class set). Used by
214132
+ * the benchmark UI to populate the `enabledMicroClasses` filter
214133
+ * specific to the selected backend without a separate fetch.
214134
+ */
214135
+ rawLabels: zod.z.array(zod.z.string()).readonly().optional()
214136
+ });
214137
+ var AudioCapabilitiesSchema = zod.z.object({
214138
+ activeBackend: zod.z.string(),
214139
+ availableBackends: zod.z.array(AudioBackendSchema).readonly(),
214140
+ sampleRate: zod.z.number(),
214141
+ chunkDurationMs: zod.z.number()
214142
+ });
214143
+ var DownloadModelResultSchema = zod.z.object({
214144
+ filePath: zod.z.string(),
214145
+ sizeMB: zod.z.number(),
214146
+ durationMs: zod.z.number()
214147
+ });
214148
+ var AudioTestResultSchema = zod.z.object({
214149
+ success: zod.z.boolean(),
214150
+ error: zod.z.string().optional(),
214151
+ frame: zod.z.custom().optional()
214152
+ });
214153
+ var PipelineConfigBridge = zod.z.custom();
214154
+ var ConfigUISchemaBridge = zod.z.custom();
214155
+ var ConfigUISchemaNullableBridge = zod.z.custom();
214156
+ var InferenceCapabilitiesBridge = zod.z.custom();
214157
+ var ModelAvailabilityListBridge = zod.z.custom();
214158
+ var PipelineRunResultBridge = zod.z.custom();
214159
+ var pipelineExecutorCapability = {
214160
+ name: "pipeline-executor",
214161
+ scope: "system",
214162
+ mode: "singleton",
214163
+ methods: {
214164
+ getAvailableEngines: require_sleep.method(zod.z.void(), zod.z.array(PipelineEngineChoiceSchema)),
214165
+ getSelectedEngine: require_sleep.method(zod.z.void(), PipelineEngineChoiceSchema),
214166
+ getDefaultSteps: require_sleep.method(PipelineEngineChoiceSchema, zod.z.array(PipelineDefaultStepSchema)),
214167
+ /**
214168
+ * Per-node detection-engine provisioning snapshot. Returns the live
214169
+ * state of the lazy runtime-provisioning machine on `nodeId`
214170
+ * (idle / installing / verifying / ready / failed). The UI pairs this
214171
+ * one-shot query with the `pipeline.engine-provisioning` live event
214172
+ * (emitted on every transition) to drive a per-node "engine ready?"
214173
+ * indicator without polling. Phase 2.
214174
+ */
214175
+ getEngineProvisioning: require_sleep.method(zod.z.object({ nodeId: zod.z.string() }), EngineProvisioningSchema),
214176
+ getVideoPipelineSteps: require_sleep.method(zod.z.void(), zod.z.record(zod.z.string(), zod.z.object({
214177
+ modelId: zod.z.string(),
214178
+ settings: zod.z.record(zod.z.string(), zod.z.unknown()).readonly()
214179
+ }))),
214180
+ setVideoPipelineSteps: require_sleep.method(zod.z.object({ steps: zod.z.record(zod.z.string(), zod.z.object({
214181
+ modelId: zod.z.string(),
214182
+ settings: zod.z.record(zod.z.string(), zod.z.unknown()).readonly()
214183
+ })) }), zod.z.object({ success: zod.z.literal(true) }), {
214184
+ kind: "mutation",
214185
+ auth: "admin"
214186
+ }),
214187
+ /**
214188
+ * Clear THIS node's executor-side PER-DEVICE settings stores (the
214189
+ * per-camera step overrides the object-detection root reads via
214190
+ * `applyDeviceOverridesToTree`). `nodeId` is the ROUTING key — the
214191
+ * generated cap-router strips it (default `nodeIdMode: 'routing'`) and
214192
+ * dispatches to that node, so the provider method runs ON the target
214193
+ * node and receives no `nodeId`.
214194
+ *
214195
+ * This is the slimmed executor leg of the orchestrator's
214196
+ * `resetNodePipelineDefaults` flow (which owns the real reset: node
214197
+ * addonDefaults pins + per-camera orchestrator overrides). The legacy
214198
+ * `resetToDefault` — which reset a persisted global step-tree seed
214199
+ * nothing in the live per-camera path read — was removed together with
214200
+ * that seed.
214201
+ */
214202
+ clearDeviceOverrides: require_sleep.method(zod.z.object({ nodeId: zod.z.string() }), zod.z.object({
214203
+ success: zod.z.literal(true),
214204
+ clearedDevices: zod.z.number()
214205
+ }), {
214206
+ kind: "mutation",
214207
+ auth: "admin"
214208
+ }),
214209
+ /**
214210
+ * Which of THIS node's inference devices the executor currently refuses,
214211
+ * and why. `nodeId` is the ROUTING key (stripped by the generated router).
214212
+ *
214213
+ * The channel that did not exist. Pool health was known only inside the
214214
+ * detection addon and was an input to no routing decision anywhere: the
214215
+ * per-dispatch capability gate is keyed on model FORMAT and so can never
214216
+ * separate `openvino:gpu` from `openvino:npu`, and the orchestrator's live
214217
+ * eligibility probe (`platformProbe.getCapabilities`) answers about
214218
+ * HARDWARE — which was present throughout. So when the hub's `openvino:gpu`
214219
+ * Python worker was SIGABRT'd by the Intel GPU plugin on 2026-08-25, the
214220
+ * balancer went on handing that dead pool cameras by rotation for 31 hours:
214221
+ * ~370 000 `PoolWorker[w0]: not initialized` lines, every frame lost.
214222
+ *
214223
+ * Read semantics the caller depends on, and which the provider guarantees:
214224
+ * this is a synchronous read of in-memory state. It never probes hardware,
214225
+ * never spawns a pool and never throws — an EMPTY `unhealthy` means "asked,
214226
+ * nothing is refused", which is what re-admits a device. A read that FAILS
214227
+ * (node offline, version skew) must therefore be distinguishable from an
214228
+ * empty answer, and it is: it rejects.
214229
+ */
214230
+ getInferenceDeviceHealth: require_sleep.method(zod.z.object({ nodeId: zod.z.string() }), zod.z.object({ unhealthy: zod.z.array(zod.z.object({
214231
+ /** `<backend>:<device>`, e.g. `openvino:gpu`. */
214232
+ deviceKey: zod.z.string(),
214233
+ /**
214234
+ * `failed` — the per-device restart budget is exhausted; no pool
214235
+ * will be spawned until an operator re-arms it or the runner
214236
+ * respawns. `backoff` — under budget, waiting out the backoff (or
214237
+ * a cached pool observed dead and not yet condemned).
214238
+ */
214239
+ state: zod.z.enum(["failed", "backoff"]),
214240
+ /** Epoch ms of the death that produced this state. */
214241
+ since: zod.z.number(),
214242
+ /** Pool deaths inside the current window. */
214243
+ deaths: zod.z.number(),
214244
+ /** The last death's message. */
214245
+ lastError: zod.z.string()
214246
+ })).readonly() })),
214247
+ /**
214248
+ * Re-arm a terminally `failed` inference device on `nodeId`: forget its
214249
+ * restart budget so the next dispatch builds a fresh pool.
214250
+ *
214251
+ * The terminal state is deliberate (the abort it bounds is deterministic —
214252
+ * an automatic probation would just respawn Python forever, more slowly),
214253
+ * and a terminal state an operator cannot leave is a silent fault. This is
214254
+ * the way out. `rearmed:false` means there was nothing to forget.
214255
+ */
214256
+ rearmInferenceDevice: require_sleep.method(zod.z.object({
214257
+ nodeId: zod.z.string(),
214258
+ deviceKey: zod.z.string()
214259
+ }), zod.z.object({ rearmed: zod.z.boolean() }), {
214260
+ kind: "mutation",
214261
+ auth: "admin"
214262
+ }),
214263
+ getSchema: require_sleep.method(zod.z.void(), PipelineSchemaSchema),
214264
+ getGlobalSteps: require_sleep.method(zod.z.void(), zod.z.array(PipelineDefaultStepSchema).readonly().nullable()),
214265
+ getGlobalPipelineConfig: require_sleep.method(zod.z.void(), PipelineConfigBridge),
214266
+ getOrchestratorConfigSchema: require_sleep.method(zod.z.void(), ConfigUISchemaBridge),
214267
+ /**
214268
+ * Gate B (pre-init config-correctness gate). PURE COMPUTE against this
214269
+ * node's `currentEngine.format` — resolves `steps` the same way the
214270
+ * runtime dispatch path would, and reports what WOULD happen without
214271
+ * touching any node-global state. Called by the orchestrator at attach
214272
+ * time (`attachOn`), node-pinned to the TARGET node, so config problems
214273
+ * surface BEFORE `pipelineRunner.attachCamera` rather than at the first
214274
+ * per-frame resolve. `ok` is false iff `issues` is non-empty (both
214275
+ * `unknown-addon` and `no-format-build` are HARD issues); `substitutions`
214276
+ * is informational (a degraded-but-loadable model swap) and never
214277
+ * affects `ok`. Never throws.
214278
+ */
214279
+ validatePipeline: require_sleep.method(zod.z.object({ steps: zod.z.array(PipelineStepInputSchema) }), PipelineValidationResultSchema),
214280
+ listTemplates: require_sleep.method(zod.z.void(), zod.z.array(PipelineTemplateSchema$1).readonly()),
214281
+ saveTemplate: require_sleep.method(zod.z.object({
214282
+ name: zod.z.string(),
214283
+ steps: zod.z.array(PipelineTemplateStepSchema).readonly(),
214284
+ engine: PipelineEngineChoiceSchema
214285
+ }), PipelineTemplateSchema$1, { kind: "mutation" }),
214286
+ updateTemplate: require_sleep.method(zod.z.object({
214287
+ id: zod.z.string(),
214288
+ name: zod.z.string().optional(),
214289
+ steps: zod.z.array(PipelineTemplateStepSchema).readonly().optional()
214290
+ }), PipelineTemplateSchema$1, { kind: "mutation" }),
214291
+ deleteTemplate: require_sleep.method(zod.z.object({ id: zod.z.string() }), zod.z.void(), { kind: "mutation" }),
214292
+ getCapabilities: require_sleep.method(zod.z.void(), InferenceCapabilitiesBridge),
214293
+ getAddonModels: require_sleep.method(zod.z.object({ addonId: zod.z.string() }), ModelAvailabilityListBridge),
214294
+ downloadModel: require_sleep.method(zod.z.object({
214295
+ addonId: zod.z.string(),
214296
+ modelId: zod.z.string(),
214297
+ format: ModelFormatSchema$1
214298
+ }), DownloadModelResultSchema, { kind: "mutation" }),
214299
+ deleteModel: require_sleep.method(zod.z.object({
214300
+ addonId: zod.z.string(),
214301
+ modelId: zod.z.string(),
214302
+ format: ModelFormatSchema$1
214303
+ }), zod.z.object({ success: zod.z.literal(true) }), { kind: "mutation" }),
214304
+ /**
214305
+ * Stateless single-frame execution. Callers (runner, benchmark) pass
214306
+ * the complete `engine` + `steps` tree; the executor holds no state
214307
+ * about cameras or saved pipelines.
214308
+ *
214309
+ * `engine` is optional during the migration window to preserve the
214310
+ * legacy call shape used by existing benchmark code; once all
214311
+ * callers pass it explicitly we make it required.
214312
+ *
214313
+ * Exactly one of `frame`, `frameRef`, `frameHandle`, `imageBase64`,
214314
+ * `referenceImage` must be provided:
214315
+ * - `frame`: runtime dispatch path (runner → decoded broker frame).
214316
+ * Carries the raw buffer, dimensions, and format; the executor
214317
+ * uses it directly without base64 round-tripping.
214318
+ * - `frameHandle` (CB5): a zero-pixel shm `FrameHandle` for the SAME
214319
+ * decoded frame. Both runner and executor are hub-local processes
214320
+ * sharing `/dev/shm`, so the executor maps the named segment and
214321
+ * reads the pixels back zero-copy — eliminating the ~1.2MB
214322
+ * re-serialisation over UDS/MsgPack the `frame` path pays per call.
214323
+ * High-risk: the FrameRing is a latest-wins seqlock with no
214324
+ * refcount, so a recycled slot yields a null read; the executor
214325
+ * then degrades to an empty result and the runner ships pixels via
214326
+ * `frame` as the fallback (queue-depth gated on the runner side).
214327
+ * - `imageBase64`: one-shot test path (benchmark ImageTab).
214328
+ * - `referenceImage`: named file from the reference-image store.
214329
+ */
214330
+ runPipeline: require_sleep.method(zod.z.object({
214331
+ engine: PipelineEngineChoiceSchema.optional(),
214332
+ steps: zod.z.array(PipelineStepInputSchema).min(1),
214333
+ frame: FrameInputSchema.optional(),
214334
+ /**
214335
+ * Process-local lazy frame. Valid only when caller and provider resolve
214336
+ * in the same execution-group process; split/cross-node callers use
214337
+ * `frame`/`image` inline compatibility instead.
214338
+ */
214339
+ frameRef: FrameRefSchema.optional(),
214340
+ /**
214341
+ * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
214342
+ * the decoded pixels live in. One more member of the one-of
214343
+ * frame/frameHandle/image/imageBase64/referenceImage group.
214344
+ */
214345
+ frameHandle: require_sleep.FrameHandleSchema.optional(),
214346
+ imageBase64: zod.z.string().optional(),
214347
+ /**
214348
+ * Binary JPEG bytes — preferred over `imageBase64` on internal
214349
+ * hops (hub → forked worker via Moleculer MsgPack) because it
214350
+ * skips the 33% base64 overhead + the per-call base64 decode on
214351
+ * the detection-pipeline worker. Callers can pass either; exactly
214352
+ * one of `frame`/`image`/`imageBase64`/`referenceImage` is required.
214353
+ */
214354
+ image: zod.z.instanceof(Uint8Array).optional(),
214355
+ referenceImage: zod.z.string().optional(),
214356
+ deviceId: zod.z.number().optional(),
214357
+ sessionId: zod.z.string().optional(),
214358
+ /**
214359
+ * Execution plane. 'full' (default) runs the whole tree — benchmark,
214360
+ * reference-image, and detail-subtree calls. 'frame' is the live
214361
+ * per-frame dispatch: ONLY root-plane steps run; crop children
214362
+ * (inputClasses ≠ null) are skipped and served per-track via
214363
+ * pipelineRunner.runDetailSubtree (two-plane design).
214364
+ */
214365
+ plane: zod.z.enum(["full", "frame"]).optional(),
214366
+ /**
214367
+ * Inference-device selector (Phase 2 multi-device). Format
214368
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
214369
+ * Omitted ⇒ the runner's default device (current single-engine
214370
+ * behaviour). Selects WHICH device pool of the node runs the call.
214371
+ */
214372
+ deviceKey: zod.z.string().optional(),
214373
+ /**
214374
+ * Two-plane NATIVE child-crop reference. Set by `runDetailSubtree` ONLY
214375
+ * when the parent crop was resolved from the frame's retained NATIVE
214376
+ * surface (a frameHandle HIT). Lets the executor re-cut a LEAF crop
214377
+ * child's ROI (plate-ocr, face-embedding, leaf classifiers) at native
214378
+ * resolution from that surface — the SAME quality path faces already
214379
+ * had — instead of the downscaled parent tile. `handle` keys the native
214380
+ * surface (node-pinned to its owner); `cropFrameSpace` is the parent
214381
+ * crop's padded/clamped rectangle in FRAME-space pixels, used to compose
214382
+ * the executor's crop-normalized child ROI back into frame-normalized
214383
+ * coordinates. Auxiliary to the image source (`image`/`frame`/…), NOT one
214384
+ * of the mutually-exclusive image inputs. Absent ⇒ tile-crop children
214385
+ * (today's behaviour on the fallback path).
214386
+ */
214387
+ nativeCropRef: NativeCropRefSchema.optional()
214388
+ }), PipelineRunResultBridge, { kind: "mutation" }),
214389
+ /**
214390
+ * Batched run — N raw frames packed into one cap call. The provider
214391
+ * routes the batch through `SharedInferencePool.inferBatch`
214392
+ * (`MSG_INFER_BATCH = 0x03`) so the IPC framing and JSON response
214393
+ * envelope cost is amortised N:1 vs N concurrent `runPipeline`
214394
+ * calls. Single root step + uniform model assumed; trees with crop
214395
+ * children fall back to sequential execution.
214396
+ *
214397
+ * Used by `scripts/bench-batch-style.mts` for batch benchmarking —
214398
+ * N frames in one call to amortise per-call IPC overhead.
214399
+ */
214400
+ runPipelineBatch: require_sleep.method(zod.z.object({
214401
+ engine: PipelineEngineChoiceSchema.optional(),
214402
+ steps: zod.z.array(PipelineStepInputSchema).min(1),
214403
+ frames: zod.z.array(FrameInputSchema).min(1).max(255),
214404
+ deviceId: zod.z.number().optional(),
214405
+ sessionId: zod.z.string().optional(),
214406
+ /**
214407
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
214408
+ * the batch to the Python pool's bench preprocess cache
214409
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
214410
+ * preprocessed ONCE and every later inference is a pure-inference cache
214411
+ * hit — the sustained-throughput run measures inference, not
214412
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
214413
+ * full preprocess every call, correct). Fresh per sustained run;
214414
+ * released via `uncacheFrame`.
214415
+ */
214416
+ frameId: zod.z.number().int().nonnegative().optional(),
214417
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
214418
+ deviceKey: zod.z.string().optional()
214419
+ }), zod.z.object({ results: zod.z.array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }),
214420
+ /**
214421
+ * Cache a raw frame inside the Python inference pool's memory.
214422
+ * Returns a numeric `frameId` that `inferCached` references —
214423
+ * subsequent calls send only 5 bytes through the pipe instead of
214424
+ * 1.2MB raw data, eliminating the pipe transfer bottleneck.
214425
+ */
214426
+ cacheFrameInPool: require_sleep.method(zod.z.object({
214427
+ data: zod.z.instanceof(Uint8Array),
214428
+ width: zod.z.number().int().positive(),
214429
+ height: zod.z.number().int().positive(),
214430
+ format: zod.z.enum([
214431
+ "rgb",
214432
+ "bgr",
214433
+ "gray"
214434
+ ])
214435
+ }), zod.z.object({
214436
+ frameId: zod.z.number(),
214437
+ width: zod.z.number(),
214438
+ height: zod.z.number()
214439
+ }), { kind: "mutation" }),
214440
+ /**
214441
+ * Run inference on a previously cached frame. Sends only 5 bytes
214442
+ * (model_idx + frameId) through the IPC pipe — eliminates the
214443
+ * ~35ms per-call overhead of transferring 1.2MB raw data.
214444
+ */
214445
+ inferCached: require_sleep.method(zod.z.object({
214446
+ stepId: zod.z.string(),
214447
+ frameId: zod.z.number().int()
214448
+ }), zod.z.record(zod.z.string(), zod.z.unknown()), { kind: "mutation" }),
214449
+ /**
214450
+ * Release a cached frame from the Python pool's memory.
214451
+ */
214452
+ uncacheFrame: require_sleep.method(zod.z.object({ frameId: zod.z.number().int() }), zod.z.void(), { kind: "mutation" }),
214453
+ /** Returns the effective pool tuning (resolved from user overrides + backend defaults). */
214454
+ getEffectiveTuning: require_sleep.method(zod.z.void(), zod.z.object({
214455
+ batchMode: zod.z.string(),
214456
+ windowMs: zod.z.number(),
214457
+ maxBatchSize: zod.z.number(),
214458
+ concurrency: zod.z.number()
214459
+ })),
214460
+ /**
214461
+ * List every EngineFactory currently loaded in this executor's RAM,
214462
+ * with the models resident and a coarse "in use" marker derived from
214463
+ * ongoing inference activity. Used by the Pipeline page Engines tab.
214464
+ */
214465
+ listLoadedEngines: require_sleep.method(zod.z.void(), zod.z.array(zod.z.object({
214466
+ engineKey: zod.z.string(),
214467
+ engine: PipelineEngineChoiceSchema,
214468
+ modelsLoaded: zod.z.array(zod.z.string()).readonly(),
214469
+ inUseByCameras: zod.z.array(zod.z.number()).readonly(),
214470
+ /**
214471
+ * Origin of this resident factory.
214472
+ * - `runtime` — main camera-serving engine (no idle TTL).
214473
+ * - `warm-override` — benchmark/test override held in the warm
214474
+ * cache; auto-disposed after the idle TTL.
214475
+ * - `device-pool` — a concurrent per-device pool (Phase 2
214476
+ * multi-device, keyed by `deviceKey`) resolved
214477
+ * via `resolveDeviceFactory`. Runs alongside the
214478
+ * `runtime` engine on a DIFFERENT accelerator
214479
+ * (NPU / iGPU / Coral) — this is how the
214480
+ * Engines tab shows all pools running at once.
214481
+ */
214482
+ kind: zod.z.enum([
214483
+ "runtime",
214484
+ "warm-override",
214485
+ "device-pool"
214486
+ ]),
214487
+ /** Native pid of the underlying Python pool (null when no pool). */
214488
+ poolPid: zod.z.number().nullable(),
214489
+ /** ms since this factory was last used (null when not warm-tracked). */
214490
+ idleMs: zod.z.number().nullable(),
214491
+ /** Idle TTL after which `warm-override` factories self-evict (null when not applicable). */
214492
+ idleTtlMs: zod.z.number().nullable()
214493
+ })).readonly()),
214494
+ /** Warm up an engine without running a frame. No-op if already loaded. */
214495
+ spinEngine: require_sleep.method(zod.z.object({ engine: PipelineEngineChoiceSchema }), zod.z.object({ success: zod.z.literal(true) }), {
214496
+ kind: "mutation",
214497
+ auth: "admin"
214498
+ }),
214499
+ /**
214500
+ * Unload an engine from RAM. `force:true` unloads even when cameras
214501
+ * are actively using it (they re-spin on next frame). Default is
214502
+ * gated — returns `{success:false, reason}` when in use.
214503
+ */
214504
+ killEngine: require_sleep.method(zod.z.object({
214505
+ engine: PipelineEngineChoiceSchema,
214506
+ force: zod.z.boolean().optional()
214507
+ }), zod.z.object({
214508
+ success: zod.z.boolean(),
214509
+ reason: zod.z.string().optional()
214510
+ }), {
214511
+ kind: "mutation",
214512
+ auth: "admin"
214513
+ }),
214514
+ listReferenceImages: require_sleep.method(zod.z.void(), zod.z.array(ReferenceImageEntrySchema).readonly()),
214515
+ getReferenceImage: require_sleep.method(zod.z.object({ filename: zod.z.string() }), ReferenceImageBodySchema.nullable()),
214516
+ getReferenceAudioFiles: require_sleep.method(zod.z.void(), zod.z.array(ReferenceAudioEntrySchema).readonly()),
214517
+ getReferenceAudio: require_sleep.method(zod.z.object({ filename: zod.z.string() }), ReferenceAudioBodySchema.nullable()),
214518
+ getAudioCapabilities: require_sleep.method(zod.z.void(), AudioCapabilitiesSchema),
214519
+ runAudioTest: require_sleep.method(zod.z.object({
214520
+ addonId: zod.z.string(),
214521
+ modelId: zod.z.string(),
214522
+ filename: zod.z.string().optional(),
214523
+ settings: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
214524
+ }), AudioTestResultSchema, { kind: "mutation" }),
214525
+ getDetectionConfigSchema: require_sleep.method(zod.z.void(), ConfigUISchemaNullableBridge)
214526
+ }
214527
+ };
214528
+ var ZoneRuleModeEnum = zod.z.enum(["include", "exclude"]);
214529
+ var ZoneRuleSchema = zod.z.object({
214530
+ /** Stable rule id — survives edits, used by the UI for diffing. */
214531
+ id: zod.z.string(),
214532
+ /** Optional human-readable label rendered in the rule editor. */
214533
+ name: zod.z.string().optional(),
214534
+ /** Zones this rule targets. The rule's `mode` applies to ALL
214535
+ * listed zones (OR-set: a detection in any one of them counts).
214536
+ * At least one zone id required — a rule with no targets is a
214537
+ * configuration mistake and the form validator rejects it. */
214538
+ zoneIds: zod.z.array(zod.z.string()).min(1).readonly(),
214539
+ mode: ZoneRuleModeEnum,
214540
+ /**
214541
+ * Class names this rule applies to. Empty / undefined ⇒ rule
214542
+ * applies to every class. Class strings match the `macroClass`
214543
+ * field on detections (e.g. `person`, `car`, `dog`).
214544
+ */
214545
+ classFilter: zod.z.array(zod.z.string()).readonly().optional(),
214546
+ /**
214547
+ * Minimum bbox/mask overlap (0–1) with any of the rule's zones
214548
+ * required to consider an entity "in the zone". Defaults to the
214549
+ * consumer's stage default when omitted. Kept for back-compat with
214550
+ * existing per-rule overrides; new operators pick the value via
214551
+ * `bboxInclusionPct` (operator-friendly 0–100). Whichever field is
214552
+ * set, the lower-level engine reads it as a 0–1 fraction.
214553
+ */
214554
+ overlapThreshold: zod.z.number().min(0).max(1).optional(),
214555
+ /**
214556
+ * Operator-friendly version of `overlapThreshold` — the percentage
214557
+ * of the detection's bbox that must lie inside the zone for the
214558
+ * rule to match. Documented default is 85%; the engine substitutes
214559
+ * that when the field is omitted (kept optional so existing rules
214560
+ * stored without it stay valid).
214561
+ *
214562
+ * When BOTH `overlapThreshold` and `bboxInclusionPct` are set on a
214563
+ * rule, the engine prefers `bboxInclusionPct` because it's the
214564
+ * field exposed in the UI. Internally both feed the same gate.
214565
+ */
214566
+ bboxInclusionPct: zod.z.number().min(0).max(100).optional(),
214567
+ /**
214568
+ * When `true` and a detection has a segmentation mask, use the
214569
+ * mask for overlap instead of the bbox. Detection-stage only;
214570
+ * motion rules ignore this field.
214571
+ */
214572
+ preferMask: zod.z.boolean().optional(),
214573
+ /**
214574
+ * Soft-toggle: `false` disables the rule without deleting it.
214575
+ * Defaults to `true` so operators creating a rule via the UI
214576
+ * see it active immediately.
214577
+ */
214578
+ enabled: zod.z.boolean().default(true)
214579
+ });
214580
+ var ZoneRulesArraySchema = zod.z.array(ZoneRuleSchema).readonly();
214581
+ var ZoneKindEnum = zod.z.enum(["polygon", "tripwire"]);
214582
+ var PolygonPointSchema = zod.z.object({
214583
+ x: zod.z.number(),
214584
+ y: zod.z.number()
214585
+ });
214586
+ var ZoneSchema = zod.z.object({
214587
+ id: zod.z.string(),
214588
+ name: zod.z.string(),
214589
+ kind: ZoneKindEnum.default("polygon"),
214590
+ /** Polygon vertices, fraction of frame (0–1). */
214591
+ polygon: zod.z.array(PolygonPointSchema).readonly(),
214592
+ /** Visual color for UI rendering. */
214593
+ color: zod.z.string().default("#3b82f6")
214594
+ });
214595
+ var zonesCapability = {
214596
+ name: "zones",
214597
+ scope: "device",
214598
+ mode: "singleton",
214599
+ deviceTypes: [require_sleep.DeviceType.Camera],
214600
+ methods: {
214601
+ listZones: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), zod.z.array(ZoneSchema).readonly()),
214602
+ addZone: require_sleep.method(zod.z.object({
214603
+ deviceId: zod.z.number(),
214604
+ zone: ZoneSchema
214605
+ }), zod.z.void(), {
214606
+ kind: "mutation",
214607
+ auth: "admin"
214608
+ }),
214609
+ removeZone: require_sleep.method(zod.z.object({
214610
+ deviceId: zod.z.number(),
214611
+ zoneId: zod.z.string()
214612
+ }), zod.z.void(), {
214613
+ kind: "mutation",
214614
+ auth: "admin"
214615
+ }),
214616
+ updateZone: require_sleep.method(zod.z.object({
214617
+ deviceId: zod.z.number(),
214618
+ zone: ZoneSchema
214619
+ }), zod.z.void(), {
214620
+ kind: "mutation",
214621
+ auth: "admin"
214622
+ })
214623
+ },
214624
+ /**
214625
+ * Runtime-state slice — the live zone catalogue mirrored by the
214626
+ * orchestrator on every CRUD mutation. Consumers read via
214627
+ * `device.state.zones.value` / `.watch(...)` without round-tripping
214628
+ * the cap, and the codegen DeviceProxy auto-wires the reactive
214629
+ * handle. Slice shape is `{ zones: Zone[] }` so future extensions
214630
+ * (e.g. zone groupings) can sit alongside the polygon list.
214631
+ */
214632
+ runtimeState: zod.z.object({ zones: zod.z.array(ZoneSchema).readonly() }),
214633
+ /**
214634
+ * Runtime-state durability: **restored** — written only on operator mutation, so a camera that never had one has nothing to re-derive from. This is the slice `zone-mirror-hydration.ts` exists to paper over.
214635
+ *
214636
+ * See `RuntimeStateDurability`. Enforced by
214637
+ * `scripts/check-runtime-state-durability.ts`.
214638
+ */
214639
+ durability: "restored"
214640
+ };
213542
214641
  var TrackStateSchema = zod.z.enum([
213543
214642
  "new",
213544
214643
  "entered",
@@ -214359,6 +215458,25 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
214359
215458
  /** Present when the pass ended by throwing. */
214360
215459
  error: zod.z.string().nullable()
214361
215460
  });
215461
+ var ReplayFrameInputSchema = zod.z.object({
215462
+ timestamp: zod.z.number(),
215463
+ frame: PipelineRunResultBridge
215464
+ });
215465
+ var ReplayTrackSchema = zod.z.object({
215466
+ className: zod.z.string(),
215467
+ firstSeenMs: zod.z.number(),
215468
+ lastSeenMs: zod.z.number(),
215469
+ /** Bbox of the track's FIRST matched detection, pixel-space in the clip's
215470
+ * frame — a representative box for the diff's `(className, window, IoU)`
215471
+ * pairing (`replay-diff.ts`). A replay does not need the full per-frame
215472
+ * trajectory production's `Track.positions` keeps. */
215473
+ bbox: BoundingBoxSchema,
215474
+ /** How many of the input frames this track matched a real detection on
215475
+ * (never a coasted/extrapolated frame) — the replay's own signal for "how
215476
+ * solid is this track", cheaper than re-deriving it from a trajectory. */
215477
+ framesMatched: zod.z.number().int()
215478
+ });
215479
+ var RunReplayFrameProcessorResultSchema = zod.z.object({ tracks: zod.z.array(ReplayTrackSchema).readonly() });
214362
215480
  var pipelineAnalyticsCapability = {
214363
215481
  name: "pipeline-analytics",
214364
215482
  scope: "device",
@@ -214849,6 +215967,32 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
214849
215967
  kind: "mutation",
214850
215968
  auth: "admin"
214851
215969
  }),
215970
+ /**
215971
+ * The FrameProcessor pass of a replay run — see the `Replay` section
215972
+ * above this capability's definition for why this is not the
215973
+ * `processFrame` method this file's header says pipeline-analytics does
215974
+ * not have.
215975
+ *
215976
+ * Constructs a FRESH `FrameProcessor` for `(deviceId, source)`, feeds it
215977
+ * `frames` IN THE ORDER GIVEN (the caller is responsible for time
215978
+ * ordering — this method does not sort), and returns the tracks it
215979
+ * produced. Zero persistence: no `TrackStore`, no event bus, no media
215980
+ * capture. `zones` / `detectionRules` are the run's OWN zone set —
215981
+ * typically the camera's real zones plus an ephemeral overlay
215982
+ * (`addon-benchmark`'s `replay-plan.ts`), never read from or written to
215983
+ * the `zones` capability by this method itself.
215984
+ */
215985
+ runReplayFrameProcessor: require_sleep.method(zod.z.object({
215986
+ deviceId: zod.z.number(),
215987
+ source: DetectionSourceSchema,
215988
+ zones: zod.z.array(ZoneSchema).readonly().optional(),
215989
+ detectionRules: zod.z.array(ZoneRuleSchema).readonly().optional(),
215990
+ zoneMembershipMinOverlap: zod.z.number().min(0).max(1).optional(),
215991
+ frames: zod.z.array(ReplayFrameInputSchema).min(1)
215992
+ }), RunReplayFrameProcessorResultSchema, {
215993
+ kind: "mutation",
215994
+ auth: "admin"
215995
+ }),
214852
215996
  /** Every annotation on a track, oldest first. */
214853
215997
  listRetrainAnnotations: require_sleep.method(zod.z.object({ trackId: zod.z.string() }), zod.z.array(RetrainAnnotationSchema).readonly(), {
214854
215998
  kind: "query",
@@ -215015,559 +216159,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
215015
216159
  }) }
215016
216160
  }
215017
216161
  };
215018
- var NativeCropRefSchema = zod.z.object({
215019
- /** Handle keying the retained native surface (node-pinned to its owner). */
215020
- handle: require_sleep.FrameHandleSchema,
215021
- /** The parent crop's padded/clamped rectangle in FRAME-space pixels. */
215022
- cropFrameSpace: zod.z.object({
215023
- x: zod.z.number(),
215024
- y: zod.z.number(),
215025
- w: zod.z.number(),
215026
- h: zod.z.number()
215027
- })
215028
- });
215029
- zod.z.object({
215030
- crop: zod.z.object({
215031
- left: zod.z.number(),
215032
- top: zod.z.number(),
215033
- width: zod.z.number().positive(),
215034
- height: zod.z.number().positive()
215035
- }).optional(),
215036
- content: zod.z.object({
215037
- width: zod.z.number().int().positive(),
215038
- height: zod.z.number().int().positive()
215039
- }),
215040
- fit: zod.z.enum(["stretch", "contain"]),
215041
- format: zod.z.enum([
215042
- "rgb",
215043
- "gray",
215044
- "jpeg"
215045
- ])
215046
- });
215047
- var FrameRefSchema = zod.z.object({
215048
- registryId: zod.z.string().min(1),
215049
- id: zod.z.string().min(1),
215050
- width: zod.z.number().int().positive(),
215051
- height: zod.z.number().int().positive(),
215052
- format: zod.z.enum(["rgb", "gray"]),
215053
- timestamp: zod.z.number(),
215054
- capturedAt: zod.z.number().optional()
215055
- });
215056
- var ModelFormatSchema$1 = zod.z.enum([
215057
- "onnx",
215058
- "coreml",
215059
- "openvino",
215060
- "tflite",
215061
- "pt",
215062
- "gguf"
215063
- ]);
215064
- var PipelineSlotSchema = zod.z.enum([
215065
- "detector",
215066
- "cropper",
215067
- "classifier",
215068
- "refiner",
215069
- "audio-classifier"
215070
- ]);
215071
- var PipelineEngineChoiceSchema = zod.z.object({
215072
- runtime: zod.z.enum(["node", "python"]),
215073
- backend: zod.z.string(),
215074
- format: ModelFormatSchema$1,
215075
- device: zod.z.string().optional()
215076
- });
215077
- var EngineDeviceInfoSchema = zod.z.object({
215078
- id: zod.z.string(),
215079
- label: zod.z.string(),
215080
- description: zod.z.string().optional()
215081
- });
215082
- var AvailableEngineSchema = zod.z.object({
215083
- engine: PipelineEngineChoiceSchema,
215084
- devices: zod.z.array(EngineDeviceInfoSchema).readonly(),
215085
- defaultDevice: zod.z.string()
215086
- });
215087
- var PipelineDefaultStepSchema = zod.z.lazy(() => zod.z.object({
215088
- addonId: zod.z.string(),
215089
- addonName: zod.z.string(),
215090
- slot: PipelineSlotSchema,
215091
- inputClasses: zod.z.array(zod.z.string()).readonly(),
215092
- outputClasses: zod.z.array(zod.z.string()).readonly(),
215093
- enabled: zod.z.boolean(),
215094
- modelId: zod.z.string(),
215095
- children: zod.z.array(PipelineDefaultStepSchema).readonly(),
215096
- group: zod.z.string().optional(),
215097
- settings: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
215098
- }));
215099
- var PipelineTemplateStepSchema = zod.z.lazy(() => zod.z.object({
215100
- addonId: zod.z.string(),
215101
- enabled: zod.z.boolean(),
215102
- modelId: zod.z.string(),
215103
- children: zod.z.array(PipelineTemplateStepSchema).readonly(),
215104
- settings: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
215105
- }));
215106
- var PipelineTemplateSchema$1 = zod.z.object({
215107
- id: zod.z.string(),
215108
- name: zod.z.string(),
215109
- createdAt: zod.z.string(),
215110
- updatedAt: zod.z.string(),
215111
- engine: PipelineEngineChoiceSchema,
215112
- steps: zod.z.array(PipelineTemplateStepSchema).readonly()
215113
- });
215114
- var PipelineModelOptionSchema = zod.z.object({
215115
- id: zod.z.string(),
215116
- name: zod.z.string(),
215117
- formats: zod.z.record(zod.z.string(), zod.z.object({
215118
- downloaded: zod.z.boolean(),
215119
- sizeMB: zod.z.number()
215120
- })),
215121
- group: ModelVariantGroupSchema.optional(),
215122
- legacy: zod.z.boolean().optional(),
215123
- provider: ModelProviderIdSchema.optional()
215124
- });
215125
- var ConfigFieldBridge = zod.z.custom();
215126
- var PipelineAddonSchemaSchema = zod.z.object({
215127
- id: zod.z.string(),
215128
- name: zod.z.string(),
215129
- slot: PipelineSlotSchema,
215130
- inputClasses: zod.z.array(zod.z.string()).readonly(),
215131
- outputClasses: zod.z.array(zod.z.string()).readonly(),
215132
- childSlots: zod.z.array(PipelineSlotSchema).readonly(),
215133
- models: zod.z.array(PipelineModelOptionSchema).readonly(),
215134
- defaultModelId: zod.z.string(),
215135
- defaultModelIdByFormat: zod.z.record(zod.z.string(), zod.z.string()).optional(),
215136
- enabledByDefault: zod.z.boolean().optional(),
215137
- backfillIntoExistingOverrides: zod.z.boolean().optional(),
215138
- defaultConfidence: zod.z.number(),
215139
- group: zod.z.string().optional(),
215140
- configSchema: zod.z.array(ConfigFieldBridge).readonly().optional()
215141
- });
215142
- var PipelineSlotSchemaSchema = zod.z.object({
215143
- id: PipelineSlotSchema,
215144
- label: zod.z.string(),
215145
- priority: zod.z.number(),
215146
- parentSlot: PipelineSlotSchema.nullable(),
215147
- addons: zod.z.array(PipelineAddonSchemaSchema).readonly()
215148
- });
215149
- var PipelineSchemaSchema = zod.z.object({
215150
- availableEngines: zod.z.array(AvailableEngineSchema).readonly(),
215151
- selectedEngine: PipelineEngineChoiceSchema,
215152
- slots: zod.z.array(PipelineSlotSchemaSchema).readonly()
215153
- });
215154
- var EngineProvisioningSchema = zod.z.object({
215155
- runtimeId: zod.z.enum([
215156
- "onnx",
215157
- "openvino",
215158
- "coreml",
215159
- "edgetpu"
215160
- ]).nullable(),
215161
- device: zod.z.string().nullable(),
215162
- state: zod.z.enum([
215163
- "idle",
215164
- "installing",
215165
- "verifying",
215166
- "ready",
215167
- "failed"
215168
- ]),
215169
- progress: zod.z.number().optional(),
215170
- error: zod.z.string().optional(),
215171
- nextRetryAt: zod.z.number().optional(),
215172
- /**
215173
- * Gate A (config-correctness gate at engine change): human-readable
215174
- * config issues surfaced EAGERLY when the node's engine changes — model
215175
- * substitutions ("chose X, running Y") and zero-build steps ("no model
215176
- * has a <format> build"). Additive/optional: informational only, never
215177
- * enforced here — `assertEngineReady` (readiness) still gates inference.
215178
- * Absent/empty when the node-default tree resolves cleanly.
215179
- */
215180
- configIssues: zod.z.array(zod.z.string()).optional()
215181
- });
215182
- var PipelineStepInputSchema = zod.z.lazy(() => zod.z.object({
215183
- addonId: zod.z.string(),
215184
- modelId: zod.z.string().optional(),
215185
- enabled: zod.z.boolean().default(true),
215186
- children: zod.z.array(PipelineStepInputSchema).optional(),
215187
- settings: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
215188
- jumpDeviceKey: zod.z.string().optional()
215189
- }));
215190
- var ModelSubstitutionSchema = zod.z.object({
215191
- addonId: zod.z.string(),
215192
- chosen: zod.z.string(),
215193
- running: zod.z.string(),
215194
- format: zod.z.string()
215195
- });
215196
- var PipelineValidationIssueSchema = zod.z.object({
215197
- addonId: zod.z.string(),
215198
- kind: zod.z.enum(["unknown-addon", "no-format-build"]),
215199
- detail: zod.z.string()
215200
- });
215201
- var PipelineValidationResultSchema = zod.z.object({
215202
- ok: zod.z.boolean(),
215203
- issues: zod.z.array(PipelineValidationIssueSchema).readonly(),
215204
- substitutions: zod.z.array(ModelSubstitutionSchema).readonly(),
215205
- /** The node's `currentEngine.format` this validation ran against. */
215206
- format: zod.z.string()
215207
- });
215208
- var ReferenceImageEntrySchema = zod.z.object({
215209
- filename: zod.z.string(),
215210
- stepIds: zod.z.array(zod.z.string()).readonly().optional()
215211
- });
215212
- var ReferenceImageBodySchema = zod.z.object({
215213
- base64: zod.z.string(),
215214
- filename: zod.z.string()
215215
- });
215216
- var ReferenceAudioEntrySchema = zod.z.object({
215217
- filename: zod.z.string(),
215218
- sizeKb: zod.z.number()
215219
- });
215220
- var ReferenceAudioBodySchema = zod.z.object({ base64: zod.z.string() });
215221
- var AudioBackendSchema = zod.z.object({
215222
- id: zod.z.string(),
215223
- name: zod.z.string(),
215224
- description: zod.z.string(),
215225
- available: zod.z.boolean(),
215226
- /**
215227
- * Raw classifier labels this backend can emit (e.g. YAMNet's
215228
- * 521-class set or Apple SoundAnalysis's 303-class set). Used by
215229
- * the benchmark UI to populate the `enabledMicroClasses` filter
215230
- * specific to the selected backend without a separate fetch.
215231
- */
215232
- rawLabels: zod.z.array(zod.z.string()).readonly().optional()
215233
- });
215234
- var AudioCapabilitiesSchema = zod.z.object({
215235
- activeBackend: zod.z.string(),
215236
- availableBackends: zod.z.array(AudioBackendSchema).readonly(),
215237
- sampleRate: zod.z.number(),
215238
- chunkDurationMs: zod.z.number()
215239
- });
215240
- var DownloadModelResultSchema = zod.z.object({
215241
- filePath: zod.z.string(),
215242
- sizeMB: zod.z.number(),
215243
- durationMs: zod.z.number()
215244
- });
215245
- var AudioTestResultSchema = zod.z.object({
215246
- success: zod.z.boolean(),
215247
- error: zod.z.string().optional(),
215248
- frame: zod.z.custom().optional()
215249
- });
215250
- var PipelineConfigBridge = zod.z.custom();
215251
- var ConfigUISchemaBridge = zod.z.custom();
215252
- var ConfigUISchemaNullableBridge = zod.z.custom();
215253
- var InferenceCapabilitiesBridge = zod.z.custom();
215254
- var ModelAvailabilityListBridge = zod.z.custom();
215255
- var PipelineRunResultBridge = zod.z.custom();
215256
- var pipelineExecutorCapability = {
215257
- name: "pipeline-executor",
215258
- scope: "system",
215259
- mode: "singleton",
215260
- methods: {
215261
- getAvailableEngines: require_sleep.method(zod.z.void(), zod.z.array(PipelineEngineChoiceSchema)),
215262
- getSelectedEngine: require_sleep.method(zod.z.void(), PipelineEngineChoiceSchema),
215263
- getDefaultSteps: require_sleep.method(PipelineEngineChoiceSchema, zod.z.array(PipelineDefaultStepSchema)),
215264
- /**
215265
- * Per-node detection-engine provisioning snapshot. Returns the live
215266
- * state of the lazy runtime-provisioning machine on `nodeId`
215267
- * (idle / installing / verifying / ready / failed). The UI pairs this
215268
- * one-shot query with the `pipeline.engine-provisioning` live event
215269
- * (emitted on every transition) to drive a per-node "engine ready?"
215270
- * indicator without polling. Phase 2.
215271
- */
215272
- getEngineProvisioning: require_sleep.method(zod.z.object({ nodeId: zod.z.string() }), EngineProvisioningSchema),
215273
- getVideoPipelineSteps: require_sleep.method(zod.z.void(), zod.z.record(zod.z.string(), zod.z.object({
215274
- modelId: zod.z.string(),
215275
- settings: zod.z.record(zod.z.string(), zod.z.unknown()).readonly()
215276
- }))),
215277
- setVideoPipelineSteps: require_sleep.method(zod.z.object({ steps: zod.z.record(zod.z.string(), zod.z.object({
215278
- modelId: zod.z.string(),
215279
- settings: zod.z.record(zod.z.string(), zod.z.unknown()).readonly()
215280
- })) }), zod.z.object({ success: zod.z.literal(true) }), {
215281
- kind: "mutation",
215282
- auth: "admin"
215283
- }),
215284
- /**
215285
- * Clear THIS node's executor-side PER-DEVICE settings stores (the
215286
- * per-camera step overrides the object-detection root reads via
215287
- * `applyDeviceOverridesToTree`). `nodeId` is the ROUTING key — the
215288
- * generated cap-router strips it (default `nodeIdMode: 'routing'`) and
215289
- * dispatches to that node, so the provider method runs ON the target
215290
- * node and receives no `nodeId`.
215291
- *
215292
- * This is the slimmed executor leg of the orchestrator's
215293
- * `resetNodePipelineDefaults` flow (which owns the real reset: node
215294
- * addonDefaults pins + per-camera orchestrator overrides). The legacy
215295
- * `resetToDefault` — which reset a persisted global step-tree seed
215296
- * nothing in the live per-camera path read — was removed together with
215297
- * that seed.
215298
- */
215299
- clearDeviceOverrides: require_sleep.method(zod.z.object({ nodeId: zod.z.string() }), zod.z.object({
215300
- success: zod.z.literal(true),
215301
- clearedDevices: zod.z.number()
215302
- }), {
215303
- kind: "mutation",
215304
- auth: "admin"
215305
- }),
215306
- getSchema: require_sleep.method(zod.z.void(), PipelineSchemaSchema),
215307
- getGlobalSteps: require_sleep.method(zod.z.void(), zod.z.array(PipelineDefaultStepSchema).readonly().nullable()),
215308
- getGlobalPipelineConfig: require_sleep.method(zod.z.void(), PipelineConfigBridge),
215309
- getOrchestratorConfigSchema: require_sleep.method(zod.z.void(), ConfigUISchemaBridge),
215310
- /**
215311
- * Gate B (pre-init config-correctness gate). PURE COMPUTE against this
215312
- * node's `currentEngine.format` — resolves `steps` the same way the
215313
- * runtime dispatch path would, and reports what WOULD happen without
215314
- * touching any node-global state. Called by the orchestrator at attach
215315
- * time (`attachOn`), node-pinned to the TARGET node, so config problems
215316
- * surface BEFORE `pipelineRunner.attachCamera` rather than at the first
215317
- * per-frame resolve. `ok` is false iff `issues` is non-empty (both
215318
- * `unknown-addon` and `no-format-build` are HARD issues); `substitutions`
215319
- * is informational (a degraded-but-loadable model swap) and never
215320
- * affects `ok`. Never throws.
215321
- */
215322
- validatePipeline: require_sleep.method(zod.z.object({ steps: zod.z.array(PipelineStepInputSchema) }), PipelineValidationResultSchema),
215323
- listTemplates: require_sleep.method(zod.z.void(), zod.z.array(PipelineTemplateSchema$1).readonly()),
215324
- saveTemplate: require_sleep.method(zod.z.object({
215325
- name: zod.z.string(),
215326
- steps: zod.z.array(PipelineTemplateStepSchema).readonly(),
215327
- engine: PipelineEngineChoiceSchema
215328
- }), PipelineTemplateSchema$1, { kind: "mutation" }),
215329
- updateTemplate: require_sleep.method(zod.z.object({
215330
- id: zod.z.string(),
215331
- name: zod.z.string().optional(),
215332
- steps: zod.z.array(PipelineTemplateStepSchema).readonly().optional()
215333
- }), PipelineTemplateSchema$1, { kind: "mutation" }),
215334
- deleteTemplate: require_sleep.method(zod.z.object({ id: zod.z.string() }), zod.z.void(), { kind: "mutation" }),
215335
- getCapabilities: require_sleep.method(zod.z.void(), InferenceCapabilitiesBridge),
215336
- getAddonModels: require_sleep.method(zod.z.object({ addonId: zod.z.string() }), ModelAvailabilityListBridge),
215337
- downloadModel: require_sleep.method(zod.z.object({
215338
- addonId: zod.z.string(),
215339
- modelId: zod.z.string(),
215340
- format: ModelFormatSchema$1
215341
- }), DownloadModelResultSchema, { kind: "mutation" }),
215342
- deleteModel: require_sleep.method(zod.z.object({
215343
- addonId: zod.z.string(),
215344
- modelId: zod.z.string(),
215345
- format: ModelFormatSchema$1
215346
- }), zod.z.object({ success: zod.z.literal(true) }), { kind: "mutation" }),
215347
- /**
215348
- * Stateless single-frame execution. Callers (runner, benchmark) pass
215349
- * the complete `engine` + `steps` tree; the executor holds no state
215350
- * about cameras or saved pipelines.
215351
- *
215352
- * `engine` is optional during the migration window to preserve the
215353
- * legacy call shape used by existing benchmark code; once all
215354
- * callers pass it explicitly we make it required.
215355
- *
215356
- * Exactly one of `frame`, `frameRef`, `frameHandle`, `imageBase64`,
215357
- * `referenceImage` must be provided:
215358
- * - `frame`: runtime dispatch path (runner → decoded broker frame).
215359
- * Carries the raw buffer, dimensions, and format; the executor
215360
- * uses it directly without base64 round-tripping.
215361
- * - `frameHandle` (CB5): a zero-pixel shm `FrameHandle` for the SAME
215362
- * decoded frame. Both runner and executor are hub-local processes
215363
- * sharing `/dev/shm`, so the executor maps the named segment and
215364
- * reads the pixels back zero-copy — eliminating the ~1.2MB
215365
- * re-serialisation over UDS/MsgPack the `frame` path pays per call.
215366
- * High-risk: the FrameRing is a latest-wins seqlock with no
215367
- * refcount, so a recycled slot yields a null read; the executor
215368
- * then degrades to an empty result and the runner ships pixels via
215369
- * `frame` as the fallback (queue-depth gated on the runner side).
215370
- * - `imageBase64`: one-shot test path (benchmark ImageTab).
215371
- * - `referenceImage`: named file from the reference-image store.
215372
- */
215373
- runPipeline: require_sleep.method(zod.z.object({
215374
- engine: PipelineEngineChoiceSchema.optional(),
215375
- steps: zod.z.array(PipelineStepInputSchema).min(1),
215376
- frame: FrameInputSchema.optional(),
215377
- /**
215378
- * Process-local lazy frame. Valid only when caller and provider resolve
215379
- * in the same execution-group process; split/cross-node callers use
215380
- * `frame`/`image` inline compatibility instead.
215381
- */
215382
- frameRef: FrameRefSchema.optional(),
215383
- /**
215384
- * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
215385
- * the decoded pixels live in. One more member of the one-of
215386
- * frame/frameHandle/image/imageBase64/referenceImage group.
215387
- */
215388
- frameHandle: require_sleep.FrameHandleSchema.optional(),
215389
- imageBase64: zod.z.string().optional(),
215390
- /**
215391
- * Binary JPEG bytes — preferred over `imageBase64` on internal
215392
- * hops (hub → forked worker via Moleculer MsgPack) because it
215393
- * skips the 33% base64 overhead + the per-call base64 decode on
215394
- * the detection-pipeline worker. Callers can pass either; exactly
215395
- * one of `frame`/`image`/`imageBase64`/`referenceImage` is required.
215396
- */
215397
- image: zod.z.instanceof(Uint8Array).optional(),
215398
- referenceImage: zod.z.string().optional(),
215399
- deviceId: zod.z.number().optional(),
215400
- sessionId: zod.z.string().optional(),
215401
- /**
215402
- * Execution plane. 'full' (default) runs the whole tree — benchmark,
215403
- * reference-image, and detail-subtree calls. 'frame' is the live
215404
- * per-frame dispatch: ONLY root-plane steps run; crop children
215405
- * (inputClasses ≠ null) are skipped and served per-track via
215406
- * pipelineRunner.runDetailSubtree (two-plane design).
215407
- */
215408
- plane: zod.z.enum(["full", "frame"]).optional(),
215409
- /**
215410
- * Inference-device selector (Phase 2 multi-device). Format
215411
- * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
215412
- * Omitted ⇒ the runner's default device (current single-engine
215413
- * behaviour). Selects WHICH device pool of the node runs the call.
215414
- */
215415
- deviceKey: zod.z.string().optional(),
215416
- /**
215417
- * Two-plane NATIVE child-crop reference. Set by `runDetailSubtree` ONLY
215418
- * when the parent crop was resolved from the frame's retained NATIVE
215419
- * surface (a frameHandle HIT). Lets the executor re-cut a LEAF crop
215420
- * child's ROI (plate-ocr, face-embedding, leaf classifiers) at native
215421
- * resolution from that surface — the SAME quality path faces already
215422
- * had — instead of the downscaled parent tile. `handle` keys the native
215423
- * surface (node-pinned to its owner); `cropFrameSpace` is the parent
215424
- * crop's padded/clamped rectangle in FRAME-space pixels, used to compose
215425
- * the executor's crop-normalized child ROI back into frame-normalized
215426
- * coordinates. Auxiliary to the image source (`image`/`frame`/…), NOT one
215427
- * of the mutually-exclusive image inputs. Absent ⇒ tile-crop children
215428
- * (today's behaviour on the fallback path).
215429
- */
215430
- nativeCropRef: NativeCropRefSchema.optional()
215431
- }), PipelineRunResultBridge, { kind: "mutation" }),
215432
- /**
215433
- * Batched run — N raw frames packed into one cap call. The provider
215434
- * routes the batch through `SharedInferencePool.inferBatch`
215435
- * (`MSG_INFER_BATCH = 0x03`) so the IPC framing and JSON response
215436
- * envelope cost is amortised N:1 vs N concurrent `runPipeline`
215437
- * calls. Single root step + uniform model assumed; trees with crop
215438
- * children fall back to sequential execution.
215439
- *
215440
- * Used by `scripts/bench-batch-style.mts` for batch benchmarking —
215441
- * N frames in one call to amortise per-call IPC overhead.
215442
- */
215443
- runPipelineBatch: require_sleep.method(zod.z.object({
215444
- engine: PipelineEngineChoiceSchema.optional(),
215445
- steps: zod.z.array(PipelineStepInputSchema).min(1),
215446
- frames: zod.z.array(FrameInputSchema).min(1).max(255),
215447
- deviceId: zod.z.number().optional(),
215448
- sessionId: zod.z.string().optional(),
215449
- /**
215450
- * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
215451
- * the batch to the Python pool's bench preprocess cache
215452
- * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
215453
- * preprocessed ONCE and every later inference is a pure-inference cache
215454
- * hit — the sustained-throughput run measures inference, not
215455
- * decode+preprocess+infer. Omitted/0 for live frames (all different →
215456
- * full preprocess every call, correct). Fresh per sustained run;
215457
- * released via `uncacheFrame`.
215458
- */
215459
- frameId: zod.z.number().int().nonnegative().optional(),
215460
- /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
215461
- deviceKey: zod.z.string().optional()
215462
- }), zod.z.object({ results: zod.z.array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }),
215463
- /**
215464
- * Cache a raw frame inside the Python inference pool's memory.
215465
- * Returns a numeric `frameId` that `inferCached` references —
215466
- * subsequent calls send only 5 bytes through the pipe instead of
215467
- * 1.2MB raw data, eliminating the pipe transfer bottleneck.
215468
- */
215469
- cacheFrameInPool: require_sleep.method(zod.z.object({
215470
- data: zod.z.instanceof(Uint8Array),
215471
- width: zod.z.number().int().positive(),
215472
- height: zod.z.number().int().positive(),
215473
- format: zod.z.enum([
215474
- "rgb",
215475
- "bgr",
215476
- "gray"
215477
- ])
215478
- }), zod.z.object({
215479
- frameId: zod.z.number(),
215480
- width: zod.z.number(),
215481
- height: zod.z.number()
215482
- }), { kind: "mutation" }),
215483
- /**
215484
- * Run inference on a previously cached frame. Sends only 5 bytes
215485
- * (model_idx + frameId) through the IPC pipe — eliminates the
215486
- * ~35ms per-call overhead of transferring 1.2MB raw data.
215487
- */
215488
- inferCached: require_sleep.method(zod.z.object({
215489
- stepId: zod.z.string(),
215490
- frameId: zod.z.number().int()
215491
- }), zod.z.record(zod.z.string(), zod.z.unknown()), { kind: "mutation" }),
215492
- /**
215493
- * Release a cached frame from the Python pool's memory.
215494
- */
215495
- uncacheFrame: require_sleep.method(zod.z.object({ frameId: zod.z.number().int() }), zod.z.void(), { kind: "mutation" }),
215496
- /** Returns the effective pool tuning (resolved from user overrides + backend defaults). */
215497
- getEffectiveTuning: require_sleep.method(zod.z.void(), zod.z.object({
215498
- batchMode: zod.z.string(),
215499
- windowMs: zod.z.number(),
215500
- maxBatchSize: zod.z.number(),
215501
- concurrency: zod.z.number()
215502
- })),
215503
- /**
215504
- * List every EngineFactory currently loaded in this executor's RAM,
215505
- * with the models resident and a coarse "in use" marker derived from
215506
- * ongoing inference activity. Used by the Pipeline page Engines tab.
215507
- */
215508
- listLoadedEngines: require_sleep.method(zod.z.void(), zod.z.array(zod.z.object({
215509
- engineKey: zod.z.string(),
215510
- engine: PipelineEngineChoiceSchema,
215511
- modelsLoaded: zod.z.array(zod.z.string()).readonly(),
215512
- inUseByCameras: zod.z.array(zod.z.number()).readonly(),
215513
- /**
215514
- * Origin of this resident factory.
215515
- * - `runtime` — main camera-serving engine (no idle TTL).
215516
- * - `warm-override` — benchmark/test override held in the warm
215517
- * cache; auto-disposed after the idle TTL.
215518
- * - `device-pool` — a concurrent per-device pool (Phase 2
215519
- * multi-device, keyed by `deviceKey`) resolved
215520
- * via `resolveDeviceFactory`. Runs alongside the
215521
- * `runtime` engine on a DIFFERENT accelerator
215522
- * (NPU / iGPU / Coral) — this is how the
215523
- * Engines tab shows all pools running at once.
215524
- */
215525
- kind: zod.z.enum([
215526
- "runtime",
215527
- "warm-override",
215528
- "device-pool"
215529
- ]),
215530
- /** Native pid of the underlying Python pool (null when no pool). */
215531
- poolPid: zod.z.number().nullable(),
215532
- /** ms since this factory was last used (null when not warm-tracked). */
215533
- idleMs: zod.z.number().nullable(),
215534
- /** Idle TTL after which `warm-override` factories self-evict (null when not applicable). */
215535
- idleTtlMs: zod.z.number().nullable()
215536
- })).readonly()),
215537
- /** Warm up an engine without running a frame. No-op if already loaded. */
215538
- spinEngine: require_sleep.method(zod.z.object({ engine: PipelineEngineChoiceSchema }), zod.z.object({ success: zod.z.literal(true) }), {
215539
- kind: "mutation",
215540
- auth: "admin"
215541
- }),
215542
- /**
215543
- * Unload an engine from RAM. `force:true` unloads even when cameras
215544
- * are actively using it (they re-spin on next frame). Default is
215545
- * gated — returns `{success:false, reason}` when in use.
215546
- */
215547
- killEngine: require_sleep.method(zod.z.object({
215548
- engine: PipelineEngineChoiceSchema,
215549
- force: zod.z.boolean().optional()
215550
- }), zod.z.object({
215551
- success: zod.z.boolean(),
215552
- reason: zod.z.string().optional()
215553
- }), {
215554
- kind: "mutation",
215555
- auth: "admin"
215556
- }),
215557
- listReferenceImages: require_sleep.method(zod.z.void(), zod.z.array(ReferenceImageEntrySchema).readonly()),
215558
- getReferenceImage: require_sleep.method(zod.z.object({ filename: zod.z.string() }), ReferenceImageBodySchema.nullable()),
215559
- getReferenceAudioFiles: require_sleep.method(zod.z.void(), zod.z.array(ReferenceAudioEntrySchema).readonly()),
215560
- getReferenceAudio: require_sleep.method(zod.z.object({ filename: zod.z.string() }), ReferenceAudioBodySchema.nullable()),
215561
- getAudioCapabilities: require_sleep.method(zod.z.void(), AudioCapabilitiesSchema),
215562
- runAudioTest: require_sleep.method(zod.z.object({
215563
- addonId: zod.z.string(),
215564
- modelId: zod.z.string(),
215565
- filename: zod.z.string().optional(),
215566
- settings: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
215567
- }), AudioTestResultSchema, { kind: "mutation" }),
215568
- getDetectionConfigSchema: require_sleep.method(zod.z.void(), ConfigUISchemaNullableBridge)
215569
- }
215570
- };
215571
216162
  var OrchestratorMetricsSchema = zod.z.object({
215572
216163
  activeCameras: zod.z.number(),
215573
216164
  throttledCameras: zod.z.number(),
@@ -215592,66 +216183,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
215592
216183
  ])
215593
216184
  });
215594
216185
  var CameraMetricsWithDeviceIdSchema = CameraMetricsSchema.extend({ deviceId: zod.z.number() });
215595
- var ZoneKindEnum = zod.z.enum(["polygon", "tripwire"]);
215596
- var PolygonPointSchema = zod.z.object({
215597
- x: zod.z.number(),
215598
- y: zod.z.number()
215599
- });
215600
- var ZoneSchema = zod.z.object({
215601
- id: zod.z.string(),
215602
- name: zod.z.string(),
215603
- kind: ZoneKindEnum.default("polygon"),
215604
- /** Polygon vertices, fraction of frame (0–1). */
215605
- polygon: zod.z.array(PolygonPointSchema).readonly(),
215606
- /** Visual color for UI rendering. */
215607
- color: zod.z.string().default("#3b82f6")
215608
- });
215609
- var zonesCapability = {
215610
- name: "zones",
215611
- scope: "device",
215612
- mode: "singleton",
215613
- deviceTypes: [require_sleep.DeviceType.Camera],
215614
- methods: {
215615
- listZones: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), zod.z.array(ZoneSchema).readonly()),
215616
- addZone: require_sleep.method(zod.z.object({
215617
- deviceId: zod.z.number(),
215618
- zone: ZoneSchema
215619
- }), zod.z.void(), {
215620
- kind: "mutation",
215621
- auth: "admin"
215622
- }),
215623
- removeZone: require_sleep.method(zod.z.object({
215624
- deviceId: zod.z.number(),
215625
- zoneId: zod.z.string()
215626
- }), zod.z.void(), {
215627
- kind: "mutation",
215628
- auth: "admin"
215629
- }),
215630
- updateZone: require_sleep.method(zod.z.object({
215631
- deviceId: zod.z.number(),
215632
- zone: ZoneSchema
215633
- }), zod.z.void(), {
215634
- kind: "mutation",
215635
- auth: "admin"
215636
- })
215637
- },
215638
- /**
215639
- * Runtime-state slice — the live zone catalogue mirrored by the
215640
- * orchestrator on every CRUD mutation. Consumers read via
215641
- * `device.state.zones.value` / `.watch(...)` without round-tripping
215642
- * the cap, and the codegen DeviceProxy auto-wires the reactive
215643
- * handle. Slice shape is `{ zones: Zone[] }` so future extensions
215644
- * (e.g. zone groupings) can sit alongside the polygon list.
215645
- */
215646
- runtimeState: zod.z.object({ zones: zod.z.array(ZoneSchema).readonly() }),
215647
- /**
215648
- * Runtime-state durability: **restored** — written only on operator mutation, so a camera that never had one has nothing to re-derive from. This is the slice `zone-mirror-hydration.ts` exists to paper over.
215649
- *
215650
- * See `RuntimeStateDurability`. Enforced by
215651
- * `scripts/check-runtime-state-durability.ts`.
215652
- */
215653
- durability: "restored"
215654
- };
215655
216186
  var NativeCropBboxSchema = zod.z.object({
215656
216187
  x: zod.z.number(),
215657
216188
  y: zod.z.number(),
@@ -224617,6 +225148,23 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
224617
225148
  /** Media ms the returned fragment covers. */
224618
225149
  gopDurMs: zod.z.number()
224619
225150
  });
225151
+ var ReadWindowBytesResultSchema = zod.z.discriminatedUnion("kind", [zod.z.object({
225152
+ kind: zod.z.literal("ok"),
225153
+ data: zod.z.instanceof(Uint8Array),
225154
+ /** Absolute epoch ms of the returned bytes' first sample — at or before
225155
+ * the requested `fromMs` (anchored on the nearest keyframe). */
225156
+ gopStartMs: zod.z.number(),
225157
+ /** Media ms the returned bytes cover, from `gopStartMs`. */
225158
+ gopDurMs: zod.z.number(),
225159
+ /** `false` ⇒ the safety byte cap cut the read short before it reached
225160
+ * the requested `toMs`; the caller got fewer frames than asked for. */
225161
+ reachesRequestedEnd: zod.z.boolean()
225162
+ }), zod.z.object({
225163
+ kind: zod.z.literal("spans-multiple-segments"),
225164
+ /** Where the covering segment's own footage runs out — informational,
225165
+ * not a retry hint (retrying the same window would refuse again). */
225166
+ segmentEndMs: zod.z.number()
225167
+ })]);
224620
225168
  var recordingCapability = {
224621
225169
  name: "recording",
224622
225170
  scope: "system",
@@ -224696,6 +225244,21 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
224696
225244
  kind: "query",
224697
225245
  auth: "admin"
224698
225246
  }),
225247
+ /** Read a WINDOW `[fromMs, toMs)` of segment `startMs`, by mfra byte
225248
+ * range — the multi-GOP twin of `readGopBytes`. Refuses (does not
225249
+ * degrade) when the window runs past the covering segment. Used by the
225250
+ * replay clip's `recording` source to fetch several seconds of native
225251
+ * footage for its single ffmpeg decode pass. */
225252
+ readWindowBytes: require_sleep.method(zod.z.object({
225253
+ deviceId: zod.z.number(),
225254
+ profile: zod.z.string(),
225255
+ startMs: zod.z.number(),
225256
+ fromMs: zod.z.number(),
225257
+ toMs: zod.z.number()
225258
+ }), ReadWindowBytesResultSchema, {
225259
+ kind: "query",
225260
+ auth: "admin"
225261
+ }),
224699
225262
  setDeviceConfig: require_sleep.method(zod.z.object({
224700
225263
  deviceId: zod.z.number(),
224701
225264
  config: RecordingConfigSchema
@@ -225275,59 +225838,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
225275
225838
  */
225276
225839
  durability: "session"
225277
225840
  };
225278
- var ZoneRuleModeEnum = zod.z.enum(["include", "exclude"]);
225279
- var ZoneRuleSchema = zod.z.object({
225280
- /** Stable rule id — survives edits, used by the UI for diffing. */
225281
- id: zod.z.string(),
225282
- /** Optional human-readable label rendered in the rule editor. */
225283
- name: zod.z.string().optional(),
225284
- /** Zones this rule targets. The rule's `mode` applies to ALL
225285
- * listed zones (OR-set: a detection in any one of them counts).
225286
- * At least one zone id required — a rule with no targets is a
225287
- * configuration mistake and the form validator rejects it. */
225288
- zoneIds: zod.z.array(zod.z.string()).min(1).readonly(),
225289
- mode: ZoneRuleModeEnum,
225290
- /**
225291
- * Class names this rule applies to. Empty / undefined ⇒ rule
225292
- * applies to every class. Class strings match the `macroClass`
225293
- * field on detections (e.g. `person`, `car`, `dog`).
225294
- */
225295
- classFilter: zod.z.array(zod.z.string()).readonly().optional(),
225296
- /**
225297
- * Minimum bbox/mask overlap (0–1) with any of the rule's zones
225298
- * required to consider an entity "in the zone". Defaults to the
225299
- * consumer's stage default when omitted. Kept for back-compat with
225300
- * existing per-rule overrides; new operators pick the value via
225301
- * `bboxInclusionPct` (operator-friendly 0–100). Whichever field is
225302
- * set, the lower-level engine reads it as a 0–1 fraction.
225303
- */
225304
- overlapThreshold: zod.z.number().min(0).max(1).optional(),
225305
- /**
225306
- * Operator-friendly version of `overlapThreshold` — the percentage
225307
- * of the detection's bbox that must lie inside the zone for the
225308
- * rule to match. Documented default is 85%; the engine substitutes
225309
- * that when the field is omitted (kept optional so existing rules
225310
- * stored without it stay valid).
225311
- *
225312
- * When BOTH `overlapThreshold` and `bboxInclusionPct` are set on a
225313
- * rule, the engine prefers `bboxInclusionPct` because it's the
225314
- * field exposed in the UI. Internally both feed the same gate.
225315
- */
225316
- bboxInclusionPct: zod.z.number().min(0).max(100).optional(),
225317
- /**
225318
- * When `true` and a detection has a segmentation mask, use the
225319
- * mask for overlap instead of the bbox. Detection-stage only;
225320
- * motion rules ignore this field.
225321
- */
225322
- preferMask: zod.z.boolean().optional(),
225323
- /**
225324
- * Soft-toggle: `false` disables the rule without deleting it.
225325
- * Defaults to `true` so operators creating a rule via the UI
225326
- * see it active immediately.
225327
- */
225328
- enabled: zod.z.boolean().default(true)
225329
- });
225330
- var ZoneRulesArraySchema = zod.z.array(ZoneRuleSchema).readonly();
225331
225841
  var ScriptRunnerStatusSchema = zod.z.object({
225332
225842
  /** Whether the script is currently executing. */
225333
225843
  isRunning: zod.z.boolean(),
@@ -233176,6 +233686,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
233176
233686
  addonId: null,
233177
233687
  access: "view"
233178
233688
  },
233689
+ "notificationRules.resolveArtifactUrl": {
233690
+ capName: "notification-rules",
233691
+ capScope: "system",
233692
+ addonId: null,
233693
+ access: "view"
233694
+ },
233179
233695
  "notificationRules.setAlarmConfig": {
233180
233696
  capName: "notification-rules",
233181
233697
  capScope: "system",
@@ -233620,6 +234136,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
233620
234136
  addonId: null,
233621
234137
  access: "create"
233622
234138
  },
234139
+ "pipelineAnalytics.runReplayFrameProcessor": {
234140
+ capName: "pipeline-analytics",
234141
+ capScope: "device",
234142
+ addonId: null,
234143
+ access: "create"
234144
+ },
233623
234145
  "pipelineAnalytics.saveRetrainAnnotations": {
233624
234146
  capName: "pipeline-analytics",
233625
234147
  capScope: "device",
@@ -233752,6 +234274,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
233752
234274
  addonId: null,
233753
234275
  access: "view"
233754
234276
  },
234277
+ "pipelineExecutor.getInferenceDeviceHealth": {
234278
+ capName: "pipeline-executor",
234279
+ capScope: "system",
234280
+ addonId: null,
234281
+ access: "view"
234282
+ },
233755
234283
  "pipelineExecutor.getOrchestratorConfigSchema": {
233756
234284
  capName: "pipeline-executor",
233757
234285
  capScope: "system",
@@ -233824,6 +234352,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
233824
234352
  addonId: null,
233825
234353
  access: "view"
233826
234354
  },
234355
+ "pipelineExecutor.rearmInferenceDevice": {
234356
+ capName: "pipeline-executor",
234357
+ capScope: "system",
234358
+ addonId: null,
234359
+ access: "create"
234360
+ },
233827
234361
  "pipelineExecutor.runAudioTest": {
233828
234362
  capName: "pipeline-executor",
233829
234363
  capScope: "system",
@@ -234562,6 +235096,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
234562
235096
  addonId: null,
234563
235097
  access: "view"
234564
235098
  },
235099
+ "recording.readWindowBytes": {
235100
+ capName: "recording",
235101
+ capScope: "system",
235102
+ addonId: null,
235103
+ access: "view"
235104
+ },
234565
235105
  "recording.refreshStorageLocationsForMigration": {
234566
235106
  capName: "recording",
234567
235107
  capScope: "system",
@@ -237324,6 +237864,11 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
237324
237864
  form: "single",
237325
237865
  optional: false
237326
237866
  }],
237867
+ "pipelineAnalytics.runReplayFrameProcessor": [{
237868
+ name: "deviceId",
237869
+ form: "single",
237870
+ optional: false
237871
+ }],
237327
237872
  "pipelineAnalytics.saveRetrainAnnotations": [{
237328
237873
  name: "deviceId",
237329
237874
  form: "single",
@@ -237639,6 +238184,11 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
237639
238184
  form: "single",
237640
238185
  optional: false
237641
238186
  }],
238187
+ "recording.readWindowBytes": [{
238188
+ name: "deviceId",
238189
+ form: "single",
238190
+ optional: false
238191
+ }],
237642
238192
  "recording.relocateFootage": [{
237643
238193
  name: "deviceId",
237644
238194
  form: "single",
@@ -238115,6 +238665,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
238115
238665
  "recording.pruneFootage",
238116
238666
  "recording.readGopBytes",
238117
238667
  "recording.readSegmentBytes",
238668
+ "recording.readWindowBytes",
238118
238669
  "recording.relocateFootage",
238119
238670
  "recording.renderClip",
238120
238671
  "recording.renderGif",
@@ -238900,6 +239451,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
238900
239451
  getVideoPipelineSteps: (input) => dispatch("pipelineExecutor", "getVideoPipelineSteps", "query", input),
238901
239452
  setVideoPipelineSteps: (input) => dispatch("pipelineExecutor", "setVideoPipelineSteps", "mutation", input),
238902
239453
  clearDeviceOverrides: (input) => dispatch("pipelineExecutor", "clearDeviceOverrides", "mutation", input),
239454
+ getInferenceDeviceHealth: (input) => dispatch("pipelineExecutor", "getInferenceDeviceHealth", "query", input),
239455
+ rearmInferenceDevice: (input) => dispatch("pipelineExecutor", "rearmInferenceDevice", "mutation", input),
238903
239456
  getSchema: (input) => dispatch("pipelineExecutor", "getSchema", "query", input),
238904
239457
  getGlobalSteps: (input) => dispatch("pipelineExecutor", "getGlobalSteps", "query", input),
238905
239458
  getGlobalPipelineConfig: (input) => dispatch("pipelineExecutor", "getGlobalPipelineConfig", "query", input),
@@ -242434,6 +242987,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
242434
242987
  exports.RawStateResultSchema = require_sleep.RawStateResultSchema;
242435
242988
  exports.ReadGopBytesResultSchema = ReadGopBytesResultSchema;
242436
242989
  exports.ReadSegmentBytesResultSchema = ReadSegmentBytesResultSchema;
242990
+ exports.ReadWindowBytesResultSchema = ReadWindowBytesResultSchema;
242437
242991
  exports.ReadinessRegistry = require_sleep.ReadinessRegistry;
242438
242992
  exports.ReadinessTimeoutError = require_sleep.ReadinessTimeoutError;
242439
242993
  exports.RecentTracksPageSchema = RecentTracksPageSchema;