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
@@ -192,6 +192,14 @@ def _valid_sha256(value: Any, *, optional: bool = False) -> bool:
192
192
  )
193
193
 
194
194
 
195
+ def _capability_pin(value: Any, name: str) -> str | None:
196
+ if value is None:
197
+ return None
198
+ if not isinstance(value, str) or SHA256.fullmatch(value) is None:
199
+ raise ValueError(f"{name} must be a lowercase SHA-256 digest")
200
+ return value
201
+
202
+
195
203
  def _valid_nonnegative_number(value: Any) -> bool:
196
204
  return (
197
205
  not isinstance(value, bool)
@@ -357,6 +365,8 @@ class CMSLocalizationHTTPClient:
357
365
  allow_loopback_http: bool = False,
358
366
  clock: Callable[[], float | int] = time.time,
359
367
  request_id_factory: Callable[[], str] | None = None,
368
+ capabilities_sha256: str | None = None,
369
+ commercial_rendering_registry_sha256: str | None = None,
360
370
  ):
361
371
  if not isinstance(allow_loopback_http, bool):
362
372
  raise TypeError("allow_loopback_http must be boolean")
@@ -380,6 +390,14 @@ class CMSLocalizationHTTPClient:
380
390
  raise TypeError("transport must provide post")
381
391
  self.timeout = float(timeout)
382
392
  self.clock = clock
393
+ self._capabilities_sha256 = _capability_pin(
394
+ capabilities_sha256,
395
+ "capabilities_sha256",
396
+ )
397
+ self._commercial_rendering_registry_sha256 = _capability_pin(
398
+ commercial_rendering_registry_sha256,
399
+ "commercial_rendering_registry_sha256",
400
+ )
383
401
  self.request_id_factory = (
384
402
  (lambda: secrets.token_hex(16))
385
403
  if request_id_factory is None else request_id_factory
@@ -387,6 +405,16 @@ class CMSLocalizationHTTPClient:
387
405
  if not callable(self.request_id_factory):
388
406
  raise TypeError("request_id_factory must be callable")
389
407
 
408
+ @property
409
+ def capabilities_sha256(self) -> str | None:
410
+ """Return the constructor-fixed complete capability deployment pin."""
411
+ return self._capabilities_sha256
412
+
413
+ @property
414
+ def commercial_rendering_registry_sha256(self) -> str | None:
415
+ """Return the constructor-fixed commercial rendering registry pin."""
416
+ return self._commercial_rendering_registry_sha256
417
+
390
418
  def submit_change(self, change: Mapping[str, Any]) -> Mapping[str, Any]:
391
419
  request = _copy_request(
392
420
  change, schema=_CMS.CHANGE_SCHEMA, code="change_invalid",
@@ -520,6 +548,25 @@ class CMSLocalizationHTTPClient:
520
548
  or not self._valid_capabilities(response)
521
549
  ):
522
550
  raise CMSClientFailed("response_binding", retryable=True)
551
+ capabilities = response["capabilities"]
552
+ if (
553
+ self.capabilities_sha256 is not None
554
+ and capabilities["sha256"] != self.capabilities_sha256
555
+ ):
556
+ raise CMSClientFailed(
557
+ "capabilities_pin_mismatch",
558
+ retryable=False,
559
+ )
560
+ registry = capabilities["commercial_rendering_registry"]
561
+ if (
562
+ self.commercial_rendering_registry_sha256 is not None
563
+ and registry["sha256"]
564
+ != self.commercial_rendering_registry_sha256
565
+ ):
566
+ raise CMSClientFailed(
567
+ "commercial_rendering_registry_pin_mismatch",
568
+ retryable=False,
569
+ )
523
570
  return response
524
571
 
525
572
  def _read_request(
@@ -827,6 +874,42 @@ class CMSLocalizationHTTPClient:
827
874
  digest = unsigned_capabilities.pop("sha256", None)
828
875
  unsigned_contract = dict(contract)
829
876
  contract_digest = unsigned_contract.pop("sha256", None)
877
+ commercial = capabilities.get("commercial_profile")
878
+ rendering_registry = capabilities.get("commercial_rendering_registry")
879
+ expected_locales = []
880
+ try:
881
+ for profile in _CMS._PLANNER.EU_OFFICIAL_LOCALES:
882
+ commercial_quality = (
883
+ _CMS._PLANNER.commercial_quality_profile_for(profile.locale)
884
+ )
885
+ expected_locales.append({
886
+ "locale": profile.locale,
887
+ "eu_code": profile.eu_code,
888
+ "language": profile.language,
889
+ "native_name": profile.native_name,
890
+ "script": profile.script,
891
+ "direction": profile.direction,
892
+ "quality_profile_version": profile.quality_profile_version,
893
+ "quality_profile_sha256": profile.quality_profile_sha256,
894
+ "commercial_quality_profile_version": (
895
+ commercial_quality["version"]
896
+ ),
897
+ "commercial_quality_profile_sha256": (
898
+ commercial_quality["sha256"]
899
+ ),
900
+ })
901
+ expected_commercial = _CMS._COMMERCIAL.public_profile(
902
+ _CMS._PLANNER.COMMERCIAL_PROFILE,
903
+ )
904
+ expected_rendering_registry = (
905
+ _CMS._PLANNER.commercial_rendering_registry()
906
+ )
907
+ expected_publication_http = (
908
+ _CMS.WebsiteLocalizationCMSBridge
909
+ ._publication_http_capabilities()
910
+ )
911
+ except Exception:
912
+ return False
830
913
  if (
831
914
  not isinstance(digest, str)
832
915
  or SHA256.fullmatch(digest) is None
@@ -846,6 +929,39 @@ class CMSLocalizationHTTPClient:
846
929
  or contract.get("schema") != _API.API_CAPABILITIES_SCHEMA
847
930
  or contract.get("api_schema") != _API.API_SCHEMA
848
931
  or contract.get("error_schema") != _API.API_SCHEMA
932
+ or set(capabilities) != {
933
+ "schema", "change_schema", "cancellation_schema",
934
+ "tombstone_schema", "publication_schema",
935
+ "tombstone_delivery_schema", "plan_schema", "job_schema",
936
+ "eu_language_source", "default_target_policy",
937
+ "content_types", "quality_passes", "commercial_profile",
938
+ "commercial_rendering_registry", "publication_http",
939
+ "locales", "sha256",
940
+ }
941
+ or capabilities.get("schema") != _CMS.CAPABILITIES_SCHEMA
942
+ or capabilities.get("change_schema") != _CMS.CHANGE_SCHEMA
943
+ or capabilities.get("cancellation_schema")
944
+ != _CMS.CANCELLATION_SCHEMA
945
+ or capabilities.get("tombstone_schema") != _CMS.TOMBSTONE_SCHEMA
946
+ or capabilities.get("publication_schema")
947
+ != _CMS.PUBLICATION_SCHEMA
948
+ or capabilities.get("tombstone_delivery_schema")
949
+ != _CMS.TOMBSTONE_DELIVERY_SCHEMA
950
+ or capabilities.get("plan_schema") != _CMS._PLANNER.SCHEMA
951
+ or capabilities.get("job_schema") != _CMS._PLANNER.JOB_SCHEMA
952
+ or capabilities.get("eu_language_source")
953
+ != _CMS._PLANNER.EU_LANGUAGE_SOURCE
954
+ or capabilities.get("default_target_policy")
955
+ != _CMS._DEFAULT_TARGET_POLICY
956
+ or capabilities.get("content_types")
957
+ != sorted(_CMS._PLANNER.CONTENT_TYPES)
958
+ or capabilities.get("quality_passes")
959
+ != list(_CMS._PLANNER.QUALITY_PASSES)
960
+ or commercial != expected_commercial
961
+ or rendering_registry != expected_rendering_registry
962
+ or capabilities.get("publication_http")
963
+ != expected_publication_http
964
+ or capabilities.get("locales") != expected_locales
849
965
  ):
850
966
  return False
851
967
  operations = contract.get("operations")
@@ -156,6 +156,27 @@ class HealthChecker(Protocol):
156
156
  def __call__(self, probe: VerifiedHealthProbe) -> Mapping[str, Any]: ...
157
157
 
158
158
 
159
+ def release_evidence_is_current(
160
+ value: Any,
161
+ *,
162
+ locale: Any,
163
+ target_sha256: Any,
164
+ approval_id: Any,
165
+ require_current_locale_quality: bool = True,
166
+ ) -> bool:
167
+ """Apply the same current release contract at ingress and durable reads."""
168
+
169
+ return _CMS._valid_release_evidence(
170
+ value,
171
+ locale=locale,
172
+ target_sha256=target_sha256,
173
+ approval_id=approval_id,
174
+ require_current_locale_quality=(
175
+ require_current_locale_quality
176
+ ),
177
+ )
178
+
179
+
159
180
  class PublicationExpectationResolver(Protocol):
160
181
  def __call__(
161
182
  self, publication: VerifiedPublication,
@@ -302,6 +323,30 @@ def _signature(value: Any) -> Any:
302
323
  )
303
324
 
304
325
 
326
+ def message_signature_is_valid(
327
+ payload: Any,
328
+ signature: Any,
329
+ authority: Any,
330
+ ) -> bool:
331
+ """Verify one canonical message without trusting stored signature fields."""
332
+
333
+ if (
334
+ not isinstance(payload, bytes)
335
+ or not payload
336
+ or not callable(getattr(authority, "verify", None))
337
+ ):
338
+ return False
339
+ try:
340
+ normalized = (
341
+ signature
342
+ if isinstance(signature, _CMS.CMSMessageSignature)
343
+ else _signature(signature)
344
+ )
345
+ return authority.verify(payload, normalized) is True
346
+ except Exception:
347
+ return False
348
+
349
+
305
350
  def _timestamp(value: Any) -> float:
306
351
  if isinstance(value, bool) or not isinstance(value, (int, float)):
307
352
  raise CMSReceiverBlocked(
@@ -365,11 +410,12 @@ def _verify_publication(value: Any, payload_sha256: str, *, now: float) -> bytes
365
410
  or not isinstance(approval_id, str)
366
411
  or TOKEN.fullmatch(approval_id) is None
367
412
  or _timestamp(item.get("approval_expires_at")) <= now
368
- or not _CMS._valid_release_evidence(
413
+ or not release_evidence_is_current(
369
414
  item.get("release_evidence"),
370
415
  locale=locale,
371
416
  target_sha256=target_sha256,
372
417
  approval_id=approval_id,
418
+ require_current_locale_quality=False,
373
419
  )
374
420
  ):
375
421
  raise CMSReceiverBlocked(
@@ -466,9 +512,57 @@ def _verify_expectation(
466
512
  )
467
513
  for item in localizations:
468
514
  evidence = item["release_evidence"]
515
+ try:
516
+ profile = _CMS._PLANNER.quality_profile_for(item["locale"])
517
+ if (
518
+ not isinstance(profile, dict)
519
+ or profile.get("locale") != item["locale"]
520
+ or not isinstance(profile.get("version"), str)
521
+ or TOKEN.fullmatch(profile["version"]) is None
522
+ or not isinstance(profile.get("sha256"), str)
523
+ or SHA256.fullmatch(profile["sha256"]) is None
524
+ ):
525
+ raise ValueError("quality profile is invalid")
526
+ except Exception:
527
+ raise CMSReceiverBlocked(
528
+ "receiver.release_scope", retryable=False, http_status=409,
529
+ ) from None
530
+ expected_quality = {
531
+ "locale": profile["locale"],
532
+ "version": profile["version"],
533
+ "sha256": profile["sha256"],
534
+ }
535
+ expected_commercial_quality = None
536
+ if expectation.content_type == "commercial":
537
+ try:
538
+ profile = _CMS._PLANNER.commercial_quality_profile_for(
539
+ item["locale"],
540
+ )
541
+ if (
542
+ not isinstance(profile, dict)
543
+ or not isinstance(profile.get("commercial_profile"), str)
544
+ or TOKEN.fullmatch(profile["commercial_profile"]) is None
545
+ or not isinstance(profile.get("version"), str)
546
+ or TOKEN.fullmatch(profile["version"]) is None
547
+ or not isinstance(profile.get("sha256"), str)
548
+ or SHA256.fullmatch(profile["sha256"]) is None
549
+ ):
550
+ raise ValueError("commercial quality profile is invalid")
551
+ except Exception:
552
+ raise CMSReceiverBlocked(
553
+ "receiver.release_scope", retryable=False, http_status=409,
554
+ ) from None
555
+ expected_commercial_quality = {
556
+ "profile": profile["commercial_profile"],
557
+ "version": profile["version"],
558
+ "sha256": profile["sha256"],
559
+ }
469
560
  if (
470
561
  evidence["content_type"] != expectation.content_type
562
+ or evidence["quality_profile"] != expected_quality
471
563
  or evidence["commercial_profile"] != expectation.commercial_profile
564
+ or evidence["commercial_quality_profile"]
565
+ != expected_commercial_quality
472
566
  ):
473
567
  raise CMSReceiverBlocked(
474
568
  "receiver.release_scope", retryable=False, http_status=409,
@@ -630,11 +724,9 @@ def _verify_publication_transport(
630
724
  "receiver.framing_invalid", retryable=False, http_status=400,
631
725
  )
632
726
  signature = _signature(envelope.get("signature"))
633
- try:
634
- verified = publication_authority.verify(payload_bytes, signature) is True
635
- except Exception:
636
- verified = False
637
- if not verified:
727
+ if not message_signature_is_valid(
728
+ payload_bytes, signature, publication_authority,
729
+ ):
638
730
  raise CMSReceiverBlocked(
639
731
  "receiver.signature_invalid", retryable=False, http_status=401,
640
732
  )
@@ -706,11 +798,9 @@ def _verify_tombstone_transport(
706
798
  "receiver.framing_invalid", retryable=False, http_status=400,
707
799
  )
708
800
  signature = _signature(envelope.get("signature"))
709
- try:
710
- verified = publication_authority.verify(payload_bytes, signature) is True
711
- except Exception:
712
- verified = False
713
- if not verified:
801
+ if not message_signature_is_valid(
802
+ payload_bytes, signature, publication_authority,
803
+ ):
714
804
  raise CMSReceiverBlocked(
715
805
  "receiver.signature_invalid", retryable=False, http_status=401,
716
806
  )
@@ -380,7 +380,18 @@ def open_durable_cms_receiver(
380
380
  "CMS receiver database could not be opened"
381
381
  ) from error
382
382
  try:
383
- store = _STORE.DurableCMSReceiverStore(connection, clock=clock)
383
+ store = _STORE.DurableCMSReceiverStore(
384
+ connection,
385
+ release_evidence_validator=(
386
+ _RECEIVER.release_evidence_is_current
387
+ ),
388
+ publication_signature_validator=lambda payload, signature: (
389
+ _RECEIVER.message_signature_is_valid(
390
+ payload, signature, publication_authority,
391
+ )
392
+ ),
393
+ clock=clock,
394
+ )
384
395
  database_guard()
385
396
  synchronized_store = _SynchronizedStore(
386
397
  connection, store, database_guard,