@clowder-ai/plugin-contract 0.1.0-beta.1

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 (74) hide show
  1. package/dist/codegen/generate-contract.d.ts +24 -0
  2. package/dist/codegen/generate-contract.d.ts.map +1 -0
  3. package/dist/codegen/generate-contract.js +328 -0
  4. package/dist/codegen/generate-contract.js.map +1 -0
  5. package/dist/conformance/index.d.ts +13 -0
  6. package/dist/conformance/index.d.ts.map +1 -0
  7. package/dist/conformance/index.js +13 -0
  8. package/dist/conformance/index.js.map +1 -0
  9. package/dist/conformance/runner.d.ts +18 -0
  10. package/dist/conformance/runner.d.ts.map +1 -0
  11. package/dist/conformance/runner.js +252 -0
  12. package/dist/conformance/runner.js.map +1 -0
  13. package/dist/generated/contract.generated.d.ts +282 -0
  14. package/dist/generated/contract.generated.d.ts.map +1 -0
  15. package/dist/generated/contract.generated.js +37 -0
  16. package/dist/generated/contract.generated.js.map +1 -0
  17. package/dist/index.d.ts +35 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +36 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/types/capability.d.ts +4 -0
  22. package/dist/types/capability.d.ts.map +1 -0
  23. package/dist/types/capability.js +11 -0
  24. package/dist/types/capability.js.map +1 -0
  25. package/dist/types/data-class.d.ts +3 -0
  26. package/dist/types/data-class.d.ts.map +1 -0
  27. package/dist/types/data-class.js +12 -0
  28. package/dist/types/data-class.js.map +1 -0
  29. package/dist/types/index.d.ts +5 -0
  30. package/dist/types/index.d.ts.map +1 -0
  31. package/dist/types/index.js +5 -0
  32. package/dist/types/index.js.map +1 -0
  33. package/dist/validation/index.d.ts +2 -0
  34. package/dist/validation/index.d.ts.map +1 -0
  35. package/dist/validation/index.js +2 -0
  36. package/dist/validation/index.js.map +1 -0
  37. package/dist/validation/messaging-semantic.d.ts +10 -0
  38. package/dist/validation/messaging-semantic.d.ts.map +1 -0
  39. package/dist/validation/messaging-semantic.js +99 -0
  40. package/dist/validation/messaging-semantic.js.map +1 -0
  41. package/fixtures/behavior/messaging/adversarial-invariants.json +475 -0
  42. package/fixtures/manifest/invalid/.gitkeep +0 -0
  43. package/fixtures/manifest/invalid/contract-version-leading-zero-prerelease.json +17 -0
  44. package/fixtures/manifest/invalid/contract-version-range.json +17 -0
  45. package/fixtures/manifest/invalid/ipc-empty-entrypoint.json +18 -0
  46. package/fixtures/manifest/invalid/plugin-version-leading-zero-prerelease.json +17 -0
  47. package/fixtures/manifest/invalid/plugin-version-not-semver.json +17 -0
  48. package/fixtures/manifest/invalid/semver-empty-build-metadata.json +17 -0
  49. package/fixtures/manifest/invalid/stdio-missing-entrypoint.json +17 -0
  50. package/fixtures/manifest/invalid/unknown-capability.json +19 -0
  51. package/fixtures/manifest/invalid/user-authored-lifecycle.json +24 -0
  52. package/fixtures/manifest/valid/.gitkeep +0 -0
  53. package/fixtures/manifest/valid/minimal-builtin.json +17 -0
  54. package/fixtures/manifest/valid/protocol-intrinsic-lifecycle.json +18 -0
  55. package/fixtures/manifest/valid/semver-build-metadata.json +17 -0
  56. package/fixtures/manifest/valid/stdio-entrypoint.json +18 -0
  57. package/fixtures/messaging/invalid/.gitkeep +0 -0
  58. package/fixtures/messaging/invalid/non-rfc3339-occurred-at.json +34 -0
  59. package/fixtures/messaging/invalid/non-utc-occurred-at.json +34 -0
  60. package/fixtures/messaging/invalid/raw-message-id-append.json +16 -0
  61. package/fixtures/messaging/invalid/raw-thread-id.json +28 -0
  62. package/fixtures/messaging/invalid/stale-read-with-ack-token.json +8 -0
  63. package/fixtures/messaging/invalid/system-draft-audience.json +31 -0
  64. package/fixtures/messaging/invalid/text-payload-missing-text.json +10 -0
  65. package/fixtures/messaging/invalid/untyped-root-document.json +3 -0
  66. package/fixtures/messaging/valid/.gitkeep +0 -0
  67. package/fixtures/messaging/valid/append-elements-with-handle.json +21 -0
  68. package/fixtures/messaging/valid/message-draft.json +28 -0
  69. package/fixtures/messaging/valid/root-message-draft.json +25 -0
  70. package/fixtures/messaging/valid/subscription-delivery.json +45 -0
  71. package/package.json +59 -0
  72. package/src/schemas/behavior-fixture.schema.json +370 -0
  73. package/src/schemas/manifest.schema.json +186 -0
  74. package/src/schemas/messaging.schema.json +473 -0
@@ -0,0 +1,475 @@
1
+ {
2
+ "_meta": {
3
+ "executor": "loopback",
4
+ "domain": "messaging",
5
+ "contractVersion": "0.1.0",
6
+ "source": "K-1 mirror adversarial tests plus G-0 signed values (2026-07-15)",
7
+ "description": "Host and SDK loopback scenarios shipped with the contract; P-2 supplies the executor. Signed policy cases remain executable data rather than prose-only obligations."
8
+ },
9
+ "cases": [
10
+ {
11
+ "id": "raw-thread-id-rejection",
12
+ "invariant": "Plugins cannot address threads by raw threadId.",
13
+ "given": {
14
+ "caller": { "pluginInstanceId": "plugin-a" },
15
+ "grants": ["messaging.send"],
16
+ "handles": {},
17
+ "state": { "threads": [{ "threadId": "thread-1" }] }
18
+ },
19
+ "when": {
20
+ "operation": "send",
21
+ "input": {
22
+ "address": { "kind": "thread_id", "threadId": "thread-1" },
23
+ "idempotencyKey": "raw-thread-id-1",
24
+ "payload": {
25
+ "provenance": { "epistemicStatus": "inference" },
26
+ "elements": [{ "elementId": "text-1", "kind": "text", "payload": { "text": "blocked" } }]
27
+ }
28
+ }
29
+ },
30
+ "expect": {
31
+ "status": "error",
32
+ "errorCode": "PERMISSION",
33
+ "sideEffects": [
34
+ { "target": "messages", "assertion": "unchanged" },
35
+ { "target": "output_events", "assertion": "none" },
36
+ { "target": "idempotency_ledger", "assertion": "unchanged" }
37
+ ]
38
+ }
39
+ },
40
+ {
41
+ "id": "system-audience-dual-rejection",
42
+ "invariant": "Drafts cannot express the host-only system audience.",
43
+ "given": {
44
+ "caller": { "pluginInstanceId": "plugin-a" },
45
+ "grants": ["messaging.send"],
46
+ "handles": {
47
+ "target": { "kind": "thread_handle", "token": "thread-handle-a", "ownerPluginInstanceId": "plugin-a", "threadId": "thread-1" }
48
+ },
49
+ "state": {}
50
+ },
51
+ "when": {
52
+ "operation": "send",
53
+ "input": {
54
+ "address": { "kind": "thread_handle", "handle": "thread-handle-a" },
55
+ "draftAudience": { "kind": "system" },
56
+ "idempotencyKey": "system-audience-1",
57
+ "payload": {
58
+ "provenance": { "epistemicStatus": "inference" },
59
+ "elements": [{ "elementId": "text-1", "kind": "text", "payload": { "text": "blocked" } }]
60
+ }
61
+ }
62
+ },
63
+ "expect": {
64
+ "status": "error",
65
+ "errorCode": "VALIDATION",
66
+ "sideEffects": [
67
+ { "target": "messages", "assertion": "unchanged" },
68
+ { "target": "output_events", "assertion": "none" },
69
+ { "target": "idempotency_ledger", "assertion": "unchanged" }
70
+ ]
71
+ }
72
+ },
73
+ {
74
+ "id": "cross-instance-handle-rejection",
75
+ "invariant": "A message handle issued to one plugin instance cannot authorize another.",
76
+ "given": {
77
+ "caller": { "pluginInstanceId": "plugin-a" },
78
+ "grants": ["messaging.appendElements"],
79
+ "handles": {
80
+ "foreignMessage": { "kind": "message_handle", "token": "message-handle-b", "ownerPluginInstanceId": "plugin-b", "threadId": "thread-1", "messageId": "message-1" }
81
+ },
82
+ "state": { "messages": [{ "messageId": "message-1", "revision": 1 }] }
83
+ },
84
+ "when": {
85
+ "operation": "appendElements",
86
+ "input": {
87
+ "handle": { "kind": "message", "token": "message-handle-b" },
88
+ "operationId": "cross-instance-append-1",
89
+ "baseRevision": 1,
90
+ "elements": [{ "elementId": "text-2", "kind": "text", "payload": { "text": "blocked" } }]
91
+ }
92
+ },
93
+ "expect": {
94
+ "status": "error",
95
+ "errorCode": "PERMISSION",
96
+ "sideEffects": [
97
+ { "target": "messages", "assertion": "unchanged" },
98
+ { "target": "output_events", "assertion": "none" },
99
+ { "target": "idempotency_ledger", "assertion": "unchanged" }
100
+ ]
101
+ }
102
+ },
103
+ {
104
+ "id": "origin-forgery-rejection",
105
+ "invariant": "A thread-handle send may only declare the caller's own plugin origin.",
106
+ "given": {
107
+ "caller": { "pluginInstanceId": "plugin-a" },
108
+ "grants": ["messaging.send"],
109
+ "handles": {
110
+ "target": { "kind": "thread_handle", "token": "thread-handle-a", "ownerPluginInstanceId": "plugin-a", "threadId": "thread-1" }
111
+ },
112
+ "state": {}
113
+ },
114
+ "when": {
115
+ "operation": "send",
116
+ "input": {
117
+ "address": { "kind": "thread_handle", "handle": "thread-handle-a" },
118
+ "idempotencyKey": "origin-forgery-1",
119
+ "payload": {
120
+ "provenance": { "origin": { "kind": "plugin", "instanceId": "plugin-b" }, "epistemicStatus": "inference" },
121
+ "elements": [{ "elementId": "text-1", "kind": "text", "payload": { "text": "blocked" } }]
122
+ }
123
+ }
124
+ },
125
+ "expect": {
126
+ "status": "error",
127
+ "errorCode": "PERMISSION",
128
+ "sideEffects": [
129
+ { "target": "messages", "assertion": "unchanged" },
130
+ { "target": "output_events", "assertion": "none" },
131
+ { "target": "provenance", "assertion": "unchanged" }
132
+ ]
133
+ }
134
+ },
135
+ {
136
+ "id": "base-revision-conflict-zero-change",
137
+ "invariant": "A stale baseRevision produces CONFLICT and no mutation.",
138
+ "given": {
139
+ "caller": { "pluginInstanceId": "plugin-a" },
140
+ "grants": ["messaging.appendElements"],
141
+ "handles": {
142
+ "message": { "kind": "message_handle", "token": "message-handle-a", "ownerPluginInstanceId": "plugin-a", "threadId": "thread-1", "messageId": "message-1" }
143
+ },
144
+ "state": { "messages": [{ "messageId": "message-1", "revision": 2 }] }
145
+ },
146
+ "when": {
147
+ "operation": "appendElements",
148
+ "input": {
149
+ "handle": { "kind": "message", "token": "message-handle-a" },
150
+ "operationId": "stale-revision-1",
151
+ "baseRevision": 1,
152
+ "elements": [{ "elementId": "text-2", "kind": "text", "payload": { "text": "blocked" } }]
153
+ }
154
+ },
155
+ "expect": {
156
+ "status": "error",
157
+ "errorCode": "CONFLICT",
158
+ "sideEffects": [
159
+ { "target": "messages", "assertion": "unchanged" },
160
+ { "target": "output_events", "assertion": "none" },
161
+ { "target": "idempotency_ledger", "assertion": "unchanged" }
162
+ ]
163
+ }
164
+ },
165
+ {
166
+ "id": "stale-cursor-snapshot-roundtrip",
167
+ "invariant": "A stale cursor reports stale and snapshot catch-up supplies an exact resume point.",
168
+ "given": {
169
+ "caller": { "pluginInstanceId": "plugin-a" },
170
+ "grants": ["message.event.subscribe"],
171
+ "handles": {
172
+ "subscription": { "kind": "subscription", "token": "subscription-token-a", "ownerPluginInstanceId": "plugin-a", "threadId": "thread-1", "subscriptionId": "subscription-a" }
173
+ },
174
+ "state": { "subscription": { "cursorSequence": 4 }, "eventWindow": { "oldestSequence": 8, "headSequence": 12 } }
175
+ },
176
+ "when": {
177
+ "operation": "read",
178
+ "input": { "subscriptionId": "subscription-a", "limit": 32 }
179
+ },
180
+ "expect": {
181
+ "status": "success",
182
+ "sideEffects": [
183
+ { "target": "subscription", "assertion": "state_equals", "value": { "stale": true, "ackToken": null, "events": [] } },
184
+ { "target": "snapshot", "assertion": "round_trip", "value": { "operation": "snapshot", "resumeSequence": 12, "nextReadStartsAfter": 12 } },
185
+ { "target": "output_events", "assertion": "unchanged" }
186
+ ]
187
+ }
188
+ },
189
+ {
190
+ "id": "cross-subscription-ack-rejection",
191
+ "invariant": "Ack tokens are subscription-local and cannot cross subscriptions.",
192
+ "given": {
193
+ "caller": { "pluginInstanceId": "plugin-a" },
194
+ "grants": ["message.event.subscribe"],
195
+ "handles": {
196
+ "subscriptionA": { "kind": "subscription", "token": "ack-from-a", "ownerPluginInstanceId": "plugin-a", "threadId": "thread-1", "subscriptionId": "subscription-a" },
197
+ "subscriptionB": { "kind": "subscription", "token": "subscription-token-b", "ownerPluginInstanceId": "plugin-a", "threadId": "thread-2", "subscriptionId": "subscription-b" }
198
+ },
199
+ "state": { "subscriptions": [{ "subscriptionId": "subscription-a", "ackedSequence": 1 }, { "subscriptionId": "subscription-b", "ackedSequence": 2 }] }
200
+ },
201
+ "when": {
202
+ "operation": "ack",
203
+ "input": { "subscriptionId": "subscription-b", "ackToken": "ack-from-a" }
204
+ },
205
+ "expect": {
206
+ "status": "error",
207
+ "errorCode": "VALIDATION",
208
+ "sideEffects": [
209
+ { "target": "subscription", "assertion": "unchanged" },
210
+ { "target": "output_events", "assertion": "unchanged" }
211
+ ]
212
+ }
213
+ },
214
+ {
215
+ "id": "reply-to-cross-thread-leakage",
216
+ "invariant": "replyTo cannot reference a message from another thread.",
217
+ "given": {
218
+ "caller": { "pluginInstanceId": "plugin-a" },
219
+ "grants": ["messaging.send"],
220
+ "handles": {
221
+ "target": { "kind": "thread_handle", "token": "thread-handle-a", "ownerPluginInstanceId": "plugin-a", "threadId": "thread-1" }
222
+ },
223
+ "state": { "messages": [{ "messageId": "message-other", "threadId": "thread-2", "text": "secret" }] }
224
+ },
225
+ "when": {
226
+ "operation": "send",
227
+ "input": {
228
+ "address": { "kind": "thread_handle", "handle": "thread-handle-a" },
229
+ "replyTo": "message-other",
230
+ "idempotencyKey": "cross-thread-reply-1",
231
+ "payload": {
232
+ "provenance": { "epistemicStatus": "inference" },
233
+ "elements": [{ "elementId": "text-1", "kind": "text", "payload": { "text": "blocked" } }]
234
+ }
235
+ }
236
+ },
237
+ "expect": {
238
+ "status": "error",
239
+ "errorCode": "VALIDATION",
240
+ "sideEffects": [
241
+ { "target": "messages", "assertion": "unchanged" },
242
+ { "target": "reply_preview", "assertion": "none" },
243
+ { "target": "output_events", "assertion": "none" }
244
+ ]
245
+ }
246
+ },
247
+ {
248
+ "id": "epistemic-status-upgrade-rejection",
249
+ "invariant": "Derived content cannot claim a stronger epistemic status than its source.",
250
+ "given": {
251
+ "caller": { "pluginInstanceId": "plugin-a" },
252
+ "grants": ["messaging.appendElements"],
253
+ "handles": {
254
+ "message": { "kind": "message_handle", "token": "message-handle-a", "ownerPluginInstanceId": "plugin-a", "threadId": "thread-1", "messageId": "message-1" }
255
+ },
256
+ "state": { "messages": [{ "messageId": "message-1", "revision": 1, "elements": [{ "elementId": "source-1", "epistemicStatus": "inference" }] }] }
257
+ },
258
+ "when": {
259
+ "operation": "appendElements",
260
+ "input": {
261
+ "handle": { "kind": "message", "token": "message-handle-a" },
262
+ "operationId": "epistemic-upgrade-1",
263
+ "baseRevision": 1,
264
+ "elements": [{ "elementId": "derived-1", "kind": "text", "payload": { "text": "blocked" }, "derivedFromElementId": "source-1", "epistemicStatus": "user_intent" }]
265
+ }
266
+ },
267
+ "expect": {
268
+ "status": "error",
269
+ "errorCode": "VALIDATION",
270
+ "sideEffects": [
271
+ { "target": "messages", "assertion": "unchanged" },
272
+ { "target": "output_events", "assertion": "none" },
273
+ { "target": "provenance", "assertion": "unchanged" }
274
+ ]
275
+ }
276
+ },
277
+ {
278
+ "id": "preset-l2-rejected",
279
+ "invariant": "First-party presets are derived from L1 and cannot silently grant L2 capabilities.",
280
+ "given": {
281
+ "caller": { "pluginInstanceId": "first-party-plugin" },
282
+ "grants": ["messaging.send"],
283
+ "handles": {},
284
+ "state": { "grantState": { "presetCapabilities": ["messaging.send"] } }
285
+ },
286
+ "when": {
287
+ "operation": "applyGrantPreset",
288
+ "input": { "presetKind": "first_party", "capabilities": ["onMessage"] }
289
+ },
290
+ "expect": {
291
+ "status": "error",
292
+ "errorCode": "PERMISSION",
293
+ "sideEffects": [
294
+ { "target": "grant_state", "assertion": "unchanged" }
295
+ ]
296
+ }
297
+ },
298
+ {
299
+ "id": "preset-visible-revocable",
300
+ "invariant": "A first-party L1 preset remains visible and independently revocable.",
301
+ "given": {
302
+ "caller": { "pluginInstanceId": "first-party-plugin" },
303
+ "grants": ["messaging.send"],
304
+ "handles": {},
305
+ "state": { "grantState": { "messaging.send": { "visible": true, "granted": true } } }
306
+ },
307
+ "when": {
308
+ "operation": "revokeGrant",
309
+ "input": { "capability": "messaging.send" }
310
+ },
311
+ "expect": {
312
+ "status": "success",
313
+ "sideEffects": [
314
+ { "target": "grant_state", "assertion": "state_equals", "value": { "capability": "messaging.send", "visible": true, "granted": false } }
315
+ ]
316
+ }
317
+ },
318
+ {
319
+ "id": "whisper-target-beyond-default-empty-grant-rejected",
320
+ "invariant": "The default whisper target grant is empty; messaging.send alone cannot add a target.",
321
+ "given": {
322
+ "caller": { "pluginInstanceId": "plugin-a" },
323
+ "grants": ["messaging.send"],
324
+ "handles": {
325
+ "target": { "kind": "thread_handle", "token": "thread-handle-a", "ownerPluginInstanceId": "plugin-a", "threadId": "thread-1" }
326
+ },
327
+ "state": { "whisperGrantTargets": [] }
328
+ },
329
+ "when": {
330
+ "operation": "send",
331
+ "input": {
332
+ "address": { "kind": "thread_handle", "handle": "thread-handle-a" },
333
+ "draftAudience": { "kind": "whisper", "targets": ["cat-b"] },
334
+ "idempotencyKey": "whisper-without-target-grant-1",
335
+ "payload": {
336
+ "provenance": { "epistemicStatus": "inference" },
337
+ "elements": [{ "elementId": "text-1", "kind": "text", "payload": { "text": "blocked" } }]
338
+ }
339
+ }
340
+ },
341
+ "expect": {
342
+ "status": "error",
343
+ "errorCode": "PERMISSION",
344
+ "sideEffects": [
345
+ { "target": "messages", "assertion": "unchanged" },
346
+ { "target": "output_events", "assertion": "none" },
347
+ { "target": "idempotency_ledger", "assertion": "unchanged" }
348
+ ]
349
+ }
350
+ },
351
+ {
352
+ "id": "append-without-grant-rejected",
353
+ "invariant": "A valid message handle does not bypass the messaging.appendElements grant.",
354
+ "given": {
355
+ "caller": { "pluginInstanceId": "plugin-a" },
356
+ "grants": [],
357
+ "handles": {
358
+ "message": { "kind": "message_handle", "token": "message-handle-a", "ownerPluginInstanceId": "plugin-a", "threadId": "thread-1", "messageId": "message-1" }
359
+ },
360
+ "state": { "messages": [{ "messageId": "message-1", "revision": 1 }] }
361
+ },
362
+ "when": {
363
+ "operation": "appendElements",
364
+ "input": {
365
+ "handle": { "kind": "message", "token": "message-handle-a" },
366
+ "operationId": "append-without-grant-1",
367
+ "baseRevision": 1,
368
+ "elements": [{ "elementId": "text-2", "kind": "text", "payload": { "text": "blocked" } }]
369
+ }
370
+ },
371
+ "expect": {
372
+ "status": "error",
373
+ "errorCode": "PERMISSION",
374
+ "sideEffects": [
375
+ { "target": "messages", "assertion": "unchanged" },
376
+ { "target": "output_events", "assertion": "none" },
377
+ { "target": "idempotency_ledger", "assertion": "unchanged" }
378
+ ]
379
+ }
380
+ },
381
+ {
382
+ "id": "denied-on-message-rejected",
383
+ "invariant": "The Host cannot deliver an onMessage envelope without an explicit scoped L2 grant.",
384
+ "given": {
385
+ "caller": { "pluginInstanceId": "plugin-a" },
386
+ "grants": [],
387
+ "handles": {
388
+ "target": { "kind": "thread_handle", "token": "thread-handle-a", "ownerPluginInstanceId": "plugin-a", "threadId": "thread-1" }
389
+ },
390
+ "state": {}
391
+ },
392
+ "when": {
393
+ "operation": "deliverOnMessage",
394
+ "input": {
395
+ "threadHandle": "thread-handle-a",
396
+ "envelope": { "messageId": "message-1", "threadId": "thread-1" }
397
+ }
398
+ },
399
+ "expect": {
400
+ "status": "error",
401
+ "errorCode": "PERMISSION",
402
+ "sideEffects": [
403
+ { "target": "messages", "assertion": "unchanged" },
404
+ { "target": "output_events", "assertion": "none" }
405
+ ]
406
+ }
407
+ },
408
+ {
409
+ "id": "permission-matrix-complete",
410
+ "invariant": "The loopback policy matrix covers every signed capability and presets only L1 for first-party plugins.",
411
+ "given": {
412
+ "caller": { "pluginInstanceId": "policy-auditor" },
413
+ "grants": [],
414
+ "handles": {},
415
+ "state": {}
416
+ },
417
+ "when": {
418
+ "operation": "checkPermissionMatrix",
419
+ "input": {
420
+ "entries": [
421
+ { "capability": "plugin.config.read", "layer": "L0", "firstPartyPreset": false },
422
+ { "capability": "plugin.state.get", "layer": "L0", "firstPartyPreset": false },
423
+ { "capability": "plugin.state.set", "layer": "L0", "firstPartyPreset": false },
424
+ { "capability": "messaging.send", "layer": "L1", "firstPartyPreset": true },
425
+ { "capability": "schedule.register", "layer": "L1", "firstPartyPreset": true },
426
+ { "capability": "events.publish", "layer": "L1", "firstPartyPreset": true },
427
+ { "capability": "messaging.appendElements", "layer": "L1", "firstPartyPreset": true },
428
+ { "capability": "onMessage", "layer": "L2", "firstPartyPreset": false },
429
+ { "capability": "message.event.subscribe", "layer": "L2", "firstPartyPreset": false },
430
+ { "capability": "secret.read", "layer": "L2", "firstPartyPreset": false },
431
+ { "capability": "thread.listMetadata", "layer": "L2", "firstPartyPreset": false },
432
+ { "capability": "thread.readContent", "layer": "L2", "firstPartyPreset": false },
433
+ { "capability": "memory.query", "layer": "L2", "firstPartyPreset": false },
434
+ { "capability": "memory.append", "layer": "L2", "firstPartyPreset": false },
435
+ { "capability": "memory.retrieve", "layer": "L2", "firstPartyPreset": false },
436
+ { "capability": "windows.create", "layer": "L2", "firstPartyPreset": false },
437
+ { "capability": "whisper.extend", "layer": "L2", "firstPartyPreset": false }
438
+ ]
439
+ }
440
+ },
441
+ "expect": {
442
+ "status": "success",
443
+ "sideEffects": [
444
+ { "target": "permission_matrix", "assertion": "matches", "value": { "complete": true, "firstPartyPresetLayers": ["L1"], "defaultWhisperTargets": [] } }
445
+ ]
446
+ }
447
+ },
448
+ {
449
+ "id": "delete-replay-events-preserves-canonical-messages",
450
+ "invariant": "Deleting the replay transport buffer cannot delete canonical messages or threads.",
451
+ "given": {
452
+ "caller": { "pluginInstanceId": "host-retention" },
453
+ "grants": ["message.event.subscribe"],
454
+ "handles": {
455
+ "subscription": { "kind": "subscription", "token": "subscription-token-a", "ownerPluginInstanceId": "host-retention", "threadId": "thread-1", "subscriptionId": "subscription-a" }
456
+ },
457
+ "state": {
458
+ "messages": [{ "messageId": "message-1", "threadId": "thread-1" }],
459
+ "replayEvents": [{ "eventId": "event-1", "sequence": 1 }]
460
+ }
461
+ },
462
+ "when": {
463
+ "operation": "deleteReplayEvents",
464
+ "input": { "subscriptionId": "subscription-a", "throughSequence": 1 }
465
+ },
466
+ "expect": {
467
+ "status": "success",
468
+ "sideEffects": [
469
+ { "target": "replay_events", "assertion": "none" },
470
+ { "target": "messages", "assertion": "unchanged" }
471
+ ]
472
+ }
473
+ }
474
+ ]
475
+ }
File without changes
@@ -0,0 +1,17 @@
1
+ {
2
+ "pluginId": "dev.clowder.fixture.invalid-contract-prerelease",
3
+ "version": "1.0.0",
4
+ "contractVersion": "0.1.0-01",
5
+ "name": "Invalid contract prerelease fixture",
6
+ "features": [
7
+ {
8
+ "id": "echo",
9
+ "name": "Echo",
10
+ "resources": [],
11
+ "capabilities": []
12
+ }
13
+ ],
14
+ "runtime": {
15
+ "transport": "builtin"
16
+ }
17
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "pluginId": "dev.clowder.fixture.invalid-contract-range",
3
+ "version": "0.1.0",
4
+ "contractVersion": "^0.1.0",
5
+ "name": "Invalid contract range fixture",
6
+ "features": [
7
+ {
8
+ "id": "echo",
9
+ "name": "Echo",
10
+ "resources": [],
11
+ "capabilities": []
12
+ }
13
+ ],
14
+ "runtime": {
15
+ "transport": "builtin"
16
+ }
17
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "pluginId": "dev.clowder.fixture.ipc-empty-entrypoint",
3
+ "version": "0.1.0",
4
+ "contractVersion": "0.1.0",
5
+ "name": "Invalid IPC fixture plugin",
6
+ "features": [
7
+ {
8
+ "id": "echo",
9
+ "name": "Echo",
10
+ "resources": [],
11
+ "capabilities": []
12
+ }
13
+ ],
14
+ "runtime": {
15
+ "transport": "ipc",
16
+ "entrypoint": ""
17
+ }
18
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "pluginId": "dev.clowder.fixture.invalid-plugin-prerelease",
3
+ "version": "1.0.0-01",
4
+ "contractVersion": "0.1.0",
5
+ "name": "Invalid plugin prerelease fixture",
6
+ "features": [
7
+ {
8
+ "id": "echo",
9
+ "name": "Echo",
10
+ "resources": [],
11
+ "capabilities": []
12
+ }
13
+ ],
14
+ "runtime": {
15
+ "transport": "builtin"
16
+ }
17
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "pluginId": "dev.clowder.fixture.invalid-plugin-version",
3
+ "version": "latest",
4
+ "contractVersion": "0.1.0",
5
+ "name": "Invalid plugin version fixture",
6
+ "features": [
7
+ {
8
+ "id": "echo",
9
+ "name": "Echo",
10
+ "resources": [],
11
+ "capabilities": []
12
+ }
13
+ ],
14
+ "runtime": {
15
+ "transport": "builtin"
16
+ }
17
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "pluginId": "dev.clowder.fixture.semver-empty-build",
3
+ "version": "1.2.3+",
4
+ "contractVersion": "0.1.0",
5
+ "name": "Invalid empty SemVer build metadata fixture",
6
+ "features": [
7
+ {
8
+ "id": "echo",
9
+ "name": "Echo",
10
+ "resources": [],
11
+ "capabilities": []
12
+ }
13
+ ],
14
+ "runtime": {
15
+ "transport": "builtin"
16
+ }
17
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "pluginId": "dev.clowder.fixture.stdio-missing-entrypoint",
3
+ "version": "0.1.0",
4
+ "contractVersion": "0.1.0",
5
+ "name": "Invalid stdio fixture plugin",
6
+ "features": [
7
+ {
8
+ "id": "echo",
9
+ "name": "Echo",
10
+ "resources": [],
11
+ "capabilities": []
12
+ }
13
+ ],
14
+ "runtime": {
15
+ "transport": "stdio"
16
+ }
17
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "pluginId": "dev.clowder.fixture.unknown-capability",
3
+ "version": "0.1.0",
4
+ "contractVersion": "0.1.0",
5
+ "name": "Unknown capability fixture",
6
+ "features": [
7
+ {
8
+ "id": "escape",
9
+ "name": "Escape",
10
+ "resources": [],
11
+ "capabilities": [
12
+ "host.unrestricted"
13
+ ]
14
+ }
15
+ ],
16
+ "runtime": {
17
+ "transport": "builtin"
18
+ }
19
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "pluginId": "dev.clowder.fixture.invalid-lifecycle",
3
+ "version": "0.1.0",
4
+ "contractVersion": "0.1.0",
5
+ "name": "Invalid lifecycle fixture",
6
+ "features": [
7
+ {
8
+ "id": "notes",
9
+ "name": "Notes",
10
+ "resources": [],
11
+ "capabilities": []
12
+ }
13
+ ],
14
+ "data": [
15
+ {
16
+ "name": "notes",
17
+ "dataClass": "user-authored",
18
+ "strategy": "lifecycle"
19
+ }
20
+ ],
21
+ "runtime": {
22
+ "transport": "builtin"
23
+ }
24
+ }
File without changes