@camstack/addon-post-analysis 1.2.6 → 1.2.7

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.
@@ -6837,7 +6837,6 @@ object({ deviceId: number() }), object({ deviceId: number() }), object({
6837
6837
  patch: record(string(), unknown())
6838
6838
  }), object({ success: literal(true) });
6839
6839
  object({ deviceId: number() }), unknown().nullable();
6840
- /** Shorthand to define a method schema */
6841
6840
  function method(input, output, options) {
6842
6841
  return {
6843
6842
  input,
@@ -6845,6 +6844,7 @@ function method(input, output, options) {
6845
6844
  kind: options?.kind ?? "query",
6846
6845
  auth: options?.auth ?? "protected",
6847
6846
  ...options?.access !== void 0 ? { access: options.access } : {},
6847
+ ...options?.caller !== void 0 ? { caller: options.caller } : {},
6848
6848
  timeoutMs: options?.timeoutMs
6849
6849
  };
6850
6850
  }
@@ -16007,6 +16007,348 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
16007
16007
  enabled: boolean()
16008
16008
  }), _void(), { kind: "mutation" });
16009
16009
  /**
16010
+ * notification-rules — the Notification Center rule surface (P1 core).
16011
+ *
16012
+ * Spec: `docs/superpowers/specs/2026-07-22-notification-center-requirements.md`
16013
+ * (operator decisions D-1/D-2/D-3 are binding):
16014
+ *
16015
+ * - D-2: rule EVALUATION lives in `addon-post-analysis` (the
16016
+ * `notification-center` module), hooked on the durable persistence
16017
+ * moments (object-event insert, TrackCloser.closeExpired) with a
16018
+ * persisted outbox + retry — never the lossy telemetry bus (D8).
16019
+ * - D-3: urgency belongs to the RULE. `delivery: 'immediate'` fires on the
16020
+ * FIRST persisted detection matching the conditions (per-track dedup,
16021
+ * `maxPerTrack` fixed at 1 — see {@link NC_MAX_PER_TRACK_IMMEDIATE});
16022
+ * `delivery: 'track-end'` evaluates the finalized track record at close.
16023
+ * - DISPATCH stays behind `notification-output` (rules reference targets
16024
+ * by id; per-backend params are a passthrough blob capped by the
16025
+ * target kind's own caps/degrade engine).
16026
+ *
16027
+ * P1 scope: admin-authored rules only (`createdBy` stamped from the
16028
+ * server-injected caller identity — the first `caller: 'required'`
16029
+ * adopter). The P1 condition subset is: devices, classes(+exclude),
16030
+ * minConfidence, admin zones (any/all + exclude), weekly schedule
16031
+ * windows, and the optional label/identity/plate matchers. User rules,
16032
+ * private zones, per-recipient fan-out and the wider condition table are
16033
+ * P2+ (see spec §7).
16034
+ *
16035
+ * All schemas here are the single source of truth — `NcRule` etc. are
16036
+ * `z.infer` exports; no duplicate interfaces (the advanced-notifier
16037
+ * schema/interface drift is explicitly not repeated).
16038
+ */
16039
+ /** D-3: the urgency of a rule — which persistence moment evaluates it. */
16040
+ var NcDeliverySchema = _enum(["immediate", "track-end"]);
16041
+ /** Weekly schedule — OR of windows; absence on the rule = always active. */
16042
+ var NcScheduleSchema = object({
16043
+ windows: array(object({
16044
+ /** Days of week the window STARTS on (0 = Sunday … 6 = Saturday). */
16045
+ days: array(number().int().min(0).max(6)).min(1),
16046
+ startMinute: number().int().min(0).max(1439),
16047
+ endMinute: number().int().min(0).max(1439)
16048
+ })).min(1),
16049
+ /** IANA timezone; default = hub host timezone. */
16050
+ timezone: string().optional(),
16051
+ /** Active OUTSIDE the windows (e.g. "only outside business hours"). */
16052
+ invert: boolean().optional()
16053
+ });
16054
+ /** Fuzzy plate matcher — OCR noise makes exact match useless (spec row 12/13). */
16055
+ var NcPlateMatcherSchema = object({
16056
+ values: array(string().min(1)).min(1),
16057
+ /** Max Levenshtein distance after normalization (uppercase alphanumeric). */
16058
+ maxDistance: number().int().min(0).max(3).default(1)
16059
+ });
16060
+ /** Admin-zone membership condition (zone IDs as stamped by the ZoneEngine). */
16061
+ var NcZoneConditionSchema = object({
16062
+ ids: array(string().min(1)).min(1),
16063
+ /** Quantifier over `ids` — at least one / every one visited. */
16064
+ match: _enum(["any", "all"]).default("any")
16065
+ });
16066
+ /**
16067
+ * The P1 condition set — a flat AND of groups; absent group = pass;
16068
+ * membership lists are OR within the list (spec §2.3).
16069
+ */
16070
+ var NcConditionsSchema = object({
16071
+ /** Device scope — absent = all devices. */
16072
+ devices: array(number()).optional(),
16073
+ /** Detector class names (any overlap with the record's class set). */
16074
+ classes: array(string().min(1)).optional(),
16075
+ /** Veto classes — any overlap fails the rule. */
16076
+ classesExclude: array(string().min(1)).optional(),
16077
+ /** Minimum detection confidence 0–1 (fails when the record has none). */
16078
+ minConfidence: number().min(0).max(1).optional(),
16079
+ /** Admin zone membership over event `zones` / track `zonesVisited`. */
16080
+ zones: NcZoneConditionSchema.optional(),
16081
+ /** Veto zones — any hit fails the rule. */
16082
+ zonesExclude: array(string().min(1)).optional(),
16083
+ /**
16084
+ * Exact (case-insensitive) match on the record's collapsed `label`
16085
+ * (identity name / plate text / subclass).
16086
+ */
16087
+ labelEquals: array(string().min(1)).optional(),
16088
+ /**
16089
+ * Identity matcher. P1 boundary: matched against the record's collapsed
16090
+ * `label` (the identity display name propagated by the face pipeline) —
16091
+ * identity-ID matching rides in P2 when identity ids reach the record.
16092
+ */
16093
+ identities: array(string().min(1)).optional(),
16094
+ /** Fuzzy plate matcher against the record's `label` (plate text). */
16095
+ plates: NcPlateMatcherSchema.optional()
16096
+ });
16097
+ /** One delivery target: a `notification-output` Target ref + passthrough params. */
16098
+ var NcRuleTargetSchema = object({
16099
+ /** `notification-output` Target id. */
16100
+ targetId: string().min(1),
16101
+ /**
16102
+ * Per-backend passthrough. Recognized keys are mapped onto the canonical
16103
+ * Notification (`priority`, `level`, `sound`, `clickUrl`, `ttl`); the
16104
+ * degrade engine drops what the backend can't render.
16105
+ */
16106
+ params: record(string(), unknown()).optional()
16107
+ });
16108
+ /**
16109
+ * Media attachment policy (P1 still-image subset). `best` = the best
16110
+ * AVAILABLE media at dispatch time (D-3); `best-matching` (track-end
16111
+ * condition-best) is deferred — operator open point.
16112
+ */
16113
+ var NcMediaPolicySchema = object({ attach: _enum([
16114
+ "best",
16115
+ "keyFrame",
16116
+ "none"
16117
+ ]).default("best") });
16118
+ /** Throttle — cooldown survives restarts (rebuilt from the outbox on boot). */
16119
+ var NcThrottleSchema = object({
16120
+ cooldownSec: number().int().min(0).max(86400).default(60),
16121
+ /** `rule` = one shared cooldown; `rule-device` = per-camera cooldown. */
16122
+ scope: _enum(["rule", "rule-device"]).default("rule-device")
16123
+ });
16124
+ /** Client-supplied rule fields (server stamps id/createdBy/createdAt/updatedAt). */
16125
+ var NcRuleInputSchema = object({
16126
+ name: string().min(1).max(200),
16127
+ enabled: boolean().default(true),
16128
+ delivery: NcDeliverySchema,
16129
+ conditions: NcConditionsSchema.default({}),
16130
+ schedule: NcScheduleSchema.optional(),
16131
+ targets: array(NcRuleTargetSchema).min(1),
16132
+ media: NcMediaPolicySchema.default({ attach: "best" }),
16133
+ throttle: NcThrottleSchema.default({
16134
+ cooldownSec: 60,
16135
+ scope: "rule-device"
16136
+ }),
16137
+ /** `{{var}}` templating over camera/class/label/zones/confidence/time. */
16138
+ template: object({
16139
+ title: string().max(500).optional(),
16140
+ body: string().max(2e3).optional()
16141
+ }).optional(),
16142
+ /** Canonical notification priority ordinal (1..5); per-target overridable. */
16143
+ priority: number().int().min(1).max(5).default(3)
16144
+ });
16145
+ /** Partial patch for `updateRule` — any subset of the input fields. */
16146
+ var NcRulePatchSchema = NcRuleInputSchema.partial();
16147
+ /** A persisted rule. */
16148
+ var NcRuleSchema = NcRuleInputSchema.extend({
16149
+ id: string(),
16150
+ /** userId of the admin who created the rule (server-stamped caller). */
16151
+ createdBy: string(),
16152
+ createdAt: number(),
16153
+ updatedAt: number()
16154
+ });
16155
+ var NcTestResultSchema = object({
16156
+ recordId: string(),
16157
+ recordKind: _enum(["object-event", "track"]),
16158
+ deviceId: number(),
16159
+ timestamp: number(),
16160
+ wouldFire: boolean(),
16161
+ /** Condition id that failed (first failing group), when `wouldFire` is false. */
16162
+ failedCondition: string().optional(),
16163
+ className: string().optional(),
16164
+ label: string().optional()
16165
+ });
16166
+ var NcConditionDescriptorSchema = object({
16167
+ /** Field id inside `NcConditions` (or `'schedule'` for the rule-level group). */
16168
+ id: string(),
16169
+ group: _enum([
16170
+ "scope",
16171
+ "class",
16172
+ "zones",
16173
+ "quality",
16174
+ "label",
16175
+ "schedule"
16176
+ ]),
16177
+ label: string(),
16178
+ /** Editor widget the UI renders — never hardcode per-condition forms. */
16179
+ valueType: _enum([
16180
+ "deviceIdList",
16181
+ "stringList",
16182
+ "number01",
16183
+ "zoneSelection",
16184
+ "zoneIdList",
16185
+ "schedule",
16186
+ "plateMatcher"
16187
+ ]),
16188
+ operator: _enum([
16189
+ "in",
16190
+ "notIn",
16191
+ "anyOf",
16192
+ "allOf",
16193
+ "gte",
16194
+ "fuzzyIn",
16195
+ "withinSchedule"
16196
+ ]),
16197
+ /** Which delivery kinds the condition applies to. */
16198
+ appliesTo: array(NcDeliverySchema),
16199
+ phase: string(),
16200
+ description: string().optional()
16201
+ });
16202
+ /**
16203
+ * The P1 condition surface as data — served by `getConditionCatalog` so
16204
+ * rule editors render from the catalog, not hardcoded forms (spec §4.2).
16205
+ */
16206
+ var NC_CONDITION_CATALOG = [
16207
+ {
16208
+ id: "devices",
16209
+ group: "scope",
16210
+ label: "Cameras",
16211
+ valueType: "deviceIdList",
16212
+ operator: "in",
16213
+ appliesTo: ["immediate", "track-end"],
16214
+ phase: "P1",
16215
+ description: "Restrict the rule to these devices; absent = all devices."
16216
+ },
16217
+ {
16218
+ id: "classes",
16219
+ group: "class",
16220
+ label: "Object classes",
16221
+ valueType: "stringList",
16222
+ operator: "in",
16223
+ appliesTo: ["immediate", "track-end"],
16224
+ phase: "P1",
16225
+ description: "Any overlap with the detection class set passes."
16226
+ },
16227
+ {
16228
+ id: "classesExclude",
16229
+ group: "class",
16230
+ label: "Excluded classes",
16231
+ valueType: "stringList",
16232
+ operator: "notIn",
16233
+ appliesTo: ["immediate", "track-end"],
16234
+ phase: "P1"
16235
+ },
16236
+ {
16237
+ id: "minConfidence",
16238
+ group: "quality",
16239
+ label: "Minimum confidence",
16240
+ valueType: "number01",
16241
+ operator: "gte",
16242
+ appliesTo: ["immediate", "track-end"],
16243
+ phase: "P1"
16244
+ },
16245
+ {
16246
+ id: "zones",
16247
+ group: "zones",
16248
+ label: "Zones",
16249
+ valueType: "zoneSelection",
16250
+ operator: "anyOf",
16251
+ appliesTo: ["immediate", "track-end"],
16252
+ phase: "P1",
16253
+ description: "Admin zone ids; quantifier any/all over the visited set."
16254
+ },
16255
+ {
16256
+ id: "zonesExclude",
16257
+ group: "zones",
16258
+ label: "Excluded zones",
16259
+ valueType: "zoneIdList",
16260
+ operator: "notIn",
16261
+ appliesTo: ["immediate", "track-end"],
16262
+ phase: "P1"
16263
+ },
16264
+ {
16265
+ id: "labelEquals",
16266
+ group: "label",
16267
+ label: "Label equals",
16268
+ valueType: "stringList",
16269
+ operator: "in",
16270
+ appliesTo: ["immediate", "track-end"],
16271
+ phase: "P1",
16272
+ description: "Exact match on the collapsed label (identity / plate / subclass)."
16273
+ },
16274
+ {
16275
+ id: "identities",
16276
+ group: "label",
16277
+ label: "Identities",
16278
+ valueType: "stringList",
16279
+ operator: "in",
16280
+ appliesTo: ["immediate", "track-end"],
16281
+ phase: "P1",
16282
+ description: "P1: matched against the identity display name on the record label."
16283
+ },
16284
+ {
16285
+ id: "plates",
16286
+ group: "label",
16287
+ label: "License plates",
16288
+ valueType: "plateMatcher",
16289
+ operator: "fuzzyIn",
16290
+ appliesTo: ["immediate", "track-end"],
16291
+ phase: "P1",
16292
+ description: "Levenshtein-tolerant match against the plate text."
16293
+ },
16294
+ {
16295
+ id: "schedule",
16296
+ group: "schedule",
16297
+ label: "Schedule",
16298
+ valueType: "schedule",
16299
+ operator: "withinSchedule",
16300
+ appliesTo: ["immediate", "track-end"],
16301
+ phase: "P1",
16302
+ description: "Weekly activation windows (invertible); absent = always active."
16303
+ }
16304
+ ];
16305
+ var notificationRulesCapability = {
16306
+ name: "notification-rules",
16307
+ scope: "system",
16308
+ mode: "singleton",
16309
+ methods: {
16310
+ listRules: method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }),
16311
+ getRule: method(object({ ruleId: string() }), object({ rule: NcRuleSchema.nullable() }), { auth: "admin" }),
16312
+ createRule: method(object({ rule: NcRuleInputSchema }), object({ rule: NcRuleSchema }), {
16313
+ kind: "mutation",
16314
+ auth: "admin",
16315
+ caller: "required"
16316
+ }),
16317
+ updateRule: method(object({
16318
+ ruleId: string(),
16319
+ patch: NcRulePatchSchema
16320
+ }), object({ rule: NcRuleSchema }), {
16321
+ kind: "mutation",
16322
+ auth: "admin",
16323
+ caller: "required"
16324
+ }),
16325
+ deleteRule: method(object({ ruleId: string() }), object({ success: literal(true) }), {
16326
+ kind: "mutation",
16327
+ auth: "admin"
16328
+ }),
16329
+ setRuleEnabled: method(object({
16330
+ ruleId: string(),
16331
+ enabled: boolean()
16332
+ }), object({ success: literal(true) }), {
16333
+ kind: "mutation",
16334
+ auth: "admin"
16335
+ }),
16336
+ /**
16337
+ * Dry-run a rule against recently persisted records (object events for
16338
+ * `immediate`, closed tracks for `track-end`). Mutation kind only to
16339
+ * carry the full rule object safely; no side effects.
16340
+ */
16341
+ testRule: method(object({
16342
+ rule: NcRuleInputSchema,
16343
+ lookbackMinutes: number().int().min(1).max(1440).default(60)
16344
+ }), object({ results: array(NcTestResultSchema) }), {
16345
+ kind: "mutation",
16346
+ auth: "admin"
16347
+ }),
16348
+ getConditionCatalog: method(object({}), object({ catalog: array(NcConditionDescriptorSchema) }))
16349
+ }
16350
+ };
16351
+ /**
16010
16352
  * Shared LLM generate contracts — imported by BOTH `llm.cap.ts` (consumer
16011
16353
  * surface) and `llm-runtime.cap.ts` (node-side managed executor) so the two
16012
16354
  * caps stay wire-compatible without a circular cap→cap import.
@@ -23648,6 +23990,54 @@ Object.freeze({
23648
23990
  addonId: null,
23649
23991
  access: "create"
23650
23992
  },
23993
+ "notificationRules.createRule": {
23994
+ capName: "notification-rules",
23995
+ capScope: "system",
23996
+ addonId: null,
23997
+ access: "create"
23998
+ },
23999
+ "notificationRules.deleteRule": {
24000
+ capName: "notification-rules",
24001
+ capScope: "system",
24002
+ addonId: null,
24003
+ access: "delete"
24004
+ },
24005
+ "notificationRules.getConditionCatalog": {
24006
+ capName: "notification-rules",
24007
+ capScope: "system",
24008
+ addonId: null,
24009
+ access: "view"
24010
+ },
24011
+ "notificationRules.getRule": {
24012
+ capName: "notification-rules",
24013
+ capScope: "system",
24014
+ addonId: null,
24015
+ access: "view"
24016
+ },
24017
+ "notificationRules.listRules": {
24018
+ capName: "notification-rules",
24019
+ capScope: "system",
24020
+ addonId: null,
24021
+ access: "view"
24022
+ },
24023
+ "notificationRules.setRuleEnabled": {
24024
+ capName: "notification-rules",
24025
+ capScope: "system",
24026
+ addonId: null,
24027
+ access: "create"
24028
+ },
24029
+ "notificationRules.testRule": {
24030
+ capName: "notification-rules",
24031
+ capScope: "system",
24032
+ addonId: null,
24033
+ access: "create"
24034
+ },
24035
+ "notificationRules.updateRule": {
24036
+ capName: "notification-rules",
24037
+ capScope: "system",
24038
+ addonId: null,
24039
+ access: "create"
24040
+ },
23651
24041
  "notifier.cancel": {
23652
24042
  capName: "notifier",
23653
24043
  capScope: "device",
@@ -25903,6 +26293,18 @@ Object.defineProperty(exports, "MACRO_LABELS", {
25903
26293
  return MACRO_LABELS;
25904
26294
  }
25905
26295
  });
26296
+ Object.defineProperty(exports, "NC_CONDITION_CATALOG", {
26297
+ enumerable: true,
26298
+ get: function() {
26299
+ return NC_CONDITION_CATALOG;
26300
+ }
26301
+ });
26302
+ Object.defineProperty(exports, "NcRuleSchema", {
26303
+ enumerable: true,
26304
+ get: function() {
26305
+ return NcRuleSchema;
26306
+ }
26307
+ });
25906
26308
  Object.defineProperty(exports, "OpsLogEntrySchema", {
25907
26309
  enumerable: true,
25908
26310
  get: function() {
@@ -25993,6 +26395,12 @@ Object.defineProperty(exports, "nodePin", {
25993
26395
  return nodePin;
25994
26396
  }
25995
26397
  });
26398
+ Object.defineProperty(exports, "notificationRulesCapability", {
26399
+ enumerable: true,
26400
+ get: function() {
26401
+ return notificationRulesCapability;
26402
+ }
26403
+ });
25996
26404
  Object.defineProperty(exports, "number", {
25997
26405
  enumerable: true,
25998
26406
  get: function() {