@camstack/addon-pipeline 1.2.622 → 1.2.623

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/audio-analyzer/index.js +3 -3
  2. package/dist/audio-analyzer/index.mjs +3 -3
  3. package/dist/{custom-model-catalog-CKCK-QcT.js → custom-model-catalog-BTBdgN2p.js} +24 -11
  4. package/dist/{custom-model-catalog-wpMi2U7e.mjs → custom-model-catalog-D5zTw1LU.mjs} +24 -5
  5. package/dist/detection-pipeline/index.js +16 -6
  6. package/dist/detection-pipeline/index.mjs +16 -6
  7. package/dist/{dist-DUFGDd_2.js → dist-BVo3wa7s.js} +636 -71
  8. package/dist/{dist-BF60SjmI.mjs → dist-opSx6v5i.mjs} +576 -71
  9. package/dist/{model-catalogs-CjTm_0O-.js → model-catalogs-CQytR2AN.js} +1 -1
  10. package/dist/{model-catalogs-CqFDhb0B.mjs → model-catalogs-rIvLUVlw.mjs} +1 -1
  11. package/dist/motion-wasm/index.js +1 -1
  12. package/dist/motion-wasm/index.mjs +1 -1
  13. package/dist/{node-DkphOOM2.mjs → node-CGIYuJMN.mjs} +1 -1
  14. package/dist/{node-DzGVn7bD.js → node-DqLsuN_A.js} +1 -1
  15. package/dist/pipeline-runner/index.js +101 -460
  16. package/dist/pipeline-runner/index.mjs +94 -453
  17. package/dist/{process-memory-CBzfdSvC.mjs → process-memory-Bd-TuWfM.mjs} +1 -1
  18. package/dist/{process-memory-8kKexHYR.js → process-memory-DyxG-0mG.js} +1 -1
  19. package/dist/recorder/index.js +2 -2
  20. package/dist/recorder/index.mjs +2 -2
  21. package/dist/{segment-demux-js-DRLQb1pc.js → segment-demux-js-B4bfTQpK.js} +1 -1
  22. package/dist/{segment-demux-js-DQNoNSJt.mjs → segment-demux-js-CdVRzwSd.mjs} +1 -1
  23. package/dist/stream-broker/_stub.js +2 -2
  24. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BmANAxAe.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DF3FGCR8.mjs} +2 -2
  25. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-oUZNn2Kr.mjs +26 -0
  26. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-Bb-ETNGC.mjs +26 -0
  27. package/dist/stream-broker/demux-worker-child.js +1 -1
  28. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  29. package/dist/stream-broker/{hostInit-B3hXAfr1.mjs → hostInit-BiyFzSqq.mjs} +2 -2
  30. package/dist/stream-broker/index.js +3 -3
  31. package/dist/stream-broker/index.mjs +3 -3
  32. package/dist/stream-broker/remoteEntry.js +1 -1
  33. package/package.json +1 -1
  34. package/python/accelerator_facts.py +275 -0
  35. package/python/inference_pool.py +19 -2
  36. package/python/postprocessors/ssd.py +12 -4
  37. package/python/postprocessors/test_ssd.py +64 -0
  38. package/python/test_accelerator_facts.py +105 -0
  39. package/python/webgpu_isolated.py +19 -0
  40. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-H6RKsaiZ.mjs +0 -26
  41. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CNoWeZHH.mjs +0 -26
@@ -4724,10 +4724,10 @@ var ZodType = /*@__PURE__*/ $constructor("ZodType", (inst, def) => {
4724
4724
  return array(this);
4725
4725
  },
4726
4726
  or(arg) {
4727
- return union([this, arg]);
4727
+ return union$1([this, arg]);
4728
4728
  },
4729
4729
  and(arg) {
4730
- return intersection(this, arg);
4730
+ return intersection$1(this, arg);
4731
4731
  },
4732
4732
  transform(tx) {
4733
4733
  return pipe(this, transform(tx));
@@ -5198,7 +5198,7 @@ var ZodUnion = /*@__PURE__*/ $constructor("ZodUnion", (inst, def) => {
5198
5198
  inst._zod.processJSONSchema = (ctx, json, params) => unionProcessor(inst, ctx, json, params);
5199
5199
  inst.options = def.options;
5200
5200
  });
5201
- function union(options, params) {
5201
+ function union$1(options, params) {
5202
5202
  return new ZodUnion({
5203
5203
  type: "union",
5204
5204
  options,
@@ -5222,7 +5222,7 @@ var ZodIntersection = /*@__PURE__*/ $constructor("ZodIntersection", (inst, def)
5222
5222
  ZodType.init(inst, def);
5223
5223
  inst._zod.processJSONSchema = (ctx, json, params) => intersectionProcessor(inst, ctx, json, params);
5224
5224
  });
5225
- function intersection(left, right) {
5225
+ function intersection$1(left, right) {
5226
5226
  return new ZodIntersection({
5227
5227
  type: "intersection",
5228
5228
  left,
@@ -8180,7 +8180,7 @@ var RecordIndexSchema = object({
8180
8180
  fields: array(RecordFieldPathSchema).min(1).max(4),
8181
8181
  unique: boolean().optional()
8182
8182
  }).strict();
8183
- var RecordRetentionSchema = union([
8183
+ var RecordRetentionSchema = union$1([
8184
8184
  object({
8185
8185
  ttlMs: number().int().positive(),
8186
8186
  from: _enum(["at", "write"])
@@ -8333,7 +8333,7 @@ var CAM_PROFILE_ORDER = [
8333
8333
  * the operator picked. Today's one user is the audio analyser, whose rule is
8334
8334
  * "the lowest AVAILABLE native rung" (D537).
8335
8335
  */
8336
- var AudioStreamPickSchema = union([CamProfileSchema, literal("auto")]);
8336
+ var AudioStreamPickSchema = union$1([CamProfileSchema, literal("auto")]);
8337
8337
  var CamStreamKindSchema = _enum([
8338
8338
  "pull-rtsp",
8339
8339
  "pull-rtmp",
@@ -8765,7 +8765,7 @@ var ContributionSectionSchema = object({
8765
8765
  description: string().optional(),
8766
8766
  style: _enum(["card", "accordion"]).optional(),
8767
8767
  defaultCollapsed: boolean().optional(),
8768
- columns: union([
8768
+ columns: union$1([
8769
8769
  literal(1),
8770
8770
  literal(2),
8771
8771
  literal(3),
@@ -8929,7 +8929,7 @@ var HF_REPO = "camstack/camstack-models";
8929
8929
  `${HF_REPO}`;
8930
8930
  object({
8931
8931
  code: string().regex(/^[a-z][a-z0-9-]*(\.[a-z0-9-]+)+$/),
8932
- params: record(string(), union([string(), number()])).optional()
8932
+ params: record(string(), union$1([string(), number()])).optional()
8933
8933
  });
8934
8934
  var CamstackError = class extends Error {
8935
8935
  i18nCode;
@@ -11032,7 +11032,7 @@ var CompositionFieldReadSchema = object({
11032
11032
  var CompositionFromSourceSchema = CompositionFieldReadSchema.extend({ kind: literal("from") });
11033
11033
  var CompositionBindingSchema = discriminatedUnion("kind", [CompositionFromSourceSchema, object({
11034
11034
  kind: literal("literal"),
11035
- value: union([
11035
+ value: union$1([
11036
11036
  string(),
11037
11037
  number(),
11038
11038
  boolean(),
@@ -11227,7 +11227,7 @@ var EncodeProfileSchema = object({
11227
11227
  */
11228
11228
  singleSlicePerFrame: boolean().optional()
11229
11229
  }),
11230
- audio: union([literal("passthrough"), object({
11230
+ audio: union$1([literal("passthrough"), object({
11231
11231
  codec: _enum([
11232
11232
  "opus",
11233
11233
  "aac",
@@ -11237,7 +11237,7 @@ var EncodeProfileSchema = object({
11237
11237
  ]),
11238
11238
  bitrateKbps: number().int().positive().optional(),
11239
11239
  sampleRateHz: number().int().positive().optional(),
11240
- channels: union([literal(1), literal(2)]).optional()
11240
+ channels: union$1([literal(1), literal(2)]).optional()
11241
11241
  })]),
11242
11242
  /**
11243
11243
  * ffmpeg input-side args, inserted between the fixed global flags
@@ -13982,7 +13982,7 @@ var StorageDrainProgressSchema = object({
13982
13982
  *
13983
13983
  * The orchestrator's `resolveRef(ref)` handles both cases.
13984
13984
  */
13985
- var StorageLocationRefSchema = union([StorageLocationTypeSchema, string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/)]);
13985
+ var StorageLocationRefSchema = union$1([StorageLocationTypeSchema, string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/)]);
13986
13986
  /**
13987
13987
  * `StorageLocationDeclaration` — a single storage-location entry declared by
13988
13988
  * an addon in its `package.json` under `camstack.storageLocations`.
@@ -15338,7 +15338,7 @@ var ViewerFeatureIdSchema = _enum([
15338
15338
  "scenes"
15339
15339
  ]);
15340
15340
  /** Which devices a viewer reaches: `allow` minus `block`. */
15341
- var ViewerDeviceAllowSchema = union([literal("*"), array(number().int().nonnegative())]);
15341
+ var ViewerDeviceAllowSchema = union$1([literal("*"), array(number().int().nonnegative())]);
15342
15342
  /**
15343
15343
  * The viewer grant, stored as ONE row of a user's `scopes`. A principal that
15344
15344
  * holds it is decided by the viewer rules ALONE — any other row it carries is
@@ -15402,8 +15402,8 @@ var MethodAccessSchema = _enum([
15402
15402
  * `access` is REQUIRED with min 1 element. `admin` bypass is the
15403
15403
  * "give me everything" escape; there is no wildcard scope.
15404
15404
  */
15405
- var AllowedProviderSchema = union([literal("*"), array(string())]);
15406
- var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
15405
+ var AllowedProviderSchema = union$1([literal("*"), array(string())]);
15406
+ var AllowedDevicesSchema = record(string(), union$1([literal("*"), array(string())]));
15407
15407
  var CapScopeSchema = _enum(["device", "system"]);
15408
15408
  /**
15409
15409
  * DeviceSelector (scope model v3 — 2026-08-12).
@@ -15509,7 +15509,7 @@ var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("t
15509
15509
  * own rules alone. Kept apart from {@link TokenScope} so every surface that
15510
15510
  * edits capability rows (the scope picker, token minting) keeps its shape.
15511
15511
  */
15512
- var UserScopeSchema = union([ViewerTokenScopeSchema, TokenScopeSchema]);
15512
+ var UserScopeSchema = union$1([ViewerTokenScopeSchema, TokenScopeSchema]);
15513
15513
  object({
15514
15514
  id: string(),
15515
15515
  username: string(),
@@ -15558,7 +15558,7 @@ var AddonRoutesScopeSchema = object({
15558
15558
  access: array(MethodAccessSchema).min(1)
15559
15559
  });
15560
15560
  var PreferencesSelfScopeSchema = object({ type: literal("preferences-self") });
15561
- var CredentialScopeSchema = union([
15561
+ var CredentialScopeSchema = union$1([
15562
15562
  literal("*"),
15563
15563
  ShareViewScopeSchema,
15564
15564
  AddonRoutesScopeSchema,
@@ -15792,7 +15792,7 @@ object({
15792
15792
  /** The app executes it itself, silently (D858). */
15793
15793
  app: AppLoginPathSchema.nullable()
15794
15794
  });
15795
- var ExpressionBindingSourceSchema = union([
15795
+ var ExpressionBindingSourceSchema = union$1([
15796
15796
  object({
15797
15797
  kind: literal("field").optional(),
15798
15798
  sourceKey: string(),
@@ -15801,7 +15801,7 @@ var ExpressionBindingSourceSchema = union([
15801
15801
  }),
15802
15802
  object({
15803
15803
  kind: literal("literal"),
15804
- value: union([
15804
+ value: union$1([
15805
15805
  string(),
15806
15806
  number(),
15807
15807
  boolean(),
@@ -15844,11 +15844,11 @@ var AutomationConditionLeafSchema = object({
15844
15844
  cap: string().min(1),
15845
15845
  fieldPath: string().min(1),
15846
15846
  operator: AutomationConditionOperatorSchema,
15847
- value: union([
15847
+ value: union$1([
15848
15848
  string(),
15849
15849
  number(),
15850
15850
  boolean(),
15851
- array(union([string(), number()]))
15851
+ array(union$1([string(), number()]))
15852
15852
  ])
15853
15853
  });
15854
15854
  /**
@@ -15903,7 +15903,7 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
15903
15903
  cap: string().min(1),
15904
15904
  fieldPath: string().min(1),
15905
15905
  /** Fire when the field takes this value. Omit to fire on any change. */
15906
- becomes: union([
15906
+ becomes: union$1([
15907
15907
  string(),
15908
15908
  number(),
15909
15909
  boolean()
@@ -15957,7 +15957,7 @@ var AutomationActionSchema = discriminatedUnion("kind", [
15957
15957
  * or `{ $var }` for the device that triggered the run. An array is
15958
15958
  * fan-out at codegen — each id gets the same cap.method call.
15959
15959
  */
15960
- deviceId: union([
15960
+ deviceId: union$1([
15961
15961
  number().int(),
15962
15962
  array(number().int()).min(1),
15963
15963
  object({ $var: string().min(1) })
@@ -16125,7 +16125,7 @@ var ConfigSectionWithValuesSchema = object({
16125
16125
  descriptionKey: string().optional(),
16126
16126
  style: _enum(["card", "accordion"]).optional(),
16127
16127
  defaultCollapsed: boolean().optional(),
16128
- columns: union([
16128
+ columns: union$1([
16129
16129
  literal(1),
16130
16130
  literal(2),
16131
16131
  literal(3),
@@ -16943,7 +16943,7 @@ var CollectionColumnSchema = object({
16943
16943
  * collection surface simply cannot express their existing tables — which is
16944
16944
  * why they were still on the uncapped `table*` API.
16945
16945
  */
16946
- defaultValue: union([
16946
+ defaultValue: union$1([
16947
16947
  string(),
16948
16948
  number(),
16949
16949
  boolean()
@@ -17005,7 +17005,7 @@ method(object({
17005
17005
  }), object({
17006
17006
  /** The id the row HAS: assigned by SQLite on an insert, or the existing
17007
17007
  * row's own id on an update. */
17008
- id: union([string(), number()]) }), {
17008
+ id: union$1([string(), number()]) }), {
17009
17009
  kind: "mutation",
17010
17010
  auth: "admin"
17011
17011
  }), method(object({
@@ -17029,7 +17029,7 @@ id: union([string(), number()]) }), {
17029
17029
  * for an absent one, or the ROWID SQLite assigned on an `INTEGER`
17030
17030
  * primary key — which is the only place an auto key is knowable.
17031
17031
  */
17032
- id: union([string(), number()]) }), {
17032
+ id: union$1([string(), number()]) }), {
17033
17033
  kind: "mutation",
17034
17034
  auth: "admin"
17035
17035
  }), method(object({
@@ -19144,7 +19144,7 @@ var CompositionFieldStateSchema = object({
19144
19144
  item: CompositionFieldItemSchema.optional(),
19145
19145
  state: CompositionFieldStateKindSchema,
19146
19146
  /** `null` whenever `state === 'unavailable'`; never a plausible stand-in (D393). */
19147
- value: union([
19147
+ value: union$1([
19148
19148
  string(),
19149
19149
  number(),
19150
19150
  boolean(),
@@ -19306,7 +19306,7 @@ var RawSecretMigrationReportSchema = object({
19306
19306
  });
19307
19307
  /** Where a block runs. The operator chooses — a block driving a device on an
19308
19308
  * agent is the reason placement is not fixed to the hub. */
19309
- var CoreBlockPlacementSchema = union([literal("hub"), string().min(1)]);
19309
+ var CoreBlockPlacementSchema = union$1([literal("hub"), string().min(1)]);
19310
19310
  /** What a block's process is doing. Mirrors the addon runner's own lifecycle so
19311
19311
  * a failing block reads the same way a failing addon does. `degraded` is a
19312
19312
  * composition on an existing device with a field handed back to the native
@@ -19703,7 +19703,7 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
19703
19703
  * for an absent one, or the ROWID SQLite assigned on an `INTEGER`
19704
19704
  * primary key — which is the only place an auto key is knowable.
19705
19705
  */
19706
- id: union([string(), number()]) }), {
19706
+ id: union$1([string(), number()]) }), {
19707
19707
  kind: "mutation",
19708
19708
  auth: "admin"
19709
19709
  }), method(object({
@@ -19711,7 +19711,7 @@ id: union([string(), number()]) }), {
19711
19711
  collection: string(),
19712
19712
  conflict: array(string()).readonly(),
19713
19713
  record: BulkRecordSchema
19714
- }), object({ id: union([string(), number()]) }), {
19714
+ }), object({ id: union$1([string(), number()]) }), {
19715
19715
  kind: "mutation",
19716
19716
  auth: "admin"
19717
19717
  }), method(object({
@@ -19838,20 +19838,20 @@ var RecordViewSchema = object({
19838
19838
  data: RecordBodySchema
19839
19839
  });
19840
19840
  /** Declared index fields only. Anything else does not compile, by design. */
19841
- var RecordWhereSchema = record(RecordFieldPathSchema, union([
19841
+ var RecordWhereSchema = record(RecordFieldPathSchema, union$1([
19842
19842
  string(),
19843
19843
  number(),
19844
19844
  boolean(),
19845
- object({ in: array(union([
19845
+ object({ in: array(union$1([
19846
19846
  string(),
19847
19847
  number(),
19848
19848
  boolean()
19849
19849
  ])).min(1).max(RECORD_BATCH_MAX) }),
19850
19850
  object({
19851
- gt: union([string(), number()]).optional(),
19852
- gte: union([string(), number()]).optional(),
19853
- lt: union([string(), number()]).optional(),
19854
- lte: union([string(), number()]).optional()
19851
+ gt: union$1([string(), number()]).optional(),
19852
+ gte: union$1([string(), number()]).optional(),
19853
+ lt: union$1([string(), number()]).optional(),
19854
+ lte: union$1([string(), number()]).optional()
19855
19855
  }).refine((r) => Object.keys(r).length > 0, { message: "name at least one bound" })
19856
19856
  ]));
19857
19857
  var KindShape = { kind: RecordKindNameSchema };
@@ -20002,7 +20002,7 @@ method(object({ kinds: record(RecordKindNameSchema, RecordKindDeclarationSchema)
20002
20002
  groupBy: RecordFieldPathSchema.optional(),
20003
20003
  where: RecordWhereSchema.optional()
20004
20004
  }), object({ rows: array(object({
20005
- group: union([
20005
+ group: union$1([
20006
20006
  string(),
20007
20007
  number(),
20008
20008
  _null()
@@ -20348,7 +20348,7 @@ var QueryWindowShape = {
20348
20348
  scope: string().optional(),
20349
20349
  from: number(),
20350
20350
  to: number(),
20351
- resolution: union([DeviceHistoryResolutionSchema, literal("auto")]).default("auto"),
20351
+ resolution: union$1([DeviceHistoryResolutionSchema, literal("auto")]).default("auto"),
20352
20352
  maxPoints: number().int().positive().max(5e3).default(1e3)
20353
20353
  };
20354
20354
  var QueryResultSchema = object({
@@ -20357,7 +20357,7 @@ var QueryResultSchema = object({
20357
20357
  truncated: boolean()
20358
20358
  });
20359
20359
  /** One read of a batch: a device's key or a node's key (exactly one subject). */
20360
- var QueryManyItemSchema = union([object({
20360
+ var QueryManyItemSchema = union$1([object({
20361
20361
  deviceId: number().int(),
20362
20362
  key: string(),
20363
20363
  scope: string().optional()
@@ -43309,7 +43309,7 @@ var LlmListModelsResultSchema = discriminatedUnion("ok", [object({
43309
43309
  code: LlmListModelsErrorCodeSchema,
43310
43310
  message: string()
43311
43311
  })]);
43312
- var LlmDefaultSelectorSchema = union([object({ consumer: string() }), object({ purpose: _enum(["text", "vision"]) })]);
43312
+ var LlmDefaultSelectorSchema = union$1([object({ consumer: string() }), object({ purpose: _enum(["text", "vision"]) })]);
43313
43313
  var LlmDefaultSchema = object({
43314
43314
  selector: LlmDefaultSelectorSchema,
43315
43315
  profileId: string()
@@ -43647,7 +43647,7 @@ var LogReadQuerySchema = object({
43647
43647
  */
43648
43648
  fromNodeId: string().min(1).max(200).optional(),
43649
43649
  /** Any other tag, exact match (AND). */
43650
- tags: record(string(), union([string(), number()])).optional(),
43650
+ tags: record(string(), union$1([string(), number()])).optional(),
43651
43651
  /** Severity FLOOR: this level and above. */
43652
43652
  level: LogLevelSchema.optional(),
43653
43653
  /** Case-insensitive substring over message, scope, tag values and meta values. */
@@ -43674,7 +43674,7 @@ var LogReadResultSchema = object({
43674
43674
  level: LogLevelSchema,
43675
43675
  scope: string().optional(),
43676
43676
  message: string(),
43677
- tags: record(string(), union([string(), number()])).optional(),
43677
+ tags: record(string(), union$1([string(), number()])).optional(),
43678
43678
  meta: record(string(), unknown()).optional()
43679
43679
  })),
43680
43680
  /** Pass as `before` for the next older page; null when nothing older exists. */
@@ -48165,7 +48165,7 @@ var OauthIntegrationDescriptorSchema = object({
48165
48165
  * The value is baked into the authorization code at `/authorize` and travels
48166
48166
  * on the tokens, so editing this field changes FUTURE links only.
48167
48167
  */
48168
- refreshTokenTtlSec: union([number().int().positive(), literal("never")]).optional()
48168
+ refreshTokenTtlSec: union$1([number().int().positive(), literal("never")]).optional()
48169
48169
  });
48170
48170
  method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
48171
48171
  /**
@@ -48201,7 +48201,7 @@ var OidcConnectionUpdateInputSchema = object({
48201
48201
  icon: string().optional(),
48202
48202
  issuerUrl: string().optional(),
48203
48203
  clientId: string().optional(),
48204
- clientSecret: union([string(), object({ $secretClear: literal(true) })]).optional(),
48204
+ clientSecret: union$1([string(), object({ $secretClear: literal(true) })]).optional(),
48205
48205
  redirectUri: string().optional(),
48206
48206
  scopes: string().optional(),
48207
48207
  usernameClaim: _enum([
@@ -48368,6 +48368,18 @@ Object.freeze({
48368
48368
  reason: "camera sensor mappings not loaded"
48369
48369
  });
48370
48370
  /**
48371
+ * What the vision model must answer before a package event is emitted
48372
+ * when a packages rule has "Confirm with the LLM" on.
48373
+ *
48374
+ * `package` is the verdict. `confidence` is 0..1. `reason` is the model's
48375
+ * one-line explanation, stored on the event so a drop is not silent.
48376
+ */
48377
+ var PackageLlmVerdictSchema = object({
48378
+ package: boolean(),
48379
+ confidence: number().min(0).max(1),
48380
+ reason: string().min(1)
48381
+ });
48382
+ /**
48371
48383
  * A model choice has a SCOPE, and a scope change has a workflow.
48372
48384
  *
48373
48385
  * ## The two scopes
@@ -48844,7 +48856,7 @@ var TrackFlagsSchema = object({
48844
48856
  * `trained` without a re-fetch. */
48845
48857
  retrainStatus: RetrainStatusSchema
48846
48858
  });
48847
- union([literal(1), literal(2)]);
48859
+ union$1([literal(1), literal(2)]);
48848
48860
  /**
48849
48861
  * WHO decided a label, and when. Carried per tier so a value can be traced to
48850
48862
  * the step and model that produced it — which is what makes the write rule
@@ -49351,6 +49363,21 @@ var ObjectEventSchema = object({
49351
49363
  * pick-up written before D684 — UNKNOWN, never 0 and never `timestamp`.
49352
49364
  */
49353
49365
  observedAt: number().optional(),
49366
+ /**
49367
+ * The delivery was emitted without a vision-model yes (D931).
49368
+ *
49369
+ * Set only when the packages rule asked for an LLM confirm and the model
49370
+ * was unreachable, timed out, or answered something that did not parse.
49371
+ * A notification reads it so it can say "possible" instead of a definite
49372
+ * delivery. Absent on every other row, and on a confirm that said yes.
49373
+ */
49374
+ packagePossible: boolean().optional(),
49375
+ /**
49376
+ * The vision model's verdict, when a packages rule asked for one and the
49377
+ * model answered the schema (D931). A "no" never becomes a row — the
49378
+ * detector drops it and logs the reason. Present on a yes.
49379
+ */
49380
+ packageLlmVerdict: PackageLlmVerdictSchema.optional(),
49354
49381
  /** The parent track's key-event importance [0,1], propagated to every object
49355
49382
  * event of the track (so an event row can be sorted by importance without a
49356
49383
  * track join). Absent on legacy rows / before the track was scored. */
@@ -51731,11 +51758,18 @@ var KeptReasonSchema = _enum(["custom-pin"]);
51731
51758
  * Where a choice's latency came from (D791): this node's own benchmark
51732
51759
  * result, or the hardware-class table. Measured on the node beats the table.
51733
51760
  */
51734
- var LatencySourceSchema = discriminatedUnion("kind", [object({ kind: literal("table") }), object({
51735
- kind: literal("node-benchmark"),
51736
- resultId: string(),
51737
- measuredAtMs: number()
51738
- })]);
51761
+ var LatencySourceSchema = discriminatedUnion("kind", [
51762
+ object({ kind: literal("table") }),
51763
+ object({
51764
+ kind: literal("node-benchmark"),
51765
+ resultId: string(),
51766
+ measuredAtMs: number()
51767
+ }),
51768
+ object({
51769
+ kind: literal("estimated"),
51770
+ estimatedFrom: HardwareClassIdSchema
51771
+ })
51772
+ ]);
51739
51773
  var DeviceModelChoiceSchema = object({
51740
51774
  modelId: string(),
51741
51775
  p50Ms: number(),
@@ -51743,6 +51777,11 @@ var DeviceModelChoiceSchema = object({
51743
51777
  measured: boolean(),
51744
51778
  /** Absent on a plan from before D791 — read as the table. */
51745
51779
  source: LatencySourceSchema.optional(),
51780
+ /**
51781
+ * The measured class this latency was scaled from (D930). Absent when the
51782
+ * cell is the table or a node benchmark.
51783
+ */
51784
+ estimatedFrom: HardwareClassIdSchema.optional(),
51746
51785
  /** Parallel sessions at the camera rate with the headroom kept free: ⌊fps × (1 − headroom) / camera fps⌋. */
51747
51786
  capacitySessions: number(),
51748
51787
  personRecall: number()
@@ -52644,7 +52683,7 @@ var DetailResultSchema = object({
52644
52683
  * stops enriching rather than guessing, which is why addon-pipeline is
52645
52684
  * deployed BEFORE addon-post-analysis.
52646
52685
  */
52647
- labelTier: union([literal(1), literal(2)]).optional(),
52686
+ labelTier: union$1([literal(1), literal(2)]).optional(),
52648
52687
  /** Model that produced `label` — carried into the tier's attribution. */
52649
52688
  labelModelId: string().optional(),
52650
52689
  alignedCropJpeg: string().optional(),
@@ -54844,7 +54883,7 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
54844
54883
  */
54845
54884
  var EmailAddressSchema = email();
54846
54885
  var SendEmailInputSchema = object({
54847
- to: union([EmailAddressSchema, array(EmailAddressSchema).min(1)]),
54886
+ to: union$1([EmailAddressSchema, array(EmailAddressSchema).min(1)]),
54848
54887
  cc: array(EmailAddressSchema).optional(),
54849
54888
  bcc: array(EmailAddressSchema).optional(),
54850
54889
  /** RFC 5322 `From` field. Most relays will reject if the domain
@@ -55065,7 +55104,7 @@ var SsoBridgeClaimsSchema = object({
55065
55104
  * editing a descriptor must not retroactively extend or shorten a link the
55066
55105
  * operator already consented to.
55067
55106
  */
55068
- refreshTtl: union([number().int().positive(), literal("never")]).optional(),
55107
+ refreshTtl: union$1([number().int().positive(), literal("never")]).optional(),
55069
55108
  /**
55070
55109
  * Opaque handle of a hub-side LINK intent (D782). Set by an OIDC callback
55071
55110
  * that was started from a signed-in user's "Link" button: the finish then
@@ -55112,7 +55151,7 @@ method(object({
55112
55151
  * went through the numeric path would fail at mint time and break
55113
55152
  * linking rather than produce an eternal token.
55114
55153
  */
55115
- ttlSec: union([number().int().positive(), literal("never")]).optional()
55154
+ ttlSec: union$1([number().int().positive(), literal("never")]).optional()
55116
55155
  }), object({ token: string() }), { auth: "admin" }), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable(), { auth: "admin" });
55117
55156
  var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
55118
55157
  providerId: string().min(1),
@@ -55680,7 +55719,7 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
55680
55719
  */
55681
55720
  var TurnServerSchema = object({
55682
55721
  /** Single URL or list of URLs (e.g. "turn:turn.example.com:3478?transport=udp"). */
55683
- urls: union([string(), array(string())]),
55722
+ urls: union$1([string(), array(string())]),
55684
55723
  username: string().optional(),
55685
55724
  credential: string().optional()
55686
55725
  });
@@ -55878,7 +55917,7 @@ var VectorMetricSchema = _enum([
55878
55917
  * Callers keep their rich records in their own store and put only the filterable
55879
55918
  * columns here (deviceId, timestamp, className, modelId, …).
55880
55919
  */
55881
- var VectorMetadataSchema = record(string(), union([
55920
+ var VectorMetadataSchema = record(string(), union$1([
55882
55921
  string(),
55883
55922
  number(),
55884
55923
  boolean()
@@ -55891,7 +55930,7 @@ var VectorMetadataSchema = record(string(), union([
55891
55930
  * caller will treat as matches.
55892
55931
  */
55893
55932
  var VectorFilterSchema = object({
55894
- equals: record(string(), union([
55933
+ equals: record(string(), union$1([
55895
55934
  string(),
55896
55935
  number(),
55897
55936
  boolean()
@@ -59055,7 +59094,7 @@ object({
59055
59094
  /** Current value. Numeric kind → number; select/text/datetime → string.
59056
59095
  * Datetime values are ISO 8601 strings (full timestamp, date-only,
59057
59096
  * or time-only — HA accepts all three forms). */
59058
- value: union([number(), string()]),
59097
+ value: union$1([number(), string()]),
59059
59098
  /** Acceptable values when `kind === 'select'`. Empty for other kinds. */
59060
59099
  options: array(string()),
59061
59100
  /** Ms epoch when the slice was last updated. */
@@ -59369,7 +59408,7 @@ object({
59369
59408
  });
59370
59409
  var DeviceIdSchema = number().int().nonnegative();
59371
59410
  /** A value a program option or setting can take. */
59372
- var DishwasherValueSchema = union([
59411
+ var DishwasherValueSchema = union$1([
59373
59412
  boolean(),
59374
59413
  string(),
59375
59414
  number()
@@ -62682,6 +62721,30 @@ includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly(
62682
62721
  kind: "mutation",
62683
62722
  auth: "admin"
62684
62723
  });
62724
+ var CORE_ULTRA_265K_IGPU_CLOCK_MHZ = 2e3;
62725
+ var positiveOrNull = number().nullable().transform((value) => value !== null && Number.isFinite(value) && value > 0 ? value : null);
62726
+ var textOrNull = string().nullable().transform((value) => value !== null && value.trim() !== "" ? value : null);
62727
+ var PrecisionGopsSchema = object({
62728
+ int8: positiveOrNull,
62729
+ fp16: positiveOrNull
62730
+ });
62731
+ /** What one accelerator device reported. Every failed query is null. */
62732
+ var AcceleratorComputeSchema = object({
62733
+ deviceKey: string(),
62734
+ fullDeviceName: textOrNull,
62735
+ executionUnits: positiveOrNull,
62736
+ clockMHz: positiveOrNull,
62737
+ gops: PrecisionGopsSchema.nullable(),
62738
+ architecture: textOrNull,
62739
+ npuName: textOrNull,
62740
+ tops: positiveOrNull,
62741
+ vendor: textOrNull,
62742
+ webgpuArchitecture: textOrNull,
62743
+ webgpuDevice: textOrNull,
62744
+ computeUnits: positiveOrNull
62745
+ });
62746
+ object({ accelerators: array(AcceleratorComputeSchema).nullable() });
62747
+ 64 * CORE_ULTRA_265K_IGPU_CLOCK_MHZ;
62685
62748
  var ModelFormatSchema = _enum(MODEL_FORMATS);
62686
62749
  var HwAccelBackendInputSchema = _enum([
62687
62750
  "videotoolbox",
@@ -62795,7 +62858,12 @@ var HardwareInfoSchema = object({
62795
62858
  availableRAM_MB: number(),
62796
62859
  gpu: GpuInfoSchema.nullable(),
62797
62860
  npu: NpuInfoSchema.nullable(),
62798
- coral: CoralInfoSchema.nullable().optional()
62861
+ coral: CoralInfoSchema.nullable().optional(),
62862
+ /**
62863
+ * Per-accelerator compute (D930). Omitted by a node that has not probed it.
62864
+ * `null` fields inside an entry are a failed query, never a zero.
62865
+ */
62866
+ accelerators: array(AcceleratorComputeSchema).readonly().optional()
62799
62867
  });
62800
62868
  var PlatformScoreSchema = object({
62801
62869
  runtime: literal("python"),
@@ -65691,7 +65759,20 @@ var ZoneRuleSchema = object({
65691
65759
  * Defaults to `true` so operators creating a rule via the UI
65692
65760
  * see it active immediately.
65693
65761
  */
65694
- enabled: boolean().default(true)
65762
+ enabled: boolean().default(true),
65763
+ /**
65764
+ * Package stage only. When true, a package candidate may open from a
65765
+ * scene difference in this rule's zone after a person leaves. The
65766
+ * package model, when it boxes something overlapping, is extra evidence
65767
+ * and is not the gate. Absent or false keeps today's model path.
65768
+ */
65769
+ detectBySceneDifference: boolean().optional(),
65770
+ /**
65771
+ * Package stage only. When true, a promoted package is shown to the
65772
+ * `llm` cap before the delivery event is emitted. Absent or false emits
65773
+ * as today. Usable with or without `detectBySceneDifference`.
65774
+ */
65775
+ confirmWithLlm: boolean().optional()
65695
65776
  });
65696
65777
  array(ZoneRuleSchema).readonly();
65697
65778
  object({
@@ -66503,8 +66584,8 @@ var UserSummarySchema = object({
66503
66584
  id: string(),
66504
66585
  username: string(),
66505
66586
  isAdmin: boolean().default(false),
66506
- allowedProviders: union([array(string()), literal("*")]),
66507
- allowedDevices: record(string(), union([array(string()), literal("*")])),
66587
+ allowedProviders: union$1([array(string()), literal("*")]),
66588
+ allowedDevices: record(string(), union$1([array(string()), literal("*")])),
66508
66589
  /** Capability rows, or ONE viewer row (D921) — see `UserScopeSchema`. */
66509
66590
  scopes: array(UserScopeSchema).default([]),
66510
66591
  /**
@@ -66521,15 +66602,15 @@ var CreateUserInputSchema = object({
66521
66602
  username: string(),
66522
66603
  password: string().min(6),
66523
66604
  isAdmin: boolean().default(false),
66524
- allowedProviders: union([array(string()), literal("*")]).optional(),
66525
- allowedDevices: record(string(), union([array(string()), literal("*")])).optional(),
66605
+ allowedProviders: union$1([array(string()), literal("*")]).optional(),
66606
+ allowedDevices: record(string(), union$1([array(string()), literal("*")])).optional(),
66526
66607
  scopes: array(UserScopeSchema).optional()
66527
66608
  });
66528
66609
  var UpdateUserInputSchema = object({
66529
66610
  id: string(),
66530
66611
  isAdmin: boolean().optional(),
66531
- allowedProviders: union([array(string()), literal("*")]).optional(),
66532
- allowedDevices: record(string(), union([array(string()), literal("*")])).optional(),
66612
+ allowedProviders: union$1([array(string()), literal("*")]).optional(),
66613
+ allowedDevices: record(string(), union$1([array(string()), literal("*")])).optional(),
66533
66614
  scopes: array(UserScopeSchema).optional()
66534
66615
  });
66535
66616
  var OauthSessionSummarySchema = object({
@@ -66648,7 +66729,7 @@ method(_void(), array(UserSummarySchema), { auth: "admin" }), method(CreateUserI
66648
66729
  * From `OauthIntegrationDescriptor.refreshTokenTtlSec`. Baked into the
66649
66730
  * code so the link carries its own lifetime; omit for the 30-day
66650
66731
  * default. */
66651
- refreshTtlSec: union([number().int().positive(), literal("never")]).optional()
66732
+ refreshTtlSec: union$1([number().int().positive(), literal("never")]).optional()
66652
66733
  }), object({ code: string() }), {
66653
66734
  kind: "mutation",
66654
66735
  access: "create"
@@ -67138,7 +67219,7 @@ var TrainingProviderUpdateEntryInputSchema = object({
67138
67219
  entryId: string().min(1),
67139
67220
  name: string().min(1),
67140
67221
  tokenId: string().optional(),
67141
- tokenSecret: union([string(), object({ $secretClear: literal(true) })]).optional(),
67222
+ tokenSecret: union$1([string(), object({ $secretClear: literal(true) })]).optional(),
67142
67223
  environment: string().optional(),
67143
67224
  gpuType: string().optional(),
67144
67225
  region: string().optional(),
@@ -85223,6 +85304,430 @@ function buildBenchStepTree(schema, rootModelId, enabledStepIds) {
85223
85304
  ]
85224
85305
  }];
85225
85306
  }
85307
+ /** The object-detection step id — the first-level detector both recipes run. */
85308
+ var OBJECT_DETECTION_STEP_ID = "object-detection";
85309
+ /** Motion regions older than this describe a different frame. */
85310
+ var RECOVERY_MOTION_MAX_AGE_MS = 2e3;
85311
+ /** Area of a normalised rectangle, never negative. Shared with the D396 recipe. */
85312
+ function normalizedArea(r) {
85313
+ return Math.max(0, r.w) * Math.max(0, r.h);
85314
+ }
85315
+ var area = normalizedArea;
85316
+ function intersection(a, b) {
85317
+ return Math.max(0, Math.min(a.x + a.w, b.x + b.w) - Math.max(a.x, b.x)) * Math.max(0, Math.min(a.y + a.h, b.y + b.h) - Math.max(a.y, b.y));
85318
+ }
85319
+ /**
85320
+ * Share of `region` not covered by any detection box. The sum of pairwise
85321
+ * intersections over-counts where detections overlap each other, which only
85322
+ * makes the pass MORE conservative; clipped at 1.
85323
+ */
85324
+ function uncoveredFraction(region, detections) {
85325
+ const regionArea = area(region);
85326
+ if (regionArea <= 0) return 0;
85327
+ let covered = 0;
85328
+ for (const detection of detections) covered += intersection(region, detection);
85329
+ return 1 - Math.min(1, covered / regionArea);
85330
+ }
85331
+ /**
85332
+ * THE square-placement rule of this pass — one owner, shared by both recipes.
85333
+ *
85334
+ * A crop is a square of `side` native pixels centred where it can be and slid
85335
+ * inside the frame where it cannot. Both region rules place their squares
85336
+ * through this function so a crop means the same rectangle whichever rule
85337
+ * chose the region.
85338
+ */
85339
+ function squareAround(centreX, centreY, side, native) {
85340
+ return {
85341
+ left: Math.round(Math.max(0, Math.min(native.width - side, centreX - side / 2))),
85342
+ top: Math.round(Math.max(0, Math.min(native.height - side, centreY - side / 2))),
85343
+ side
85344
+ };
85345
+ }
85346
+ function contains(crop, x, y) {
85347
+ return x >= crop.left && x <= crop.left + crop.side && y >= crop.top && y <= crop.top + crop.side;
85348
+ }
85349
+ /**
85350
+ * Squares for one region: one when it fits the largest side, otherwise up to
85351
+ * two tiles along its longer axis (a person-sized blob with something small
85352
+ * at either end). The padded region decides the side, never the frame.
85353
+ */
85354
+ function cropsForRegion(region, native) {
85355
+ const widthPx = region.w * native.width;
85356
+ const heightPx = region.h * native.height;
85357
+ const longest = Math.max(widthPx, heightPx);
85358
+ const padded = longest * 1.5;
85359
+ const side = Math.round(Math.min(Math.max(padded, 640), 800));
85360
+ const centreX = (region.x + region.w / 2) * native.width;
85361
+ const centreY = (region.y + region.h / 2) * native.height;
85362
+ if (padded <= 800) return [squareAround(centreX, centreY, side, native)];
85363
+ const alongX = widthPx >= heightPx;
85364
+ const quarter = longest / 4;
85365
+ return [squareAround(alongX ? centreX - quarter : centreX, alongX ? centreY : centreY - quarter, side, native), squareAround(alongX ? centreX + quarter : centreX, alongX ? centreY : centreY + quarter, side, native)];
85366
+ }
85367
+ /**
85368
+ * The ONE place the two region gates are decided. `planRecoveryCrops` filters
85369
+ * on it and {@link countRecoverySkips} counts it, so the report can never
85370
+ * describe a gate the planner does not actually apply.
85371
+ */
85372
+ function classifyRegion(region, uncovered) {
85373
+ if (area(region) > .25) return "too-large";
85374
+ if (uncovered < .35) return "covered";
85375
+ return "eligible";
85376
+ }
85377
+ /**
85378
+ * WHY A FRAME PLANNED NO CROP, AS A NUMBER (D378).
85379
+ *
85380
+ * The pass report counts the passes that RAN, so "the second pass stops running
85381
+ * when a person is beside the dog" — a plausible reading of
85382
+ * {@link RECOVERY_MAX_REGION_FRAME_FRACTION} and
85383
+ * {@link RECOVERY_MIN_UNCOVERED_FRACTION}, and a live hypothesis on camera 615
85384
+ * — could not be confirmed or killed from the logs at all. This classifies the
85385
+ * regions a frame offered, against the same predicate the planner filters on.
85386
+ */
85387
+ function countRecoverySkips(input) {
85388
+ let tooLarge = 0;
85389
+ let covered = 0;
85390
+ let eligible = 0;
85391
+ for (const region of input.regions) switch (classifyRegion(region, uncoveredFraction(region, input.detections))) {
85392
+ case "too-large":
85393
+ tooLarge += 1;
85394
+ break;
85395
+ case "covered":
85396
+ covered += 1;
85397
+ break;
85398
+ case "eligible":
85399
+ eligible += 1;
85400
+ break;
85401
+ }
85402
+ return {
85403
+ tooLarge,
85404
+ covered,
85405
+ eligible
85406
+ };
85407
+ }
85408
+ function planRecoveryCrops(input) {
85409
+ const { native } = input;
85410
+ if (Math.min(native.width, native.height) < 640) return [];
85411
+ const ranked = input.regions.map((region) => ({
85412
+ region,
85413
+ uncovered: uncoveredFraction(region, input.detections)
85414
+ })).filter(({ region, uncovered }) => classifyRegion(region, uncovered) === "eligible").toSorted((a, b) => area(b.region) * b.uncovered - area(a.region) * a.uncovered);
85415
+ const planned = [];
85416
+ for (const { region } of ranked) for (const crop of cropsForRegion(region, native)) {
85417
+ if (planned.length >= 2) return planned;
85418
+ const centreX = crop.left + crop.side / 2;
85419
+ const centreY = crop.top + crop.side / 2;
85420
+ if (planned.some((existing) => contains(existing, centreX, centreY))) continue;
85421
+ planned.push(crop);
85422
+ }
85423
+ return planned;
85424
+ }
85425
+ /**
85426
+ * Boxes the executor produced on the crop VIEW → the result's own space.
85427
+ *
85428
+ * Three spaces meet here: the view the model saw (`view`, ≤640 px), the native
85429
+ * square it was cut from (`crop`, native px) and the space the full-frame
85430
+ * result reports in (`result`, native px on the `frameRef` path, the inline
85431
+ * ≤640 view otherwise). A box touching the view edge was clipped by the crop
85432
+ * and is dropped: half a person is not a person, and the tracker would take
85433
+ * the half as the whole.
85434
+ */
85435
+ function remapCropDetections(detections, crop, view, result, native) {
85436
+ const viewToNative = crop.side / Math.max(1, view.width);
85437
+ const nativeToResultX = result.width / Math.max(1, native.width);
85438
+ const nativeToResultY = result.height / Math.max(1, native.height);
85439
+ const out = [];
85440
+ for (const detection of detections) {
85441
+ const { x, y, width, height } = detection.bbox;
85442
+ if (x <= 2 || y <= 2 || x + width >= view.width - 2 || y + height >= view.height - 2) continue;
85443
+ out.push({
85444
+ ...detection,
85445
+ nativeRecovery: {
85446
+ score: detection.score,
85447
+ cropSidePx: crop.side,
85448
+ viewPx: view.width
85449
+ },
85450
+ bbox: {
85451
+ x: (crop.left + x * viewToNative) * nativeToResultX,
85452
+ y: (crop.top + y * viewToNative) * nativeToResultY,
85453
+ width: width * viewToNative * nativeToResultX,
85454
+ height: height * viewToNative * nativeToResultY
85455
+ }
85456
+ });
85457
+ }
85458
+ return out;
85459
+ }
85460
+ function boxIntersection(a, b) {
85461
+ return Math.max(0, Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x)) * Math.max(0, Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y));
85462
+ }
85463
+ function sameSubject(recovered, existing) {
85464
+ const inter = boxIntersection(recovered, existing);
85465
+ if (inter <= 0) return false;
85466
+ const areaR = recovered.width * recovered.height;
85467
+ const union = areaR + existing.width * existing.height - inter;
85468
+ if (union > 0 && inter / union >= .5) return true;
85469
+ return areaR > 0 && inter / areaR >= .7;
85470
+ }
85471
+ /**
85472
+ * Class-aware NMS of the recovered boxes against the full-frame ones. Every
85473
+ * full-frame box is kept as it is — the second pass only ADDS. A recovered box
85474
+ * is added when no full-frame box of the same macro class is the same subject
85475
+ * (`RECOVERY_FUSE_IOU`) or contains it (`RECOVERY_FUSE_CONTAINMENT`). Ids are
85476
+ * re-minted so a crop's `d0` cannot collide with the frame's.
85477
+ */
85478
+ function fuseRecoveredDetections(full, recovered) {
85479
+ const accepted = [];
85480
+ for (const [index, candidate] of recovered.entries()) {
85481
+ if ([...full, ...accepted].some((existing) => existing.macroClass === candidate.macroClass && sameSubject(candidate.bbox, existing.bbox))) continue;
85482
+ accepted.push({
85483
+ ...candidate,
85484
+ id: `recovered-${index}-${candidate.id}`
85485
+ });
85486
+ }
85487
+ return [...full, ...accepted];
85488
+ }
85489
+ /**
85490
+ * The step the second pass runs: the enabled first-level object detector of
85491
+ * the camera's own tree, with its own model and no children. Same model as
85492
+ * the full pass so a recovered box is comparable to a full-frame one.
85493
+ */
85494
+ function selectRecoveryStep(steps) {
85495
+ const detector = steps.find((step) => step.addonId === "object-detection" && step.enabled !== false);
85496
+ if (!detector) return null;
85497
+ return {
85498
+ addonId: detector.addonId,
85499
+ ...detector.modelId !== void 0 ? { modelId: detector.modelId } : {},
85500
+ enabled: true
85501
+ };
85502
+ }
85503
+ /**
85504
+ * Read the opt-in. Anything that is not the boolean `true` is the shipped
85505
+ * rule: a stored string or a number is a mis-write (this repo has shipped a
85506
+ * setting written into the wrong sqlite table before), and a mis-write must
85507
+ * not silently change what a camera detects on.
85508
+ */
85509
+ function readRecoveryRecipe(settings) {
85510
+ return settings?.["motionCropDispatch"] === true ? "motion-crop" : "uncovered-region";
85511
+ }
85512
+ /**
85513
+ * The recipe this camera's resolved step tree selects.
85514
+ *
85515
+ * Reads the ENABLED first-level detector's own settings, which is where
85516
+ * `pipelineOrchestrator.setCameraStepSettings` writes a per-camera value. A
85517
+ * tree with no enabled detector runs no second pass at all, so the answer
85518
+ * there is the shipped rule and it is never used.
85519
+ */
85520
+ function readRecoveryRecipeForSteps(steps) {
85521
+ return readRecoveryRecipe(steps.find((step) => step.addonId === "object-detection" && step.enabled !== false)?.settings);
85522
+ }
85523
+ /**
85524
+ * A motion box is grown ×1.5 about its centre before anything else.
85525
+ *
85526
+ * The motion step's box is the CHANGED pixels, which on a moving animal is the
85527
+ * leading edge plus its shadow, not the animal. The detector needs the subject
85528
+ * and the context it was trained on.
85529
+ */
85530
+ var MOTION_CROP_MOTION_PAD = 1.5;
85531
+ /**
85532
+ * An intersecting tracked box is grown ×1.25 — less, because it is already a
85533
+ * subject rectangle rather than a change mask.
85534
+ */
85535
+ var MOTION_CROP_TRACKED_PAD = 1.25;
85536
+ /**
85537
+ * The floor a `person` or `vehicle` box must clear when it came out of a CROP
85538
+ * rather than out of the full frame.
85539
+ *
85540
+ * Collected, not assumed: 3 085 quiet frames produced 27 motion-positive
85541
+ * frames and 28 crop invocations. The full-frame arm produced 0 boxes above
85542
+ * any gate; the crop arm produced 3, of which two are genuine false positives,
85543
+ * and both are night-IR corners upscaled into a 320 model — `vehicle 0.749`
85544
+ * and `person 0.701`. Zero false `animal` at any gate, which is why `animal`
85545
+ * is deliberately absent from this table: the class the pass exists to recover
85546
+ * is the class that never produced a false positive, and gating it would cost
85547
+ * the whole measured gain.
85548
+ *
85549
+ * This is a CROP-ONLY floor, applied on top of the per-macro floors the
85550
+ * executor already enforced. A full-frame `person` at 0.70 is untouched — the
85551
+ * full-frame arm is the one arm with ground truth behind its floor
85552
+ * (`minConfidencePerson` 0.65, raised on 2026-07-30 from a measured
85553
+ * distribution).
85554
+ *
85555
+ * The cost of this floor is stated rather than assumed away: it can also
85556
+ * remove a real distant person or vehicle recovered from a crop at 0.70–0.80.
85557
+ * Nothing in the quiet-footage sample measures that, so the floor is a
85558
+ * constant here rather than a slider, and the runner reports every drop it
85559
+ * makes per camera.
85560
+ */
85561
+ var CROP_CLASS_FLOORS = Object.freeze({
85562
+ person: .8,
85563
+ vehicle: .8
85564
+ });
85565
+ /**
85566
+ * Apply the crop-only class floors, REPORTING every drop.
85567
+ *
85568
+ * A class with no entry in {@link CROP_CLASS_FLOORS} is kept: a floor this
85569
+ * table cannot express is not a floor of zero, and silently dropping a class
85570
+ * nobody measured is the exact shape of silent drop this repo has paid for
85571
+ * three separate times.
85572
+ */
85573
+ function applyCropClassFloors(detections) {
85574
+ const kept = [];
85575
+ const dropped = [];
85576
+ for (const detection of detections) {
85577
+ const floor = CROP_CLASS_FLOORS[detection.macroClass];
85578
+ if (floor !== void 0 && detection.score < floor) {
85579
+ dropped.push({
85580
+ macroClass: detection.macroClass,
85581
+ score: detection.score,
85582
+ floor
85583
+ });
85584
+ continue;
85585
+ }
85586
+ kept.push(detection);
85587
+ }
85588
+ return {
85589
+ kept,
85590
+ dropped
85591
+ };
85592
+ }
85593
+ function scaleAbout(rect, factor) {
85594
+ const w = rect.w * factor;
85595
+ const h = rect.h * factor;
85596
+ return {
85597
+ x: rect.x + rect.w / 2 - w / 2,
85598
+ y: rect.y + rect.h / 2 - h / 2,
85599
+ w,
85600
+ h
85601
+ };
85602
+ }
85603
+ function overlaps(a, b) {
85604
+ return Math.min(a.x + a.w, b.x + b.w) > Math.max(a.x, b.x) && Math.min(a.y + a.h, b.y + b.h) > Math.max(a.y, b.y);
85605
+ }
85606
+ function union(a, b) {
85607
+ const x = Math.min(a.x, b.x);
85608
+ const y = Math.min(a.y, b.y);
85609
+ return {
85610
+ x,
85611
+ y,
85612
+ w: Math.max(a.x + a.w, b.x + b.w) - x,
85613
+ h: Math.max(a.y + a.h, b.y + b.h) - y
85614
+ };
85615
+ }
85616
+ function clampToFrame(rect) {
85617
+ const x = Math.max(0, rect.x);
85618
+ const y = Math.max(0, rect.y);
85619
+ return {
85620
+ x,
85621
+ y,
85622
+ w: Math.min(1, rect.x + rect.w) - x,
85623
+ h: Math.min(1, rect.y + rect.h) - y
85624
+ };
85625
+ }
85626
+ /**
85627
+ * Merge transitively every seed whose padded box overlaps another, and answer
85628
+ * the union rectangles.
85629
+ *
85630
+ * Transitive on purpose: a person, the dog beside them and the dog's shadow
85631
+ * are three components of one subject, and cutting three crops for them spends
85632
+ * two inferences on pixels the first one already showed the detector.
85633
+ */
85634
+ function mergeSeeds(seeds) {
85635
+ const merged = [];
85636
+ for (const seed of seeds) {
85637
+ let current = seed;
85638
+ let changed = true;
85639
+ while (changed) {
85640
+ changed = false;
85641
+ for (let i = merged.length - 1; i >= 0; i -= 1) {
85642
+ const other = merged[i];
85643
+ if (other === void 0 || !overlaps(current, other)) continue;
85644
+ current = union(current, other);
85645
+ merged.splice(i, 1);
85646
+ changed = true;
85647
+ }
85648
+ }
85649
+ merged.push(current);
85650
+ }
85651
+ return merged;
85652
+ }
85653
+ /**
85654
+ * The regions the measured recipe judges this frame on.
85655
+ *
85656
+ * Motion boxes padded {@link MOTION_CROP_MOTION_PAD}; every full-frame box
85657
+ * that intersects a RAW motion box padded {@link MOTION_CROP_TRACKED_PAD} and
85658
+ * added as a seed of its own; everything that overlaps merged; the result
85659
+ * clamped to the frame, ordered largest first and capped at
85660
+ * {@link MOTION_CROP_MAX_REGIONS}.
85661
+ *
85662
+ * There is deliberately no coverage gate and no size gate here. Those belong
85663
+ * to the shipped rule, and they are what makes it a rule about a subject
85664
+ * BESIDE a detected one; the measurement says the pass is worth running on the
85665
+ * detected subject's own region too.
85666
+ */
85667
+ function planMotionCropRegions(input) {
85668
+ if (input.regions.length === 0) return [];
85669
+ const seeds = input.regions.map((r) => scaleAbout(r, MOTION_CROP_MOTION_PAD));
85670
+ for (const detection of input.detections) {
85671
+ if (!input.regions.some((region) => overlaps(detection, region))) continue;
85672
+ seeds.push(scaleAbout(detection, MOTION_CROP_TRACKED_PAD));
85673
+ }
85674
+ return mergeSeeds(seeds).map(clampToFrame).filter((r) => r.w > 0 && r.h > 0).toSorted((a, b) => normalizedArea(b) - normalizedArea(a)).slice(0, 3);
85675
+ }
85676
+ /**
85677
+ * The square the detector is fed for one measured region.
85678
+ *
85679
+ * "Grow to the detector's input aspect ratio without ever shrinking" is a
85680
+ * SQUARE here, and that is a fact about the production detector rather than a
85681
+ * simplification: `yolov9m-320-int8` takes 320×320, as does every hardware
85682
+ * default in the catalog.
85683
+ *
85684
+ * The side is the region's longer side, floored at
85685
+ * {@link RECOVERY_CROP_MIN_SIDE_PX} — the growth is what magnifies the subject
85686
+ * — and ceilinged at {@link RECOVERY_CROP_MAX_SIDE_PX}. The ceiling is NOT a
85687
+ * simplification either: at 1024 px the same 320 model scored the reference
85688
+ * dog 0.52–0.76, and at 1280 px lost it entirely
85689
+ * (`docs/benchmarks/pipeline-frame-model-eval.md`, case `miss-dog-615`). A
85690
+ * region longer than the ceiling is TILED along its longer axis, never zoomed
85691
+ * out.
85692
+ */
85693
+ function motionCropsForRegion(region, native) {
85694
+ const widthPx = region.w * native.width;
85695
+ const heightPx = region.h * native.height;
85696
+ const longest = Math.max(widthPx, heightPx);
85697
+ const side = Math.round(Math.min(Math.max(longest, 640), 800));
85698
+ const centreX = (region.x + region.w / 2) * native.width;
85699
+ const centreY = (region.y + region.h / 2) * native.height;
85700
+ if (longest <= 800) return [squareAround(centreX, centreY, side, native)];
85701
+ const alongX = widthPx >= heightPx;
85702
+ const quarter = longest / 4;
85703
+ return [squareAround(alongX ? centreX - quarter : centreX, alongX ? centreY : centreY - quarter, side, native), squareAround(alongX ? centreX + quarter : centreX, alongX ? centreY : centreY + quarter, side, native)];
85704
+ }
85705
+ /** `true` when `inner` lies entirely inside `outer`. */
85706
+ function cropContains(outer, inner) {
85707
+ return inner.left >= outer.left && inner.top >= outer.top && inner.left + inner.side <= outer.left + outer.side && inner.top + inner.side <= outer.top + outer.side;
85708
+ }
85709
+ /**
85710
+ * The crops the measured recipe runs on this frame.
85711
+ *
85712
+ * Dedupe is by CONTAINMENT only. Two squares that merely overlap both run: the
85713
+ * pixels the second adds are pixels no other crop shows the detector, and the
85714
+ * fusion step downstream already refuses a duplicate BOX. Dropping a crop
85715
+ * because it overlapped would drop the subject sitting at its edge, which is
85716
+ * the whole population this pass exists for.
85717
+ *
85718
+ * A frame that cannot hold the smallest crop yields nothing: it is already
85719
+ * seen at scale.
85720
+ */
85721
+ function planMotionCrops(input) {
85722
+ const { native } = input;
85723
+ if (Math.min(native.width, native.height) < 640) return [];
85724
+ const planned = [];
85725
+ for (const region of planMotionCropRegions(input)) for (const crop of motionCropsForRegion(region, native)) {
85726
+ if (planned.some((existing) => cropContains(existing, crop))) continue;
85727
+ planned.push(crop);
85728
+ }
85729
+ return planned;
85730
+ }
85226
85731
  /** Side of the square, as a multiple of the face box's longer side. */
85227
85732
  var ROOT_FACE_REFINE_SCALE = 1.6;
85228
85733
  /**
@@ -86778,6 +87283,12 @@ Object.defineProperty(exports, "NodeBenchRunnerInputSchema", {
86778
87283
  return NodeBenchRunnerInputSchema;
86779
87284
  }
86780
87285
  });
87286
+ Object.defineProperty(exports, "OBJECT_DETECTION_STEP_ID", {
87287
+ enumerable: true,
87288
+ get: function() {
87289
+ return OBJECT_DETECTION_STEP_ID;
87290
+ }
87291
+ });
86781
87292
  Object.defineProperty(exports, "OpsLogEntrySchema", {
86782
87293
  enumerable: true,
86783
87294
  get: function() {
@@ -86826,6 +87337,12 @@ Object.defineProperty(exports, "RECORDING_SOURCE_CAMSTACK_ADDON", {
86826
87337
  return RECORDING_SOURCE_CAMSTACK_ADDON;
86827
87338
  }
86828
87339
  });
87340
+ Object.defineProperty(exports, "RECOVERY_MOTION_MAX_AGE_MS", {
87341
+ enumerable: true,
87342
+ get: function() {
87343
+ return RECOVERY_MOTION_MAX_AGE_MS;
87344
+ }
87345
+ });
86829
87346
  Object.defineProperty(exports, "ROOT_SUPPLIABLE_STEP_IDS", {
86830
87347
  enumerable: true,
86831
87348
  get: function() {
@@ -86886,6 +87403,12 @@ Object.defineProperty(exports, "allowedSuppliedEffect", {
86886
87403
  return allowedSuppliedEffect;
86887
87404
  }
86888
87405
  });
87406
+ Object.defineProperty(exports, "applyCropClassFloors", {
87407
+ enumerable: true,
87408
+ get: function() {
87409
+ return applyCropClassFloors;
87410
+ }
87411
+ });
86889
87412
  Object.defineProperty(exports, "applyModelBoundSettings", {
86890
87413
  enumerable: true,
86891
87414
  get: function() {
@@ -86988,6 +87511,12 @@ Object.defineProperty(exports, "containingParentIndex", {
86988
87511
  return containingParentIndex;
86989
87512
  }
86990
87513
  });
87514
+ Object.defineProperty(exports, "countRecoverySkips", {
87515
+ enumerable: true,
87516
+ get: function() {
87517
+ return countRecoverySkips;
87518
+ }
87519
+ });
86991
87520
  Object.defineProperty(exports, "createEvent", {
86992
87521
  enumerable: true,
86993
87522
  get: function() {
@@ -87168,6 +87697,12 @@ Object.defineProperty(exports, "ffmpegRate", {
87168
87697
  return ffmpegRate;
87169
87698
  }
87170
87699
  });
87700
+ Object.defineProperty(exports, "fuseRecoveredDetections", {
87701
+ enumerable: true,
87702
+ get: function() {
87703
+ return fuseRecoveredDetections;
87704
+ }
87705
+ });
87171
87706
  Object.defineProperty(exports, "gbToBytes", {
87172
87707
  enumerable: true,
87173
87708
  get: function() {
@@ -87396,6 +87931,18 @@ Object.defineProperty(exports, "pipelineRunnerCapability", {
87396
87931
  return pipelineRunnerCapability;
87397
87932
  }
87398
87933
  });
87934
+ Object.defineProperty(exports, "planMotionCrops", {
87935
+ enumerable: true,
87936
+ get: function() {
87937
+ return planMotionCrops;
87938
+ }
87939
+ });
87940
+ Object.defineProperty(exports, "planRecoveryCrops", {
87941
+ enumerable: true,
87942
+ get: function() {
87943
+ return planRecoveryCrops;
87944
+ }
87945
+ });
87399
87946
  Object.defineProperty(exports, "preprocess", {
87400
87947
  enumerable: true,
87401
87948
  get: function() {
@@ -87408,6 +87955,12 @@ Object.defineProperty(exports, "rankStreams", {
87408
87955
  return rankStreams;
87409
87956
  }
87410
87957
  });
87958
+ Object.defineProperty(exports, "readRecoveryRecipeForSteps", {
87959
+ enumerable: true,
87960
+ get: function() {
87961
+ return readRecoveryRecipeForSteps;
87962
+ }
87963
+ });
87411
87964
  Object.defineProperty(exports, "record", {
87412
87965
  enumerable: true,
87413
87966
  get: function() {
@@ -87438,6 +87991,12 @@ Object.defineProperty(exports, "recordingSignalCapability", {
87438
87991
  return recordingSignalCapability;
87439
87992
  }
87440
87993
  });
87994
+ Object.defineProperty(exports, "remapCropDetections", {
87995
+ enumerable: true,
87996
+ get: function() {
87997
+ return remapCropDetections;
87998
+ }
87999
+ });
87441
88000
  Object.defineProperty(exports, "resolveClusterStepModelId", {
87442
88001
  enumerable: true,
87443
88002
  get: function() {
@@ -87504,6 +88063,12 @@ Object.defineProperty(exports, "selectAssignedProfileSlots", {
87504
88063
  return selectAssignedProfileSlots;
87505
88064
  }
87506
88065
  });
88066
+ Object.defineProperty(exports, "selectRecoveryStep", {
88067
+ enumerable: true,
88068
+ get: function() {
88069
+ return selectRecoveryStep;
88070
+ }
88071
+ });
87507
88072
  Object.defineProperty(exports, "sleep", {
87508
88073
  enumerable: true,
87509
88074
  get: function() {
@@ -87561,7 +88126,7 @@ Object.defineProperty(exports, "supportedRuntimes", {
87561
88126
  Object.defineProperty(exports, "union", {
87562
88127
  enumerable: true,
87563
88128
  get: function() {
87564
- return union;
88129
+ return union$1;
87565
88130
  }
87566
88131
  });
87567
88132
  Object.defineProperty(exports, "userError", {