@audiofool/moondog 0.1.0

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 (109) hide show
  1. package/README.md +125 -0
  2. package/assets/brand/moondog-lunar-record/moondog-logo-1x1.png +0 -0
  3. package/contracts/v1/capability-policy.schema.json +572 -0
  4. package/contracts/v1/definitions.schema.json +365 -0
  5. package/contracts/v1/library-track-observation.schema.json +138 -0
  6. package/contracts/v1/listening-event.schema.json +111 -0
  7. package/contracts/v1/profile-evidence.schema.json +237 -0
  8. package/contracts/v1/taste-event.schema.json +258 -0
  9. package/contracts/v1/tool-invocation.schema.json +318 -0
  10. package/contracts/v1/tool-receipt.schema.json +1066 -0
  11. package/contracts/v1/track-ref.schema.json +275 -0
  12. package/contracts/v2/profile-evidence.schema.json +284 -0
  13. package/package.json +105 -0
  14. package/scripts/contract-lib.mjs +361 -0
  15. package/scripts/contract-semantics.mjs +2630 -0
  16. package/scripts/demo-studio.mjs +58 -0
  17. package/scripts/moondog +52 -0
  18. package/scripts/moondog.mjs +954 -0
  19. package/src/core/apple-data-migration.mjs +50 -0
  20. package/src/core/apple-library-source-status.mjs +220 -0
  21. package/src/core/apple-projection-domain-services.mjs +2356 -0
  22. package/src/core/capability-catalog.mjs +577 -0
  23. package/src/core/listening-profile-domain-services.mjs +1010 -0
  24. package/src/core/lyric-library.mjs +121 -0
  25. package/src/core/lyric-profile.mjs +50 -0
  26. package/src/core/lyric-service.mjs +41 -0
  27. package/src/core/moondog-application.mjs +1827 -0
  28. package/src/core/state-directory.mjs +19 -0
  29. package/src/core/synthetic-domain-services.mjs +1485 -0
  30. package/src/core/uuid-v5.mjs +42 -0
  31. package/src/demo/fictional-spotify-history.mjs +400 -0
  32. package/src/demo/moondog-demo.mjs +355 -0
  33. package/src/demo/moondog-interactive-tasteprint-demo.mjs +293 -0
  34. package/src/demo/moondog-tasteprint-demo.mjs +631 -0
  35. package/src/importers/apple-music-library/import-batch.mjs +290 -0
  36. package/src/importers/apple-music-library/index.mjs +76 -0
  37. package/src/importers/apple-music-library/normalize.mjs +1126 -0
  38. package/src/importers/apple-music-library/parse-plist.mjs +357 -0
  39. package/src/importers/apple-music-library/projection-records.mjs +557 -0
  40. package/src/importers/apple-music-library/read-batch.mjs +276 -0
  41. package/src/importers/apple-music-library/sqlite-projection.mjs +1307 -0
  42. package/src/importers/apple-music-library/stable-ids.mjs +19 -0
  43. package/src/importers/apple-music-library/track-snapshot.schema.json +136 -0
  44. package/src/integrations/apple-music/catalog.mjs +770 -0
  45. package/src/integrations/cross-catalog-artist-identity.mjs +222 -0
  46. package/src/integrations/listenbrainz/history-file.mjs +560 -0
  47. package/src/integrations/lyrics/lrclib.mjs +55 -0
  48. package/src/integrations/open-music-similarity/artist-radio.mjs +806 -0
  49. package/src/integrations/public-playlists.mjs +104 -0
  50. package/src/integrations/spotify/account-data-history.mjs +545 -0
  51. package/src/integrations/spotify/account-data-profile.mjs +918 -0
  52. package/src/integrations/spotify/authentication.mjs +791 -0
  53. package/src/integrations/spotify/catalog-resolver.mjs +657 -0
  54. package/src/integrations/spotify/connection.mjs +125 -0
  55. package/src/integrations/spotify/credential-store.mjs +26 -0
  56. package/src/integrations/spotify/extended-streaming-history.mjs +525 -0
  57. package/src/integrations/spotify/history-archive.mjs +59 -0
  58. package/src/integrations/spotify/index.mjs +80 -0
  59. package/src/integrations/spotify/recent-activity.mjs +198 -0
  60. package/src/integrations/spotify/resolution-cache.mjs +260 -0
  61. package/src/integrations/spotify/service.mjs +713 -0
  62. package/src/integrations/spotify/settings.mjs +89 -0
  63. package/src/integrations/spotify/web-api-client.mjs +761 -0
  64. package/src/integrations/web/codex-web.mjs +263 -0
  65. package/src/integrations/youtube-music/takeout.mjs +143 -0
  66. package/src/memory/local-memory-store.mjs +1705 -0
  67. package/src/memory/reflection-worker.mjs +87 -0
  68. package/src/profile/apple-library-import.mjs +28 -0
  69. package/src/profile/history-import.mjs +163 -0
  70. package/src/profile/listener-corrections.mjs +217 -0
  71. package/src/profile/listening-history-store.mjs +2055 -0
  72. package/src/profile/listening-profile-projection.mjs +2520 -0
  73. package/src/profile/local-music-data.mjs +812 -0
  74. package/src/profile/music-import-bundle.mjs +91 -0
  75. package/src/profile/music-providers.mjs +25 -0
  76. package/src/profile/spotify-archive-catalog-hints.mjs +210 -0
  77. package/src/profile/spotify-archive-taste.mjs +482 -0
  78. package/src/runtime/pi/agent-runtime.mjs +6444 -0
  79. package/src/runtime/pi/authentication.mjs +104 -0
  80. package/src/runtime/pi/configured-runtime.mjs +98 -0
  81. package/src/runtime/pi/memory-agent-runtime.mjs +337 -0
  82. package/src/runtime/pi/model-catalog.mjs +62 -0
  83. package/src/runtime/pi/model-transport.mjs +117 -0
  84. package/src/runtime/pi/persistent-credential-store.mjs +670 -0
  85. package/src/runtime/pi/provider-registry.mjs +74 -0
  86. package/src/runtime/pi/runtime-settings.mjs +81 -0
  87. package/src/surfaces/cli/auth-command.mjs +355 -0
  88. package/src/surfaces/cli/brand-components.mjs +390 -0
  89. package/src/surfaces/cli/brand-theme.mjs +231 -0
  90. package/src/surfaces/cli/catalog-command.mjs +509 -0
  91. package/src/surfaces/cli/command-completions.mjs +113 -0
  92. package/src/surfaces/cli/data-command.mjs +214 -0
  93. package/src/surfaces/cli/format-output.mjs +750 -0
  94. package/src/surfaces/cli/history-import-view.mjs +445 -0
  95. package/src/surfaces/cli/import-guides.mjs +9 -0
  96. package/src/surfaces/cli/listenbrainz-command.mjs +129 -0
  97. package/src/surfaces/cli/listening-menu.mjs +171 -0
  98. package/src/surfaces/cli/profile-command.mjs +290 -0
  99. package/src/surfaces/cli/spotify-command.mjs +849 -0
  100. package/src/surfaces/cli/taste-profile-model.mjs +306 -0
  101. package/src/surfaces/cli/taste-profile-view.mjs +272 -0
  102. package/src/surfaces/cli/terminal-art.mjs +295 -0
  103. package/src/surfaces/cli/tui.mjs +1714 -0
  104. package/src/surfaces/cli/web-command.mjs +52 -0
  105. package/src/surfaces/html/tasteprint-artifact.mjs +96 -0
  106. package/src/surfaces/html/tasteprint.mjs +3575 -0
  107. package/src/surfaces/web/showcase.mjs +272 -0
  108. package/src/surfaces/web/studio-page.mjs +4148 -0
  109. package/src/surfaces/web/studio.mjs +2288 -0
@@ -0,0 +1,365 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:moondog:contract:definitions:1",
4
+ "title": "Moondog Contract Definitions v1",
5
+ "$defs": {
6
+ "schema_version": {
7
+ "type": "integer",
8
+ "const": 1
9
+ },
10
+ "uuid": {
11
+ "type": "string",
12
+ "format": "uuid"
13
+ },
14
+ "utc_timestamp": {
15
+ "type": "string",
16
+ "format": "date-time",
17
+ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(?:\\.[0-9]+)?Z$"
18
+ },
19
+ "non_empty_string": {
20
+ "type": "string",
21
+ "minLength": 1,
22
+ "maxLength": 512,
23
+ "pattern": "\\S"
24
+ },
25
+ "namespaced_key": {
26
+ "type": "string",
27
+ "minLength": 3,
28
+ "maxLength": 128,
29
+ "pattern": "^[a-z][a-z0-9_]*(?:\\.[a-z][a-z0-9_]*)+$"
30
+ },
31
+ "sha256": {
32
+ "type": "string",
33
+ "pattern": "^[a-f0-9]{64}$"
34
+ },
35
+ "extensions": {
36
+ "type": "object",
37
+ "maxProperties": 32,
38
+ "propertyNames": {
39
+ "pattern": "^[a-z][a-z0-9_]*(?:\\.[a-z0-9][a-z0-9_-]*)+$"
40
+ },
41
+ "additionalProperties": true
42
+ },
43
+ "external_ref": {
44
+ "type": "object",
45
+ "properties": {
46
+ "system": {
47
+ "$ref": "#/$defs/non_empty_string"
48
+ },
49
+ "entity_type": {
50
+ "$ref": "#/$defs/namespaced_key"
51
+ },
52
+ "external_id": {
53
+ "$ref": "#/$defs/non_empty_string"
54
+ },
55
+ "url": {
56
+ "type": "string",
57
+ "format": "uri",
58
+ "pattern": "^[^?#]+$"
59
+ }
60
+ },
61
+ "required": [
62
+ "system",
63
+ "entity_type",
64
+ "external_id"
65
+ ],
66
+ "additionalProperties": false
67
+ },
68
+ "typed_record_ref": {
69
+ "type": "object",
70
+ "properties": {
71
+ "record_type": {
72
+ "enum": [
73
+ "track_ref",
74
+ "listening_event",
75
+ "taste_event",
76
+ "profile_evidence",
77
+ "capability_policy",
78
+ "tool_invocation",
79
+ "tool_receipt"
80
+ ]
81
+ },
82
+ "record_id": {
83
+ "$ref": "#/$defs/uuid"
84
+ }
85
+ },
86
+ "required": [
87
+ "record_type",
88
+ "record_id"
89
+ ],
90
+ "additionalProperties": false
91
+ },
92
+ "entity_ref": {
93
+ "type": "object",
94
+ "properties": {
95
+ "entity_type": {
96
+ "enum": [
97
+ "track",
98
+ "artist",
99
+ "release",
100
+ "playlist",
101
+ "genre",
102
+ "scene",
103
+ "era",
104
+ "musical_attribute",
105
+ "creative_artifact"
106
+ ]
107
+ },
108
+ "entity_id": {
109
+ "$ref": "#/$defs/uuid"
110
+ },
111
+ "entity_revision": {
112
+ "type": "integer",
113
+ "minimum": 1
114
+ },
115
+ "entity_key": {
116
+ "$ref": "#/$defs/namespaced_key"
117
+ },
118
+ "label": {
119
+ "$ref": "#/$defs/non_empty_string"
120
+ }
121
+ },
122
+ "required": [
123
+ "entity_type"
124
+ ],
125
+ "anyOf": [
126
+ {
127
+ "required": [
128
+ "entity_id"
129
+ ]
130
+ },
131
+ {
132
+ "required": [
133
+ "entity_key"
134
+ ]
135
+ }
136
+ ],
137
+ "additionalProperties": false
138
+ },
139
+ "actor_ref": {
140
+ "type": "object",
141
+ "properties": {
142
+ "actor_type": {
143
+ "enum": [
144
+ "user",
145
+ "agent",
146
+ "service"
147
+ ]
148
+ },
149
+ "actor_id": {
150
+ "$ref": "#/$defs/uuid"
151
+ }
152
+ },
153
+ "required": [
154
+ "actor_type",
155
+ "actor_id"
156
+ ],
157
+ "additionalProperties": false
158
+ },
159
+ "context": {
160
+ "type": "object",
161
+ "properties": {
162
+ "context_type": {
163
+ "enum": [
164
+ "album",
165
+ "playlist",
166
+ "artist",
167
+ "search",
168
+ "queue",
169
+ "radio",
170
+ "recommendation",
171
+ "external",
172
+ "unknown"
173
+ ]
174
+ },
175
+ "entity_ref": {
176
+ "$ref": "#/$defs/entity_ref"
177
+ }
178
+ },
179
+ "required": [
180
+ "context_type"
181
+ ],
182
+ "additionalProperties": false
183
+ },
184
+ "provenance": {
185
+ "type": "object",
186
+ "properties": {
187
+ "source_kind": {
188
+ "enum": [
189
+ "user_input",
190
+ "import",
191
+ "tool",
192
+ "derived",
193
+ "system"
194
+ ]
195
+ },
196
+ "captured_at": {
197
+ "$ref": "#/$defs/utc_timestamp"
198
+ },
199
+ "external_ref": {
200
+ "$ref": "#/$defs/external_ref"
201
+ },
202
+ "import_batch_id": {
203
+ "$ref": "#/$defs/uuid"
204
+ },
205
+ "source_record_refs": {
206
+ "type": "array",
207
+ "items": {
208
+ "$ref": "#/$defs/typed_record_ref"
209
+ },
210
+ "uniqueItems": true
211
+ }
212
+ },
213
+ "required": [
214
+ "source_kind",
215
+ "captured_at"
216
+ ],
217
+ "additionalProperties": false
218
+ },
219
+ "data_class": {
220
+ "enum": [
221
+ "public_metadata",
222
+ "personal_profile",
223
+ "raw_listening_history",
224
+ "user_content",
225
+ "generated_content",
226
+ "credentials"
227
+ ]
228
+ },
229
+ "effect_kind": {
230
+ "enum": [
231
+ "read_external",
232
+ "write_local",
233
+ "write_external",
234
+ "send_message",
235
+ "spend",
236
+ "publish",
237
+ "delete",
238
+ "use_credentials"
239
+ ]
240
+ },
241
+ "risk_domain": {
242
+ "enum": [
243
+ "privacy",
244
+ "account",
245
+ "copyright",
246
+ "spend"
247
+ ]
248
+ },
249
+ "budget_amount": {
250
+ "type": "object",
251
+ "properties": {
252
+ "resource": {
253
+ "$ref": "#/$defs/namespaced_key"
254
+ },
255
+ "quantity": {
256
+ "type": "integer",
257
+ "minimum": 0
258
+ },
259
+ "unit": {
260
+ "$ref": "#/$defs/non_empty_string"
261
+ }
262
+ },
263
+ "required": [
264
+ "resource",
265
+ "quantity",
266
+ "unit"
267
+ ],
268
+ "additionalProperties": false
269
+ },
270
+ "policy_ref": {
271
+ "type": "object",
272
+ "properties": {
273
+ "policy_id": {
274
+ "$ref": "#/$defs/uuid"
275
+ },
276
+ "policy_version": {
277
+ "type": "integer",
278
+ "minimum": 1
279
+ }
280
+ },
281
+ "required": [
282
+ "policy_id",
283
+ "policy_version"
284
+ ],
285
+ "additionalProperties": false
286
+ },
287
+ "tool_ref": {
288
+ "type": "object",
289
+ "properties": {
290
+ "tool_id": {
291
+ "$ref": "#/$defs/namespaced_key"
292
+ },
293
+ "contract_version": {
294
+ "type": "integer",
295
+ "minimum": 1
296
+ }
297
+ },
298
+ "required": [
299
+ "tool_id",
300
+ "contract_version"
301
+ ],
302
+ "additionalProperties": false
303
+ },
304
+ "artifact_ref": {
305
+ "type": "object",
306
+ "properties": {
307
+ "artifact_id": {
308
+ "$ref": "#/$defs/uuid"
309
+ },
310
+ "media_type": {
311
+ "type": "string",
312
+ "pattern": "^[A-Za-z0-9!#$&^_.+-]+/[A-Za-z0-9!#$&^_.+-]+$"
313
+ },
314
+ "uri": {
315
+ "type": "string",
316
+ "format": "uri",
317
+ "pattern": "^(?:https://[^?#]+|urn:moondog:artifact:[A-Za-z0-9._~-]+)$"
318
+ },
319
+ "sha256": {
320
+ "$ref": "#/$defs/sha256"
321
+ },
322
+ "external_refs": {
323
+ "type": "array",
324
+ "items": {
325
+ "$ref": "#/$defs/external_ref"
326
+ },
327
+ "uniqueItems": true
328
+ }
329
+ },
330
+ "required": [
331
+ "artifact_id",
332
+ "media_type",
333
+ "sha256"
334
+ ],
335
+ "additionalProperties": false
336
+ },
337
+ "external_destination": {
338
+ "type": "object",
339
+ "properties": {
340
+ "destination_type": {
341
+ "enum": [
342
+ "local",
343
+ "external"
344
+ ]
345
+ },
346
+ "system": {
347
+ "$ref": "#/$defs/non_empty_string"
348
+ },
349
+ "purpose_id": {
350
+ "$ref": "#/$defs/namespaced_key"
351
+ },
352
+ "purpose": {
353
+ "$ref": "#/$defs/non_empty_string"
354
+ }
355
+ },
356
+ "required": [
357
+ "destination_type",
358
+ "system",
359
+ "purpose_id",
360
+ "purpose"
361
+ ],
362
+ "additionalProperties": false
363
+ }
364
+ }
365
+ }
@@ -0,0 +1,138 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:moondog:contract:library-track-observation:1",
4
+ "title": "Moondog LibraryTrackObservation v1",
5
+ "type": "object",
6
+ "$defs": {
7
+ "rating": {
8
+ "type": "object",
9
+ "properties": {
10
+ "value": {
11
+ "type": "integer",
12
+ "minimum": 0,
13
+ "maximum": 100
14
+ },
15
+ "computed": {
16
+ "type": "boolean"
17
+ }
18
+ },
19
+ "required": [
20
+ "value"
21
+ ],
22
+ "additionalProperties": false
23
+ }
24
+ },
25
+ "properties": {
26
+ "schema_version": {
27
+ "$ref": "urn:moondog:contract:definitions:1#/$defs/schema_version"
28
+ },
29
+ "library_track_observation_id": {
30
+ "$ref": "urn:moondog:contract:definitions:1#/$defs/uuid"
31
+ },
32
+ "subject_id": {
33
+ "$ref": "urn:moondog:contract:definitions:1#/$defs/uuid"
34
+ },
35
+ "track_ref_id": {
36
+ "$ref": "urn:moondog:contract:definitions:1#/$defs/uuid"
37
+ },
38
+ "track_ref_revision": {
39
+ "type": "integer",
40
+ "minimum": 1
41
+ },
42
+ "observed_at": {
43
+ "$ref": "urn:moondog:contract:definitions:1#/$defs/utc_timestamp"
44
+ },
45
+ "source_kind": {
46
+ "const": "provider_library_snapshot"
47
+ },
48
+ "source_batch_ref": {
49
+ "$ref": "urn:moondog:contract:definitions:1#/$defs/uuid"
50
+ },
51
+ "source_record_digest": {
52
+ "$ref": "urn:moondog:contract:definitions:1#/$defs/sha256"
53
+ },
54
+ "aggregate_state": {
55
+ "type": "object",
56
+ "properties": {
57
+ "play_count": {
58
+ "type": "integer",
59
+ "minimum": 0
60
+ },
61
+ "last_played_at": {
62
+ "$ref": "urn:moondog:contract:definitions:1#/$defs/utc_timestamp"
63
+ },
64
+ "skip_count": {
65
+ "type": "integer",
66
+ "minimum": 0
67
+ },
68
+ "last_skipped_at": {
69
+ "$ref": "urn:moondog:contract:definitions:1#/$defs/utc_timestamp"
70
+ },
71
+ "rating": {
72
+ "$ref": "#/$defs/rating"
73
+ },
74
+ "album_rating": {
75
+ "$ref": "#/$defs/rating"
76
+ },
77
+ "loved": {
78
+ "type": "boolean"
79
+ },
80
+ "favorited": {
81
+ "type": "boolean"
82
+ }
83
+ },
84
+ "additionalProperties": false
85
+ },
86
+ "library_state": {
87
+ "type": "object",
88
+ "properties": {
89
+ "date_added": {
90
+ "$ref": "urn:moondog:contract:definitions:1#/$defs/utc_timestamp"
91
+ },
92
+ "date_modified": {
93
+ "$ref": "urn:moondog:contract:definitions:1#/$defs/utc_timestamp"
94
+ },
95
+ "subscription_catalog_item": {
96
+ "type": "boolean"
97
+ },
98
+ "purchased": {
99
+ "type": "boolean"
100
+ },
101
+ "playlist_only": {
102
+ "type": "boolean"
103
+ },
104
+ "compilation": {
105
+ "type": "boolean"
106
+ },
107
+ "unplayed": {
108
+ "type": "boolean"
109
+ },
110
+ "disabled": {
111
+ "type": "boolean"
112
+ }
113
+ },
114
+ "additionalProperties": false
115
+ },
116
+ "provenance": {
117
+ "$ref": "urn:moondog:contract:definitions:1#/$defs/provenance"
118
+ },
119
+ "extensions": {
120
+ "$ref": "urn:moondog:contract:definitions:1#/$defs/extensions"
121
+ }
122
+ },
123
+ "required": [
124
+ "schema_version",
125
+ "library_track_observation_id",
126
+ "subject_id",
127
+ "track_ref_id",
128
+ "track_ref_revision",
129
+ "observed_at",
130
+ "source_kind",
131
+ "source_batch_ref",
132
+ "source_record_digest",
133
+ "aggregate_state",
134
+ "library_state",
135
+ "provenance"
136
+ ],
137
+ "additionalProperties": false
138
+ }
@@ -0,0 +1,111 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:moondog:contract:listening-event:1",
4
+ "title": "Moondog ListeningEvent v1",
5
+ "type": "object",
6
+ "properties": {
7
+ "schema_version": {
8
+ "$ref": "urn:moondog:contract:definitions:1#/$defs/schema_version"
9
+ },
10
+ "listening_event_id": {
11
+ "$ref": "urn:moondog:contract:definitions:1#/$defs/uuid"
12
+ },
13
+ "subject_id": {
14
+ "$ref": "urn:moondog:contract:definitions:1#/$defs/uuid"
15
+ },
16
+ "track_ref_id": {
17
+ "$ref": "urn:moondog:contract:definitions:1#/$defs/uuid"
18
+ },
19
+ "track_ref_revision": {
20
+ "type": "integer",
21
+ "minimum": 1
22
+ },
23
+ "event_type": {
24
+ "enum": [
25
+ "play_observed",
26
+ "play_started",
27
+ "play_progress",
28
+ "play_completed",
29
+ "play_skipped"
30
+ ]
31
+ },
32
+ "occurred_at": {
33
+ "$ref": "urn:moondog:contract:definitions:1#/$defs/utc_timestamp"
34
+ },
35
+ "ingested_at": {
36
+ "$ref": "urn:moondog:contract:definitions:1#/$defs/utc_timestamp"
37
+ },
38
+ "interaction_mode": {
39
+ "enum": [
40
+ "active",
41
+ "passive",
42
+ "unknown"
43
+ ]
44
+ },
45
+ "played_ms": {
46
+ "type": "integer",
47
+ "minimum": 0
48
+ },
49
+ "position_ms": {
50
+ "type": "integer",
51
+ "minimum": 0
52
+ },
53
+ "completion_ratio": {
54
+ "type": "number",
55
+ "minimum": 0,
56
+ "maximum": 1
57
+ },
58
+ "context": {
59
+ "$ref": "urn:moondog:contract:definitions:1#/$defs/context"
60
+ },
61
+ "provenance": {
62
+ "$ref": "urn:moondog:contract:definitions:1#/$defs/provenance"
63
+ },
64
+ "deduplication_key": {
65
+ "type": "string",
66
+ "minLength": 8,
67
+ "maxLength": 256,
68
+ "pattern": "\\S"
69
+ },
70
+ "supersedes_listening_event_id": {
71
+ "$ref": "urn:moondog:contract:definitions:1#/$defs/uuid"
72
+ },
73
+ "extensions": {
74
+ "$ref": "urn:moondog:contract:definitions:1#/$defs/extensions"
75
+ }
76
+ },
77
+ "required": [
78
+ "schema_version",
79
+ "listening_event_id",
80
+ "subject_id",
81
+ "track_ref_id",
82
+ "track_ref_revision",
83
+ "event_type",
84
+ "occurred_at",
85
+ "ingested_at",
86
+ "interaction_mode",
87
+ "provenance"
88
+ ],
89
+ "allOf": [
90
+ {
91
+ "if": {
92
+ "properties": {
93
+ "event_type": {
94
+ "const": "play_started"
95
+ }
96
+ },
97
+ "required": [
98
+ "event_type"
99
+ ]
100
+ },
101
+ "then": {
102
+ "not": {
103
+ "required": [
104
+ "completion_ratio"
105
+ ]
106
+ }
107
+ }
108
+ }
109
+ ],
110
+ "additionalProperties": false
111
+ }