feat: add validated learned skill registry core

This commit is contained in:
Maximiliano Korp
2026-09-10 14:42:58 -07:00
parent ee045e49b3
commit 2e08636afe
20 changed files with 2151 additions and 2 deletions
@@ -0,0 +1,19 @@
# Learned skill delivery conformance, version 1
These JSON fixtures contain synthetic skills and virtual-clock scenarios. Each language adapter can use the same input and expected result. The fixtures test the learned-skill contract independently of Runtime socket tests.
`snapshots.v1.json` provides complete ZIP bytes in `archiveBase64`, the response revision and ETag, and an expected result. `valid` means validation succeeds. Any other expected value is a delivery error code. The ZIP bytes include stored files, deflated files, data descriptors, UTF-8 names, and invalid structures. Supporting binary resources remain part of verified membership but have no model-readable text.
`lifecycle.v1.json` starts each independent scenario at `initialTimeMs`. Each step advances the virtual clock by `advanceMs`, queues its optional registry reply, and acquires a snapshot. A reply refers to a named snapshot fixture, a matching unchanged result, or a typed error. The expected value gives the result or error, request count, and relevant status fields. Request-count assertions enforce freshness and the absence of hidden retries. Concurrent refreshes and native cancellation also have focused TypeScript tests.
## Ordering and names
Compare skill names and relative file paths by their UTF-8 bytes, in ascending lexicographic order. For valid Unicode strings, this is Unicode codepoint order. Do not apply locale collation, case folding, or Unicode normalization. The fixtures include U+E000 and U+10000 to distinguish this order from JavaScript's default UTF-16 order.
Archive names must decode as strict UTF-8 and retain their exact characters, including a leading BOM. Paths are relative and contain no empty component, dot component, parent component, backslash, control character, or Windows drive prefix. File paths remain relative to one skill directory. Explicit directory entries contain no bytes and do not add readable files. Every regular file must match manifest membership. Each skill has exactly one `SKILL.md`, which must decode as UTF-8.
## Internal corruption bounds
The adapter accepts at most 32 MiB of archive bytes, 32 MiB of total uncompressed entry bytes, and 1,000 archive entries. The entry count includes the manifest and explicit directories. The decoded byte total includes the manifest. These are internal validation bounds, not additional configuration options or server publication rules.
Each file must match its declared size and SHA-256 digest. The complete ZIP must match the strong quoted SHA-256 ETag. The manifest revision must match the response revision. Unknown numeric schema versions produce `UNSUPPORTED_SERVER`; malformed snapshots produce `INVALID_SNAPSHOT`. Additive manifest fields remain compatible.
@@ -0,0 +1,259 @@
{
"contract": "copilotkit-learned-skills-v1",
"initialTimeMs": 0,
"cases": [
{
"name": "empty-initialization",
"steps": [
{
"reply": {
"snapshot": "empty"
},
"expect": {
"revision": "r1",
"stale": false,
"requests": 1
}
}
]
},
{
"name": "latest-freshness-and-change",
"steps": [
{
"reply": {
"snapshot": "text-skill"
},
"expect": {
"revision": "r1",
"stale": false,
"requests": 1
}
},
{
"advanceMs": 4999,
"expect": {
"revision": "r1",
"stale": false,
"requests": 1
}
},
{
"advanceMs": 1,
"reply": {
"snapshot": "empty-r2"
},
"expect": {
"revision": "r2",
"stale": false,
"requests": 2
}
}
]
},
{
"name": "unchanged-restarts-window",
"steps": [
{
"reply": {
"snapshot": "text-skill"
},
"expect": {
"revision": "r1",
"requests": 1
}
},
{
"advanceMs": 5000,
"reply": {
"unchanged": "text-skill"
},
"expect": {
"revision": "r1",
"stale": false,
"requests": 2,
"lastCheckedAt": "1970-01-01T00:00:05.000Z"
}
},
{
"advanceMs": 4999,
"expect": {
"revision": "r1",
"requests": 2
}
}
]
},
{
"name": "cold-retry",
"steps": [
{
"reply": {
"error": "NETWORK_ERROR",
"retryable": true
},
"expect": {
"error": "NETWORK_ERROR",
"initialized": false,
"requests": 1
}
},
{
"reply": {
"snapshot": "empty"
},
"expect": {
"revision": "r1",
"initialized": true,
"requests": 2
}
}
]
},
{
"name": "indefinite-warm-stale",
"steps": [
{
"reply": {
"snapshot": "text-skill"
},
"expect": {
"revision": "r1",
"requests": 1
}
},
{
"advanceMs": 31536000000,
"reply": {
"error": "NETWORK_ERROR",
"retryable": true
},
"expect": {
"revision": "r1",
"stale": true,
"requests": 2
}
},
{
"advanceMs": 31536000000,
"reply": {
"error": "TIMEOUT",
"retryable": true
},
"expect": {
"revision": "r1",
"stale": true,
"requests": 3
}
}
]
},
{
"name": "confirmed-revocation-survives-network-failure",
"steps": [
{
"reply": {
"snapshot": "text-skill"
},
"expect": {
"revision": "r1",
"requests": 1
}
},
{
"advanceMs": 5000,
"reply": {
"error": "REVISION_REVOKED",
"retryable": false
},
"expect": {
"error": "REVISION_REVOKED",
"requests": 2
}
},
{
"reply": {
"error": "NETWORK_ERROR",
"retryable": true
},
"expect": {
"error": "REVISION_REVOKED",
"requests": 3
}
}
]
},
{
"name": "pin-never-advances",
"config": {
"revision": "r1"
},
"steps": [
{
"reply": {
"snapshot": "text-skill"
},
"expect": {
"revision": "r1",
"requests": 1
}
},
{
"advanceMs": 5000,
"reply": {
"snapshot": "empty-r2"
},
"expect": {
"revision": "r1",
"stale": true,
"requests": 2
}
}
]
},
{
"name": "missing-pin-is-not-latest",
"config": {
"revision": "not-found"
},
"steps": [
{
"reply": {
"error": "REVISION_NOT_FOUND",
"retryable": false
},
"expect": {
"error": "REVISION_NOT_FOUND",
"initialized": false,
"requests": 1
}
}
]
},
{
"name": "invalid-replacement-keeps-verified-snapshot",
"steps": [
{
"reply": {
"snapshot": "text-skill"
},
"expect": {
"revision": "r1",
"requests": 1
}
},
{
"advanceMs": 5000,
"reply": {
"snapshot": "file-digest-mismatch"
},
"expect": {
"revision": "r1",
"stale": true,
"requests": 2
}
}
]
}
]
}
@@ -0,0 +1,201 @@
{
"contract": "copilotkit-learned-skills-v1",
"cases": [
{
"name": "empty",
"archiveBase64": "UEsDBBQAAAAAAAAAIVA5p4r9LwAAAC8AAAANAAAAbWFuaWZlc3QuanNvbnsic2NoZW1hVmVyc2lvbiI6MSwicmV2aXNpb24iOiJyMSIsInNraWxscyI6W119UEsBAhQDFAAAAAAAAAAhUDmniv0vAAAALwAAAA0AAAAAAAAAAAAAAIABAAAAAG1hbmlmZXN0Lmpzb25QSwUGAAAAAAEAAQA7AAAAWgAAAAAA",
"revision": "r1",
"etag": "\"50aa73bf13398b9fbe841e6e97c9eaff17099b4b0167e4fc224d1854d3273308\"",
"expected": "valid"
},
{
"name": "text-skill",
"archiveBase64": "UEsDBBQAAAAAAAAAIVB5aqleVQEAAFUBAAANAAAAbWFuaWZlc3QuanNvbnsic2NoZW1hVmVyc2lvbiI6MSwicmV2aXNpb24iOiJyMSIsInNraWxscyI6W3sibmFtZSI6InJlZnVuZC1wb2xpY3kiLCJkZXNjcmlwdGlvbiI6IlVzZSB3aGVuIGhhbmRsaW5nIHJlZnVuZHMuIiwiZmlsZXMiOlt7InBhdGgiOiJTS0lMTC5tZCIsInNpemUiOjQ5LCJzaGEyNTYiOiI2ZWQ0MjVhYjc5NzQ5N2MxNTM3YjRmOTg4MTE5OGU4NjU5Y2UyZjUzYjE3OTZhMDFiZmI3NmU0NGZhMDIyZTM5In0seyJwYXRoIjoicmVmZXJlbmNlLnR4dCIsInNpemUiOjM1LCJzaGEyNTYiOiI0ZWYxNGYwOTk4OTQ4YmQ1ZmRiZDliZWMyMzYyMmM5YjJiYmI3M2ZkZDQzNGFmYzg0ZjQxOWRiYjc1YzBiZDJjIn1dfV19UEsDBBQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAcmVmdW5kLXBvbGljeS9TS0lMTC5tZCMgUmVmdW5kIHBvbGljeQpVc2UgdGhlIHB1Ymxpc2hlZCByZWZ1bmQgcG9saWN5LgpQSwMEFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAByZWZ1bmQtcG9saWN5L3JlZmVyZW5jZS50eHRSZWZ1bmRzIGFyZSBhdmFpbGFibGUgZm9yIDMwIGRheXMuClBLAQIUAxQAAAAAAAAAIVB5aqleVQEAAFUBAAANAAAAAAAAAAAAAACAAQAAAABtYW5pZmVzdC5qc29uUEsBAhQDFAAAAAAAAAAhUAYiQNMxAAAAMQAAABYAAAAAAAAAAAAAAIABgAEAAHJlZnVuZC1wb2xpY3kvU0tJTEwubWRQSwECFAMUAAAAAAAAACFQ07pcbyMAAAAjAAAAGwAAAAAAAAAAAAAAgAHlAQAAcmVmdW5kLXBvbGljeS9yZWZlcmVuY2UudHh0UEsFBgAAAAADAAMAyAAAAEECAAAAAA==",
"revision": "r1",
"etag": "\"139773b71315c562db80ead7ec4318982cab1ddc6671d53c776a2eab55d9a775\"",
"expected": "valid"
},
{
"name": "additive-fields",
"archiveBase64": "UEsDBBQAAAAAAAAAIVATQbT6lgEAAJYBAAANAAAAbWFuaWZlc3QuanNvbnsic2NoZW1hVmVyc2lvbiI6MSwicmV2aXNpb24iOiJyMSIsInNraWxscyI6W3sibmFtZSI6InJlZnVuZC1wb2xpY3kiLCJkZXNjcmlwdGlvbiI6IlVzZSB3aGVuIGhhbmRsaW5nIHJlZnVuZHMuIiwiZmlsZXMiOlt7InBhdGgiOiJTS0lMTC5tZCIsInNpemUiOjQ5LCJzaGEyNTYiOiI2ZWQ0MjVhYjc5NzQ5N2MxNTM3YjRmOTg4MTE5OGU4NjU5Y2UyZjUzYjE3OTZhMDFiZmI3NmU0NGZhMDIyZTM5IiwiZnV0dXJlIjoic3VwcG9ydGVkIn0seyJwYXRoIjoicmVmZXJlbmNlLnR4dCIsInNpemUiOjM1LCJzaGEyNTYiOiI0ZWYxNGYwOTk4OTQ4YmQ1ZmRiZDliZWMyMzYyMmM5YjJiYmI3M2ZkZDQzNGFmYzg0ZjQxOWRiYjc1YzBiZDJjIn1dLCJmdXR1cmUiOjEyM31dLCJmdXR1cmVGaWVsZCI6eyJlbmFibGVkIjp0cnVlfX1QSwMEFAAAAAAAAAAhUAYiQNMxAAAAMQAAABYAAAByZWZ1bmQtcG9saWN5L1NLSUxMLm1kIyBSZWZ1bmQgcG9saWN5ClVzZSB0aGUgcHVibGlzaGVkIHJlZnVuZCBwb2xpY3kuClBLAwQUAAAAAAAAACFQ07pcbyMAAAAjAAAAGwAAAHJlZnVuZC1wb2xpY3kvcmVmZXJlbmNlLnR4dFJlZnVuZHMgYXJlIGF2YWlsYWJsZSBmb3IgMzAgZGF5cy4KUEsBAhQDFAAAAAAAAAAhUBNBtPqWAQAAlgEAAA0AAAAAAAAAAAAAAIABAAAAAG1hbmlmZXN0Lmpzb25QSwECFAMUAAAAAAAAACFQBiJA0zEAAAAxAAAAFgAAAAAAAAAAAAAAgAHBAQAAcmVmdW5kLXBvbGljeS9TS0lMTC5tZFBLAQIUAxQAAAAAAAAAIVDTulxvIwAAACMAAAAbAAAAAAAAAAAAAACAASYCAAByZWZ1bmQtcG9saWN5L3JlZmVyZW5jZS50eHRQSwUGAAAAAAMAAwDIAAAAggIAAAAA",
"revision": "r1",
"etag": "\"510aa6f240caddbbf1655c175dd31a0df68b2e9eeaf1bc56fcb6bb37144ba5a0\"",
"expected": "valid"
},
{
"name": "unknown-schema",
"archiveBase64": "UEsDBBQAAAAAAAAAIVDtBaXCVQEAAFUBAAANAAAAbWFuaWZlc3QuanNvbnsic2NoZW1hVmVyc2lvbiI6MiwicmV2aXNpb24iOiJyMSIsInNraWxscyI6W3sibmFtZSI6InJlZnVuZC1wb2xpY3kiLCJkZXNjcmlwdGlvbiI6IlVzZSB3aGVuIGhhbmRsaW5nIHJlZnVuZHMuIiwiZmlsZXMiOlt7InBhdGgiOiJTS0lMTC5tZCIsInNpemUiOjQ5LCJzaGEyNTYiOiI2ZWQ0MjVhYjc5NzQ5N2MxNTM3YjRmOTg4MTE5OGU4NjU5Y2UyZjUzYjE3OTZhMDFiZmI3NmU0NGZhMDIyZTM5In0seyJwYXRoIjoicmVmZXJlbmNlLnR4dCIsInNpemUiOjM1LCJzaGEyNTYiOiI0ZWYxNGYwOTk4OTQ4YmQ1ZmRiZDliZWMyMzYyMmM5YjJiYmI3M2ZkZDQzNGFmYzg0ZjQxOWRiYjc1YzBiZDJjIn1dfV19UEsDBBQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAcmVmdW5kLXBvbGljeS9TS0lMTC5tZCMgUmVmdW5kIHBvbGljeQpVc2UgdGhlIHB1Ymxpc2hlZCByZWZ1bmQgcG9saWN5LgpQSwMEFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAByZWZ1bmQtcG9saWN5L3JlZmVyZW5jZS50eHRSZWZ1bmRzIGFyZSBhdmFpbGFibGUgZm9yIDMwIGRheXMuClBLAQIUAxQAAAAAAAAAIVDtBaXCVQEAAFUBAAANAAAAAAAAAAAAAACAAQAAAABtYW5pZmVzdC5qc29uUEsBAhQDFAAAAAAAAAAhUAYiQNMxAAAAMQAAABYAAAAAAAAAAAAAAIABgAEAAHJlZnVuZC1wb2xpY3kvU0tJTEwubWRQSwECFAMUAAAAAAAAACFQ07pcbyMAAAAjAAAAGwAAAAAAAAAAAAAAgAHlAQAAcmVmdW5kLXBvbGljeS9yZWZlcmVuY2UudHh0UEsFBgAAAAADAAMAyAAAAEECAAAAAA==",
"revision": "r1",
"etag": "\"eeda89905e12ad423dd9b547c8bd4d529af1c59310cb91cc199fcf46ef0fce8a\"",
"expected": "UNSUPPORTED_SERVER"
},
{
"name": "revision-mismatch",
"archiveBase64": "UEsDBBQAAAAAAAAAIVCeZeMOWAEAAFgBAAANAAAAbWFuaWZlc3QuanNvbnsic2NoZW1hVmVyc2lvbiI6MSwicmV2aXNpb24iOiJvdGhlciIsInNraWxscyI6W3sibmFtZSI6InJlZnVuZC1wb2xpY3kiLCJkZXNjcmlwdGlvbiI6IlVzZSB3aGVuIGhhbmRsaW5nIHJlZnVuZHMuIiwiZmlsZXMiOlt7InBhdGgiOiJTS0lMTC5tZCIsInNpemUiOjQ5LCJzaGEyNTYiOiI2ZWQ0MjVhYjc5NzQ5N2MxNTM3YjRmOTg4MTE5OGU4NjU5Y2UyZjUzYjE3OTZhMDFiZmI3NmU0NGZhMDIyZTM5In0seyJwYXRoIjoicmVmZXJlbmNlLnR4dCIsInNpemUiOjM1LCJzaGEyNTYiOiI0ZWYxNGYwOTk4OTQ4YmQ1ZmRiZDliZWMyMzYyMmM5YjJiYmI3M2ZkZDQzNGFmYzg0ZjQxOWRiYjc1YzBiZDJjIn1dfV19UEsDBBQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAcmVmdW5kLXBvbGljeS9TS0lMTC5tZCMgUmVmdW5kIHBvbGljeQpVc2UgdGhlIHB1Ymxpc2hlZCByZWZ1bmQgcG9saWN5LgpQSwMEFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAByZWZ1bmQtcG9saWN5L3JlZmVyZW5jZS50eHRSZWZ1bmRzIGFyZSBhdmFpbGFibGUgZm9yIDMwIGRheXMuClBLAQIUAxQAAAAAAAAAIVCeZeMOWAEAAFgBAAANAAAAAAAAAAAAAACAAQAAAABtYW5pZmVzdC5qc29uUEsBAhQDFAAAAAAAAAAhUAYiQNMxAAAAMQAAABYAAAAAAAAAAAAAAIABgwEAAHJlZnVuZC1wb2xpY3kvU0tJTEwubWRQSwECFAMUAAAAAAAAACFQ07pcbyMAAAAjAAAAGwAAAAAAAAAAAAAAgAHoAQAAcmVmdW5kLXBvbGljeS9yZWZlcmVuY2UudHh0UEsFBgAAAAADAAMAyAAAAEQCAAAAAA==",
"revision": "r1",
"etag": "\"0ac5591761aaeaa7aa2bebbd9c7bfaab2dea6ce2d35c42673d16b7cf953da608\"",
"expected": "INVALID_SNAPSHOT"
},
{
"name": "file-digest-mismatch",
"archiveBase64": "UEsDBBQAAAAAAAAAIVDx+HBhVQEAAFUBAAANAAAAbWFuaWZlc3QuanNvbnsic2NoZW1hVmVyc2lvbiI6MSwicmV2aXNpb24iOiJyMSIsInNraWxscyI6W3sibmFtZSI6InJlZnVuZC1wb2xpY3kiLCJkZXNjcmlwdGlvbiI6IlVzZSB3aGVuIGhhbmRsaW5nIHJlZnVuZHMuIiwiZmlsZXMiOlt7InBhdGgiOiJTS0lMTC5tZCIsInNpemUiOjQ5LCJzaGEyNTYiOiIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn0seyJwYXRoIjoicmVmZXJlbmNlLnR4dCIsInNpemUiOjM1LCJzaGEyNTYiOiI0ZWYxNGYwOTk4OTQ4YmQ1ZmRiZDliZWMyMzYyMmM5YjJiYmI3M2ZkZDQzNGFmYzg0ZjQxOWRiYjc1YzBiZDJjIn1dfV19UEsDBBQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAcmVmdW5kLXBvbGljeS9TS0lMTC5tZCMgUmVmdW5kIHBvbGljeQpVc2UgdGhlIHB1Ymxpc2hlZCByZWZ1bmQgcG9saWN5LgpQSwMEFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAByZWZ1bmQtcG9saWN5L3JlZmVyZW5jZS50eHRSZWZ1bmRzIGFyZSBhdmFpbGFibGUgZm9yIDMwIGRheXMuClBLAQIUAxQAAAAAAAAAIVDx+HBhVQEAAFUBAAANAAAAAAAAAAAAAACAAQAAAABtYW5pZmVzdC5qc29uUEsBAhQDFAAAAAAAAAAhUAYiQNMxAAAAMQAAABYAAAAAAAAAAAAAAIABgAEAAHJlZnVuZC1wb2xpY3kvU0tJTEwubWRQSwECFAMUAAAAAAAAACFQ07pcbyMAAAAjAAAAGwAAAAAAAAAAAAAAgAHlAQAAcmVmdW5kLXBvbGljeS9yZWZlcmVuY2UudHh0UEsFBgAAAAADAAMAyAAAAEECAAAAAA==",
"revision": "r1",
"etag": "\"48ea4a449c8089175ba31b2f39a9e4ab9a74367713add42e44ab95015712e83e\"",
"expected": "INVALID_SNAPSHOT"
},
{
"name": "file-size-mismatch",
"archiveBase64": "UEsDBBQAAAAAAAAAIVDnVs55VQEAAFUBAAANAAAAbWFuaWZlc3QuanNvbnsic2NoZW1hVmVyc2lvbiI6MSwicmV2aXNpb24iOiJyMSIsInNraWxscyI6W3sibmFtZSI6InJlZnVuZC1wb2xpY3kiLCJkZXNjcmlwdGlvbiI6IlVzZSB3aGVuIGhhbmRsaW5nIHJlZnVuZHMuIiwiZmlsZXMiOlt7InBhdGgiOiJTS0lMTC5tZCIsInNpemUiOjUwLCJzaGEyNTYiOiI2ZWQ0MjVhYjc5NzQ5N2MxNTM3YjRmOTg4MTE5OGU4NjU5Y2UyZjUzYjE3OTZhMDFiZmI3NmU0NGZhMDIyZTM5In0seyJwYXRoIjoicmVmZXJlbmNlLnR4dCIsInNpemUiOjM1LCJzaGEyNTYiOiI0ZWYxNGYwOTk4OTQ4YmQ1ZmRiZDliZWMyMzYyMmM5YjJiYmI3M2ZkZDQzNGFmYzg0ZjQxOWRiYjc1YzBiZDJjIn1dfV19UEsDBBQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAcmVmdW5kLXBvbGljeS9TS0lMTC5tZCMgUmVmdW5kIHBvbGljeQpVc2UgdGhlIHB1Ymxpc2hlZCByZWZ1bmQgcG9saWN5LgpQSwMEFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAByZWZ1bmQtcG9saWN5L3JlZmVyZW5jZS50eHRSZWZ1bmRzIGFyZSBhdmFpbGFibGUgZm9yIDMwIGRheXMuClBLAQIUAxQAAAAAAAAAIVDnVs55VQEAAFUBAAANAAAAAAAAAAAAAACAAQAAAABtYW5pZmVzdC5qc29uUEsBAhQDFAAAAAAAAAAhUAYiQNMxAAAAMQAAABYAAAAAAAAAAAAAAIABgAEAAHJlZnVuZC1wb2xpY3kvU0tJTEwubWRQSwECFAMUAAAAAAAAACFQ07pcbyMAAAAjAAAAGwAAAAAAAAAAAAAAgAHlAQAAcmVmdW5kLXBvbGljeS9yZWZlcmVuY2UudHh0UEsFBgAAAAADAAMAyAAAAEECAAAAAA==",
"revision": "r1",
"etag": "\"90ff30557b91cfd32a504d02d161954ae2d0e71b8b5787827af0eb2cd3a7aa98\"",
"expected": "INVALID_SNAPSHOT"
},
{
"name": "missing-manifest",
"archiveBase64": "UEsDBBQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAcmVmdW5kLXBvbGljeS9TS0lMTC5tZCMgUmVmdW5kIHBvbGljeQpVc2UgdGhlIHB1Ymxpc2hlZCByZWZ1bmQgcG9saWN5LgpQSwMEFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAByZWZ1bmQtcG9saWN5L3JlZmVyZW5jZS50eHRSZWZ1bmRzIGFyZSBhdmFpbGFibGUgZm9yIDMwIGRheXMuClBLAQIUAxQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAAAAAAAAAAACAAQAAAAByZWZ1bmQtcG9saWN5L1NLSUxMLm1kUEsBAhQDFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAAAAAAAAAAAAIABZQAAAHJlZnVuZC1wb2xpY3kvcmVmZXJlbmNlLnR4dFBLBQYAAAAAAgACAI0AAADBAAAAAAA=",
"revision": "r1",
"etag": "\"eb2dc113f780ec0df3e0905e24c69589d324786db03b7a82555ca97602bbe2d7\"",
"expected": "INVALID_SNAPSHOT"
},
{
"name": "invalid-manifest-utf8",
"archiveBase64": "UEsDBBQAAAAAAAAAIVAAAAD/AQAAAAEAAAANAAAAbWFuaWZlc3QuanNvbv9QSwMEFAAAAAAAAAAhUAYiQNMxAAAAMQAAABYAAAByZWZ1bmQtcG9saWN5L1NLSUxMLm1kIyBSZWZ1bmQgcG9saWN5ClVzZSB0aGUgcHVibGlzaGVkIHJlZnVuZCBwb2xpY3kuClBLAwQUAAAAAAAAACFQ07pcbyMAAAAjAAAAGwAAAHJlZnVuZC1wb2xpY3kvcmVmZXJlbmNlLnR4dFJlZnVuZHMgYXJlIGF2YWlsYWJsZSBmb3IgMzAgZGF5cy4KUEsBAhQDFAAAAAAAAAAhUAAAAP8BAAAAAQAAAA0AAAAAAAAAAAAAAIABAAAAAG1hbmlmZXN0Lmpzb25QSwECFAMUAAAAAAAAACFQBiJA0zEAAAAxAAAAFgAAAAAAAAAAAAAAgAEsAAAAcmVmdW5kLXBvbGljeS9TS0lMTC5tZFBLAQIUAxQAAAAAAAAAIVDTulxvIwAAACMAAAAbAAAAAAAAAAAAAACAAZEAAAByZWZ1bmQtcG9saWN5L3JlZmVyZW5jZS50eHRQSwUGAAAAAAMAAwDIAAAA7QAAAAAA",
"revision": "r1",
"etag": "\"fb3d23b6047e459e12e4a5a3c268ed7e979f3c88bded5394f1a82f83c8a594c9\"",
"expected": "INVALID_SNAPSHOT"
},
{
"name": "malformed-manifest",
"archiveBase64": "UEsDBBQAAAAAAAAAIVBmy4zGCAAAAAgAAAANAAAAbWFuaWZlc3QuanNvbm5vdCBqc29uUEsDBBQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAcmVmdW5kLXBvbGljeS9TS0lMTC5tZCMgUmVmdW5kIHBvbGljeQpVc2UgdGhlIHB1Ymxpc2hlZCByZWZ1bmQgcG9saWN5LgpQSwMEFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAByZWZ1bmQtcG9saWN5L3JlZmVyZW5jZS50eHRSZWZ1bmRzIGFyZSBhdmFpbGFibGUgZm9yIDMwIGRheXMuClBLAQIUAxQAAAAAAAAAIVBmy4zGCAAAAAgAAAANAAAAAAAAAAAAAACAAQAAAABtYW5pZmVzdC5qc29uUEsBAhQDFAAAAAAAAAAhUAYiQNMxAAAAMQAAABYAAAAAAAAAAAAAAIABMwAAAHJlZnVuZC1wb2xpY3kvU0tJTEwubWRQSwECFAMUAAAAAAAAACFQ07pcbyMAAAAjAAAAGwAAAAAAAAAAAAAAgAGYAAAAcmVmdW5kLXBvbGljeS9yZWZlcmVuY2UudHh0UEsFBgAAAAADAAMAyAAAAPQAAAAAAA==",
"revision": "r1",
"etag": "\"fe2df71e7df2689969d4135db1c8cff276a15679b5f78a249b0b1566d6bf3153\"",
"expected": "INVALID_SNAPSHOT"
},
{
"name": "unlisted-member",
"archiveBase64": "UEsDBBQAAAAAAAAAIVB5aqleVQEAAFUBAAANAAAAbWFuaWZlc3QuanNvbnsic2NoZW1hVmVyc2lvbiI6MSwicmV2aXNpb24iOiJyMSIsInNraWxscyI6W3sibmFtZSI6InJlZnVuZC1wb2xpY3kiLCJkZXNjcmlwdGlvbiI6IlVzZSB3aGVuIGhhbmRsaW5nIHJlZnVuZHMuIiwiZmlsZXMiOlt7InBhdGgiOiJTS0lMTC5tZCIsInNpemUiOjQ5LCJzaGEyNTYiOiI2ZWQ0MjVhYjc5NzQ5N2MxNTM3YjRmOTg4MTE5OGU4NjU5Y2UyZjUzYjE3OTZhMDFiZmI3NmU0NGZhMDIyZTM5In0seyJwYXRoIjoicmVmZXJlbmNlLnR4dCIsInNpemUiOjM1LCJzaGEyNTYiOiI0ZWYxNGYwOTk4OTQ4YmQ1ZmRiZDliZWMyMzYyMmM5YjJiYmI3M2ZkZDQzNGFmYzg0ZjQxOWRiYjc1YzBiZDJjIn1dfV19UEsDBBQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAcmVmdW5kLXBvbGljeS9TS0lMTC5tZCMgUmVmdW5kIHBvbGljeQpVc2UgdGhlIHB1Ymxpc2hlZCByZWZ1bmQgcG9saWN5LgpQSwMEFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAByZWZ1bmQtcG9saWN5L3JlZmVyZW5jZS50eHRSZWZ1bmRzIGFyZSBhdmFpbGFibGUgZm9yIDMwIGRheXMuClBLAwQUAAAAAAAAACFQveldiAYAAAAGAAAAGAAAAHJlZnVuZC1wb2xpY3kvaGlkZGVuLnR4dGhpZGRlblBLAQIUAxQAAAAAAAAAIVB5aqleVQEAAFUBAAANAAAAAAAAAAAAAACAAQAAAABtYW5pZmVzdC5qc29uUEsBAhQDFAAAAAAAAAAhUAYiQNMxAAAAMQAAABYAAAAAAAAAAAAAAIABgAEAAHJlZnVuZC1wb2xpY3kvU0tJTEwubWRQSwECFAMUAAAAAAAAACFQ07pcbyMAAAAjAAAAGwAAAAAAAAAAAAAAgAHlAQAAcmVmdW5kLXBvbGljeS9yZWZlcmVuY2UudHh0UEsBAhQDFAAAAAAAAAAhUL3pXYgGAAAABgAAABgAAAAAAAAAAAAAAIABQQIAAHJlZnVuZC1wb2xpY3kvaGlkZGVuLnR4dFBLBQYAAAAABAAEAA4BAAB9AgAAAAA=",
"revision": "r1",
"etag": "\"ad6f23d08abb9c93f98d7b213ba9dcf97902d5601cad7c09f945198299cd8110\"",
"expected": "INVALID_SNAPSHOT"
},
{
"name": "missing-member",
"archiveBase64": "UEsDBBQAAAAAAAAAIVB5aqleVQEAAFUBAAANAAAAbWFuaWZlc3QuanNvbnsic2NoZW1hVmVyc2lvbiI6MSwicmV2aXNpb24iOiJyMSIsInNraWxscyI6W3sibmFtZSI6InJlZnVuZC1wb2xpY3kiLCJkZXNjcmlwdGlvbiI6IlVzZSB3aGVuIGhhbmRsaW5nIHJlZnVuZHMuIiwiZmlsZXMiOlt7InBhdGgiOiJTS0lMTC5tZCIsInNpemUiOjQ5LCJzaGEyNTYiOiI2ZWQ0MjVhYjc5NzQ5N2MxNTM3YjRmOTg4MTE5OGU4NjU5Y2UyZjUzYjE3OTZhMDFiZmI3NmU0NGZhMDIyZTM5In0seyJwYXRoIjoicmVmZXJlbmNlLnR4dCIsInNpemUiOjM1LCJzaGEyNTYiOiI0ZWYxNGYwOTk4OTQ4YmQ1ZmRiZDliZWMyMzYyMmM5YjJiYmI3M2ZkZDQzNGFmYzg0ZjQxOWRiYjc1YzBiZDJjIn1dfV19UEsDBBQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAcmVmdW5kLXBvbGljeS9TS0lMTC5tZCMgUmVmdW5kIHBvbGljeQpVc2UgdGhlIHB1Ymxpc2hlZCByZWZ1bmQgcG9saWN5LgpQSwECFAMUAAAAAAAAACFQeWqpXlUBAABVAQAADQAAAAAAAAAAAAAAgAEAAAAAbWFuaWZlc3QuanNvblBLAQIUAxQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAAAAAAAAAAACAAYABAAByZWZ1bmQtcG9saWN5L1NLSUxMLm1kUEsFBgAAAAACAAIAfwAAAOUBAAAAAA==",
"revision": "r1",
"etag": "\"2aac94efb06cd1f8fcd83e1064db531f3252f78ee8880ebd4adc94ce1aafb133\"",
"expected": "INVALID_SNAPSHOT"
},
{
"name": "missing-skill-md",
"archiveBase64": "UEsDBBQAAAAAAAAAIVDN/DX56wAAAOsAAAANAAAAbWFuaWZlc3QuanNvbnsic2NoZW1hVmVyc2lvbiI6MSwicmV2aXNpb24iOiJyMSIsInNraWxscyI6W3sibmFtZSI6InJlZnVuZC1wb2xpY3kiLCJkZXNjcmlwdGlvbiI6IlVzZSB3aGVuIGhhbmRsaW5nIHJlZnVuZHMuIiwiZmlsZXMiOlt7InBhdGgiOiJyZWZlcmVuY2UudHh0Iiwic2l6ZSI6MzUsInNoYTI1NiI6IjRlZjE0ZjA5OTg5NDhiZDVmZGJkOWJlYzIzNjIyYzliMmJiYjczZmRkNDM0YWZjODRmNDE5ZGJiNzVjMGJkMmMifV19XX1QSwMEFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAByZWZ1bmQtcG9saWN5L3JlZmVyZW5jZS50eHRSZWZ1bmRzIGFyZSBhdmFpbGFibGUgZm9yIDMwIGRheXMuClBLAQIUAxQAAAAAAAAAIVDN/DX56wAAAOsAAAANAAAAAAAAAAAAAACAAQAAAABtYW5pZmVzdC5qc29uUEsBAhQDFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAAAAAAAAAAAAIABFgEAAHJlZnVuZC1wb2xpY3kvcmVmZXJlbmNlLnR4dFBLBQYAAAAAAgACAIQAAAByAQAAAAA=",
"revision": "r1",
"etag": "\"caee7aa0cd022e1426b5c4948ee7ae06fbb323889bdec7fc8129d6a0587b403d\"",
"expected": "INVALID_SNAPSHOT"
},
{
"name": "invalid-skill-utf8",
"archiveBase64": "UEsDBBQAAAAAAAAAIVBbcEUrVAEAAFQBAAANAAAAbWFuaWZlc3QuanNvbnsic2NoZW1hVmVyc2lvbiI6MSwicmV2aXNpb24iOiJyMSIsInNraWxscyI6W3sibmFtZSI6InJlZnVuZC1wb2xpY3kiLCJkZXNjcmlwdGlvbiI6IlVzZSB3aGVuIGhhbmRsaW5nIHJlZnVuZHMuIiwiZmlsZXMiOlt7InBhdGgiOiJTS0lMTC5tZCIsInNpemUiOjEsInNoYTI1NiI6ImE4MTAwYWU2YWExOTQwZDBiNjYzYmIzMWNkNDY2MTQyZWJiZGJkNTE4NzEzMWI5MmQ5MzgxODk4NzgzMmViODkifSx7InBhdGgiOiJyZWZlcmVuY2UudHh0Iiwic2l6ZSI6MzUsInNoYTI1NiI6IjRlZjE0ZjA5OTg5NDhiZDVmZGJkOWJlYzIzNjIyYzliMmJiYjczZmRkNDM0YWZjODRmNDE5ZGJiNzVjMGJkMmMifV19XX1QSwMEFAAAAAAAAAAhUAAAAP8BAAAAAQAAABYAAAByZWZ1bmQtcG9saWN5L1NLSUxMLm1k/1BLAwQUAAAAAAAAACFQ07pcbyMAAAAjAAAAGwAAAHJlZnVuZC1wb2xpY3kvcmVmZXJlbmNlLnR4dFJlZnVuZHMgYXJlIGF2YWlsYWJsZSBmb3IgMzAgZGF5cy4KUEsBAhQDFAAAAAAAAAAhUFtwRStUAQAAVAEAAA0AAAAAAAAAAAAAAIABAAAAAG1hbmlmZXN0Lmpzb25QSwECFAMUAAAAAAAAACFQAAAA/wEAAAABAAAAFgAAAAAAAAAAAAAAgAF/AQAAcmVmdW5kLXBvbGljeS9TS0lMTC5tZFBLAQIUAxQAAAAAAAAAIVDTulxvIwAAACMAAAAbAAAAAAAAAAAAAACAAbQBAAByZWZ1bmQtcG9saWN5L3JlZmVyZW5jZS50eHRQSwUGAAAAAAMAAwDIAAAAEAIAAAAA",
"revision": "r1",
"etag": "\"93df6acf7cf75fd4648ea5964d14815be883f200e40bcc1bde983d8b96c463c8\"",
"expected": "INVALID_SNAPSHOT"
},
{
"name": "binary-resource",
"archiveBase64": "UEsDBBQAAAAAAAAAIVD3b4fWwgEAAMIBAAANAAAAbWFuaWZlc3QuanNvbnsic2NoZW1hVmVyc2lvbiI6MSwicmV2aXNpb24iOiJyMSIsInNraWxscyI6W3sibmFtZSI6InJlZnVuZC1wb2xpY3kiLCJkZXNjcmlwdGlvbiI6IlVzZSB3aGVuIGhhbmRsaW5nIHJlZnVuZHMuIiwiZmlsZXMiOlt7InBhdGgiOiJTS0lMTC5tZCIsInNpemUiOjQ5LCJzaGEyNTYiOiI2ZWQ0MjVhYjc5NzQ5N2MxNTM3YjRmOTg4MTE5OGU4NjU5Y2UyZjUzYjE3OTZhMDFiZmI3NmU0NGZhMDIyZTM5In0seyJwYXRoIjoicmVmZXJlbmNlLnR4dCIsInNpemUiOjM1LCJzaGEyNTYiOiI0ZWYxNGYwOTk4OTQ4YmQ1ZmRiZDliZWMyMzYyMmM5YjJiYmI3M2ZkZDQzNGFmYzg0ZjQxOWRiYjc1YzBiZDJjIn0seyJwYXRoIjoicmVzb3VyY2UuYmluIiwic2l6ZSI6Miwic2hhMjU2IjoiZWE1ZGJmOTU5NmQxODdlOTUwMGYyM2U5YTY4MDEwOTQ3NTM0MWNmNGU4MWY3ZTA0M2Y3ZDk3MTUyYzEwNzcyZiJ9XX1dfVBLAwQUAAAAAAAAACFQBiJA0zEAAAAxAAAAFgAAAHJlZnVuZC1wb2xpY3kvU0tJTEwubWQjIFJlZnVuZCBwb2xpY3kKVXNlIHRoZSBwdWJsaXNoZWQgcmVmdW5kIHBvbGljeS4KUEsDBBQAAAAAAAAAIVDTulxvIwAAACMAAAAbAAAAcmVmdW5kLXBvbGljeS9yZWZlcmVuY2UudHh0UmVmdW5kcyBhcmUgYXZhaWxhYmxlIGZvciAzMCBkYXlzLgpQSwMEFAAAAAAAAAAhUI3v/dICAAAAAgAAABoAAAByZWZ1bmQtcG9saWN5L3Jlc291cmNlLmJpbv8AUEsBAhQDFAAAAAAAAAAhUPdvh9bCAQAAwgEAAA0AAAAAAAAAAAAAAIABAAAAAG1hbmlmZXN0Lmpzb25QSwECFAMUAAAAAAAAACFQBiJA0zEAAAAxAAAAFgAAAAAAAAAAAAAAgAHtAQAAcmVmdW5kLXBvbGljeS9TS0lMTC5tZFBLAQIUAxQAAAAAAAAAIVDTulxvIwAAACMAAAAbAAAAAAAAAAAAAACAAVICAAByZWZ1bmQtcG9saWN5L3JlZmVyZW5jZS50eHRQSwECFAMUAAAAAAAAACFQje/90gIAAAACAAAAGgAAAAAAAAAAAAAAgAGuAgAAcmVmdW5kLXBvbGljeS9yZXNvdXJjZS5iaW5QSwUGAAAAAAQABAAQAQAA6AIAAAAA",
"revision": "r1",
"etag": "\"d338d16710dbc20609bfda847e6fbaf9021c712f2932aabf1b18e02b06f5ef60\"",
"expected": "valid"
},
{
"name": "duplicate-skill",
"archiveBase64": "UEsDBBQAAAAAAAAAIVB9K0YBfAIAAHwCAAANAAAAbWFuaWZlc3QuanNvbnsic2NoZW1hVmVyc2lvbiI6MSwicmV2aXNpb24iOiJyMSIsInNraWxscyI6W3sibmFtZSI6InJlZnVuZC1wb2xpY3kiLCJkZXNjcmlwdGlvbiI6IlVzZSB3aGVuIGhhbmRsaW5nIHJlZnVuZHMuIiwiZmlsZXMiOlt7InBhdGgiOiJTS0lMTC5tZCIsInNpemUiOjQ5LCJzaGEyNTYiOiI2ZWQ0MjVhYjc5NzQ5N2MxNTM3YjRmOTg4MTE5OGU4NjU5Y2UyZjUzYjE3OTZhMDFiZmI3NmU0NGZhMDIyZTM5In0seyJwYXRoIjoicmVmZXJlbmNlLnR4dCIsInNpemUiOjM1LCJzaGEyNTYiOiI0ZWYxNGYwOTk4OTQ4YmQ1ZmRiZDliZWMyMzYyMmM5YjJiYmI3M2ZkZDQzNGFmYzg0ZjQxOWRiYjc1YzBiZDJjIn1dfSx7Im5hbWUiOiJyZWZ1bmQtcG9saWN5IiwiZGVzY3JpcHRpb24iOiJVc2Ugd2hlbiBoYW5kbGluZyByZWZ1bmRzLiIsImZpbGVzIjpbeyJwYXRoIjoiU0tJTEwubWQiLCJzaXplIjo0OSwic2hhMjU2IjoiNmVkNDI1YWI3OTc0OTdjMTUzN2I0Zjk4ODExOThlODY1OWNlMmY1M2IxNzk2YTAxYmZiNzZlNDRmYTAyMmUzOSJ9LHsicGF0aCI6InJlZmVyZW5jZS50eHQiLCJzaXplIjozNSwic2hhMjU2IjoiNGVmMTRmMDk5ODk0OGJkNWZkYmQ5YmVjMjM2MjJjOWIyYmJiNzNmZGQ0MzRhZmM4NGY0MTlkYmI3NWMwYmQyYyJ9XX1dfVBLAwQUAAAAAAAAACFQBiJA0zEAAAAxAAAAFgAAAHJlZnVuZC1wb2xpY3kvU0tJTEwubWQjIFJlZnVuZCBwb2xpY3kKVXNlIHRoZSBwdWJsaXNoZWQgcmVmdW5kIHBvbGljeS4KUEsDBBQAAAAAAAAAIVDTulxvIwAAACMAAAAbAAAAcmVmdW5kLXBvbGljeS9yZWZlcmVuY2UudHh0UmVmdW5kcyBhcmUgYXZhaWxhYmxlIGZvciAzMCBkYXlzLgpQSwECFAMUAAAAAAAAACFQfStGAXwCAAB8AgAADQAAAAAAAAAAAAAAgAEAAAAAbWFuaWZlc3QuanNvblBLAQIUAxQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAAAAAAAAAAACAAacCAAByZWZ1bmQtcG9saWN5L1NLSUxMLm1kUEsBAhQDFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAAAAAAAAAAAAIABDAMAAHJlZnVuZC1wb2xpY3kvcmVmZXJlbmNlLnR4dFBLBQYAAAAAAwADAMgAAABoAwAAAAA=",
"revision": "r1",
"etag": "\"76246ce0849b05083faf5b5ead1e4569fb20569f84517fdecde3d1ce1f952948\"",
"expected": "INVALID_SNAPSHOT"
},
{
"name": "duplicate-file",
"archiveBase64": "UEsDBBQAAAAAAAAAIVDGG5YBvwEAAL8BAAANAAAAbWFuaWZlc3QuanNvbnsic2NoZW1hVmVyc2lvbiI6MSwicmV2aXNpb24iOiJyMSIsInNraWxscyI6W3sibmFtZSI6InJlZnVuZC1wb2xpY3kiLCJkZXNjcmlwdGlvbiI6IlVzZSB3aGVuIGhhbmRsaW5nIHJlZnVuZHMuIiwiZmlsZXMiOlt7InBhdGgiOiJTS0lMTC5tZCIsInNpemUiOjQ5LCJzaGEyNTYiOiI2ZWQ0MjVhYjc5NzQ5N2MxNTM3YjRmOTg4MTE5OGU4NjU5Y2UyZjUzYjE3OTZhMDFiZmI3NmU0NGZhMDIyZTM5In0seyJwYXRoIjoicmVmZXJlbmNlLnR4dCIsInNpemUiOjM1LCJzaGEyNTYiOiI0ZWYxNGYwOTk4OTQ4YmQ1ZmRiZDliZWMyMzYyMmM5YjJiYmI3M2ZkZDQzNGFmYzg0ZjQxOWRiYjc1YzBiZDJjIn0seyJwYXRoIjoiU0tJTEwubWQiLCJzaXplIjo0OSwic2hhMjU2IjoiNmVkNDI1YWI3OTc0OTdjMTUzN2I0Zjk4ODExOThlODY1OWNlMmY1M2IxNzk2YTAxYmZiNzZlNDRmYTAyMmUzOSJ9XX1dfVBLAwQUAAAAAAAAACFQBiJA0zEAAAAxAAAAFgAAAHJlZnVuZC1wb2xpY3kvU0tJTEwubWQjIFJlZnVuZCBwb2xpY3kKVXNlIHRoZSBwdWJsaXNoZWQgcmVmdW5kIHBvbGljeS4KUEsDBBQAAAAAAAAAIVDTulxvIwAAACMAAAAbAAAAcmVmdW5kLXBvbGljeS9yZWZlcmVuY2UudHh0UmVmdW5kcyBhcmUgYXZhaWxhYmxlIGZvciAzMCBkYXlzLgpQSwECFAMUAAAAAAAAACFQxhuWAb8BAAC/AQAADQAAAAAAAAAAAAAAgAEAAAAAbWFuaWZlc3QuanNvblBLAQIUAxQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAAAAAAAAAAACAAeoBAAByZWZ1bmQtcG9saWN5L1NLSUxMLm1kUEsBAhQDFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAAAAAAAAAAAAIABTwIAAHJlZnVuZC1wb2xpY3kvcmVmZXJlbmNlLnR4dFBLBQYAAAAAAwADAMgAAACrAgAAAAA=",
"revision": "r1",
"etag": "\"d33ad62eb15a728515eecda44d6151121f414b3245f3c27e217e04f8a9753bab\"",
"expected": "INVALID_SNAPSHOT"
},
{
"name": "duplicate-archive-member",
"archiveBase64": "UEsDBBQAAAAAAAAAIVB5aqleVQEAAFUBAAANAAAAbWFuaWZlc3QuanNvbnsic2NoZW1hVmVyc2lvbiI6MSwicmV2aXNpb24iOiJyMSIsInNraWxscyI6W3sibmFtZSI6InJlZnVuZC1wb2xpY3kiLCJkZXNjcmlwdGlvbiI6IlVzZSB3aGVuIGhhbmRsaW5nIHJlZnVuZHMuIiwiZmlsZXMiOlt7InBhdGgiOiJTS0lMTC5tZCIsInNpemUiOjQ5LCJzaGEyNTYiOiI2ZWQ0MjVhYjc5NzQ5N2MxNTM3YjRmOTg4MTE5OGU4NjU5Y2UyZjUzYjE3OTZhMDFiZmI3NmU0NGZhMDIyZTM5In0seyJwYXRoIjoicmVmZXJlbmNlLnR4dCIsInNpemUiOjM1LCJzaGEyNTYiOiI0ZWYxNGYwOTk4OTQ4YmQ1ZmRiZDliZWMyMzYyMmM5YjJiYmI3M2ZkZDQzNGFmYzg0ZjQxOWRiYjc1YzBiZDJjIn1dfV19UEsDBBQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAcmVmdW5kLXBvbGljeS9TS0lMTC5tZCMgUmVmdW5kIHBvbGljeQpVc2UgdGhlIHB1Ymxpc2hlZCByZWZ1bmQgcG9saWN5LgpQSwMEFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAByZWZ1bmQtcG9saWN5L3JlZmVyZW5jZS50eHRSZWZ1bmRzIGFyZSBhdmFpbGFibGUgZm9yIDMwIGRheXMuClBLAwQUAAAACACacSpdf7wPd/YAAABqAQAADQAAAG1hbmlmZXN0Lmpzb25Vjt1OwzAMhV+lyvWYmsRpYt4AsTsEN4iLOHFoRJtNTfmd9u60YxIg39jH5/jzUdTQ8+gfeKp5X8R1IzeNmPgtX0YxSbEo9SUPQ13mx6MofuTzhtNriVeH/ZDD52qKXMOUD/MleV+5ee+5NL0vccjluflJ1O1qTnngy8GDn/s1cHd7s9ttx3gG5q8VArj2vVemWx0dR1DGk0ULaIM02hIkdE5KdOw6g4FVMpqkxc63khLZjgGSb5VijeK0aX55yzs8cQm8nT/mP1Bt/kGBk4TUIjoER9GkSBGJg9KdUgFJEZHVKUbQ4FNwkEBiXDQTWooqiNPTUt9QSwECFAMUAAAAAAAAACFQeWqpXlUBAABVAQAADQAAAAAAAAAAAAAAgAEAAAAAbWFuaWZlc3QuanNvblBLAQIUAxQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAAAAAAAAAAACAAYABAAByZWZ1bmQtcG9saWN5L1NLSUxMLm1kUEsBAhQDFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAAAAAAAAAAAAIAB5QEAAHJlZnVuZC1wb2xpY3kvcmVmZXJlbmNlLnR4dFBLAQIUAxQAAAAIAJpxKl1/vA939gAAAGoBAAANAAAAAAAAAAAAAACAAUECAABtYW5pZmVzdC5qc29uUEsFBgAAAAAEAAQAAwEAAGIDAAAAAA==",
"revision": "r1",
"etag": "\"622f1b8f0eb0760b428aea830c899518df0bdaaefe662e8444292eebc67121ed\"",
"expected": "INVALID_SNAPSHOT"
},
{
"name": "symlink",
"archiveBase64": "UEsDBBQAAAAAAAAAIVB5aqleVQEAAFUBAAANAAAAbWFuaWZlc3QuanNvbnsic2NoZW1hVmVyc2lvbiI6MSwicmV2aXNpb24iOiJyMSIsInNraWxscyI6W3sibmFtZSI6InJlZnVuZC1wb2xpY3kiLCJkZXNjcmlwdGlvbiI6IlVzZSB3aGVuIGhhbmRsaW5nIHJlZnVuZHMuIiwiZmlsZXMiOlt7InBhdGgiOiJTS0lMTC5tZCIsInNpemUiOjQ5LCJzaGEyNTYiOiI2ZWQ0MjVhYjc5NzQ5N2MxNTM3YjRmOTg4MTE5OGU4NjU5Y2UyZjUzYjE3OTZhMDFiZmI3NmU0NGZhMDIyZTM5In0seyJwYXRoIjoicmVmZXJlbmNlLnR4dCIsInNpemUiOjM1LCJzaGEyNTYiOiI0ZWYxNGYwOTk4OTQ4YmQ1ZmRiZDliZWMyMzYyMmM5YjJiYmI3M2ZkZDQzNGFmYzg0ZjQxOWRiYjc1YzBiZDJjIn1dfV19UEsDBBQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAcmVmdW5kLXBvbGljeS9TS0lMTC5tZCMgUmVmdW5kIHBvbGljeQpVc2UgdGhlIHB1Ymxpc2hlZCByZWZ1bmQgcG9saWN5LgpQSwMEFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAByZWZ1bmQtcG9saWN5L3JlZmVyZW5jZS50eHRSZWZ1bmRzIGFyZSBhdmFpbGFibGUgZm9yIDMwIGRheXMuClBLAQIUAxQAAAAAAAAAIVB5aqleVQEAAFUBAAANAAAAAAAAAAAAAACAAQAAAABtYW5pZmVzdC5qc29uUEsBAhQDFAAAAAAAAAAhUAYiQNMxAAAAMQAAABYAAAAAAAAAAAAAAP+hgAEAAHJlZnVuZC1wb2xpY3kvU0tJTEwubWRQSwECFAMUAAAAAAAAACFQ07pcbyMAAAAjAAAAGwAAAAAAAAAAAAAA/6HlAQAAcmVmdW5kLXBvbGljeS9yZWZlcmVuY2UudHh0UEsFBgAAAAADAAMAyAAAAEECAAAAAA==",
"revision": "r1",
"etag": "\"04dfdbe1cd1542648984727637d7f224d66809b2c9b55ea66a27b7351eb3605f\"",
"expected": "INVALID_SNAPSHOT"
},
{
"name": "parent-path",
"archiveBase64": "UEsDBBQAAAAAAAAAIVB5aqleVQEAAFUBAAANAAAAbWFuaWZlc3QuanNvbnsic2NoZW1hVmVyc2lvbiI6MSwicmV2aXNpb24iOiJyMSIsInNraWxscyI6W3sibmFtZSI6InJlZnVuZC1wb2xpY3kiLCJkZXNjcmlwdGlvbiI6IlVzZSB3aGVuIGhhbmRsaW5nIHJlZnVuZHMuIiwiZmlsZXMiOlt7InBhdGgiOiJTS0lMTC5tZCIsInNpemUiOjQ5LCJzaGEyNTYiOiI2ZWQ0MjVhYjc5NzQ5N2MxNTM3YjRmOTg4MTE5OGU4NjU5Y2UyZjUzYjE3OTZhMDFiZmI3NmU0NGZhMDIyZTM5In0seyJwYXRoIjoicmVmZXJlbmNlLnR4dCIsInNpemUiOjM1LCJzaGEyNTYiOiI0ZWYxNGYwOTk4OTQ4YmQ1ZmRiZDliZWMyMzYyMmM5YjJiYmI3M2ZkZDQzNGFmYzg0ZjQxOWRiYjc1YzBiZDJjIn1dfV19UEsDBBQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAcmVmdW5kLXBvbGljeS9TS0lMTC5tZCMgUmVmdW5kIHBvbGljeQpVc2UgdGhlIHB1Ymxpc2hlZCByZWZ1bmQgcG9saWN5LgpQSwMEFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAByZWZ1bmQtcG9saWN5L3JlZmVyZW5jZS50eHRSZWZ1bmRzIGFyZSBhdmFpbGFibGUgZm9yIDMwIGRheXMuClBLAwQUAAAAAAAAACFQ+zkrggMAAAADAAAACQAAAC4uL2VzY2FwZWJhZFBLAQIUAxQAAAAAAAAAIVB5aqleVQEAAFUBAAANAAAAAAAAAAAAAACAAQAAAABtYW5pZmVzdC5qc29uUEsBAhQDFAAAAAAAAAAhUAYiQNMxAAAAMQAAABYAAAAAAAAAAAAAAIABgAEAAHJlZnVuZC1wb2xpY3kvU0tJTEwubWRQSwECFAMUAAAAAAAAACFQ07pcbyMAAAAjAAAAGwAAAAAAAAAAAAAAgAHlAQAAcmVmdW5kLXBvbGljeS9yZWZlcmVuY2UudHh0UEsBAhQDFAAAAAAAAAAhUPs5K4IDAAAAAwAAAAkAAAAAAAAAAAAAAIABQQIAAC4uL2VzY2FwZVBLBQYAAAAABAAEAP8AAABrAgAAAAA=",
"revision": "r1",
"etag": "\"be1478dfd3b4728bc8476a16a581d79bd7bc5f81d01aa106e8b93b5814d6c7fc\"",
"expected": "INVALID_SNAPSHOT"
},
{
"name": "absolute-path",
"archiveBase64": "UEsDBBQAAAAAAAAAIVB5aqleVQEAAFUBAAANAAAAbWFuaWZlc3QuanNvbnsic2NoZW1hVmVyc2lvbiI6MSwicmV2aXNpb24iOiJyMSIsInNraWxscyI6W3sibmFtZSI6InJlZnVuZC1wb2xpY3kiLCJkZXNjcmlwdGlvbiI6IlVzZSB3aGVuIGhhbmRsaW5nIHJlZnVuZHMuIiwiZmlsZXMiOlt7InBhdGgiOiJTS0lMTC5tZCIsInNpemUiOjQ5LCJzaGEyNTYiOiI2ZWQ0MjVhYjc5NzQ5N2MxNTM3YjRmOTg4MTE5OGU4NjU5Y2UyZjUzYjE3OTZhMDFiZmI3NmU0NGZhMDIyZTM5In0seyJwYXRoIjoicmVmZXJlbmNlLnR4dCIsInNpemUiOjM1LCJzaGEyNTYiOiI0ZWYxNGYwOTk4OTQ4YmQ1ZmRiZDliZWMyMzYyMmM5YjJiYmI3M2ZkZDQzNGFmYzg0ZjQxOWRiYjc1YzBiZDJjIn1dfV19UEsDBBQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAcmVmdW5kLXBvbGljeS9TS0lMTC5tZCMgUmVmdW5kIHBvbGljeQpVc2UgdGhlIHB1Ymxpc2hlZCByZWZ1bmQgcG9saWN5LgpQSwMEFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAByZWZ1bmQtcG9saWN5L3JlZmVyZW5jZS50eHRSZWZ1bmRzIGFyZSBhdmFpbGFibGUgZm9yIDMwIGRheXMuClBLAwQUAAAAAAAAACFQ+zkrggMAAAADAAAABwAAAC9lc2NhcGViYWRQSwECFAMUAAAAAAAAACFQeWqpXlUBAABVAQAADQAAAAAAAAAAAAAAgAEAAAAAbWFuaWZlc3QuanNvblBLAQIUAxQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAAAAAAAAAAACAAYABAAByZWZ1bmQtcG9saWN5L1NLSUxMLm1kUEsBAhQDFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAAAAAAAAAAAAIAB5QEAAHJlZnVuZC1wb2xpY3kvcmVmZXJlbmNlLnR4dFBLAQIUAxQAAAAAAAAAIVD7OSuCAwAAAAMAAAAHAAAAAAAAAAAAAACAAUECAAAvZXNjYXBlUEsFBgAAAAAEAAQA/QAAAGkCAAAAAA==",
"revision": "r1",
"etag": "\"758e23a54c3cb1b9232ff1b62ead4a4a72de8e37ad1697d48b3546c132b30cbb\"",
"expected": "INVALID_SNAPSHOT"
},
{
"name": "backslash-path",
"archiveBase64": "UEsDBBQAAAAAAAAAIVB5aqleVQEAAFUBAAANAAAAbWFuaWZlc3QuanNvbnsic2NoZW1hVmVyc2lvbiI6MSwicmV2aXNpb24iOiJyMSIsInNraWxscyI6W3sibmFtZSI6InJlZnVuZC1wb2xpY3kiLCJkZXNjcmlwdGlvbiI6IlVzZSB3aGVuIGhhbmRsaW5nIHJlZnVuZHMuIiwiZmlsZXMiOlt7InBhdGgiOiJTS0lMTC5tZCIsInNpemUiOjQ5LCJzaGEyNTYiOiI2ZWQ0MjVhYjc5NzQ5N2MxNTM3YjRmOTg4MTE5OGU4NjU5Y2UyZjUzYjE3OTZhMDFiZmI3NmU0NGZhMDIyZTM5In0seyJwYXRoIjoicmVmZXJlbmNlLnR4dCIsInNpemUiOjM1LCJzaGEyNTYiOiI0ZWYxNGYwOTk4OTQ4YmQ1ZmRiZDliZWMyMzYyMmM5YjJiYmI3M2ZkZDQzNGFmYzg0ZjQxOWRiYjc1YzBiZDJjIn1dfV19UEsDBBQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAcmVmdW5kLXBvbGljeS9TS0lMTC5tZCMgUmVmdW5kIHBvbGljeQpVc2UgdGhlIHB1Ymxpc2hlZCByZWZ1bmQgcG9saWN5LgpQSwMEFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAByZWZ1bmQtcG9saWN5L3JlZmVyZW5jZS50eHRSZWZ1bmRzIGFyZSBhdmFpbGFibGUgZm9yIDMwIGRheXMuClBLAwQUAAAAAAAAACFQ+zkrggMAAAADAAAAFAAAAHJlZnVuZC1wb2xpY3lcZXNjYXBlYmFkUEsBAhQDFAAAAAAAAAAhUHlqqV5VAQAAVQEAAA0AAAAAAAAAAAAAAIABAAAAAG1hbmlmZXN0Lmpzb25QSwECFAMUAAAAAAAAACFQBiJA0zEAAAAxAAAAFgAAAAAAAAAAAAAAgAGAAQAAcmVmdW5kLXBvbGljeS9TS0lMTC5tZFBLAQIUAxQAAAAAAAAAIVDTulxvIwAAACMAAAAbAAAAAAAAAAAAAACAAeUBAAByZWZ1bmQtcG9saWN5L3JlZmVyZW5jZS50eHRQSwECFAMUAAAAAAAAACFQ+zkrggMAAAADAAAAFAAAAAAAAAAAAAAAgAFBAgAAcmVmdW5kLXBvbGljeVxlc2NhcGVQSwUGAAAAAAQABAAKAQAAdgIAAAAA",
"revision": "r1",
"etag": "\"5e35651d101e70f3b89eeb91ceaa9aad86467ddaaf2c8d5a65ce10f6b0e4d105\"",
"expected": "INVALID_SNAPSHOT"
},
{
"name": "dot-path",
"archiveBase64": "UEsDBBQAAAAAAAAAIVB5aqleVQEAAFUBAAANAAAAbWFuaWZlc3QuanNvbnsic2NoZW1hVmVyc2lvbiI6MSwicmV2aXNpb24iOiJyMSIsInNraWxscyI6W3sibmFtZSI6InJlZnVuZC1wb2xpY3kiLCJkZXNjcmlwdGlvbiI6IlVzZSB3aGVuIGhhbmRsaW5nIHJlZnVuZHMuIiwiZmlsZXMiOlt7InBhdGgiOiJTS0lMTC5tZCIsInNpemUiOjQ5LCJzaGEyNTYiOiI2ZWQ0MjVhYjc5NzQ5N2MxNTM3YjRmOTg4MTE5OGU4NjU5Y2UyZjUzYjE3OTZhMDFiZmI3NmU0NGZhMDIyZTM5In0seyJwYXRoIjoicmVmZXJlbmNlLnR4dCIsInNpemUiOjM1LCJzaGEyNTYiOiI0ZWYxNGYwOTk4OTQ4YmQ1ZmRiZDliZWMyMzYyMmM5YjJiYmI3M2ZkZDQzNGFmYzg0ZjQxOWRiYjc1YzBiZDJjIn1dfV19UEsDBBQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAcmVmdW5kLXBvbGljeS9TS0lMTC5tZCMgUmVmdW5kIHBvbGljeQpVc2UgdGhlIHB1Ymxpc2hlZCByZWZ1bmQgcG9saWN5LgpQSwMEFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAByZWZ1bmQtcG9saWN5L3JlZmVyZW5jZS50eHRSZWZ1bmRzIGFyZSBhdmFpbGFibGUgZm9yIDMwIGRheXMuClBLAwQUAAAAAAAAACFQ+zkrggMAAAADAAAAFgAAAHJlZnVuZC1wb2xpY3kvLi9lc2NhcGViYWRQSwECFAMUAAAAAAAAACFQeWqpXlUBAABVAQAADQAAAAAAAAAAAAAAgAEAAAAAbWFuaWZlc3QuanNvblBLAQIUAxQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAAAAAAAAAAACAAYABAAByZWZ1bmQtcG9saWN5L1NLSUxMLm1kUEsBAhQDFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAAAAAAAAAAAAIAB5QEAAHJlZnVuZC1wb2xpY3kvcmVmZXJlbmNlLnR4dFBLAQIUAxQAAAAAAAAAIVD7OSuCAwAAAAMAAAAWAAAAAAAAAAAAAACAAUECAAByZWZ1bmQtcG9saWN5Ly4vZXNjYXBlUEsFBgAAAAAEAAQADAEAAHgCAAAAAA==",
"revision": "r1",
"etag": "\"df34a514ad31bd472b7763546c1e2517554434a5d06f26de8791cae401dda920\"",
"expected": "INVALID_SNAPSHOT"
},
{
"name": "unsorted-files",
"archiveBase64": "UEsDBBQAAAAAAAAAIVDLdnvPVQEAAFUBAAANAAAAbWFuaWZlc3QuanNvbnsic2NoZW1hVmVyc2lvbiI6MSwicmV2aXNpb24iOiJyMSIsInNraWxscyI6W3sibmFtZSI6InJlZnVuZC1wb2xpY3kiLCJkZXNjcmlwdGlvbiI6IlVzZSB3aGVuIGhhbmRsaW5nIHJlZnVuZHMuIiwiZmlsZXMiOlt7InBhdGgiOiJyZWZlcmVuY2UudHh0Iiwic2l6ZSI6MzUsInNoYTI1NiI6IjRlZjE0ZjA5OTg5NDhiZDVmZGJkOWJlYzIzNjIyYzliMmJiYjczZmRkNDM0YWZjODRmNDE5ZGJiNzVjMGJkMmMifSx7InBhdGgiOiJTS0lMTC5tZCIsInNpemUiOjQ5LCJzaGEyNTYiOiI2ZWQ0MjVhYjc5NzQ5N2MxNTM3YjRmOTg4MTE5OGU4NjU5Y2UyZjUzYjE3OTZhMDFiZmI3NmU0NGZhMDIyZTM5In1dfV19UEsDBBQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAcmVmdW5kLXBvbGljeS9TS0lMTC5tZCMgUmVmdW5kIHBvbGljeQpVc2UgdGhlIHB1Ymxpc2hlZCByZWZ1bmQgcG9saWN5LgpQSwMEFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAByZWZ1bmQtcG9saWN5L3JlZmVyZW5jZS50eHRSZWZ1bmRzIGFyZSBhdmFpbGFibGUgZm9yIDMwIGRheXMuClBLAQIUAxQAAAAAAAAAIVDLdnvPVQEAAFUBAAANAAAAAAAAAAAAAACAAQAAAABtYW5pZmVzdC5qc29uUEsBAhQDFAAAAAAAAAAhUAYiQNMxAAAAMQAAABYAAAAAAAAAAAAAAIABgAEAAHJlZnVuZC1wb2xpY3kvU0tJTEwubWRQSwECFAMUAAAAAAAAACFQ07pcbyMAAAAjAAAAGwAAAAAAAAAAAAAAgAHlAQAAcmVmdW5kLXBvbGljeS9yZWZlcmVuY2UudHh0UEsFBgAAAAADAAMAyAAAAEECAAAAAA==",
"revision": "r1",
"etag": "\"5a49a784877f0d3bf6c6985903a106685a5684e76759aa4c9d1e45f2e35228dd\"",
"expected": "INVALID_SNAPSHOT"
},
{
"name": "unsafe-skill-name",
"archiveBase64": "UEsDBBQAAAAAAAAAIVDZHp5oWAEAAFgBAAANAAAAbWFuaWZlc3QuanNvbnsic2NoZW1hVmVyc2lvbiI6MSwicmV2aXNpb24iOiJyMSIsInNraWxscyI6W3sibmFtZSI6Ii4uL3JlZnVuZC1wb2xpY3kiLCJkZXNjcmlwdGlvbiI6IlVzZSB3aGVuIGhhbmRsaW5nIHJlZnVuZHMuIiwiZmlsZXMiOlt7InBhdGgiOiJTS0lMTC5tZCIsInNpemUiOjQ5LCJzaGEyNTYiOiI2ZWQ0MjVhYjc5NzQ5N2MxNTM3YjRmOTg4MTE5OGU4NjU5Y2UyZjUzYjE3OTZhMDFiZmI3NmU0NGZhMDIyZTM5In0seyJwYXRoIjoicmVmZXJlbmNlLnR4dCIsInNpemUiOjM1LCJzaGEyNTYiOiI0ZWYxNGYwOTk4OTQ4YmQ1ZmRiZDliZWMyMzYyMmM5YjJiYmI3M2ZkZDQzNGFmYzg0ZjQxOWRiYjc1YzBiZDJjIn1dfV19UEsDBBQAAAAAAAAAIVAGIkDTMQAAADEAAAAWAAAAcmVmdW5kLXBvbGljeS9TS0lMTC5tZCMgUmVmdW5kIHBvbGljeQpVc2UgdGhlIHB1Ymxpc2hlZCByZWZ1bmQgcG9saWN5LgpQSwMEFAAAAAAAAAAhUNO6XG8jAAAAIwAAABsAAAByZWZ1bmQtcG9saWN5L3JlZmVyZW5jZS50eHRSZWZ1bmRzIGFyZSBhdmFpbGFibGUgZm9yIDMwIGRheXMuClBLAQIUAxQAAAAAAAAAIVDZHp5oWAEAAFgBAAANAAAAAAAAAAAAAACAAQAAAABtYW5pZmVzdC5qc29uUEsBAhQDFAAAAAAAAAAhUAYiQNMxAAAAMQAAABYAAAAAAAAAAAAAAIABgwEAAHJlZnVuZC1wb2xpY3kvU0tJTEwubWRQSwECFAMUAAAAAAAAACFQ07pcbyMAAAAjAAAAGwAAAAAAAAAAAAAAgAHoAQAAcmVmdW5kLXBvbGljeS9yZWZlcmVuY2UudHh0UEsFBgAAAAADAAMAyAAAAEQCAAAAAA==",
"revision": "r1",
"etag": "\"33c1235de2c5bb6dfe5402d7ec1ff349c3c9ae4ce5340309acd5ece401b00e97\"",
"expected": "INVALID_SNAPSHOT"
},
{
"name": "empty-r2",
"archiveBase64": "UEsDBBQAAAAAAAAAIVBAzffsLwAAAC8AAAANAAAAbWFuaWZlc3QuanNvbnsic2NoZW1hVmVyc2lvbiI6MSwicmV2aXNpb24iOiJyMiIsInNraWxscyI6W119UEsBAhQDFAAAAAAAAAAhUEDN9+wvAAAALwAAAA0AAAAAAAAAAAAAAIABAAAAAG1hbmlmZXN0Lmpzb25QSwUGAAAAAAEAAQA7AAAAWgAAAAAA",
"revision": "r2",
"etag": "\"c0f6cbcd16b544ec42b21cc5091833b442e771c58758e4f6f7cf0e25619de60c\"",
"expected": "valid"
},
{
"name": "unicode-codepoint-order",
"archiveBase64": "UEsDBBQAAAAIAAAAIVCdJeN8FQEAALIBAAANAAAAbWFuaWZlc3QuanNvbl2P3UqDMQyGb0V6PEbSNv3ZHYg7Ez1RD9omZWW/fN83Ecfu3c7BmFIITd43eZKTGstKtulVhrHtd2qBMzXIZ7smakA1U+O6bTajWryd1C5tpZd/K11hGcvQDtPV/LJrZc/yMMi4Pw5Fxnm31LaRa+8hTavuen56XC7nW74Mbt99mu+fVdLkuigaCWuwEVAjFxJX2biCbAUgO05E0VAkMZKDFPEXDaSYahCij+o8u4Hej70H5tPXdEOZO1TUmpgzo9OAiaPxVgxUGzBkL4kSxMLigg+OKHMQsMEUi2hNBbD1L4oDQI/lHxDvidUVG7LLGqyhahhryR6wb97vDAUrU2BJGZ03OuZqiLBvpivkmqx16vzR3w9QSwMEFAAAAAgAAAAhUCfFSz0JAAAABwAAAA4AAABza2lsbC9TS0lMTC5tZFNWCM7OzMkBAFBLAwQUAAAICAAAACFQAODjogUAAAADAAAADQAAAHNraWxsL+6AgC50eHRz8g0AAFBLAwQUAAAICAAAACFQ+JVWkw8AAAANAAAADgAAAHNraWxsL/CQgIAudHh0Ky4tKMhJzU3NK0ksqgQAUEsBAhQDFAAAAAgAAAAhUJ0l43wVAQAAsgEAAA0AAAAAAAAAAAAAAIABAAAAAG1hbmlmZXN0Lmpzb25QSwECFAMUAAAACAAAACFQJ8VLPQkAAAAHAAAADgAAAAAAAAAAAAAAgAFAAQAAc2tpbGwvU0tJTEwubWRQSwECFAMUAAAICAAAACFQAODjogUAAAADAAAADQAAAAAAAAAAAAAAgAF1AQAAc2tpbGwv7oCALnR4dFBLAQIUAxQAAAgIAAAAIVD4lVaTDwAAAA0AAAAOAAAAAAAAAAAAAACAAaUBAABza2lsbC/wkICALnR4dFBLBQYAAAAABAAEAO4AAADgAQAAAAA=",
"revision": "r1",
"etag": "\"0c7d080cfc75f10a579482331c7fc1abfa2931fd9c576a77f8282447ccb1e5a6\"",
"expected": "valid"
},
{
"name": "deflated-data-descriptors",
"archiveBase64": "UEsDBBQACAAIAAAAIVAAAAAAAAAAAAAAAAANAAAAbWFuaWZlc3QuanNvbl2P3UqDMQyGb0V6PEbSNv3ZHYg7Ez1RD9omZWW/fN83Ecfu3c7BmFIITd43eZKTGstKtulVhrHtd2qBMzXIZ7smakA1U+O6bTajWryd1C5tpZd/K11hGcvQDtPV/LJrZc/yMMi4Pw5Fxnm31LaRa+8hTavuen56XC7nW74Mbt99mu+fVdLkuigaCWuwEVAjFxJX2biCbAUgO05E0VAkMZKDFPEXDaSYahCij+o8u4Hej70H5tPXdEOZO1TUmpgzo9OAiaPxVgxUGzBkL4kSxMLigg+OKHMQsMEUi2hNBbD1L4oDQI/lHxDvidUVG7LLGqyhahhryR6wb97vDAUrU2BJGZ03OuZqiLBvpivkmqx16vzR3w9QSwcInSXjfBUBAACyAQAAUEsDBBQACAAIAAAAIVAAAAAAAAAAAAAAAAAOAAAAc2tpbGwvU0tJTEwubWRTVgjOzszJAQBQSwcIJ8VLPQkAAAAHAAAAUEsDBBQACAgIAAAAIVAAAAAAAAAAAAAAAAANAAAAc2tpbGwv7oCALnR4dHPyDQAAUEsHCADg46IFAAAAAwAAAFBLAwQUAAgICAAAACFQAAAAAAAAAAAAAAAADgAAAHNraWxsL/CQgIAudHh0Ky4tKMhJzU3NK0ksqgQAUEsHCPiVVpMPAAAADQAAAFBLAQIUAxQACAAIAAAAIVCdJeN8FQEAALIBAAANAAAAAAAAAAAAAACAAQAAAABtYW5pZmVzdC5qc29uUEsBAhQDFAAIAAgAAAAhUCfFSz0JAAAABwAAAA4AAAAAAAAAAAAAAIABUAEAAHNraWxsL1NLSUxMLm1kUEsBAhQDFAAICAgAAAAhUADg46IFAAAAAwAAAA0AAAAAAAAAAAAAAIABlQEAAHNraWxsL+6AgC50eHRQSwECFAMUAAgICAAAACFQ+JVWkw8AAAANAAAADgAAAAAAAAAAAAAAgAHVAQAAc2tpbGwv8JCAgC50eHRQSwUGAAAAAAQABADuAAAAIAIAAAAA",
"revision": "r1",
"etag": "\"3e7c93217223b604f3153af2506ae14f1265718cb7d8163ae85570fd3cb6c3b1\"",
"expected": "valid"
}
]
}
@@ -0,0 +1,38 @@
{
"name": "@copilotkit/intelligence-langgraph",
"version": "0.1.0",
"description": "Learned skill delivery for LangGraph agents through CopilotKit Intelligence",
"license": "MIT",
"repository": { "type": "git", "url": "https://github.com/CopilotKit/CopilotKit.git" },
"type": "module",
"files": ["dist"],
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.cts",
"exports": {
".": { "import": "./dist/index.mjs", "require": "./dist/index.cjs" },
"./package.json": "./package.json"
},
"publishConfig": { "access": "public" },
"scripts": {
"build": "tsdown",
"check-types": "tsc --noEmit",
"test": "vitest run",
"publint": "publint .",
"attw": "attw --pack . --profile node16"
},
"dependencies": {
"@copilotkit/runtime": "workspace:*",
"yauzl": "2.10.0"
},
"devDependencies": {
"@copilotkit/typescript-config": "workspace:*",
"@types/node": "^22.15.3",
"@types/yauzl": "2.10.3",
"fflate": "0.8.2",
"tsdown": "^0.20.3",
"typescript": "5.8.2",
"vitest": "^3.2.4"
},
"engines": { "node": ">=18" }
}
@@ -0,0 +1,113 @@
import { CopilotKitIntelligence } from "@copilotkit/runtime/v2";
import { describe, expect, it, vi } from "vitest";
import { resolveRegistryConfig } from "../config.js";
const environment = {
CPK_INTELLIGENCE_API_KEY: "environment-key",
INTELLIGENCE_API_URL: "https://selfhosted.example.com",
CPK_INTELLIGENCE_LEARNING_CONTAINER_ID: "environment-container",
CPK_INTELLIGENCE_SKILLS_REVISION: "environment-revision",
};
describe("registry configuration", () => {
it("uses explicit values before environment and defaults to five seconds", () => {
const result = resolveRegistryConfig(
{
apiKey: "explicit-key",
apiUrl: "https://explicit.example.com",
containerId: "explicit-container",
revision: "opaque-revision",
},
environment,
);
expect(result.client.ɵgetApiUrl()).toBe("https://explicit.example.com");
expect(result.client.ɵgetApiKey()).toBe("explicit-key");
expect(result).toMatchObject({
containerId: "explicit-container",
revision: "opaque-revision",
freshnessWindowMs: 5000,
requestTimeoutMs: 5000,
debug: false,
});
expect(result).not.toHaveProperty("apiKey");
expect(result).not.toHaveProperty("apiUrl");
});
it("constructs the canonical client from the standard environment variables", () => {
const result = resolveRegistryConfig({}, environment);
expect(result.client).toBeInstanceOf(CopilotKitIntelligence);
expect(result.client.ɵgetApiUrl()).toBe(environment.INTELLIGENCE_API_URL);
expect(result).toMatchObject({
containerId: "environment-container",
revision: "environment-revision",
});
});
it("does not inspect connection configuration when a client is injected", () => {
const client = new CopilotKitIntelligence({ apiKey: "injected-key" });
const env = new Proxy(environment, {
get(target, key) {
if (
key === "CPK_INTELLIGENCE_API_KEY" ||
key === "INTELLIGENCE_API_URL"
)
throw new Error("Connection environment read");
return Reflect.get(target, key);
},
});
const result = resolveRegistryConfig(
{
client,
get apiKey() {
throw new Error("Second credential read");
},
get apiUrl() {
throw new Error("Second endpoint read");
},
},
env,
);
expect(result.client).toBe(client);
expect(result.containerId).toBe("environment-container");
});
it("emits no log for the self-hosted environment helper", () => {
const spies = [
vi.spyOn(console, "warn"),
vi.spyOn(console, "error"),
vi.spyOn(console, "debug"),
vi.spyOn(console, "log"),
];
try {
resolveRegistryConfig({}, environment);
for (const spy of spies) expect(spy).not.toHaveBeenCalled();
} finally {
for (const spy of spies) spy.mockRestore();
}
});
it("uses managed defaults and latest when optional environment values are absent", () => {
const result = resolveRegistryConfig(
{ apiKey: "key", containerId: "container" },
{},
);
expect(result.client.ɵgetApiUrl()).toBe(
"https://api.intelligence.copilotkit.ai",
);
expect(result.revision).toBeUndefined();
});
it.each([
{ containerId: "" },
{ revision: "" },
{ freshnessWindowMs: -1 },
{ requestTimeoutMs: 0 },
{ requestTimeoutMs: Infinity },
{ debug: "true" },
{ apiKey: "" },
{ apiUrl: "file:///tmp/skills" },
])("returns a typed invalid configuration error", (override) => {
expect(() =>
resolveRegistryConfig(
{ apiKey: "key", containerId: "container", ...override },
{},
),
).toThrowError(
expect.objectContaining({ code: "INVALID_CONFIG", retryable: false }),
);
});
});
@@ -0,0 +1,98 @@
import {
CopilotKitIntelligence,
LearnedSkillsError,
} from "@copilotkit/runtime/v2";
import type { LearnedSkillsErrorCode } from "@copilotkit/runtime/v2";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import lifecycle from "../../conformance/lifecycle.v1.json";
import snapshots from "../../conformance/snapshots.v1.json";
import { SkillRegistry } from "../registry.js";
interface Step {
advanceMs?: number;
reply?: {
snapshot?: string;
unchanged?: string;
error?: string;
retryable?: boolean;
};
expect: {
revision?: string;
error?: string;
requests: number;
initialized?: boolean;
stale?: boolean;
lastCheckedAt?: string;
};
}
beforeEach(() => {
vi.useFakeTimers({ toFake: ["Date", "setTimeout", "clearTimeout"] });
vi.setSystemTime(lifecycle.initialTimeMs);
});
afterEach(() => {
vi.useRealTimers();
vi.restoreAllMocks();
});
describe("language-neutral lifecycle conformance", () => {
for (const scenario of lifecycle.cases) {
it(scenario.name, async () => {
const client = new CopilotKitIntelligence({ apiKey: "conformance-key" });
const fetch = vi.spyOn(client, "getLearnedSkillsSnapshot");
const registry = new SkillRegistry({
client,
containerId: "conformance",
...scenario.config,
});
for (const step of scenario.steps as Step[]) {
if (step.advanceMs !== undefined)
await vi.advanceTimersByTimeAsync(step.advanceMs);
const reply = step.reply;
if (reply?.error)
fetch.mockRejectedValueOnce(
new LearnedSkillsError(
reply.error as LearnedSkillsErrorCode,
reply.retryable ?? false,
),
);
else if (reply) {
const fixture = snapshots.cases.find(
(item) => item.name === (reply.snapshot ?? reply.unchanged),
)!;
fetch.mockResolvedValueOnce(
reply.unchanged
? {
status: "unchanged",
revision: fixture.revision,
etag: fixture.etag,
}
: {
status: "snapshot",
bytes: new Uint8Array(
Buffer.from(fixture.archiveBase64, "base64"),
),
revision: fixture.revision,
etag: fixture.etag,
contentType: "application/zip",
},
);
}
if (step.expect.error)
await expect(registry.acquireSnapshot()).rejects.toMatchObject({
code: step.expect.error,
});
else
expect((await registry.acquireSnapshot()).revision).toBe(
step.expect.revision,
);
expect(fetch).toHaveBeenCalledTimes(step.expect.requests);
const { requests: _requests, error: _error, ...status } = step.expect;
expect(registry.status).toMatchObject(status);
if (scenario.config?.revision) {
for (const call of fetch.mock.calls)
expect(call[0].revision).toBe(scenario.config.revision);
}
}
});
}
});
@@ -0,0 +1,364 @@
import {
CopilotKitIntelligence,
LearnedSkillsError,
} from "@copilotkit/runtime/v2";
import type { LearnedSkillsSnapshotResult } from "@copilotkit/runtime/v2";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import fixtures from "../../conformance/snapshots.v1.json";
import { SkillRegistry } from "../registry.js";
function response(name = "text-skill"): LearnedSkillsSnapshotResult {
const fixture = fixtures.cases.find((item) => item.name === name)!;
return {
status: "snapshot",
bytes: new Uint8Array(Buffer.from(fixture.archiveBase64, "base64")),
revision: fixture.revision,
etag: fixture.etag,
contentType: "application/zip",
};
}
function setup(options: Record<string, unknown> = {}) {
const client = new CopilotKitIntelligence({ apiKey: "never-log-this-key" });
const fetch = vi.spyOn(client, "getLearnedSkillsSnapshot");
const registry = new SkillRegistry({
client,
containerId: "container",
...options,
});
return { registry, fetch, client };
}
beforeEach(() => {
vi.useFakeTimers({ toFake: ["Date", "setTimeout", "clearTimeout"] });
vi.setSystemTime(0);
});
afterEach(() => {
vi.useRealTimers();
vi.restoreAllMocks();
vi.unstubAllGlobals();
});
describe("registry lifecycle", () => {
it("shares one cold request among initialization and invocation callers", async () => {
const { registry, fetch } = setup();
let resolve!: (value: LearnedSkillsSnapshotResult) => void;
fetch.mockReturnValue(
new Promise((done) => {
resolve = done;
}),
);
const init = registry.initialize();
const first = registry.acquireSnapshot();
const second = registry.acquireSnapshot();
await Promise.resolve();
expect(fetch).toHaveBeenCalledTimes(1);
resolve(response());
await init;
expect(await first).toBe(await second);
expect(registry.status).toMatchObject({
initialized: true,
revision: "r1",
mode: "latest",
stale: false,
lastCheckedAt: "1970-01-01T00:00:00.000Z",
});
});
it("blocks warm invocations after malformed HTTP 401 from the canonical client", async () => {
const initial = response();
if (initial.status !== "snapshot") throw new Error("fixture");
const fetch = vi
.fn()
.mockResolvedValueOnce(
new Response(Buffer.from(initial.bytes), {
headers: {
"content-type": initial.contentType,
"x-copilotkit-skills-revision": initial.revision,
etag: initial.etag,
},
}),
)
.mockResolvedValueOnce(
new Response("<html>denied</html>", { status: 401 }),
);
vi.stubGlobal("fetch", fetch);
const registry = new SkillRegistry({
client: new CopilotKitIntelligence({ apiKey: "secret" }),
containerId: "container",
});
const pinned = await registry.acquireSnapshot();
await vi.advanceTimersByTimeAsync(5000);
await expect(registry.acquireSnapshot()).rejects.toMatchObject({
code: "AUTHENTICATION_FAILED",
});
expect(pinned.revision).toBe(initial.revision);
expect(registry.status.lastError?.code).toBe("AUTHENTICATION_FAILED");
expect(fetch).toHaveBeenCalledTimes(2);
});
it("clears warm stale state after a matching 304 recovery", async () => {
const { registry, fetch } = setup();
const initial = response();
fetch
.mockResolvedValueOnce(initial)
.mockRejectedValueOnce(new LearnedSkillsError("NETWORK_ERROR", true))
.mockResolvedValueOnce({
status: "unchanged",
revision: initial.revision,
etag: initial.etag,
});
const pinned = await registry.acquireSnapshot();
await vi.advanceTimersByTimeAsync(5000);
expect(await registry.acquireSnapshot()).toBe(pinned);
expect(registry.status.stale).toBe(true);
await vi.advanceTimersByTimeAsync(1000);
expect(await registry.acquireSnapshot()).toBe(pinned);
expect(registry.status).toMatchObject({
stale: false,
lastCheckedAt: "1970-01-01T00:00:06.000Z",
});
expect(registry.status.lastError).toBeUndefined();
});
it("treats an empty verified manifest as successful initialization", async () => {
const { registry, fetch } = setup();
fetch.mockResolvedValue(response("empty"));
expect((await registry.acquireSnapshot()).skills).toEqual([]);
expect(registry.status.initialized).toBe(true);
});
it("coalesces the first expired warm refresh and counts 304 as a successful check", async () => {
const { registry, fetch } = setup();
const initial = response();
fetch.mockResolvedValueOnce(initial);
const pinned = await registry.acquireSnapshot();
await vi.advanceTimersByTimeAsync(4999);
expect(await registry.acquireSnapshot()).toBe(pinned);
expect(fetch).toHaveBeenCalledTimes(1);
await vi.advanceTimersByTimeAsync(1);
fetch.mockResolvedValue({
status: "unchanged",
revision: initial.revision,
etag: initial.etag,
});
const next = await Promise.all([
registry.acquireSnapshot(),
registry.acquireSnapshot(),
registry.acquireSnapshot(),
]);
expect(next.every((value) => value === pinned)).toBe(true);
expect(fetch).toHaveBeenCalledTimes(2);
expect(fetch.mock.calls[1]?.[0]).toMatchObject({
containerId: "container",
ifNoneMatch: initial.etag,
signal: expect.any(AbortSignal),
});
expect(registry.status.lastCheckedAt).toBe("1970-01-01T00:00:05.000Z");
await registry.acquireSnapshot();
expect(fetch).toHaveBeenCalledTimes(2);
});
it("keeps immutable pinned objects while atomically installing a changed snapshot", async () => {
const { registry, fetch } = setup();
fetch.mockResolvedValueOnce(response());
const old = await registry.acquireSnapshot();
await vi.advanceTimersByTimeAsync(5000);
fetch.mockResolvedValueOnce(response("empty-r2"));
const next = await registry.acquireSnapshot();
expect(next).not.toBe(old);
expect(next.skills).toEqual([]);
expect(old.skills).toHaveLength(1);
});
it.each([
"NETWORK_ERROR",
"TIMEOUT",
"INVALID_SNAPSHOT",
"UNSUPPORTED_SERVER",
])("keeps a warm snapshot indefinitely on %s", async (code) => {
const { registry, fetch } = setup();
fetch.mockResolvedValueOnce(response());
const old = await registry.acquireSnapshot();
await vi.advanceTimersByTimeAsync(365 * 24 * 60 * 60 * 1000);
fetch.mockRejectedValue(new LearnedSkillsError(code as any, true));
expect(await registry.acquireSnapshot()).toBe(old);
expect(registry.status).toMatchObject({
stale: true,
lastError: { code },
lastCheckedAt: "1970-01-01T00:00:00.000Z",
});
expect(await registry.acquireSnapshot()).toBe(old);
expect(fetch).toHaveBeenCalledTimes(3);
});
it.each([
"AUTHENTICATION_FAILED",
"AUTHORIZATION_FAILED",
"ENTITLEMENT_REQUIRED",
"DELIVERY_DISABLED",
"REVISION_REVOKED",
])(
"blocks new invocations after %s even if the next request is unreachable",
async (code) => {
const { registry, fetch } = setup();
fetch.mockResolvedValueOnce(response());
const old = await registry.acquireSnapshot();
await vi.advanceTimersByTimeAsync(5000);
fetch.mockRejectedValueOnce(new LearnedSkillsError(code as any, false));
await expect(registry.acquireSnapshot()).rejects.toMatchObject({ code });
fetch.mockRejectedValueOnce(
new LearnedSkillsError("NETWORK_ERROR", true),
);
await expect(registry.acquireSnapshot()).rejects.toMatchObject({ code });
expect(old.skills).toHaveLength(1);
expect(registry.status.stale).toBe(false);
fetch.mockResolvedValueOnce(response());
expect(await registry.acquireSnapshot()).toMatchObject({
revision: "r1",
});
expect(registry.status.lastError).toBeUndefined();
},
);
it("allows initialization to retry after a catchable cold failure", async () => {
const { registry, fetch } = setup();
fetch.mockRejectedValueOnce(new LearnedSkillsError("NETWORK_ERROR", true));
await expect(registry.initialize()).rejects.toMatchObject({
code: "NETWORK_ERROR",
});
expect(registry.status.initialized).toBe(false);
fetch.mockResolvedValueOnce(response());
await registry.initialize();
expect(registry.status.initialized).toBe(true);
expect(fetch).toHaveBeenCalledTimes(2);
});
it("times out a client that ignores cancellation without a hidden retry", async () => {
const { registry, fetch } = setup();
fetch.mockReturnValue(new Promise(() => {}));
const pending = registry.acquireSnapshot();
const rejection = expect(pending).rejects.toMatchObject({
code: "TIMEOUT",
retryable: true,
});
await vi.advanceTimersByTimeAsync(5000);
await rejection;
expect(fetch).toHaveBeenCalledTimes(1);
expect(fetch.mock.calls[0]?.[0].signal?.aborted).toBe(true);
});
it("never installs a late response after a timed-out refresh", async () => {
const { registry, fetch } = setup();
let resolve!: (value: LearnedSkillsSnapshotResult) => void;
fetch.mockReturnValueOnce(
new Promise((done) => {
resolve = done;
}),
);
const first = registry.acquireSnapshot();
const rejection = expect(first).rejects.toMatchObject({ code: "TIMEOUT" });
await vi.advanceTimersByTimeAsync(5000);
await rejection;
fetch.mockResolvedValueOnce(response("empty"));
await registry.initialize();
resolve(response());
await Promise.resolve();
expect((await registry.acquireSnapshot()).skills).toEqual([]);
});
it("always sends an exact opaque pin and rejects another revision", async () => {
const { registry, fetch } = setup({ revision: "opaque/pin" });
fetch.mockResolvedValue(response());
await expect(registry.acquireSnapshot()).rejects.toMatchObject({
code: "INVALID_SNAPSHOT",
});
expect(fetch.mock.calls[0]?.[0].revision).toBe("opaque/pin");
expect(registry.status.mode).toBe("pinned");
});
it("rejects cold 304 and mismatched warm 304 metadata", async () => {
const { registry, fetch } = setup();
fetch.mockResolvedValueOnce({
status: "unchanged",
revision: "r1",
etag: "bad",
});
await expect(registry.initialize()).rejects.toMatchObject({
code: "INVALID_SNAPSHOT",
});
fetch.mockResolvedValueOnce(response());
const old = await registry.acquireSnapshot();
await vi.advanceTimersByTimeAsync(5000);
fetch.mockResolvedValueOnce({
status: "unchanged",
revision: "other",
etag: old.etag,
});
expect(await registry.acquireSnapshot()).toBe(old);
expect(registry.status.stale).toBe(true);
});
it("maps a malformed injected-client response to INVALID_SNAPSHOT", async () => {
const { registry, fetch } = setup();
fetch.mockResolvedValue(null as unknown as LearnedSkillsSnapshotResult);
await expect(registry.initialize()).rejects.toMatchObject({
code: "INVALID_SNAPSHOT",
});
});
it("coalesces confirmed denial requests for concurrent warm invocations", async () => {
const { registry, fetch } = setup();
fetch.mockResolvedValueOnce(response());
await registry.initialize();
await vi.advanceTimersByTimeAsync(5000);
fetch.mockRejectedValueOnce(
new LearnedSkillsError("REVISION_REVOKED", false),
);
const results = await Promise.allSettled([
registry.acquireSnapshot(),
registry.acquireSnapshot(),
registry.acquireSnapshot(),
]);
expect(results).toHaveLength(3);
for (const result of results)
expect(result).toMatchObject({
status: "rejected",
reason: { code: "REVISION_REVOKED" },
});
expect(fetch).toHaveBeenCalledTimes(2);
});
it("returns a frozen status value without exception causes or caller mutation", async () => {
const { registry, fetch } = setup();
fetch.mockRejectedValue(
new LearnedSkillsError(
"NETWORK_ERROR",
true,
new Error("private diagnostic"),
),
);
await expect(registry.initialize()).rejects.toMatchObject({
code: "NETWORK_ERROR",
});
const status = registry.status;
expect(Object.isFrozen(status)).toBe(true);
expect(Object.isFrozen(status.lastError)).toBe(true);
expect(status.lastError).not.toHaveProperty("cause");
});
it.each([false, true])(
"logs only safe metadata when debug=%s",
async (debug) => {
const log = vi.spyOn(console, "debug").mockImplementation(() => {});
const { registry, fetch } = setup({ debug });
fetch.mockResolvedValueOnce(response());
await registry.initialize();
await vi.advanceTimersByTimeAsync(5000);
fetch.mockRejectedValueOnce(
new LearnedSkillsError(
"NETWORK_ERROR",
true,
new Error("private prompt"),
),
);
await registry.acquireSnapshot();
if (debug) expect(log).toHaveBeenCalled();
else expect(log).not.toHaveBeenCalled();
const output = JSON.stringify(log.mock.calls);
for (const secret of [
"never-log-this-key",
"private prompt",
"Refunds are available",
"# Refund policy",
])
expect(output).not.toContain(secret);
},
);
});
@@ -0,0 +1,230 @@
import { createHash } from "node:crypto";
import { describe, expect, it } from "vitest";
import { zipSync } from "fflate";
import fixtures from "../../conformance/snapshots.v1.json";
import { validateSnapshot } from "../snapshot.js";
export function fixtureResponse(name = "text-skill") {
const fixture = fixtures.cases.find((item) => item.name === name)!;
return {
status: "snapshot" as const,
bytes: new Uint8Array(Buffer.from(fixture.archiveBase64, "base64")),
revision: fixture.revision,
etag: fixture.etag,
contentType: "application/zip",
};
}
describe("language-neutral snapshot conformance", () => {
for (const fixture of fixtures.cases) {
it(fixture.name, async () => {
const result = validateSnapshot(fixtureResponse(fixture.name));
if (fixture.expected === "valid") {
const snapshot = await result;
expect(snapshot.revision).toBe(fixture.revision);
expect(snapshot.etag).toBe(fixture.etag);
expect(Object.isFrozen(snapshot)).toBe(true);
} else {
await expect(result).rejects.toMatchObject({ code: fixture.expected });
}
});
}
it("returns immutable decoded content and exact supporting membership", async () => {
const snapshot = await validateSnapshot(fixtureResponse());
expect(snapshot.skills).toEqual([
{
name: "refund-policy",
description: "Use when handling refunds.",
files: [
expect.objectContaining({
path: "SKILL.md",
text: "# Refund policy\nUse the published refund policy.\n",
}),
expect.objectContaining({
path: "reference.txt",
text: "Refunds are available for 30 days.\n",
}),
],
},
]);
expect(Object.isFrozen(snapshot.skills)).toBe(true);
expect(Object.isFrozen(snapshot.skills[0])).toBe(true);
expect(Object.isFrozen(snapshot.skills[0].files)).toBe(true);
expect(Object.isFrozen(snapshot.skills[0].files[0])).toBe(true);
});
it("does not expose unreadable supporting bytes as model text", async () => {
const snapshot = await validateSnapshot(fixtureResponse("binary-resource"));
expect(
snapshot.skills[0].files.find(
(file: { path: string }) => file.path === "resource.bin",
).text,
).toBeUndefined();
});
it("rejects archive bytes that do not match the complete archive ETag", async () => {
await expect(
validateSnapshot({ ...fixtureResponse(), etag: `"${"0".repeat(64)}"` }),
).rejects.toMatchObject({ code: "INVALID_SNAPSHOT" });
});
it("rejects a corrupt ZIP even when its ETag matches", async () => {
const bytes = new Uint8Array([1, 2, 3]);
await expect(
validateSnapshot({
...fixtureResponse(),
bytes,
etag: `"${createHash("sha256").update(bytes).digest("hex")}"`,
}),
).rejects.toMatchObject({ code: "INVALID_SNAPSHOT" });
});
it("returns a typed error for a malformed null response", async () => {
await expect(
validateSnapshot(null as unknown as ReturnType<typeof fixtureResponse>),
).rejects.toMatchObject({ code: "INVALID_SNAPSHOT" });
});
it("owns archive bytes before asynchronous parsing", async () => {
const response = fixtureResponse();
const pending = validateSnapshot(response);
response.bytes.fill(0);
const snapshot = await pending;
expect(
snapshot.skills[0].files.find((file) => file.path === "SKILL.md")?.text,
).toContain("# Refund policy");
});
it("captures response metadata before asynchronous parsing", async () => {
const response = fixtureResponse();
const etag = response.etag;
const pending = validateSnapshot(response);
response.etag = `"${"0".repeat(64)}"`;
expect((await pending).etag).toBe(etag);
});
it("preserves the exact decoded text including a UTF-8 BOM", async () => {
const content = new TextEncoder().encode("\ufeff# Skill");
const manifest = {
schemaVersion: 1,
revision: "r1",
skills: [
{
name: "skill",
description: "description",
files: [
{
path: "SKILL.md",
size: content.length,
sha256: createHash("sha256").update(content).digest("hex"),
},
],
},
],
};
const bytes = zipSync({
"manifest.json": new TextEncoder().encode(JSON.stringify(manifest)),
"skill/SKILL.md": content,
});
const snapshot = await validateSnapshot({
...fixtureResponse(),
bytes,
etag: `"${createHash("sha256").update(bytes).digest("hex")}"`,
});
expect(snapshot.skills[0].files[0].text).toBe("\ufeff# Skill");
});
it("does not normalize a BOM-prefixed archive filename into manifest.json", async () => {
const bytes = zipSync({
"\ufeffmanifest.json": new TextEncoder().encode(
JSON.stringify({ schemaVersion: 1, revision: "r1", skills: [] }),
),
});
await expect(
validateSnapshot({
...fixtureResponse(),
bytes,
etag: `"${createHash("sha256").update(bytes).digest("hex")}"`,
}),
).rejects.toMatchObject({ code: "INVALID_SNAPSHOT" });
});
it("rejects more than 1000 archive entries before installing content", async () => {
const fileBytes = new TextEncoder().encode("text");
const files = [
"SKILL.md",
...Array.from(
{ length: 999 },
(_, i) => `file-${String(i).padStart(4, "0")}.txt`,
),
];
const manifest = {
schemaVersion: 1,
revision: "r1",
skills: [
{
name: "skill",
description: "description",
files: files.map((path) => ({
path,
size: fileBytes.length,
sha256: createHash("sha256").update(fileBytes).digest("hex"),
})),
},
],
};
const bytes = zipSync({
"manifest.json": new TextEncoder().encode(JSON.stringify(manifest)),
...Object.fromEntries(files.map((path) => [`skill/${path}`, fileBytes])),
});
await expect(
validateSnapshot({
...fixtureResponse(),
bytes,
etag: `"${createHash("sha256").update(bytes).digest("hex")}"`,
}),
).rejects.toMatchObject({ code: "INVALID_SNAPSHOT" });
});
it("rejects a compressed archive declaring more than 32 MiB decoded content", async () => {
const content = new Uint8Array(32 * 1024 * 1024 + 1).fill(65);
const manifest = {
schemaVersion: 1,
revision: "r1",
skills: [
{
name: "skill",
description: "description",
files: [
{
path: "SKILL.md",
size: content.length,
sha256: createHash("sha256").update(content).digest("hex"),
},
],
},
],
};
const bytes = zipSync({
"manifest.json": new TextEncoder().encode(JSON.stringify(manifest)),
"skill/SKILL.md": content,
});
await expect(
validateSnapshot({
...fixtureResponse(),
bytes,
etag: `"${createHash("sha256").update(bytes).digest("hex")}"`,
}),
).rejects.toMatchObject({ code: "INVALID_SNAPSHOT" });
});
it("rejects archives over the 32 MiB compressed limit", async () => {
await expect(
validateSnapshot({
...fixtureResponse(),
bytes: new Uint8Array(32 * 1024 * 1024 + 1),
}),
).rejects.toMatchObject({ code: "INVALID_SNAPSHOT" });
});
});
@@ -0,0 +1,192 @@
import { fromBuffer } from "yauzl";
import type { Entry, ZipFile } from "yauzl";
import type { Readable } from "node:stream";
import { invalidSnapshot } from "./errors.js";
/** Internal corruption bounds, including manifest and directory entries. */
export const MAX_ARCHIVE_ENTRIES = 1_000;
export const MAX_SNAPSHOT_BYTES = 32 * 1024 * 1024;
export function safeRelativePath(path: string): boolean {
return (
path.length > 0 &&
// Reject control bytes in archive paths, including NUL and DEL.
// eslint-disable-next-line no-control-regex
!/[\\\x00-\x1f\x7f]/.test(path) &&
!/^[a-z]:/i.test(path) &&
path
.split("/")
.every((part) => part !== "" && part !== "." && part !== "..")
);
}
/** Decode only the listed ZIP namespace. No filesystem is involved. */
export function readArchive(
bytes: Buffer,
signal?: AbortSignal,
): Promise<Map<string, Buffer>> {
return new Promise((resolve, reject) => {
let archive: ZipFile | undefined;
let stream: Readable | undefined;
let settled = false;
const finish = (error?: unknown) => {
if (settled) return;
settled = true;
signal?.removeEventListener("abort", abort);
stream?.destroy();
archive?.close();
if (error !== undefined) reject(error);
else resolve(files);
};
const abort = () => finish(signal?.reason ?? invalidSnapshot());
const files = new Map<string, Buffer>();
const names = new Set<string>();
let decodedBytes = 0;
signal?.addEventListener("abort", abort, { once: true });
if (signal?.aborted) {
abort();
return;
}
fromBuffer(
bytes,
{
lazyEntries: true,
decodeStrings: false,
validateEntrySizes: true,
autoClose: false,
},
(error, zip) => {
if (error) {
finish(invalidSnapshot(error));
return;
}
archive = zip;
if (settled) {
zip.close();
return;
}
zip.on("error", (cause: unknown) => finish(invalidSnapshot(cause)));
zip.on("end", () => finish());
if (zip.entryCount > MAX_ARCHIVE_ENTRIES) {
finish(invalidSnapshot());
return;
}
zip.on("entry", (entry: Entry) => {
try {
// decodeStrings:false preserves raw UTF-8 names instead of normalizing
// backslashes or consulting alternate Unicode-path extra fields.
const rawName: unknown = entry.fileName;
if (!Buffer.isBuffer(rawName)) throw invalidSnapshot();
const name = new TextDecoder("utf-8", {
fatal: true,
ignoreBOM: true,
}).decode(rawName);
const directory = name.endsWith("/");
if (
!safeRelativePath(directory ? name.slice(0, -1) : name) ||
names.has(name)
)
throw invalidSnapshot();
names.add(name);
const fileType = (entry.externalFileAttributes >>> 16) & 0o170000;
if (
fileType !== 0 &&
fileType !== (directory ? 0o040000 : 0o100000)
)
throw invalidSnapshot();
if (
entry.isEncrypted() ||
![0, 8].includes(entry.compressionMethod)
)
throw invalidSnapshot();
if (
!Number.isSafeInteger(entry.uncompressedSize) ||
entry.uncompressedSize < 0
)
throw invalidSnapshot();
decodedBytes += entry.uncompressedSize;
if (
decodedBytes > MAX_SNAPSHOT_BYTES ||
(directory && entry.uncompressedSize !== 0)
)
throw invalidSnapshot();
// Require the central directory and the local entry to name the same
// bytes. ZIP data descriptors may leave local sizes unset.
const local = entry.relativeOffsetOfLocalHeader;
if (
local < 0 ||
local + 30 > bytes.length ||
bytes.readUInt32LE(local) !== 0x04034b50
)
throw invalidSnapshot();
if (
bytes.readUInt16LE(local + 6) !== entry.generalPurposeBitFlag ||
bytes.readUInt16LE(local + 8) !== entry.compressionMethod
)
throw invalidSnapshot();
const localNameLength = bytes.readUInt16LE(local + 26);
const localExtraLength = bytes.readUInt16LE(local + 28);
const dataStart = local + 30 + localNameLength + localExtraLength;
if (
dataStart + entry.compressedSize > bytes.length ||
!bytes
.subarray(local + 30, local + 30 + localNameLength)
.equals(rawName)
)
throw invalidSnapshot();
if (
!(entry.generalPurposeBitFlag & 8) &&
(bytes.readUInt32LE(local + 18) !== entry.compressedSize ||
bytes.readUInt32LE(local + 22) !== entry.uncompressedSize)
)
throw invalidSnapshot();
if (directory) {
zip.readEntry();
return;
}
zip.openReadStream(entry, (readError, readStream) => {
if (readError) {
finish(invalidSnapshot(readError));
return;
}
stream = readStream;
if (settled) {
readStream.destroy();
return;
}
const chunks: Buffer[] = [];
let size = 0;
readStream.on("error", (cause: unknown) =>
finish(invalidSnapshot(cause)),
);
readStream.on("data", (chunk: Buffer) => {
size += chunk.length;
if (
size > entry.uncompressedSize ||
size > MAX_SNAPSHOT_BYTES
) {
finish(invalidSnapshot());
return;
}
chunks.push(chunk);
});
readStream.on("end", () => {
if (settled) return;
if (size !== entry.uncompressedSize) {
finish(invalidSnapshot());
return;
}
files.set(name, Buffer.concat(chunks, size));
stream = undefined;
zip.readEntry();
});
});
} catch (cause) {
finish(invalidSnapshot(cause));
}
});
zip.readEntry();
},
);
});
}
@@ -0,0 +1,91 @@
import { CopilotKitIntelligence } from "@copilotkit/runtime/v2";
import { SkillDeliveryError } from "./errors.js";
export interface SkillRegistryOptions {
client?: CopilotKitIntelligence;
apiKey?: string;
apiUrl?: string;
containerId?: string;
revision?: string;
freshnessWindowMs?: number;
requestTimeoutMs?: number;
debug?: boolean;
}
export interface RegistryConfig {
readonly client: CopilotKitIntelligence;
readonly containerId: string;
readonly revision?: string;
readonly freshnessWindowMs: number;
readonly requestTimeoutMs: number;
readonly debug: boolean;
}
/** Resolve once. Never retain the options object, environment, or a second key. */
export function resolveRegistryConfig(
options: SkillRegistryOptions = {},
environment: NodeJS.ProcessEnv = process.env,
): RegistryConfig {
try {
const containerId =
options.containerId ?? environment.CPK_INTELLIGENCE_LEARNING_CONTAINER_ID;
const revision =
options.revision ?? environment.CPK_INTELLIGENCE_SKILLS_REVISION;
const freshnessWindowMs = options.freshnessWindowMs ?? 5_000;
const requestTimeoutMs = options.requestTimeoutMs ?? 5_000;
const debug = options.debug ?? false;
if (
typeof containerId !== "string" ||
!containerId.trim() ||
(revision !== undefined &&
(typeof revision !== "string" || revision.length === 0)) ||
!Number.isSafeInteger(freshnessWindowMs) ||
freshnessWindowMs < 0 ||
!Number.isSafeInteger(requestTimeoutMs) ||
requestTimeoutMs < 1 ||
requestTimeoutMs > 2_147_483_647 ||
typeof debug !== "boolean"
)
throw new SkillDeliveryError("INVALID_CONFIG", false);
let client = options.client;
if (client !== undefined) {
if (!client || typeof client.getLearnedSkillsSnapshot !== "function")
throw new SkillDeliveryError("INVALID_CONFIG", false);
} else {
const apiKey = options.apiKey ?? environment.CPK_INTELLIGENCE_API_KEY;
const apiUrl = options.apiUrl ?? environment.INTELLIGENCE_API_URL;
if (typeof apiKey !== "string" || !apiKey.trim())
throw new SkillDeliveryError("INVALID_CONFIG", false);
if (apiUrl?.trim()) {
const url = new URL(apiUrl);
if (
!["http:", "https:"].includes(url.protocol) ||
url.username ||
url.password ||
url.search ||
url.hash
)
throw new SkillDeliveryError("INVALID_CONFIG", false);
}
client = new CopilotKitIntelligence({
apiKey,
...(apiUrl !== undefined ? { apiUrl } : {}),
// This private helper client uses REST only. Explicitly retain the
// canonical websocket default to prevent an irrelevant host warning.
...(apiUrl?.trim()
? { wsUrl: "wss://realtime.intelligence.copilotkit.ai" }
: {}),
});
}
return Object.freeze({
client,
containerId,
...(revision !== undefined ? { revision } : {}),
freshnessWindowMs,
requestTimeoutMs,
debug,
});
} catch (error) {
if (error instanceof SkillDeliveryError) throw error;
throw new SkillDeliveryError("INVALID_CONFIG", false, error);
}
}
@@ -0,0 +1,9 @@
import { LearnedSkillsError } from "@copilotkit/runtime/v2";
// Reuse the canonical delivery error identity across transport and adapter code.
export { LearnedSkillsError as SkillDeliveryError } from "@copilotkit/runtime/v2";
export type { LearnedSkillsErrorCode as SkillDeliveryErrorCode } from "@copilotkit/runtime/v2";
export function invalidSnapshot(cause?: unknown): LearnedSkillsError {
return new LearnedSkillsError("INVALID_SNAPSHOT", false, cause);
}
@@ -0,0 +1,2 @@
/* eslint-disable unicorn/no-empty-file */
// The framework-native middleware will define this package's public entry point.
@@ -0,0 +1,190 @@
import { resolveRegistryConfig } from "./config.js";
import type { RegistryConfig, SkillRegistryOptions } from "./config.js";
import { SkillDeliveryError, invalidSnapshot } from "./errors.js";
import type { SkillDeliveryErrorCode } from "./errors.js";
import { validateSnapshot } from "./snapshot.js";
import type { VerifiedSnapshot } from "./snapshot.js";
export interface SkillRegistryStatus {
readonly initialized: boolean;
readonly revision?: string;
readonly mode: "latest" | "pinned";
/** ISO 8601 timestamp of the last successful 200 or matching 304 check. */
readonly lastCheckedAt?: string;
readonly stale: boolean;
readonly lastError?: Readonly<{
code: SkillDeliveryErrorCode;
message: string;
retryable: boolean;
}>;
}
function transient(error: SkillDeliveryError): boolean {
return [
"NETWORK_ERROR",
"TIMEOUT",
"INVALID_SNAPSHOT",
"UNSUPPORTED_SERVER",
].includes(error.code);
}
function deliveryError(error: unknown): SkillDeliveryError {
if (error instanceof SkillDeliveryError) return error;
return new SkillDeliveryError(
error instanceof Error && error.name === "TimeoutError"
? "TIMEOUT"
: "NETWORK_ERROR",
true,
error,
);
}
/** Internal registry shared by framework adapters; public API is framework-native. */
export class SkillRegistry {
readonly #config: RegistryConfig;
#snapshot?: VerifiedSnapshot;
#inFlight?: Promise<VerifiedSnapshot>;
#lastCheckedAt?: number;
#stale = false;
#lastError?: SkillDeliveryError;
#blocked?: SkillDeliveryError;
constructor(options: SkillRegistryOptions = {}) {
this.#config = resolveRegistryConfig(options);
}
async initialize(): Promise<void> {
await this.acquireSnapshot();
}
/** Capture exactly one immutable snapshot before model work begins. */
acquireSnapshot(): Promise<VerifiedSnapshot> {
if (this.#inFlight) return this.#inFlight;
if (
this.#snapshot &&
!this.#blocked &&
this.#lastCheckedAt !== undefined &&
Date.now() - this.#lastCheckedAt < this.#config.freshnessWindowMs
) {
return Promise.resolve(this.#snapshot);
}
// Defer work one microtask so reentrant and concurrent callers observe the
// same promise before an injected client can start its request.
this.#inFlight = Promise.resolve()
.then(() => this.#refresh())
.finally(() => {
this.#inFlight = undefined;
});
return this.#inFlight;
}
get status(): SkillRegistryStatus {
const error = this.#lastError;
return Object.freeze({
initialized: this.#snapshot !== undefined,
...(this.#snapshot ? { revision: this.#snapshot.revision } : {}),
mode: this.#config.revision === undefined ? "latest" : "pinned",
...(this.#lastCheckedAt !== undefined
? { lastCheckedAt: new Date(this.#lastCheckedAt).toISOString() }
: {}),
stale: this.#stale,
...(error
? {
lastError: Object.freeze({
code: error.code,
message: error.message,
retryable: error.retryable,
}),
}
: {}),
});
}
async #refresh(): Promise<VerifiedSnapshot> {
const startedAt = Date.now();
const controller = new AbortController();
let timer: ReturnType<typeof setTimeout> | undefined;
const deadline = new Promise<never>((_, reject) => {
timer = setTimeout(() => {
const error = new SkillDeliveryError("TIMEOUT", true);
reject(error);
controller.abort(error);
}, this.#config.requestTimeoutMs);
});
const replacement = async (): Promise<VerifiedSnapshot> => {
const response = await this.#config.client.getLearnedSkillsSnapshot({
containerId: this.#config.containerId,
...(this.#config.revision !== undefined
? { revision: this.#config.revision }
: {}),
...(this.#snapshot ? { ifNoneMatch: this.#snapshot.etag } : {}),
signal: controller.signal,
});
controller.signal.throwIfAborted();
if (!response || typeof response !== "object") throw invalidSnapshot();
if (response.status === "unchanged") {
if (
!this.#snapshot ||
response.etag !== this.#snapshot.etag ||
response.revision !== this.#snapshot.revision
)
throw invalidSnapshot();
return this.#snapshot;
}
if (
this.#config.revision !== undefined &&
response.revision !== this.#config.revision
)
throw invalidSnapshot();
return validateSnapshot(response, controller.signal);
};
try {
const snapshot = await Promise.race([replacement(), deadline]);
// An abandoned parse never writes registry state: only the winning
// complete replacement reaches this assignment.
this.#snapshot = snapshot;
this.#lastCheckedAt = Date.now();
this.#stale = false;
this.#lastError = undefined;
this.#blocked = undefined;
this.#debug("refresh-success", startedAt);
return snapshot;
} catch (cause) {
const error = deliveryError(cause);
if (!transient(error)) {
this.#blocked = error;
this.#stale = false;
this.#lastError = error;
this.#debug("refresh-denied", startedAt, error.code);
throw error;
}
// A later unreachable server cannot undo an already confirmed denial.
if (this.#blocked) {
this.#lastError = this.#blocked;
this.#debug("refresh-failed", startedAt, error.code);
throw this.#blocked;
}
this.#lastError = error;
this.#stale = this.#snapshot !== undefined;
this.#debug("refresh-failed", startedAt, error.code);
if (this.#snapshot) return this.#snapshot;
throw error;
} finally {
if (timer !== undefined) clearTimeout(timer);
}
}
#debug(
event: string,
startedAt: number,
errorCode?: SkillDeliveryErrorCode,
): void {
if (!this.#config.debug) return;
console.debug("CopilotKit learned skills", {
event,
containerId: this.#config.containerId,
revision: this.#snapshot?.revision,
durationMs: Date.now() - startedAt,
...(errorCode ? { errorCode } : {}),
});
}
}
@@ -0,0 +1,174 @@
import { createHash } from "node:crypto";
import type { LearnedSkillsSnapshotResult } from "@copilotkit/runtime/v2";
import { SkillDeliveryError, invalidSnapshot } from "./errors.js";
import {
MAX_SNAPSHOT_BYTES,
readArchive,
safeRelativePath,
} from "./archive.js";
export interface SnapshotFile {
readonly path: string;
readonly size: number;
readonly sha256: string;
/** Absent for supporting resources that cannot be decoded as UTF-8 text. */
readonly text?: string;
}
export interface SnapshotSkill {
readonly name: string;
readonly description: string;
readonly files: readonly SnapshotFile[];
}
export interface VerifiedSnapshot {
readonly revision: string;
readonly etag: string;
readonly skills: readonly SnapshotSkill[];
}
function object(value: unknown): value is Record<string, unknown> {
return value !== null && typeof value === "object" && !Array.isArray(value);
}
function sha256(bytes: Uint8Array): string {
return createHash("sha256").update(bytes).digest("hex");
}
function utf8(bytes: Uint8Array): string | undefined {
try {
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(
bytes,
);
} catch {
return undefined;
}
}
/** Validate a replacement fully before it can become an invocation snapshot. */
export async function validateSnapshot(
response: Extract<LearnedSkillsSnapshotResult, { status: "snapshot" }>,
signal?: AbortSignal,
): Promise<VerifiedSnapshot> {
try {
if (
!object(response) ||
!(response.bytes instanceof Uint8Array) ||
typeof response.revision !== "string" ||
typeof response.etag !== "string" ||
typeof response.contentType !== "string"
)
throw invalidSnapshot();
const { revision, etag, contentType } = response;
if (
response.status !== "snapshot" ||
response.bytes.byteLength > MAX_SNAPSHOT_BYTES ||
contentType.split(";", 1)[0]?.trim().toLowerCase() !==
"application/zip" ||
!revision ||
!/^"[a-f0-9]{64}"$/.test(etag)
)
throw invalidSnapshot();
// Own the bytes before asynchronous decompression so an injected client
// cannot mutate an in-flight replacement after its digest is checked.
const bytes = Buffer.from(response.bytes);
if (`"${sha256(bytes)}"` !== etag) throw invalidSnapshot();
const archive = await readArchive(bytes, signal);
const manifestBytes = archive.get("manifest.json");
if (!manifestBytes) throw invalidSnapshot();
const decoded = utf8(manifestBytes);
if (decoded === undefined) throw invalidSnapshot();
const manifest: unknown = JSON.parse(decoded);
if (!object(manifest)) throw invalidSnapshot();
if (
typeof manifest.schemaVersion === "number" &&
manifest.schemaVersion !== 1
)
throw new SkillDeliveryError("UNSUPPORTED_SERVER", false);
if (
manifest.schemaVersion !== 1 ||
manifest.revision !== revision ||
!Array.isArray(manifest.skills)
)
throw invalidSnapshot();
const expected = new Set(["manifest.json"]);
let previousName: string | undefined;
const skills: SnapshotSkill[] = [];
for (const skill of manifest.skills) {
if (
!object(skill) ||
typeof skill.name !== "string" ||
!safeRelativePath(skill.name) ||
skill.name.includes("/") ||
(previousName !== undefined &&
Buffer.compare(Buffer.from(previousName), Buffer.from(skill.name)) >=
0) ||
typeof skill.description !== "string" ||
!Array.isArray(skill.files)
)
throw invalidSnapshot();
previousName = skill.name;
let previousPath: string | undefined;
let hasSkill = false;
const files: SnapshotFile[] = [];
for (const file of skill.files) {
if (
!object(file) ||
typeof file.path !== "string" ||
!safeRelativePath(file.path) ||
(previousPath !== undefined &&
Buffer.compare(Buffer.from(previousPath), Buffer.from(file.path)) >=
0) ||
typeof file.size !== "number" ||
!Number.isSafeInteger(file.size) ||
file.size < 0 ||
typeof file.sha256 !== "string" ||
!/^[a-f0-9]{64}$/.test(file.sha256)
)
throw invalidSnapshot();
previousPath = file.path;
const path = `${skill.name}/${file.path}`;
if (expected.has(path)) throw invalidSnapshot();
expected.add(path);
const content = archive.get(path);
if (
!content ||
content.length !== file.size ||
sha256(content) !== file.sha256
)
throw invalidSnapshot();
const text = utf8(content);
if (file.path === "SKILL.md") {
if (text === undefined) throw invalidSnapshot();
hasSkill = true;
}
files.push(
Object.freeze({
path: file.path,
size: file.size,
sha256: file.sha256,
...(text !== undefined ? { text } : {}),
}),
);
}
if (!hasSkill) throw invalidSnapshot();
skills.push(
Object.freeze({
name: skill.name,
description: skill.description,
files: Object.freeze(files),
}),
);
}
if (
expected.size !== archive.size ||
[...archive.keys()].some((path) => !expected.has(path))
)
throw invalidSnapshot();
return Object.freeze({
revision: revision,
etag: etag,
skills: Object.freeze(skills),
});
} catch (error) {
if (signal?.aborted) throw signal.reason;
if (error instanceof SkillDeliveryError) throw error;
throw invalidSnapshot(error);
}
}
@@ -0,0 +1,6 @@
{
"extends": "@copilotkit/typescript-config/base.json",
"compilerOptions": { "outDir": "dist", "rootDir": "src", "types": ["node", "vitest/globals"] },
"include": ["src/**/*.ts"],
"exclude": ["dist", "node_modules", "src/**/__tests__/**"]
}
@@ -0,0 +1,10 @@
import { defineConfig } from "tsdown";
export default defineConfig({
entry: ["src/index.ts"],
format: ["esm", "cjs"],
dts: true,
sourcemap: true,
target: "es2022",
outDir: "dist",
unbundle: true,
});
@@ -0,0 +1,9 @@
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
environment: "node",
include: ["src/**/__tests__/**/*.test.ts"],
maxWorkers: 2,
env: { COPILOTKIT_TELEMETRY_DISABLED: "true" },
},
});
@@ -118,6 +118,76 @@ describe("getLearnedSkillsSnapshot", () => {
},
);
it.each([401, 403])(
"treats malformed HTTP %s as a confirmed denial",
async (status) => {
for (const body of [
"<html>secret</html>",
"{}",
JSON.stringify({
error: {
code: "NETWORK_ERROR",
message: "secret",
category: "transient",
retryable: true,
},
}),
]) {
fetchMock.mockResolvedValue(new Response(body, { status }));
const error = await client
.getLearnedSkillsSnapshot({ containerId: "c" })
.catch((failure) => failure);
expect(error).toMatchObject({
code:
status === 401 ? "AUTHENTICATION_FAILED" : "AUTHORIZATION_FAILED",
retryable: false,
});
expect(JSON.stringify(error)).not.toContain("secret");
}
},
);
it.each([401, 403])(
"preserves denial status %s when the body read fails",
async (status) => {
const cause = new TypeError("socket closed");
const response = new Response(null, { status });
vi.spyOn(response, "json").mockRejectedValue(cause);
fetchMock.mockResolvedValue(response);
const error = await client
.getLearnedSkillsSnapshot({ containerId: "c" })
.catch((failure) => failure);
expect(error).toMatchObject({
code: status === 401 ? "AUTHENTICATION_FAILED" : "AUTHORIZATION_FAILED",
retryable: false,
cause,
});
expect(JSON.stringify(error)).not.toContain("socket closed");
},
);
it("prioritizes HTTP 401 over a conflicting structured code", async () => {
fetchMock.mockResolvedValue(
new Response(
JSON.stringify({
error: {
code: "DELIVERY_DISABLED",
message: "secret",
category: "permanent",
retryable: false,
},
}),
{ status: 401 },
),
);
await expect(
client.getLearnedSkillsSnapshot({ containerId: "c" }),
).rejects.toMatchObject({
code: "AUTHENTICATION_FAILED",
retryable: false,
});
});
it.each([404, 200, 204])(
"rejects unsupported response status/body %s",
async (status) => {
@@ -176,6 +246,17 @@ describe("getLearnedSkillsSnapshot", () => {
expect(fetchMock).toHaveBeenCalledTimes(1);
});
it("preserves a socket failure while reading an error body", async () => {
const cause = new TypeError("socket closed");
const response = new Response(null, { status: 503 });
vi.spyOn(response, "json").mockRejectedValue(cause);
fetchMock.mockResolvedValue(response);
await expect(
client.getLearnedSkillsSnapshot({ containerId: "c" }),
).rejects.toMatchObject({ code: "NETWORK_ERROR", retryable: true, cause });
expect(fetchMock).toHaveBeenCalledTimes(1);
});
it("preserves caller cancellation", async () => {
const controller = new AbortController();
controller.abort();
@@ -865,13 +865,45 @@ export class CopilotKitIntelligence {
});
params.signal?.throwIfAborted();
if (response.status !== 200 && response.status !== 304) {
const denialCode =
response.status === 401
? "AUTHENTICATION_FAILED"
: response.status === 403
? "AUTHORIZATION_FAILED"
: undefined;
let body: unknown;
try {
body = await response.json();
} catch {
} catch (error) {
params.signal?.throwIfAborted();
if (denialCode) {
throw new LearnedSkillsError(
denialCode,
false,
error instanceof SyntaxError ? undefined : error,
);
}
if (!(error instanceof SyntaxError)) throw error;
}
throw learnedSkillsResponseError(body);
const responseError = learnedSkillsResponseError(body);
// An HTTP denial must never become a transient failure that permits
// consumers to keep serving a previously authorized snapshot.
if (
response.status === 401 ||
(response.status === 403 &&
![
"AUTHENTICATION_FAILED",
"AUTHORIZATION_FAILED",
"ENTITLEMENT_REQUIRED",
"DELIVERY_DISABLED",
"CONTAINER_NOT_FOUND",
"REVISION_NOT_FOUND",
"REVISION_REVOKED",
].includes(responseError.code))
) {
throw new LearnedSkillsError(denialCode!, false);
}
throw responseError;
}
const revision = response.headers.get("X-CopilotKit-Skills-Revision");
const etag = response.headers.get("ETag");
+31
View File
@@ -2667,6 +2667,37 @@ importers:
specifier: ^4.1.3
version: 4.1.5(@opentelemetry/api@1.9.0)(@types/node@22.19.11)(@vitest/coverage-v8@4.1.5)(jsdom@29.1.0(@noble/hashes@1.8.0))(vite@7.3.2(@types/node@22.19.11)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0))
packages/intelligence-langgraph:
dependencies:
'@copilotkit/runtime':
specifier: workspace:*
version: link:../runtime
yauzl:
specifier: 2.10.0
version: 2.10.0
devDependencies:
'@copilotkit/typescript-config':
specifier: workspace:*
version: link:../typescript-config
'@types/node':
specifier: ^22.15.3
version: 22.19.11
'@types/yauzl':
specifier: 2.10.3
version: 2.10.3
fflate:
specifier: 0.8.2
version: 0.8.2
tsdown:
specifier: ^0.20.3
version: 0.20.3(@arethetypeswrong/core@0.18.2)(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(oxc-resolver@11.24.2)(publint@0.3.17)(synckit@0.11.12)(typescript@5.8.2)
typescript:
specifier: 5.8.2
version: 5.8.2
vitest:
specifier: ^3.2.4
version: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.11)(@vitest/ui@3.2.4)(jiti@2.7.0)(jsdom@29.1.0(@noble/hashes@1.8.0))(less@4.6.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)
packages/react-core:
dependencies:
'@ag-ui/client':