blun-king-cli 9.1.597 → 9.1.600

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +17 -1
  3. package/agent-spine-plugin/CHANGELOG.md +1 -1
  4. package/agent-spine-plugin/docs/host-integration.md +1 -15
  5. package/agent-spine-plugin/docs/preflight-recall.md +1 -1
  6. package/agent-spine-plugin/scripts/check-install.js +1 -6
  7. package/agent-spine-plugin/scripts/hermetic-process.js +8 -0
  8. package/agent-spine-plugin/scripts/release-check.js +2 -3
  9. package/agent-spine-plugin/scripts/run-tests-hermetic.js +8 -11
  10. package/agent-spine-plugin/src/hook.js +40 -40
  11. package/agent-spine-plugin/src/lib/briefing.js +6 -3
  12. package/agent-spine-plugin/src/lib/hook-context.js +63 -64
  13. package/agent-spine-plugin/src/lib/hook-output.js +115 -94
  14. package/agent-spine-plugin/src/lib/hook-timeline.js +20 -11
  15. package/agent-spine-plugin/src/lib/host-instruction-budget.js +8 -14
  16. package/agent-spine-plugin/src/lib/mcp-timeline-tools.js +0 -7
  17. package/agent-spine-plugin/src/lib/pre-answer-timeline-recall.js +104 -0
  18. package/agent-spine-plugin/src/lib/session-timeline-codex.js +0 -4
  19. package/agent-spine-plugin/src/lib/session-timeline-enrollment-source.js +0 -3
  20. package/agent-spine-plugin/src/lib/session-timeline-enrollment-storage.js +0 -4
  21. package/agent-spine-plugin/src/lib/session-timeline-enrollment-transport.js +0 -2
  22. package/agent-spine-plugin/src/lib/session-timeline-enrollment.js +0 -22
  23. package/agent-spine-plugin/src/lib/session-timeline-host-origin.js +0 -4
  24. package/agent-spine-plugin/src/lib/session-timeline-prior.js +42 -50
  25. package/agent-spine-plugin/src/lib/session-timeline-provider.js +0 -3
  26. package/agent-spine-plugin/src/lib/session-timeline-query.js +0 -1
  27. package/agent-spine-plugin/src/lib/session-timeline-sid-acl.js +0 -4
  28. package/agent-spine-plugin/src/lib/session-timeline-source.js +0 -3
  29. package/agent-spine-plugin/src/lib/session-timeline-transport.js +0 -8
  30. package/agent-spine-plugin/src/lib/session-timeline-windows-acl.js +0 -3
  31. package/agent-spine-plugin/src/lib/session-timeline.js +370 -408
  32. package/agent-spine-plugin/src/lib/source-roots.js +26 -16
  33. package/agent-spine-plugin/src/lib/timeline-user-feedback.js +0 -1
  34. package/bin/core-bootstrap.js +18 -10
  35. package/bin/launcher-mode.js +5 -0
  36. package/bin/launcher-runtime.js +27 -0
  37. package/bin/verify-bundled-agent-sources.cjs +28 -2
  38. package/blun.mjs +798 -143
  39. package/bundled-agent-sources.json +192 -155
  40. package/package.json +1 -1
  41. package/standard-skills/translate-native/README.md +1334 -1
  42. package/standard-skills/translate-native/SKILL.md +1 -1
  43. package/standard-skills/translate-native/VERSION +1 -1
  44. package/standard-skills/translate-native/docs/PREMORTEM.md +901 -0
  45. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION.md +208 -41
  46. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_API.md +1319 -77
  47. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_EVIDENCE_HTTP.md +66 -11
  48. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_HTTP_PROVIDER.md +19 -1
  49. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_RECEIPT_VERIFIER_HTTP.md +24 -6
  50. package/standard-skills/translate-native/integrations/commercial_localization_profile.py +23 -0
  51. package/standard-skills/translate-native/integrations/website_localization.py +234 -10
  52. package/standard-skills/translate-native/integrations/website_localization_benchmark.py +39 -3
  53. package/standard-skills/translate-native/integrations/website_localization_cms.py +512 -10
  54. package/standard-skills/translate-native/integrations/website_localization_cms_client.py +116 -0
  55. package/standard-skills/translate-native/integrations/website_localization_cms_receiver.py +101 -11
  56. package/standard-skills/translate-native/integrations/website_localization_cms_receiver_runtime.py +12 -1
  57. package/standard-skills/translate-native/integrations/website_localization_cms_receiver_store.py +186 -24
  58. package/standard-skills/translate-native/integrations/website_localization_cms_source_client.py +64 -1
  59. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery.py +528 -5
  60. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_auth.py +918 -0
  61. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_auth_runtime.py +609 -0
  62. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_client.py +885 -0
  63. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_http.py +1244 -0
  64. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_runtime.py +220 -0
  65. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_sidecar_adapter.py +331 -0
  66. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_capabilities_http.py +444 -0
  67. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_client.py +673 -0
  68. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch.py +1317 -0
  69. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_client.py +826 -0
  70. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_http.py +1152 -0
  71. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_openapi.py +896 -0
  72. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_runtime.py +621 -0
  73. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_http.py +1180 -0
  74. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_runtime.py +1479 -0
  75. package/standard-skills/translate-native/integrations/website_localization_cms_source_http.py +133 -17
  76. package/standard-skills/translate-native/integrations/website_localization_cms_source_runtime.py +319 -4
  77. package/standard-skills/translate-native/integrations/website_localization_cms_source_service.py +72 -2
  78. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_http.py +13 -0
  79. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver.py +56 -13
  80. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver_openapi.py +335 -0
  81. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver_runtime.py +66 -1
  82. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_processing_monitor.py +73 -7
  83. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_receiver_client.py +93 -9
  84. package/standard-skills/translate-native/integrations/website_localization_evidence_http.py +221 -11
  85. package/standard-skills/translate-native/integrations/website_localization_health.py +6 -0
  86. package/standard-skills/translate-native/integrations/website_localization_http_provider.py +1 -1
  87. package/standard-skills/translate-native/integrations/website_localization_quality_profiles.py +350 -0
  88. package/standard-skills/translate-native/integrations/website_localization_queue.py +126 -56
  89. package/standard-skills/translate-native/integrations/website_localization_receipt_verifier_http.py +166 -13
  90. package/standard-skills/translate-native/integrations/website_localization_release.py +423 -17
  91. package/standard-skills/translate-native/integrations/website_localization_release_coordinator.py +168 -13
  92. package/standard-skills/translate-native/integrations/website_localization_runner.py +10 -0
  93. package/standard-skills/translate-native/integrations/website_localization_worker.py +213 -9
  94. package/standard-skills/translate-native/provenance.json +2 -2
  95. package/standard-skills/translate-native/references/commercial-localization.md +96 -17
  96. package/standard-skills/translate-native/scripts/blun_language_guard.py +3 -1
  97. package/standard-skills/translate-native/scripts/check_commercial_review.py +69 -9
  98. package/standard-skills/translate-native/scripts/commercial_localization_profile.py +829 -31
  99. package/standard-tools/language-guard/VERSION +1 -1
  100. package/standard-tools/language-guard/blun_language_guard.py +3 -1
  101. package/standard-tools/language-guard/check_commercial_review.py +69 -9
  102. package/standard-tools/language-guard/commercial_localization_profile.py +829 -31
  103. package/standard-tools/language-guard/provenance.json +2 -2
  104. package/standard-tools/manifest.json +1 -1
  105. package/worker-host.mjs +215 -82
  106. package/agent-spine-plugin/scripts/check-install-king.js +0 -37
@@ -23,15 +23,17 @@ AUTH_REQUEST_SCHEMA = "blun.cms-source-runtime-http-auth-request.v1"
23
23
  PRINCIPAL_SCHEMA = "blun.cms-source-runtime-principal.v1"
24
24
  STATUS_PRINCIPAL_SCHEMA = "blun.cms-source-status-principal.v1"
25
25
  CHANGE_REQUEST_SCHEMA = "blun.cms-source-change-enqueue-request.v1"
26
- CHANGE_RESPONSE_SCHEMA = "blun.cms-source-change-enqueue-response.v2"
26
+ CHANGE_RESPONSE_SCHEMA = "blun.cms-source-change-enqueue-response.v3"
27
27
  REMOVAL_REQUEST_SCHEMA = "blun.cms-source-removal-enqueue-request.v1"
28
- REMOVAL_RESPONSE_SCHEMA = "blun.cms-source-removal-enqueue-response.v2"
28
+ REMOVAL_RESPONSE_SCHEMA = "blun.cms-source-removal-enqueue-response.v3"
29
29
  STATUS_REQUEST_SCHEMA = "blun.cms-source-status-request.v1"
30
- STATUS_RESPONSE_SCHEMA = "blun.cms-source-status-response.v4"
31
- HEALTH_RESPONSE_SCHEMA = "blun.cms-source-health-response.v4"
32
- READINESS_RESPONSE_SCHEMA = "blun.cms-source-readiness-response.v2"
33
- CAPABILITIES_SCHEMA = "blun.cms-source-runtime-capabilities.v1"
34
- CAPABILITIES_RESPONSE_SCHEMA = "blun.cms-source-capabilities-response.v1"
30
+ STATUS_RESPONSE_SCHEMA = "blun.cms-source-status-response.v6"
31
+ HEALTH_RESPONSE_SCHEMA = "blun.cms-source-health-response.v6"
32
+ READINESS_RESPONSE_SCHEMA = "blun.cms-source-readiness-response.v3"
33
+ CAPABILITIES_SCHEMA = "blun.cms-source-runtime-capabilities.v2"
34
+ CAPABILITIES_RESPONSE_SCHEMA = "blun.cms-source-capabilities-response.v2"
35
+ CAPABILITY_BINDING_SCHEMA = "blun.cms-source-capability-binding.v2"
36
+ CAPABILITY_DATABASE_ROLES = ("changes", "removals", "lifecycle")
35
37
 
36
38
  CHANGE_PATH = "/v1/localization/source/changes"
37
39
  REMOVAL_PATH = "/v1/localization/source/removals"
@@ -261,6 +263,7 @@ def _source_status_payload(
261
263
  "queue_counts", "notification_state", "notification_id",
262
264
  "notification_sha256", "notification_attempts",
263
265
  "notification_max_attempts", "notification_error_code",
266
+ "terminal_receiver_capabilities_sha256",
264
267
  "terminal_processing_state", "terminal_processing_poll_attempts",
265
268
  "terminal_processing_failures", "terminal_processing_error_code",
266
269
  "receiver_processing_state", "receiver_processing_attempts",
@@ -269,7 +272,7 @@ def _source_status_payload(
269
272
  }
270
273
  if not isinstance(payload, Mapping) or set(payload) != fields:
271
274
  raise ValueError
272
- if payload["schema"] != "blun.cms-source-service-status.v3":
275
+ if payload["schema"] != "blun.cms-source-service-status.v4":
273
276
  raise ValueError
274
277
  event_id = _token(payload["event_id"])
275
278
  site_id = _token(payload["site_id"])
@@ -360,6 +363,16 @@ def _source_status_payload(
360
363
  notification_error = _optional_error(
361
364
  payload["notification_error_code"]
362
365
  )
366
+ terminal_receiver_capabilities_sha256 = payload[
367
+ "terminal_receiver_capabilities_sha256"
368
+ ]
369
+ if (
370
+ terminal_receiver_capabilities_sha256 is not None
371
+ and SHA256.fullmatch(
372
+ terminal_receiver_capabilities_sha256
373
+ ) is None
374
+ ):
375
+ raise ValueError
363
376
  durable_notification = notification_state in STATUSES
364
377
  if durable_notification != (notification_id is not None):
365
378
  raise ValueError
@@ -386,6 +399,10 @@ def _source_status_payload(
386
399
  }
387
400
  if terminal_processing_state not in processing_states:
388
401
  raise ValueError
402
+ if (terminal_processing_state == "disabled") != (
403
+ terminal_receiver_capabilities_sha256 is None
404
+ ):
405
+ raise ValueError
389
406
  terminal_processing_poll_attempts = _count(
390
407
  payload["terminal_processing_poll_attempts"]
391
408
  )
@@ -531,6 +548,9 @@ def _source_status_payload(
531
548
  "notification_attempts": notification_attempts,
532
549
  "notification_max_attempts": notification_max_attempts,
533
550
  "notification_error_code": notification_error,
551
+ "terminal_receiver_capabilities_sha256": (
552
+ terminal_receiver_capabilities_sha256
553
+ ),
534
554
  "terminal_processing_state": terminal_processing_state,
535
555
  "terminal_processing_poll_attempts": (
536
556
  terminal_processing_poll_attempts
@@ -557,12 +577,15 @@ def _component(
557
577
  *,
558
578
  operations: bool = False,
559
579
  remote_failures: bool = False,
580
+ receiver_capabilities: bool = False,
560
581
  ) -> dict[str, Any]:
561
582
  fields = {"status", "counts", "due", "expired_leases", "failed"}
562
583
  if operations:
563
584
  fields.add("operations")
564
585
  if remote_failures:
565
586
  fields.add("remote_failures")
587
+ if receiver_capabilities:
588
+ fields.add("expected_capabilities_sha256")
566
589
  if not isinstance(value, Mapping) or set(value) != fields:
567
590
  raise ValueError
568
591
  if value["status"] not in {"ok", "blocked"}:
@@ -589,6 +612,11 @@ def _component(
589
612
  }
590
613
  if remote_failures:
591
614
  normalized["remote_failures"] = _count(value["remote_failures"])
615
+ if receiver_capabilities:
616
+ expected = value["expected_capabilities_sha256"]
617
+ if not isinstance(expected, str) or SHA256.fullmatch(expected) is None:
618
+ raise ValueError
619
+ normalized["expected_capabilities_sha256"] = expected
592
620
  return normalized
593
621
 
594
622
 
@@ -599,10 +627,11 @@ def _health_payload(value: Any) -> dict[str, Any]:
599
627
  "schema", "status", "pending_lifecycle_registrations",
600
628
  "pending_terminal_notifications", "changes", "removals",
601
629
  "pending_terminal_processing", "lifecycle", "notifications",
602
- "terminal_processing", "error_code",
630
+ "terminal_processing", "terminal_receiver_capabilities_sha256",
631
+ "error_code",
603
632
  }:
604
633
  raise ValueError
605
- if payload["schema"] != "blun.cms-source-service-health.v3":
634
+ if payload["schema"] != "blun.cms-source-service-health.v4":
606
635
  raise ValueError
607
636
  if payload["status"] not in HEALTH_STATUSES:
608
637
  raise ValueError
@@ -637,10 +666,34 @@ def _health_payload(value: Any) -> dict[str, Any]:
637
666
  else _component(payload["terminal_processing"], {
638
667
  "pending", "leased", "watching", "retry_wait",
639
668
  "succeeded", "failed",
640
- })
669
+ }, receiver_capabilities=True)
641
670
  ),
671
+ "terminal_receiver_capabilities_sha256": payload[
672
+ "terminal_receiver_capabilities_sha256"
673
+ ],
642
674
  "error_code": payload["error_code"],
643
675
  }
676
+ receiver_capabilities_sha256 = result[
677
+ "terminal_receiver_capabilities_sha256"
678
+ ]
679
+ if (
680
+ receiver_capabilities_sha256 is not None
681
+ and (
682
+ not isinstance(receiver_capabilities_sha256, str)
683
+ or SHA256.fullmatch(receiver_capabilities_sha256) is None
684
+ )
685
+ ):
686
+ raise ValueError
687
+ if result["terminal_processing"]:
688
+ if (
689
+ receiver_capabilities_sha256
690
+ != result["terminal_processing"][
691
+ "expected_capabilities_sha256"
692
+ ]
693
+ ):
694
+ raise ValueError
695
+ elif receiver_capabilities_sha256 is not None and result["status"] != "blocked":
696
+ raise ValueError
644
697
  if result["error_code"] is not None and ERROR_CODE.fullmatch(
645
698
  result["error_code"]
646
699
  ) is None:
@@ -693,6 +746,42 @@ def _readiness_payload(value: Any) -> dict[str, Any]:
693
746
  ) from None
694
747
 
695
748
 
749
+ def _capability_binding_payload(value: Any) -> dict[str, Any]:
750
+ """Validate the exact content-free durable runtime binding."""
751
+
752
+ try:
753
+ if not isinstance(value, Mapping) or set(value) != {
754
+ "schema", "status", "capabilities_sha256",
755
+ "commercial_rendering_registry_sha256", "database_roles",
756
+ }:
757
+ raise ValueError
758
+ if (
759
+ value["schema"] != CAPABILITY_BINDING_SCHEMA
760
+ or value["status"] != "verified"
761
+ or SHA256.fullmatch(value["capabilities_sha256"]) is None
762
+ or SHA256.fullmatch(
763
+ value["commercial_rendering_registry_sha256"]
764
+ ) is None
765
+ or value["database_roles"] != list(CAPABILITY_DATABASE_ROLES)
766
+ ):
767
+ raise ValueError
768
+ result = {
769
+ "schema": value["schema"],
770
+ "status": value["status"],
771
+ "capabilities_sha256": value["capabilities_sha256"],
772
+ "commercial_rendering_registry_sha256": value[
773
+ "commercial_rendering_registry_sha256"
774
+ ],
775
+ "database_roles": list(value["database_roles"]),
776
+ }
777
+ _canonical_json(result)
778
+ return result
779
+ except Exception:
780
+ raise CMSSourceHTTPBlocked(
781
+ "source_http.capability_binding_invalid", 503,
782
+ ) from None
783
+
784
+
696
785
  def _capabilities_payload() -> dict[str, Any]:
697
786
  """Build the public contract from the exact active HTTP constants."""
698
787
  expected_scopes = {
@@ -729,7 +818,9 @@ def _capabilities_payload() -> dict[str, Any]:
729
818
  "request_schema": None,
730
819
  "request_fields": [],
731
820
  "response_schema": CAPABILITIES_RESPONSE_SCHEMA,
732
- "response_fields": ["schema", "capabilities"],
821
+ "response_fields": [
822
+ "schema", "capabilities", "capability_binding",
823
+ ],
733
824
  "success_status": 200,
734
825
  },
735
826
  "change": {
@@ -743,7 +834,7 @@ def _capabilities_payload() -> dict[str, Any]:
743
834
  "response_fields": [
744
835
  "schema", "operation", "request_id", "event_id",
745
836
  "payload_sha256", "status", "attempts", "max_attempts",
746
- "capabilities_sha256",
837
+ "capabilities_sha256", "capability_binding",
747
838
  ],
748
839
  "success_status": 202,
749
840
  },
@@ -755,7 +846,10 @@ def _capabilities_payload() -> dict[str, Any]:
755
846
  "request_schema": None,
756
847
  "request_fields": [],
757
848
  "response_schema": HEALTH_RESPONSE_SCHEMA,
758
- "response_fields": ["schema", "health", "capabilities_sha256"],
849
+ "response_fields": [
850
+ "schema", "health", "capabilities_sha256",
851
+ "capability_binding",
852
+ ],
759
853
  "success_status": 200,
760
854
  },
761
855
  "removal": {
@@ -769,7 +863,7 @@ def _capabilities_payload() -> dict[str, Any]:
769
863
  "response_fields": [
770
864
  "schema", "operation", "request_id", "event_id",
771
865
  "payload_sha256", "status", "attempts", "max_attempts",
772
- "capabilities_sha256",
866
+ "capabilities_sha256", "capability_binding",
773
867
  ],
774
868
  "success_status": 202,
775
869
  },
@@ -783,6 +877,7 @@ def _capabilities_payload() -> dict[str, Any]:
783
877
  "response_schema": READINESS_RESPONSE_SCHEMA,
784
878
  "response_fields": [
785
879
  "schema", "readiness", "capabilities_sha256",
880
+ "capability_binding",
786
881
  ],
787
882
  "success_status": 200,
788
883
  },
@@ -794,7 +889,10 @@ def _capabilities_payload() -> dict[str, Any]:
794
889
  "request_schema": STATUS_REQUEST_SCHEMA,
795
890
  "request_fields": ["schema", "event_id", "site_id"],
796
891
  "response_schema": STATUS_RESPONSE_SCHEMA,
797
- "response_fields": ["schema", "status", "capabilities_sha256"],
892
+ "response_fields": [
893
+ "schema", "status", "capabilities_sha256",
894
+ "capability_binding",
895
+ ],
798
896
  "success_status": 200,
799
897
  },
800
898
  }
@@ -851,7 +949,7 @@ class CMSSourceHTTPApplication:
851
949
  def __init__(self, runtime: Any, authenticator: Callable[[dict[str, Any]], Any]):
852
950
  if not all(callable(getattr(runtime, name, None)) for name in (
853
951
  "enqueue_change", "enqueue_removal", "status", "health",
854
- "worker_readiness", "require_worker_ready",
952
+ "worker_readiness", "require_worker_ready", "capability_binding",
855
953
  )):
856
954
  raise TypeError("runtime must provide source CMS operations")
857
955
  if not callable(authenticator):
@@ -859,6 +957,18 @@ class CMSSourceHTTPApplication:
859
957
  self.runtime = runtime
860
958
  self.authenticator = authenticator
861
959
 
960
+ def _runtime_binding(self) -> dict[str, Any]:
961
+ try:
962
+ return _capability_binding_payload(
963
+ self.runtime.capability_binding()
964
+ )
965
+ except CMSSourceHTTPBlocked:
966
+ raise
967
+ except Exception:
968
+ raise CMSSourceHTTPBlocked(
969
+ "source_http.runtime_blocked", 503,
970
+ ) from None
971
+
862
972
  @staticmethod
863
973
  def _headers(environ: Mapping[str, Any]) -> tuple[tuple[str, str], ...]:
864
974
  headers = []
@@ -1006,11 +1116,13 @@ class CMSSourceHTTPApplication:
1006
1116
  raise CMSSourceHTTPBlocked("source_http.content_type_invalid", 415)
1007
1117
  body = self._body(environ, required=True)
1008
1118
  principal = self._authenticate(environ, path, body)
1119
+ binding = self._runtime_binding()
1009
1120
 
1010
1121
  if path == CAPABILITIES_PATH:
1011
1122
  return self._send(start_response, 200, {
1012
1123
  "schema": CAPABILITIES_RESPONSE_SCHEMA,
1013
1124
  "capabilities": _capabilities_payload(),
1125
+ "capability_binding": binding,
1014
1126
  })
1015
1127
 
1016
1128
  if path == HEALTH_PATH:
@@ -1026,6 +1138,7 @@ class CMSSourceHTTPApplication:
1026
1138
  "schema": HEALTH_RESPONSE_SCHEMA,
1027
1139
  "health": report,
1028
1140
  "capabilities_sha256": _capabilities_payload()["sha256"],
1141
+ "capability_binding": self._runtime_binding(),
1029
1142
  })
1030
1143
 
1031
1144
  if path == READINESS_PATH:
@@ -1041,6 +1154,7 @@ class CMSSourceHTTPApplication:
1041
1154
  "schema": READINESS_RESPONSE_SCHEMA,
1042
1155
  "readiness": report,
1043
1156
  "capabilities_sha256": _capabilities_payload()["sha256"],
1157
+ "capability_binding": self._runtime_binding(),
1044
1158
  })
1045
1159
 
1046
1160
  request = self._request(body)
@@ -1085,6 +1199,7 @@ class CMSSourceHTTPApplication:
1085
1199
  expected_site_id=site_id,
1086
1200
  ),
1087
1201
  "capabilities_sha256": _capabilities_payload()["sha256"],
1202
+ "capability_binding": self._runtime_binding(),
1088
1203
  })
1089
1204
 
1090
1205
  envelope_key = "change" if path == CHANGE_PATH else "removal"
@@ -1143,6 +1258,7 @@ class CMSSourceHTTPApplication:
1143
1258
  ),
1144
1259
  **payload,
1145
1260
  "capabilities_sha256": _capabilities_payload()["sha256"],
1261
+ "capability_binding": self._runtime_binding(),
1146
1262
  })
1147
1263
  except CMSSourceHTTPBlocked as failure:
1148
1264
  return self._error(start_response, failure)