@passioncode-ai/fabric-agent-adapter 0.49.0 → 0.49.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.49.1 (2026-10-24)
4
+
5
+ ### Fixed
6
+
7
+ - **The Python estate kit matches every membership as the contract does** (FAA-113, from the 0.49.0 seam reading).
8
+ The contract keeps machine ids, passkeys, publisher keys, grants and revocations in a `Set`, or tests them with
9
+ `includes`; both match NaN with NaN. Fifteen lookups in `fabric_estate.py` used `===`-style matching, so a record a
10
+ Python caller builds with a NaN id was read differently from the Node twin: a NaN id listed twice passed, and a
11
+ secret from a NaN machine was flagged as coming from outside the estate. They now use `set_has`. No JSON the kit
12
+ loads carries NaN (`parse_constant` refuses it), so a service reading records from JSON saw no difference.
13
+
14
+ ### Tests
15
+
16
+ - FAC-SEM-137 on the verify path (FAA-112): six `verify` vectors vary the delta anchor on the contract's EdDSA
17
+ revision 2, re-signed with the vectors' test seed and answered by the contract's `verifyEstateRecord`.
18
+ - The board restart cases wait on their condition, not a fixed 20 ms (FAA-114).
19
+
3
20
  ## 0.49.0 (2026-10-24)
4
21
 
5
22
  The operator's sweep (FAA-111): blind seam and product readings of 0.47.0 to 0.48.3, their findings fixed, documented
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@passioncode-ai/fabric-agent-adapter",
3
- "version": "0.49.0",
3
+ "version": "0.49.1",
4
4
  "description": "Create Fabric-compatible agents, run them as always-alive local services with dashboards (fabric-service/0.1), and adapt existing projects to the Fabric Agent Contract. This package is the installer CLI.",
5
5
  "scripts": {
6
6
  "test": "python3 test/validate.py && python3 -m unittest discover -s test -q && node --test 'test/*.test.mjs'"
@@ -3,7 +3,7 @@
3
3
  "name": "fabric-agent-adapter",
4
4
  "displayName": "Fabric Agent Adapter",
5
5
  "description": "Create new Fabric-compatible agents, build them as always-alive local services with dashboards (fabric-service/0.1), and adapt existing projects to the Fabric Agent Contract through evidence-backed MCP, A2A, or local-runner provider bundles; and teach a coding agent to work in a project through Fabric (fabric-mcp, fabric_whoami, claim and release), with Claude Code session hooks.",
6
- "version": "0.49.0",
6
+ "version": "0.49.1",
7
7
  "author": {
8
8
  "name": "PassionCode.ai",
9
9
  "url": "https://passioncode.ai/"
@@ -5,7 +5,7 @@ license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
5
5
  compatibility: Requires filesystem access and Python 3.9+. Exact schema checks additionally need git, Node.js, pnpm, and the pinned fabric-agent-contract checkout (a public repository). Works without those tools in an explicitly degraded structural-check mode.
6
6
  metadata:
7
7
  author: PassionCode.ai
8
- version: "0.49.0"
8
+ version: "0.49.1"
9
9
  contract-version: "0.1.0"
10
10
  contract-commit: "2f020a696453ae299b123517f14957f6a1354379"
11
11
  ---
@@ -14,7 +14,7 @@ license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
14
14
  compatibility: Python 3.9+ or Node.js 20+ for the kits (Node.js 22+ for a Linux service's instance lock); the probe needs Python 3.9+. launchd steps are macOS-only; Linux services install a systemd user unit and Windows ones a per-user Scheduled Task with the kits' writers. Dashboard handoff optionally uses Fabric Dashboards MCP link/host_status/open; without it, report unresolved host capability. The contract checkout is optional.
15
15
  metadata:
16
16
  author: PassionCode.ai
17
- version: "0.49.0"
17
+ version: "0.49.1"
18
18
  contract-version: "0.1.0"
19
19
  extension: "fabric-service/0.1"
20
20
  extension-commit: "2f020a696453ae299b123517f14957f6a1354379"
@@ -49,7 +49,6 @@ from fabric_crypto import ( # noqa: E402 - the core (FAA-65)
49
49
  )
50
50
  from fabric_jsvalue import ( # noqa: E402 - the core (FAA-65)
51
51
  MISSING as _MISSING,
52
- contains as _contains,
53
52
  get as _get,
54
53
  get_string as _s,
55
54
  indexed_objects as _indexed,
@@ -59,7 +58,7 @@ from fabric_jsvalue import ( # noqa: E402 - the core (FAA-65)
59
58
  objects as _list,
60
59
  safe_integer as _safe_integer,
61
60
  same as _same,
62
- set_has as _set_has,
61
+ set_has as _set_has, # every membership the contract keeps in a Set or tests with includes (FAA-113)
63
62
  show as _show,
64
63
  )
65
64
  from fabric_time import ( # noqa: E402 - the core (FAA-65)
@@ -331,7 +330,7 @@ def verify_estate_record(record: Any, *, at: Any, parent: Any = _MISSING, author
331
330
  if not matches:
332
331
  return {"ok": False, "problem": "unknown-authority"}
333
332
  authority = matches[0]
334
- if _contains(revoked, _get(authority, "credentialId")):
333
+ if _set_has(revoked, _get(authority, "credentialId")):
335
334
  return {"ok": False, "problem": "revoked-authority"}
336
335
  origins = _get(authority, "origins")
337
336
  credential = {"credentialId": _s(authority, "credentialId"), "alg": _s(authority, "alg"),
@@ -385,7 +384,7 @@ def _scope_admits(record: Any, kid: Any, context: Any) -> bool:
385
384
  """DEC-0090: a key no `publisherScopes[]` entry names is trusted in any context; a partner's key, only in a context
386
385
  one of its scopes lists."""
387
386
  scopes = [s for s in _list(_get(record, "publisherScopes")) if _same(_get(s, "kid"), kid)]
388
- return not scopes or any(isinstance(_get(s, "contexts"), list) and _contains(s["contexts"], context) for s in scopes)
387
+ return not scopes or any(isinstance(_get(s, "contexts"), list) and _set_has(s["contexts"], context) for s in scopes)
389
388
 
390
389
 
391
390
  #: v4 §7, Freshness: an estate revision's window is at most 30 days (DEC-0055 item 2).
@@ -545,7 +544,7 @@ def _keys(value: Dict[str, Any], flag) -> None:
545
544
  hub_keys: List[Any] = []
546
545
  for machine, m in _indexed(_get(value, "machines")):
547
546
  at = "/machines/%d" % m
548
- if _contains(ids, _get(machine, "id")):
547
+ if _set_has(ids, _get(machine, "id")):
549
548
  flag("%s/id" % at, "machine %s is listed twice" % _show(_get(machine, "id")))
550
549
  ids.append(_get(machine, "id"))
551
550
  device_keys = _list(_get(machine, "deviceKeys"))
@@ -562,15 +561,15 @@ def _keys(value: Dict[str, Any], flag) -> None:
562
561
  flag("%s/deviceKeys/%d/publicKey" % (at, k), "a device key's octets are written in canonical padded base64, the one form a key reference can name (DEC-0084)")
563
562
  elif not _valid_device_key(key):
564
563
  flag("%s/deviceKeys/%d/publicKey" % (at, k), "an Ed25519 key is never of small order (DEC-0045) and a P-256 key is a valid point (SEC 1 §3.2.2)")
565
- if _contains(seen, _get(key, "publicKey")):
564
+ if _set_has(seen, _get(key, "publicKey")):
566
565
  flag("%s/deviceKeys/%d/publicKey" % (at, k), "one key is never used for the other role")
567
566
  seen.append(_get(key, "publicKey"))
568
567
  hub = _get(machine, "hubKey")
569
568
  if isinstance(hub, dict):
570
- if _contains(hub_kids, _get(hub, "kid")):
569
+ if _set_has(hub_kids, _get(hub, "kid")):
571
570
  flag("%s/hubKey/kid" % at, "hub key %s is certified for two machines: one kid per machine" % _show(_get(hub, "kid")))
572
571
  hub_kids.append(_get(hub, "kid"))
573
- if _contains(hub_keys, _get(hub, "x")):
572
+ if _set_has(hub_keys, _get(hub, "x")):
574
573
  flag("%s/hubKey/x" % at, "one hub public key is certified for two machines under two kids: one key per machine")
575
574
  hub_keys.append(_get(hub, "x"))
576
575
  x = _get(hub, "x")
@@ -579,7 +578,7 @@ def _keys(value: Dict[str, Any], flag) -> None:
579
578
  flag("%s/hubKey/x" % at, "a hub key of small order verifies forged tokens and is never certified (DEC-0045)")
580
579
  credentials: List[Any] = []
581
580
  for authority, index in _indexed(_get(value, "authorities")):
582
- if _contains(credentials, _get(authority, "credentialId")):
581
+ if _set_has(credentials, _get(authority, "credentialId")):
583
582
  flag("/authorities/%d/credentialId" % index, "a passkey is listed once")
584
583
  credentials.append(_get(authority, "credentialId"))
585
584
  if "problem" in credential_key(_get(authority, "publicKey"), _show(_get(authority, "alg"))):
@@ -589,7 +588,7 @@ def _keys(value: Dict[str, Any], flag) -> None:
589
588
  def _placements(value: Dict[str, Any], flag) -> None:
590
589
  kids: List[Any] = []
591
590
  for key, index in _indexed(_get(value, "publisherKeys")):
592
- if _contains(kids, _get(key, "kid")):
591
+ if _set_has(kids, _get(key, "kid")):
593
592
  flag("/publisherKeys/%d/kid" % index, "publisher key %s is pinned twice" % _show(_get(key, "kid")))
594
593
  kids.append(_get(key, "kid"))
595
594
  for machine, m in _indexed(_get(value, "machines")):
@@ -601,7 +600,7 @@ def _placements(value: Dict[str, Any], flag) -> None:
601
600
  flag("%s/agentRef" % at, "%s is placed twice on one machine in the context %s"
602
601
  % (_show(_get(placement, "agentRef")), _show(_context_of(placement))))
603
602
  agents.append(key)
604
- if not _contains(kids, _get(placement, "publisherKid")):
603
+ if not _set_has(kids, _get(placement, "publisherKid")):
605
604
  flag("%s/publisherKid" % at, "publisher key %s is not in publisherKeys, so the machine pins no key to verify the package under" % _show(_get(placement, "publisherKid")))
606
605
 
607
606
 
@@ -611,10 +610,10 @@ def _partner_scopes(value: Dict[str, Any], flag) -> None:
611
610
  pinned = [_get(k, "kid") for k in _list(_get(value, "publisherKeys"))]
612
611
  scoped: List[Any] = []
613
612
  for scope, index in _indexed(_get(value, "publisherScopes")):
614
- if _contains(scoped, _get(scope, "kid")):
613
+ if _set_has(scoped, _get(scope, "kid")):
615
614
  flag("/publisherScopes/%d/kid" % index, "publisher key %s is scoped twice" % _show(_get(scope, "kid")))
616
615
  scoped.append(_get(scope, "kid"))
617
- if not _contains(pinned, _get(scope, "kid")):
616
+ if not _set_has(pinned, _get(scope, "kid")):
618
617
  flag("/publisherScopes/%d/kid" % index, "publisher key %s is not in publisherKeys, so the scope limits no key" % _show(_get(scope, "kid")))
619
618
  for machine, m in _indexed(_get(value, "machines")):
620
619
  for placement, p in _indexed(_get(machine, "packages")):
@@ -638,7 +637,7 @@ def _secret_references(value: Dict[str, Any], flag) -> None:
638
637
  flag("%s/%s" % (at, field), "a secret placement names a slot, never a value: this looks like a credential")
639
638
  if _key_of(_get(secret, "agentRef"), _context_of(secret)) not in placed:
640
639
  flag("%s/agentRef" % at, "a secret goes only where a placed package declares it (v4 Path 2): this agent is not placed on this machine in the secret's context")
641
- if not _contains(machine_ids, _get(secret, "from")):
640
+ if not _set_has(machine_ids, _get(secret, "from")):
642
641
  flag("%s/from" % at, "the slot is filled from a machine of this estate")
643
642
  elif not _same(_context_of(secret), PERSONAL_CONTEXT) and not _same(_get(secret, "from"), _get(machine, "id")):
644
643
  # An envelope names no context (DEC-0082): an organization's instance fills its slot on its own machine.
@@ -693,7 +692,7 @@ def _machine_profiles(value: Dict[str, Any], flag) -> None:
693
692
  flag("/machines/%d/secrets" % m, "a view machine holds none of the estate's secrets")
694
693
  for machine, m in _indexed(_get(value, "machines")):
695
694
  for secret, s in _indexed(_get(machine, "secrets")):
696
- if _set_has(views, _get(secret, "from")): # the contract's Set: NaN matches NaN
695
+ if _set_has(views, _get(secret, "from")):
697
696
  flag("/machines/%d/secrets/%d/from" % (m, s), "machine %s is a view machine: it is the source of no secret" % _show(_get(secret, "from")))
698
697
 
699
698
 
@@ -709,7 +708,7 @@ def _grants_and_revocations(value: Dict[str, Any], flag) -> None:
709
708
  if _get(grant, "principalKind") != "external":
710
709
  flag("/shareGrants/%d/principalKind" % index, "a share grant is to a principal outside the estate: principalKind external")
711
710
  ident = _get(_get(grant, "meta"), "id") if isinstance(_get(grant, "meta"), dict) else _MISSING
712
- if _contains(grant_ids, _grant_key(ident)):
711
+ if _set_has(grant_ids, _grant_key(ident)):
713
712
  flag("/shareGrants/%d/meta/id" % index, "grant %s is listed twice" % _show(ident))
714
713
  grant_ids.append(_grant_key(ident))
715
714
  machines = _list(_get(value, "machines"))
@@ -735,7 +734,7 @@ def _grants_and_revocations(value: Dict[str, Any], flag) -> None:
735
734
  if _get(revocation, "kind") == "device-key" and isinstance(raw_ref, str) and "@" in raw_ref and not KEY_REFERENCE.match(raw_ref):
736
735
  flag("/revocations/%d/ref" % index, "a device-key revocation names a kid or a key reference <kid>@sha256:<64 lower-case hex digits> (DEC-0084)")
737
736
  seen.append(key)
738
- if _contains(listed.get(kind, []), ref):
737
+ if _set_has(listed.get(kind, []), ref):
739
738
  flag("/revocations/%d/ref" % index, "the record still lists the %s it revokes: revocation outranks addition" % kind)
740
739
 
741
740
 
@@ -906,7 +905,7 @@ def _installation(value: Dict[str, Any]) -> List[Dict[str, str]]:
906
905
  declared = [_get(slot, "name") for slot in _list(_get(pkg, "secrets"))]
907
906
  flag075 = _finding(findings, "FAC-SEM-075")
908
907
  for secret, index in _indexed(_get(machine, "secrets")):
909
- if _same(_get(secret, "agentRef"), _get(pkg, "agentRef")) and _same(_context_of(secret), context) and not _contains(declared, _get(secret, "slot")):
908
+ if _same(_get(secret, "agentRef"), _get(pkg, "agentRef")) and _same(_context_of(secret), context) and not _set_has(declared, _get(secret, "slot")):
910
909
  flag075("/record/machines/%d/secrets/%d/slot" % (machine_index, index),
911
910
  "the package declares no slot %s: a secret goes only where a placed package declares it" % _show(_get(secret, "slot")))
912
911
  if placement is not None and not _scope_admits(record, _get(placement, "publisherKid"), context):
@@ -158,7 +158,7 @@ def double(value: Any) -> float:
158
158
 
159
159
  def same(a: Any, b: Any) -> bool:
160
160
  """JavaScript's === on parsed JSON: an object equals only itself. A Set's SameValueZero differs only for NaN, which
161
- no JSON value is: use ``set_has`` where the contract keeps a Set (FAA-107)."""
161
+ no JSON value is: use ``set_has`` where the contract keeps a Set or tests with ``includes`` (FAA-107, FAA-113)."""
162
162
  if a is MISSING or b is MISSING:
163
163
  return a is b
164
164
  if isinstance(a, (dict, list)) or isinstance(b, (dict, list)):
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fabric-service-kit",
3
- "version": "0.49.0",
3
+ "version": "0.49.1",
4
4
  "about": "The service kit's modules in dependency order: a module imports only modules listed before it. Written by fabric_kit.py build; edit the order and layers by hand, never the digests.",
5
5
  "python": [
6
6
  {
@@ -8,7 +8,7 @@
8
8
  "file": "fabric_jsvalue.py",
9
9
  "layer": "L0",
10
10
  "imports": [],
11
- "sha256": "ff1bbfae999fb5683e59e31189143f1c85fa9d7780040b5a6952e060a143fc18"
11
+ "sha256": "12888a2c934a885d35999ac9782eaedc8d04946d9775213344a235a8814d3d28"
12
12
  },
13
13
  {
14
14
  "module": "fabric_time",
@@ -77,7 +77,7 @@
77
77
  "fabric_secrets",
78
78
  "fabric_time"
79
79
  ],
80
- "sha256": "f493cd424dae6380d1caad91ffdca2764e51a49a6b1051a77731e3d8755277cd"
80
+ "sha256": "a0ddc49d748c1efb1830de176998d6c8eac30f77d387c71eae12549c52aaab27"
81
81
  },
82
82
  {
83
83
  "module": "fabric_delegation",
@@ -2951,6 +2951,82 @@
2951
2951
  "fabric-openrouter.mjs": "ab6c4c7251d563980d895061ee19976bde2f364c21b6354efdd9a9027b1ea139",
2952
2952
  "fabric-package.schema.json": "2ba799f576fad0f608029223c4ccccd122152f1c5dbfa8ce8b31cd63ac92ad44",
2953
2953
  "share-registration.schema.json": "34b3997eeaf936ebc861342a23045634a7c18c19f01d7ed4198310d81989f5b0"
2954
+ },
2955
+ "0.49.1": {
2956
+ "fabric_jsvalue.py": "12888a2c934a885d35999ac9782eaedc8d04946d9775213344a235a8814d3d28",
2957
+ "fabric_time.py": "588f383da4ac6a243ea95b6ffc3a0aa99ca94245732dc4937734f190b7af4ae9",
2958
+ "fabric_identifiers.py": "0fd814d521ac3aa39907458265870753046a73f48af3d8581ab864b93b211607",
2959
+ "fabric_secrets.py": "68a0b5e9e90c468ec9ac67b2f0fd1848e08813d6ec1ec14a2a47337eb1a98911",
2960
+ "fabric_crypto.py": "9c34aee20dbe4d6742e4fdbaab68ceb934bbe7dbd3a904c0010824ace16cdeaa",
2961
+ "fabric_base.py": "f62bcc2ed9912660fa875d27a09f60867260cc4925f79873501fd9620be59611",
2962
+ "fabric_platform.py": "a9350dff55ef0fb3a23cdb4d0320a15868b15a8deb10d2eb74d72c58740c284b",
2963
+ "fabric_paths.py": "6c92af34292baddf7e10d3134d0a5b0bb23387741812a9f79c409cf1628bad23",
2964
+ "fabric_estate.py": "a0ddc49d748c1efb1830de176998d6c8eac30f77d387c71eae12549c52aaab27",
2965
+ "fabric_delegation.py": "6638a6039befe3b43f59018bab292e050931af21b544df1044cd5059572ded36",
2966
+ "fabric_share.py": "78506fe1a60829c525dbf6266a0aa0749a2164290ed94da8249a20b4e9be5605",
2967
+ "fabric_ceremonies.py": "a59e83cf4adb2669a9b98008e3d3a0cba991a35cbff97bc604fc82fe81cfd49f",
2968
+ "fabric_package_schema.py": "e623f0408d38e3343ed66222fb812907c6c52e1a286eb1c1176ab2e64640c9ad",
2969
+ "fabric_package.py": "ff533f1b3f92b4ea0f532fcec50b68c610d47549930eef8ad5247692a64dfb3c",
2970
+ "fabric_protocols.py": "28e693282cddff8e4396ae39ce6fa935e875e8ddac7375bea2c189dd6fb04970",
2971
+ "fabric_connections.py": "77d3ccf863c70e9209d21ef45c46a6db9c06237019bc20d54f8d3946873f3f11",
2972
+ "fabric_lock.py": "ba2399d2137c7a7791b7133b9db9cca36693f4eb7e30955b25a75ade814e95ba",
2973
+ "fabric_usage.py": "b1f335cab4625171a616cf68cf3fd892b18bdadcf4baec3a3a0fdcf12785c170",
2974
+ "fabric_backups.py": "02dc5eb66ec956723d3ddba07b6dd15f6a9726d901f4d60edfd02eee57880ceb",
2975
+ "fabric_descriptor.py": "daf16f95b1810283b1386488e006e940a0cdfa5c3c8d09386ac33d29d58df78d",
2976
+ "fabric_auth.py": "55794587750f6c9f40935b74295d83d7096cb4b26a0b36ac292334cd87d8f853",
2977
+ "fabric_events.py": "0086886c2c29dbd5c9d152be9d375c5bdf4a29f89b8095baff8490c2bcd64e47",
2978
+ "fabric_logs.py": "86dc55bfafa2a57264722e2403510a249b95de0e4a722b2e4bc3948e3bd93da9",
2979
+ "fabric_supervisors.py": "a2dbf15aa1c4eb9de42996b413103fa4814afd2e4e66d1ab7315f30e06ab6841",
2980
+ "fabric_service.py": "103ab4026f96e127c01cd91e4ad44f6b0ce2424ff33f078ed106b39e773a10e3",
2981
+ "fabric_profiles.py": "26e21ec29c016000f8ca8053b7b489184854a73be1b7fdc0a18d12990203db9b",
2982
+ "fabric_interop_rules.py": "b5f0ecb1791b4c50c5ac151f987f692ed7001bc74841a8a161125ec775145e09",
2983
+ "fabric_interop.py": "f92fa111328fe3811438f733d8f0eb5295e6941c774123a78e26318bdcb35fc3",
2984
+ "fabric_hub.py": "b9ac852e601b1d54cf61351677a628afc69faeed7d1fc74ebe541c1cf38e0c2a",
2985
+ "fabric_board_shapes.py": "0a0e8d3d40e33dfd8c457aedb85330b2ff25f8c49a33eb6cbd5a5cc8a5007f7e",
2986
+ "fabric_board_link.py": "b5bff994cb615c6a4bb0d88d020e3a59504166180e1fa1aea3be93224f2aeccb",
2987
+ "fabric_board_session.py": "2845d7d758b06ac4588bdf825a471c201a46ce535a631805697b1e35495fead6",
2988
+ "fabric_board_fixture.py": "69761ce205ec640762ec9b591c6903e5a87f844e8d423b041681924027e39d1e",
2989
+ "fabric_board.py": "0f075894b3e352194fcaf3fc7d08dee4e9f1c215e859769ed85fa5010e4a1599",
2990
+ "fabric_http.py": "5d7f72af3a73fdbe8c1f487ecf60cef23a7c46289eba542b03aff4706b8218d4",
2991
+ "fabric_openrouter.py": "309f588f9c976b1061323f3161e35d86248afd5b59b16391afbfc4e1099ecd65",
2992
+ "fabric-jsvalue.mjs": "dfbdb8c41584972c9389750a41719374a1cfc5652ed438dd1623025190b0106e",
2993
+ "fabric-time.mjs": "3ff932069038a720df1c68c6492976906737b2e67e5c12d9c81c418597f661de",
2994
+ "fabric-identifiers.mjs": "a18c2a501a4aa170bfd3d99e571ad6b7662649b4d3b9d2a540c002ea42811bbf",
2995
+ "fabric-secrets.mjs": "6ad8ab18ac899ecc2384e8ec6a3fb689b92be3b9a5ce3a59b92a37d52da5d766",
2996
+ "fabric-crypto.mjs": "038e03689701e3c9721a1e7f8e0a4d02a52d6a5d1661b0dc84035edc3ebbfcf6",
2997
+ "fabric-base.mjs": "7804b2df7715dbbf9702a9b61b6b8cef2110f9a52b04cabd8fb0a66458d87512",
2998
+ "fabric-platform.mjs": "a8119d95ebba4d589a9e401f5d6fc8dda62d59d44e4c1ff811d97fac3771ac0c",
2999
+ "fabric-paths.mjs": "2201b4ed7126f260931b63e8386377b4317d8dcad419d109bf22c683e44b7f29",
3000
+ "fabric-estate.mjs": "b6610aaad9d00d8a7f42955627fb81725950841455bcedfc050abb0e079f0f93",
3001
+ "fabric-delegation.mjs": "dafde9ea327c88d92dc73d1400282b43dc6f84dd345a2d2b9b154304f65fc9d7",
3002
+ "fabric-share.mjs": "d40a7405d7bd4c3213ed691330b7549afe8e023db0753b582e249714e672010d",
3003
+ "fabric-ceremonies.mjs": "117d4374c9e95c930a38de7adbd4323774798f4fe6ec6f24afcd6f2c7298ca44",
3004
+ "fabric-package-schema.mjs": "cd28092375db0fc21ffab1f0bb7794f7f8979ee08c64d3b9faf4b3845357caa6",
3005
+ "fabric-package.mjs": "13913ea6d12056c3a31b8222b6c4aab8395941fe519913692e1a06d8672e1c88",
3006
+ "fabric-protocols.mjs": "dc039ec3612d0090c9ba6a3a949b0b7df54712bead300dcb6cc8b9bc305cd183",
3007
+ "fabric-connections.mjs": "303432e547ce563e90ca9ffcd827f5612f51d3582929834e1add0fdaf6485dcc",
3008
+ "fabric-lock.mjs": "ac219ab0a326d5c43c7c4c255e0c3843d4794de795a60e3af661e7d5350ea052",
3009
+ "fabric-usage.mjs": "1c4d18004639409624ac3516a02f2b60bbf55abdb612e9a96c37e83666eb87a7",
3010
+ "fabric-backups.mjs": "849acdc6eba0fff648c8158e61f6b68cccd7bedf0880f7d5ec03a6a20f1e7005",
3011
+ "fabric-descriptor.mjs": "95c0b7ff36c29d7c748f1fa6fb1767d6fe8c97fca7f1ee404559930abbd3cc84",
3012
+ "fabric-auth.mjs": "3ea6796470fcd829bc72a772fb7e4c37532a09d1e37ff41592dcf1a1a3074ca2",
3013
+ "fabric-events.mjs": "c463d6ffcae24229a5c608d8fcea4268d2ccc5024b19fa10e5a452284068fa71",
3014
+ "fabric-logs.mjs": "58467e5719c16e099d05bbedec47a95712506ab03c33f3cd4be1252e8498a946",
3015
+ "fabric-supervisors.mjs": "9d61c1a17538a5e2453e33e5ae347337336748041bc054478a96dd51f3d4fc51",
3016
+ "fabric-service.mjs": "1b4639a10c530c1241d317c5e9cf3a10cd96e5d9af8b29fc1b1844de5c63bfec",
3017
+ "fabric-profiles.mjs": "314376cf2deb87f235fb18b11d3b532e211f585b9af0a9302b5f4cc39971b532",
3018
+ "fabric-interop-rules.mjs": "57a8081307956e8c381119b361fa5232a7bf791815be8e3541eb5492820e2ea5",
3019
+ "fabric-interop.mjs": "581983ec3e197e5c2340ad25e8468320c69a2459c90097c7fcf2699766b4fb26",
3020
+ "fabric-hub.mjs": "c005115a864cfd67daa4862097bb14acfe5a60bf653897e5bf8a570dd80f1070",
3021
+ "fabric-board-shapes.mjs": "c1db7b7bd0d58d82bb6d8dfbc3cd1547a256a333052d367e82ff1b3edf2804d7",
3022
+ "fabric-board-link.mjs": "4cd6d4e21713a38c8e56f7d59db3148638b8c27c08e774d96a0f6283c6049e0a",
3023
+ "fabric-board-session.mjs": "26c9b0b2818a4cedc5248b08ea6732e2ee4ee81e31f6fee2b572a019db813c93",
3024
+ "fabric-board-fixture.mjs": "83c4b7e33ee47b68385faf2569d38d5c8a0c5cb0f8656090f41e1b0eed2f436e",
3025
+ "fabric-board.mjs": "9e7bfe4d412521982224ef12bebfc2ff255eeeaf8d96cf40234ac18de68e7a59",
3026
+ "fabric-http.mjs": "f886bcb49c4353d039387ac9568738dda26697a94dd07fc6a6204500f0bc27bf",
3027
+ "fabric-openrouter.mjs": "ab6c4c7251d563980d895061ee19976bde2f364c21b6354efdd9a9027b1ea139",
3028
+ "fabric-package.schema.json": "2ba799f576fad0f608029223c4ccccd122152f1c5dbfa8ce8b31cd63ac92ad44",
3029
+ "share-registration.schema.json": "34b3997eeaf936ebc861342a23045634a7c18c19f01d7ed4198310d81989f5b0"
2954
3030
  }
2955
3031
  },
2956
3032
  "data": {
@@ -5,7 +5,7 @@ license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
5
5
  compatibility: Requires filesystem access and Python 3.9+. Exact schema checks additionally need git, Node.js, pnpm, and the pinned fabric-agent-contract checkout (a public repository). Works without those tools in an explicitly degraded structural-check mode. Ships in one plugin with adapting-projects-to-fabric, whose scripts it reuses.
6
6
  metadata:
7
7
  author: PassionCode.ai
8
- version: "0.49.0"
8
+ version: "0.49.1"
9
9
  contract-version: "0.1.0"
10
10
  contract-commit: "2f020a696453ae299b123517f14957f6a1354379"
11
11
  ---
@@ -19,7 +19,7 @@ compatibility: >-
19
19
  this plugin; Codex/Kimi use command hooks; Hermes/OpenCode/OpenClaw/Pi use native context plugins after opt-in.
20
20
  metadata:
21
21
  author: PassionCode.ai
22
- version: "0.49.0"
22
+ version: "0.49.1"
23
23
  ---
24
24
 
25
25
  # Working with Fabric
@@ -57,7 +57,7 @@ SESSION_ID = re.compile(r"[A-Za-z0-9._:-]{1,128}")
57
57
  LOOPBACK_ORIGIN = re.compile(r"http://127\.0\.0\.1:(\d{1,5})")
58
58
 
59
59
 
60
- VERSION = "0.49.0"
60
+ VERSION = "0.49.1"
61
61
 
62
62
 
63
63
  HOSTS = {"claude": "Claude Code", "codex": "Codex", "kimi": "Kimi Code",