blun-king-cli 9.1.587 → 9.1.588

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 (436) hide show
  1. package/CHANGELOG.md +11 -185
  2. package/LIESMICH.txt +51 -13
  3. package/README.md +44 -47
  4. package/agent-spine-plugin/.codex-plugin/plugin.json +16 -4
  5. package/agent-spine-plugin/CHANGELOG.md +37 -5
  6. package/agent-spine-plugin/README.md +3 -3
  7. package/agent-spine-plugin/blun.plugin.json +45 -10
  8. package/agent-spine-plugin/docs/artifact-evaluation.md +93 -0
  9. package/agent-spine-plugin/docs/host-integration.md +42 -27
  10. package/agent-spine-plugin/docs/preflight-recall.md +4 -2
  11. package/agent-spine-plugin/docs/session-timeline.md +97 -236
  12. package/agent-spine-plugin/docs/world-model.md +25 -0
  13. package/agent-spine-plugin/hooks/codex.json +1 -1
  14. package/agent-spine-plugin/hooks/hooks.json +1 -1
  15. package/agent-spine-plugin/package.json +1 -3
  16. package/agent-spine-plugin/scripts/check-hosts.js +3 -3
  17. package/agent-spine-plugin/scripts/release-check.js +10 -5
  18. package/agent-spine-plugin/scripts/run-checks.js +4 -1
  19. package/agent-spine-plugin/scripts/run-tests-hermetic.js +32 -6
  20. package/agent-spine-plugin/src/cli-learning.js +15 -0
  21. package/agent-spine-plugin/src/cli.js +2 -0
  22. package/agent-spine-plugin/src/hook.js +32 -32
  23. package/agent-spine-plugin/src/lib/action-lesson-recall.js +73 -8
  24. package/agent-spine-plugin/src/lib/briefing.js +146 -36
  25. package/agent-spine-plugin/src/lib/channel-continuity.js +19 -0
  26. package/agent-spine-plugin/src/lib/delivery-agent-usage.js +14 -7
  27. package/agent-spine-plugin/src/lib/gateway-group-response.js +128 -0
  28. package/agent-spine-plugin/src/lib/gateway-runs.js +24 -15
  29. package/agent-spine-plugin/src/lib/hook-briefing-use.js +13 -3
  30. package/agent-spine-plugin/src/lib/hook-context.js +16 -3
  31. package/agent-spine-plugin/src/lib/hook-output.js +129 -5
  32. package/agent-spine-plugin/src/lib/hook-timeline.js +5 -3
  33. package/agent-spine-plugin/src/lib/indexed-memory.js +2 -2
  34. package/agent-spine-plugin/src/lib/learning-artifact-evaluator.js +114 -0
  35. package/agent-spine-plugin/src/lib/learning-context.js +11 -4
  36. package/agent-spine-plugin/src/lib/learning-measurements.js +2 -2
  37. package/agent-spine-plugin/src/lib/mcp-runtime.js +89 -3
  38. package/agent-spine-plugin/src/lib/mcp-source-context.js +12 -2
  39. package/agent-spine-plugin/src/lib/mcp-timeline-tools.js +91 -8
  40. package/agent-spine-plugin/src/lib/mcp-world-tools.js +2 -2
  41. package/agent-spine-plugin/src/lib/owned-file-lock.js +20 -1
  42. package/agent-spine-plugin/src/lib/persona-runtime.js +2 -2
  43. package/agent-spine-plugin/src/lib/preflight-delivery-id.js +27 -0
  44. package/agent-spine-plugin/src/lib/preflight.js +4 -4
  45. package/agent-spine-plugin/src/lib/session-timeline-codex.js +15 -0
  46. package/agent-spine-plugin/src/lib/session-timeline-contract.js +12 -4
  47. package/agent-spine-plugin/src/lib/session-timeline-event-extract.js +36 -7
  48. package/agent-spine-plugin/src/lib/session-timeline-host-origin.js +13 -10
  49. package/agent-spine-plugin/src/lib/session-timeline-invocation.js +1 -1
  50. package/agent-spine-plugin/src/lib/session-timeline-king.js +14 -0
  51. package/agent-spine-plugin/src/lib/session-timeline-prior.js +18 -12
  52. package/agent-spine-plugin/src/lib/session-timeline-provider.js +5 -0
  53. package/agent-spine-plugin/src/lib/session-timeline-query.js +2 -0
  54. package/agent-spine-plugin/src/lib/session-timeline-results.js +35 -10
  55. package/agent-spine-plugin/src/lib/session-timeline-source-open.js +30 -0
  56. package/agent-spine-plugin/src/lib/session-timeline.js +122 -75
  57. package/agent-spine-plugin/src/lib/source-roots.js +3 -2
  58. package/agent-spine-plugin/src/lib/task-knowledge-context.js +22 -1
  59. package/agent-spine-plugin/src/lib/timeline-continuation-update.js +100 -0
  60. package/agent-spine-plugin/src/lib/timeline-tool-guard.js +30 -7
  61. package/agent-spine-plugin/src/lib/timeline-user-feedback.js +217 -0
  62. package/agent-spine-plugin/src/lib/timeline-world-capture.js +233 -0
  63. package/agent-spine-plugin/src/lib/world-knowledge.js +59 -2
  64. package/agent-spine-plugin/src/lib/world-model.js +64 -9
  65. package/agent-spine-plugin/src/worker.js +13 -1
  66. package/bin/blun.js +43 -28
  67. package/bin/core-bootstrap.js +5 -4
  68. package/bin/king.js +43 -28
  69. package/bin/launcher-mode.js +1 -10
  70. package/bin/launcher-runtime.js +128 -295
  71. package/bin/managed-node.js +0 -0
  72. package/bin/managed-plugin-selection.cjs +0 -1
  73. package/bin/native-module-repair.js +0 -0
  74. package/bin/node-runtime.js +0 -0
  75. package/bin/node-version.js +0 -0
  76. package/bin/plugin-bootstrap.js +56 -120
  77. package/bin/private-paths.js +11 -34
  78. package/bin/standard-tools-bootstrap.js +34 -114
  79. package/bin/turn-thinking-policy.cjs +3 -11
  80. package/bin/update-copy.js +200 -0
  81. package/bin/update-lease.js +0 -0
  82. package/bin/update-notice.js +136 -289
  83. package/bin/verify-agent-behavior.cjs +122 -0
  84. package/bin/verify-agent-components.cjs +104 -0
  85. package/bin/verify-bundled-agent-sources.cjs +57 -0
  86. package/blun.mjs +143076 -135288
  87. package/bundled-agent-sources.json +701 -0
  88. package/package.json +12 -15
  89. package/standard-skills/translate-native/README.md +1293 -0
  90. package/standard-skills/translate-native/SKILL.md +172 -22
  91. package/standard-skills/translate-native/VERSION +1 -1
  92. package/standard-skills/translate-native/agents/openai.yaml +18 -0
  93. package/standard-skills/translate-native/assets/icon.svg +8 -0
  94. package/standard-skills/translate-native/docs/BLUN_CODE_INTEGRATION.md +76 -0
  95. package/standard-skills/translate-native/docs/PREMORTEM.md +489 -0
  96. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION.md +2035 -0
  97. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_API.md +1302 -0
  98. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_EVIDENCE_HTTP.md +136 -0
  99. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_HEALTH_HTTP.md +130 -0
  100. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_HTTP_PROVIDER.md +175 -0
  101. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_RECEIPT_VERIFIER_HTTP.md +86 -0
  102. package/standard-skills/translate-native/integrations/AGENT_RULES.md +32 -0
  103. package/standard-skills/translate-native/integrations/adapters/blun-code-language-guard.js +514 -0
  104. package/standard-skills/translate-native/integrations/adapters/node-language-guard.js +230 -0
  105. package/standard-skills/translate-native/integrations/audit_log.py +327 -0
  106. package/standard-skills/translate-native/integrations/claude_language_hook.js +1536 -0
  107. package/standard-skills/translate-native/integrations/commercial_localization_profile.py +42 -0
  108. package/standard-skills/translate-native/integrations/delivery-policy.example.json +28 -0
  109. package/standard-skills/translate-native/integrations/enforced_delivery.py +543 -0
  110. package/standard-skills/translate-native/integrations/guard_service.py +435 -0
  111. package/standard-skills/translate-native/integrations/language_gateway.py +67 -0
  112. package/standard-skills/translate-native/integrations/mcp_auth_headers.py +198 -0
  113. package/standard-skills/translate-native/integrations/mcp_http_gateway.py +429 -0
  114. package/standard-skills/translate-native/integrations/non_language_html_entities.js +1485 -0
  115. package/standard-skills/translate-native/integrations/pre_output_guard.py +65 -0
  116. package/standard-skills/translate-native/integrations/task_router.py +101 -0
  117. package/standard-skills/translate-native/integrations/website_localization.py +401 -0
  118. package/standard-skills/translate-native/integrations/website_localization_api.py +581 -0
  119. package/standard-skills/translate-native/integrations/website_localization_benchmark.py +1885 -0
  120. package/standard-skills/translate-native/integrations/website_localization_benchmark_campaign.py +1772 -0
  121. package/standard-skills/translate-native/integrations/website_localization_benchmark_candidate.py +506 -0
  122. package/standard-skills/translate-native/integrations/website_localization_benchmark_http.py +400 -0
  123. package/standard-skills/translate-native/integrations/website_localization_benchmark_review_store.py +781 -0
  124. package/standard-skills/translate-native/integrations/website_localization_benchmark_reviewer_http.py +500 -0
  125. package/standard-skills/translate-native/integrations/website_localization_benchmark_runtime.py +1107 -0
  126. package/standard-skills/translate-native/integrations/website_localization_benchmark_suite.py +463 -0
  127. package/standard-skills/translate-native/integrations/website_localization_cms.py +2835 -0
  128. package/standard-skills/translate-native/integrations/website_localization_cms_client.py +875 -0
  129. package/standard-skills/translate-native/integrations/website_localization_cms_dispatch.py +805 -0
  130. package/standard-skills/translate-native/integrations/website_localization_cms_http.py +588 -0
  131. package/standard-skills/translate-native/integrations/website_localization_cms_lifecycle_monitor.py +991 -0
  132. package/standard-skills/translate-native/integrations/website_localization_cms_receiver.py +1441 -0
  133. package/standard-skills/translate-native/integrations/website_localization_cms_receiver_runtime.py +414 -0
  134. package/standard-skills/translate-native/integrations/website_localization_cms_receiver_store.py +1073 -0
  135. package/standard-skills/translate-native/integrations/website_localization_cms_removal_dispatch.py +865 -0
  136. package/standard-skills/translate-native/integrations/website_localization_cms_source_client.py +583 -0
  137. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery.py +964 -0
  138. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_runtime.py +665 -0
  139. package/standard-skills/translate-native/integrations/website_localization_cms_source_http.py +1153 -0
  140. package/standard-skills/translate-native/integrations/website_localization_cms_source_runtime.py +675 -0
  141. package/standard-skills/translate-native/integrations/website_localization_cms_source_service.py +1125 -0
  142. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification.py +674 -0
  143. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_http.py +444 -0
  144. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver.py +1469 -0
  145. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver_runtime.py +1142 -0
  146. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_processing_monitor.py +634 -0
  147. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_receiver_client.py +804 -0
  148. package/standard-skills/translate-native/integrations/website_localization_deepl_baseline.py +922 -0
  149. package/standard-skills/translate-native/integrations/website_localization_evidence_http.py +482 -0
  150. package/standard-skills/translate-native/integrations/website_localization_health.py +1541 -0
  151. package/standard-skills/translate-native/integrations/website_localization_health_http.py +372 -0
  152. package/standard-skills/translate-native/integrations/website_localization_http_provider.py +297 -0
  153. package/standard-skills/translate-native/integrations/website_localization_native_reference_http.py +479 -0
  154. package/standard-skills/translate-native/integrations/website_localization_native_reference_intake.py +363 -0
  155. package/standard-skills/translate-native/integrations/website_localization_native_reference_queue.py +1449 -0
  156. package/standard-skills/translate-native/integrations/website_localization_native_reference_store.py +420 -0
  157. package/standard-skills/translate-native/integrations/website_localization_quality_profiles.py +235 -0
  158. package/standard-skills/translate-native/integrations/website_localization_queue.py +671 -0
  159. package/standard-skills/translate-native/integrations/website_localization_receipt_verifier_http.py +516 -0
  160. package/standard-skills/translate-native/integrations/website_localization_release.py +928 -0
  161. package/standard-skills/translate-native/integrations/website_localization_release_coordinator.py +1008 -0
  162. package/standard-skills/translate-native/integrations/website_localization_runner.py +276 -0
  163. package/standard-skills/translate-native/integrations/website_localization_runtime.py +862 -0
  164. package/standard-skills/translate-native/integrations/website_localization_service.py +350 -0
  165. package/standard-skills/translate-native/integrations/website_localization_supervisor.py +511 -0
  166. package/standard-skills/translate-native/integrations/website_localization_worker.py +663 -0
  167. package/standard-skills/translate-native/provenance.json +3 -4
  168. package/standard-skills/translate-native/references/commercial-localization.md +177 -0
  169. package/standard-skills/translate-native/scripts/blun_language_guard.py +7 -1
  170. package/standard-skills/translate-native/scripts/check_commercial_review.py +80 -0
  171. package/standard-skills/translate-native/scripts/commercial_localization_profile.py +333 -0
  172. package/standard-tools/language-guard/LICENSE +21 -0
  173. package/standard-tools/language-guard/VERSION +1 -0
  174. package/standard-tools/language-guard/blun_language_guard.py +7 -1
  175. package/standard-tools/language-guard/check_commercial_review.py +80 -0
  176. package/standard-tools/language-guard/commercial_localization_profile.py +333 -0
  177. package/standard-tools/language-guard/language_gateway.py +62 -0
  178. package/standard-tools/language-guard/pre_output_guard.py +64 -0
  179. package/standard-tools/language-guard/provenance.json +4 -11
  180. package/standard-tools/manifest.json +34 -11
  181. package/telegram-plugin/commands/access.md +2 -10
  182. package/telegram-plugin/dist/bridge.mjs +64041 -687
  183. package/telegram-plugin/dist/mcp-server.mjs +72810 -9027
  184. package/telegram-plugin/dist/noise.mjs +28 -63511
  185. package/agent-spine-plugin/CONTRIBUTING.md +0 -52
  186. package/agent-spine-plugin/SECURITY.md +0 -47
  187. package/agent-spine-plugin/docs/assignment-continuation.md +0 -48
  188. package/agent-spine-plugin/docs/releasing.md +0 -85
  189. package/agent-spine-plugin/docs/structured-completion.md +0 -67
  190. package/bin/abort-listener-policy.cjs +0 -43
  191. package/bin/active-steer-priority-policy.cjs +0 -24
  192. package/bin/agent-api-http-adapter.mjs +0 -446
  193. package/bin/agent-api-private-http-server.mjs +0 -288
  194. package/bin/agent-api-runtime.mjs +0 -252
  195. package/bin/agent-api-service-environment.mjs +0 -236
  196. package/bin/agent-api-service-host.mjs +0 -209
  197. package/bin/agent-api-service-process.mjs +0 -171
  198. package/bin/agent-api-session-registry.mjs +0 -428
  199. package/bin/agent-api-tool-broker.cjs +0 -248
  200. package/bin/agent-api-turn-controller.mjs +0 -461
  201. package/bin/agent-api-usage-journal.cjs +0 -259
  202. package/bin/agent-resume-snapshot.cjs +0 -241
  203. package/bin/agentspine-king-goal-inbox.mjs +0 -111
  204. package/bin/agentspine-king-goal-intake.mjs +0 -106
  205. package/bin/approval-rejection-stop.cjs +0 -15
  206. package/bin/assistant-message-offload-policy.cjs +0 -284
  207. package/bin/baseline-skill-performance-policy.cjs +0 -39
  208. package/bin/bash-search-scope-policy.cjs +0 -49
  209. package/bin/codebase-search-runtime.cjs +0 -23
  210. package/bin/cognitive-action-checkpoint.cjs +0 -1104
  211. package/bin/cognitive-attention-delivery.cjs +0 -76
  212. package/bin/cognitive-attention-policy.cjs +0 -143
  213. package/bin/cognitive-attention-runtime.cjs +0 -91
  214. package/bin/cognitive-context-projection.cjs +0 -73
  215. package/bin/cognitive-cross-portal-acceptance.cjs +0 -443
  216. package/bin/cognitive-effective-view.cjs +0 -77
  217. package/bin/cognitive-focus-projection.cjs +0 -206
  218. package/bin/cognitive-focus-scope.cjs +0 -37
  219. package/bin/cognitive-goal-autostart-policy.cjs +0 -72
  220. package/bin/cognitive-goal-time-trigger-controller.cjs +0 -146
  221. package/bin/cognitive-memory-adapter.cjs +0 -282
  222. package/bin/cognitive-memory-command.cjs +0 -293
  223. package/bin/cognitive-memory-provider.cjs +0 -92
  224. package/bin/cognitive-salience-policy.cjs +0 -159
  225. package/bin/cognitive-state-store.cjs +0 -508
  226. package/bin/cognitive-turn-lifecycle.cjs +0 -624
  227. package/bin/cognitive-work-focus.cjs +0 -180
  228. package/bin/compaction-history-archive.cjs +0 -166
  229. package/bin/compaction-history-startup.cjs +0 -50
  230. package/bin/compaction-model-policy.cjs +0 -31
  231. package/bin/compaction-stage-policy.cjs +0 -21
  232. package/bin/compaction-transaction-policy.cjs +0 -122
  233. package/bin/config-write-dedup-policy.cjs +0 -27
  234. package/bin/context-budget-ledger.cjs +0 -31
  235. package/bin/context-doctor-policy.cjs +0 -70
  236. package/bin/context-insight-policy.cjs +0 -36
  237. package/bin/context-performance-policy.cjs +0 -19
  238. package/bin/context-pressure-policy.cjs +0 -20
  239. package/bin/cron-run-output.cjs +0 -45
  240. package/bin/cron-run-store.cjs +0 -145
  241. package/bin/curiosity-scout-policy.cjs +0 -49
  242. package/bin/default-model-output-budget-policy.cjs +0 -28
  243. package/bin/durable-task-resume-policy.cjs +0 -130
  244. package/bin/durable-task-resume-runtime.cjs +0 -117
  245. package/bin/durable-task-resume-store.cjs +0 -88
  246. package/bin/editable-tool-approval-policy.cjs +0 -540
  247. package/bin/editable-tool-approval-runtime.cjs +0 -99
  248. package/bin/effective-system-prompt-cache-policy.cjs +0 -33
  249. package/bin/error-memory-performance-policy.cjs +0 -113
  250. package/bin/file-observation-policy.cjs +0 -133
  251. package/bin/foreground-output-capture-policy.cjs +0 -41
  252. package/bin/generated-source-health.cjs +0 -142
  253. package/bin/glob-pattern-policy.cjs +0 -13
  254. package/bin/goal-completion-evidence-policy.cjs +0 -120
  255. package/bin/grep-output-limit-policy.cjs +0 -39
  256. package/bin/historical-media-projection-policy.cjs +0 -48
  257. package/bin/history-offload-pressure-policy.cjs +0 -33
  258. package/bin/html-to-research-markdown.cjs +0 -147
  259. package/bin/identity-context-policy.cjs +0 -764
  260. package/bin/identity-journal-policy.cjs +0 -107
  261. package/bin/input-draft-persistence.cjs +0 -77
  262. package/bin/king-tui-function-contract.json +0 -33
  263. package/bin/launcher-restart-policy.cjs +0 -150
  264. package/bin/live-response-repetition-guard.cjs +0 -196
  265. package/bin/llm-config-log-dedup-policy.cjs +0 -76
  266. package/bin/loop-event-record-policy.cjs +0 -174
  267. package/bin/managed-context-startup-policy.cjs +0 -27
  268. package/bin/media-activity-layout-policy.cjs +0 -34
  269. package/bin/media-auto-retrieval-policy.cjs +0 -90
  270. package/bin/media-result-policy.cjs +0 -59
  271. package/bin/micro-compaction-policy.cjs +0 -145
  272. package/bin/mistake-relevance-policy.cjs +0 -319
  273. package/bin/model-retry-progress-policy.cjs +0 -46
  274. package/bin/native-large-file-io.cjs +0 -42
  275. package/bin/native-runtime-cache.cjs +0 -76
  276. package/bin/natural-presence-policy.cjs +0 -28
  277. package/bin/noninteractive-shell-env-policy.cjs +0 -19
  278. package/bin/observer-hooks.cjs +0 -14
  279. package/bin/outbound-claim-provenance.cjs +0 -150
  280. package/bin/oversized-context-offload-policy.cjs +0 -86
  281. package/bin/pending-media-policy.cjs +0 -182
  282. package/bin/pending-token-estimate-policy.cjs +0 -41
  283. package/bin/personal-memory-consent-policy.cjs +0 -72
  284. package/bin/personal-memory-performance-policy.cjs +0 -12
  285. package/bin/personality-choice-policy.cjs +0 -101
  286. package/bin/personality-memory-adapter.cjs +0 -379
  287. package/bin/personality-mode.cjs +0 -46
  288. package/bin/personality-setup-policy.cjs +0 -197
  289. package/bin/proactive-compaction-policy.cjs +0 -25
  290. package/bin/profile-identity-resolution.cjs +0 -136
  291. package/bin/profile-runtime.cjs +0 -318
  292. package/bin/profile-tool-exclusion-policy.cjs +0 -37
  293. package/bin/programmatic-context-isolation.cjs +0 -25
  294. package/bin/programmatic-tool-runtime.mjs +0 -627
  295. package/bin/provider-idle-timeout-policy.cjs +0 -14
  296. package/bin/provider-model-refresh-deadline.cjs +0 -53
  297. package/bin/provider-model-refresh-policy.cjs +0 -107
  298. package/bin/rate-limit-recovery-policy.cjs +0 -47
  299. package/bin/read-batch-policy.cjs +0 -32
  300. package/bin/read-continuation-policy.cjs +0 -59
  301. package/bin/recurring-cron-history-policy.cjs +0 -124
  302. package/bin/relationship-continuity-policy.cjs +0 -143
  303. package/bin/relationship-curiosity-policy.cjs +0 -107
  304. package/bin/relationship-learning-policy.cjs +0 -168
  305. package/bin/release-artifact-freeze-policy.cjs +0 -30
  306. package/bin/reload-plugin-bootstrap.cjs +0 -18
  307. package/bin/reload-queue-policy.cjs +0 -38
  308. package/bin/repeated-assistant-response-policy.cjs +0 -232
  309. package/bin/repeated-injection-projection.cjs +0 -107
  310. package/bin/repeated-user-message-projection.cjs +0 -8
  311. package/bin/research-page-result.cjs +0 -74
  312. package/bin/retry-checkpoint-policy.cjs +0 -13
  313. package/bin/runtime-exit-ledger.cjs +0 -144
  314. package/bin/scoped-cron-run-policy.cjs +0 -358
  315. package/bin/session-checkpoint-policy.cjs +0 -25
  316. package/bin/session-compaction-policy.cjs +0 -84
  317. package/bin/session-replay-policy.cjs +0 -20
  318. package/bin/session-replay-window-policy.cjs +0 -40
  319. package/bin/session-resume-checkpoint.cjs +0 -254
  320. package/bin/session-scrollback-archive.cjs +0 -229
  321. package/bin/skill-activation-performance-policy.cjs +0 -69
  322. package/bin/skill-listing-performance-policy.cjs +0 -92
  323. package/bin/soul-organization-policy.cjs +0 -78
  324. package/bin/soul-preservation-policy.cjs +0 -20
  325. package/bin/startup-preferences.cjs +0 -131
  326. package/bin/streaming-flush-performance-policy.cjs +0 -28
  327. package/bin/structured-agent-swarm-output.cjs +0 -325
  328. package/bin/structured-subagent-output.cjs +0 -252
  329. package/bin/subagent-context-fork-policy.cjs +0 -155
  330. package/bin/subagent-max-tokens-handoff-policy.cjs +0 -69
  331. package/bin/subagent-parent-responsiveness.cjs +0 -19
  332. package/bin/subagent-skill-policy.cjs +0 -206
  333. package/bin/subagent-timeout-policy.cjs +0 -182
  334. package/bin/subagent-tool-policy.cjs +0 -60
  335. package/bin/subagent-usage-rollup-policy.cjs +0 -29
  336. package/bin/system-prompt-context-policy.cjs +0 -124
  337. package/bin/system-prompt-token-cache-policy.cjs +0 -60
  338. package/bin/telegram-addressed-focus.cjs +0 -55
  339. package/bin/telegram-addressed-priority.cjs +0 -12
  340. package/bin/telegram-approval-relay.cjs +0 -290
  341. package/bin/telegram-bot-priority.cjs +0 -17
  342. package/bin/telegram-console-status-policy.cjs +0 -174
  343. package/bin/telegram-context-projection-policy.cjs +0 -141
  344. package/bin/telegram-delivery-lifecycle.cjs +0 -125
  345. package/bin/telegram-direct-focus-policy.cjs +0 -273
  346. package/bin/telegram-mcp-compatibility.cjs +0 -49
  347. package/bin/telegram-media-delivery-policy.cjs +0 -42
  348. package/bin/telegram-private-conversation-policy.cjs +0 -185
  349. package/bin/telegram-queue-handoff-policy.cjs +0 -73
  350. package/bin/telegram-remote-status-policy.cjs +0 -120
  351. package/bin/telegram-session-queue-runtime.mjs +0 -306
  352. package/bin/telegram-text-chunk-policy.cjs +0 -63
  353. package/bin/telegram-truncated-reply-policy.cjs +0 -37
  354. package/bin/telegram-urgent-policy.cjs +0 -45
  355. package/bin/telemetry-spool-policy.cjs +0 -57
  356. package/bin/thinking-activity-status-policy.cjs +0 -132
  357. package/bin/thinking-only-guard.cjs +0 -80
  358. package/bin/todo-list-turn-policy.cjs +0 -131
  359. package/bin/tool-call-loop-policy.cjs +0 -51
  360. package/bin/tool-file-persistence.cjs +0 -141
  361. package/bin/tool-result-offload-policy.cjs +0 -359
  362. package/bin/tool-result-offload-telemetry.cjs +0 -12
  363. package/bin/tool-schema-token-cache-policy.cjs +0 -41
  364. package/bin/tool-stream-preview-policy.cjs +0 -9
  365. package/bin/tui-functional-contract.cjs +0 -55
  366. package/bin/turn-tool-performance-policy.cjs +0 -486
  367. package/bin/usage-cache-efficiency-policy.cjs +0 -26
  368. package/bin/user-home-path-policy.cjs +0 -13
  369. package/bin/user-message-offload-policy.cjs +0 -103
  370. package/bin/user-prompt-hook-origin-policy.cjs +0 -34
  371. package/bin/user-tool-record-policy.cjs +0 -7
  372. package/bin/validated-learning-insight-policy.cjs +0 -58
  373. package/bin/validated-learning-outcome-trace.cjs +0 -107
  374. package/bin/validated-learning-performance-policy.cjs +0 -53
  375. package/bin/validated-learning-signal.cjs +0 -463
  376. package/bin/windows-bash-dialect-policy.cjs +0 -25
  377. package/bin/windows-node-crash-dump.cjs +0 -110
  378. package/bin/write-continuation-policy.cjs +0 -69
  379. package/codebase-index/README.md +0 -82
  380. package/codebase-index/codebase_index.py +0 -470
  381. package/standard-skills/agent-browser/SKILL.md +0 -19
  382. package/standard-skills/agent-browser/references/runtime.md +0 -8
  383. package/standard-skills/blun-session-inspector/SKILL.md +0 -41
  384. package/standard-skills/blun-session-inspector/scripts/inspect-session.cjs +0 -437
  385. package/standard-skills/design-taste-frontend/SKILL.md +0 -1206
  386. package/standard-skills/full-output-enforcement/SKILL.md +0 -49
  387. package/standard-skills/high-end-visual-design/SKILL.md +0 -98
  388. package/standard-skills/image-to-code/SKILL.md +0 -1228
  389. package/standard-skills/industrial-brutalist-ui/SKILL.md +0 -92
  390. package/standard-skills/minimalist-ui/SKILL.md +0 -85
  391. package/standard-skills/motion-design-taste/SKILL.md +0 -74
  392. package/standard-skills/playwright-testing/SKILL.md +0 -19
  393. package/standard-skills/playwright-testing/references/runtime.md +0 -7
  394. package/standard-skills/premortem/SKILL.md +0 -148
  395. package/standard-skills/redesign-existing-projects/SKILL.md +0 -178
  396. package/standard-skills/research-evidence/SKILL.md +0 -39
  397. package/standard-skills/research-evidence/references/evidence-format.md +0 -104
  398. package/standard-skills/research-evidence/scripts/evidence-collection.cjs +0 -260
  399. package/standard-skills/research-evidence/scripts/score-report.cjs +0 -130
  400. package/standard-skills/screenshot-lesen/SKILL.md +0 -52
  401. package/standard-skills/stitch-design-taste/DESIGN.md +0 -121
  402. package/standard-skills/stitch-design-taste/SKILL.md +0 -184
  403. package/standard-skills/telegram-channel/SKILL.md +0 -18
  404. package/standard-skills/telegram-channel/references/runtime.md +0 -7
  405. package/standard-skills/venture-flywheel/SKILL.md +0 -32
  406. package/standard-skills/venture-flywheel/identity/project-identity.cjs +0 -146
  407. package/standard-skills/venture-flywheel/policy/capability-engine.cjs +0 -114
  408. package/standard-skills/venture-flywheel/policy/repository-trust.cjs +0 -229
  409. package/standard-skills/venture-flywheel/references/BEISPIELE-phase0.md +0 -146
  410. package/standard-skills/venture-flywheel/references/CAPABILITY-MAP.md +0 -34
  411. package/standard-skills/venture-flywheel/references/SPEC-phase0-identity-trust.md +0 -77
  412. package/standard-skills/venture-flywheel/references/SPEC-phase0-state-events.md +0 -93
  413. package/standard-skills/venture-flywheel/schemas/capability-decision.schema.json +0 -13
  414. package/standard-skills/venture-flywheel/schemas/execution-event.schema.json +0 -44
  415. package/standard-skills/venture-flywheel/schemas/project-identity.schema.json +0 -32
  416. package/standard-skills/venture-flywheel/schemas/repository-trust.schema.json +0 -57
  417. package/standard-skills/venture-flywheel/schemas/run-transition.schema.json +0 -59
  418. package/standard-skills/venture-flywheel/state/execution-event.cjs +0 -191
  419. package/standard-skills/venture-flywheel/state/task-state-machine.cjs +0 -190
  420. package/standard-skills/web-lesen/SKILL.md +0 -73
  421. package/standard-skills/web-lesen/scripts/crawl_public.py +0 -379
  422. package/standard-skills/windows-mcp/SKILL.md +0 -19
  423. package/standard-skills/windows-mcp/references/runtime.md +0 -9
  424. package/telegram-plugin/DELIVERY.md +0 -36
  425. package/telegram-plugin/bin/telegram-approval-relay.cjs +0 -290
  426. package/telegram-plugin/bin/telegram-console-status-policy.cjs +0 -175
  427. package/telegram-plugin/bin/telegram-delivery-lifecycle.cjs +0 -125
  428. package/telegram-plugin/bin/telegram-direct-reply-policy.cjs +0 -48
  429. package/telegram-plugin/bin/telegram-launcher-status-queue.cjs +0 -122
  430. package/telegram-plugin/bin/telegram-private-conversation-policy.cjs +0 -186
  431. package/telegram-plugin/bin/telegram-remote-status-policy.cjs +0 -121
  432. package/telegram-plugin/bin/telegram-reply-parts.cjs +0 -149
  433. package/telegram-plugin/bin/telegram-text-chunk-policy.cjs +0 -63
  434. package/telegram-plugin/bin/telegram-typing-keepalive.cjs +0 -89
  435. package/telegram-plugin/compat/mcp-server-fa511cd1.mjs +0 -73825
  436. /package/{bin → scripts}/fix-node-pty-perms.js +0 -0
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env python3
2
+ """Portable fail-closed hook: verify a current V6 receipt supplied as JSON on stdin."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import hashlib
7
+ import importlib.util
8
+ import json
9
+ import os
10
+ import sys
11
+ from pathlib import Path
12
+
13
+
14
+ ROOT = Path(__file__).resolve().parents[1]
15
+ QUALITY_PATH = ROOT / "translate-native" / "scripts" / "language_quality.py"
16
+ SPEC = importlib.util.spec_from_file_location("blun_hook_quality", QUALITY_PATH)
17
+ assert SPEC and SPEC.loader
18
+ QUALITY = importlib.util.module_from_spec(SPEC)
19
+ SPEC.loader.exec_module(QUALITY)
20
+
21
+
22
+ def load_verification_key(path: Path) -> bytes:
23
+ """Load an existing verifier key without creating or replacing trust state."""
24
+ environment_key = os.environ.get("BLUN_LANGUAGE_GUARD_KEY")
25
+ if environment_key:
26
+ return hashlib.sha256(environment_key.encode("utf-8")).digest()
27
+ try:
28
+ return QUALITY.load_existing_key(path)
29
+ except FileNotFoundError as error:
30
+ raise ValueError("signing key is missing; verification fails closed") from error
31
+ except OSError as error:
32
+ raise ValueError("signing key cannot be read") from error
33
+ except ValueError as error:
34
+ if "permissions" in str(error):
35
+ raise ValueError("signing key permissions are broader than owner-only") from error
36
+ raise ValueError("signing key is invalid") from error
37
+
38
+
39
+ def main() -> int:
40
+ try:
41
+ request = json.loads(sys.stdin.read().lstrip("\ufeff"))
42
+ task_kind = request["task_kind"]
43
+ source = request.get("source_text", "")
44
+ if task_kind not in {"translation", "response"}:
45
+ raise ValueError("task_kind must be translation or response")
46
+ if task_kind == "translation" and not source.strip():
47
+ raise ValueError("translation receipts require source_text")
48
+ if task_kind == "response" and source.strip():
49
+ raise ValueError("response receipts cannot carry source_text")
50
+ key_path = Path(os.environ.get("BLUN_LANGUAGE_GUARD_KEY_FILE", Path.home() / ".config" / "blun-language-guard" / "signing.key"))
51
+ result = QUALITY.verify_receipt(
52
+ request["release_token"], source, request["target_text"],
53
+ request["language"], load_verification_key(key_path),
54
+ request.get("content_type", "prose"), request.get("short_text_reviewed") is True,
55
+ purpose=task_kind,
56
+ )
57
+ except (KeyError, ValueError, json.JSONDecodeError, OSError) as error:
58
+ print(json.dumps({"allow": False, "error": str(error)}))
59
+ return 1
60
+ print(json.dumps({"allow": result["valid"], "verification": result}))
61
+ return 0 if result["valid"] else 1
62
+
63
+
64
+ if __name__ == "__main__":
65
+ raise SystemExit(main())
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env python3
2
+ """Host-owned routing from structured job metadata to the correct release gate."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import json
8
+ import re
9
+ import sys
10
+ from dataclasses import asdict, dataclass
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+
15
+ EXACT_LANGUAGE = re.compile(r"^(?:[A-Za-z]{2,8}|x)(?:-[A-Za-z0-9]{1,8})*$")
16
+ TRANSLATION_OPERATIONS = {
17
+ "translate", "translation", "localize", "localization", "transcreate",
18
+ "translation-review", "translation-proofread", "i18n", "l10n",
19
+ }
20
+ RESPONSE_OPERATIONS = {"respond", "response", "chat", "answer", "compose"}
21
+
22
+
23
+ class RoutingBlocked(ValueError):
24
+ """Raised when trusted job metadata is contradictory or incomplete."""
25
+
26
+
27
+ @dataclass(frozen=True)
28
+ class Route:
29
+ task_kind: str
30
+ language: str
31
+ source_text: str
32
+ content_type: str
33
+ reason: str
34
+
35
+
36
+ def _string(context: dict[str, Any], name: str) -> str:
37
+ value = context.get(name, "")
38
+ if value is None:
39
+ return ""
40
+ if not isinstance(value, str):
41
+ raise RoutingBlocked(f"{name} must be a string")
42
+ return value
43
+
44
+
45
+ def route_host_context(context: dict[str, Any]) -> Route:
46
+ if not isinstance(context, dict):
47
+ raise RoutingBlocked("host context must be an object")
48
+ explicit = _string(context, "task_kind").strip().casefold()
49
+ operation = _string(context, "operation").strip().casefold()
50
+ source = _string(context, "source_text")
51
+ content_type = _string(context, "content_type").strip() or "prose"
52
+ if content_type not in {"prose", "title", "meta_description", "ui"}:
53
+ raise RoutingBlocked("invalid content_type")
54
+
55
+ translation_evidence = bool(source.strip()) or operation in TRANSLATION_OPERATIONS
56
+ if explicit:
57
+ if explicit not in {"response", "translation"}:
58
+ raise RoutingBlocked("invalid task_kind")
59
+ task_kind = explicit
60
+ reason = "explicit-host-task-kind"
61
+ elif translation_evidence:
62
+ task_kind = "translation"
63
+ reason = "structured-translation-evidence"
64
+ elif not operation or operation in RESPONSE_OPERATIONS:
65
+ task_kind = "response"
66
+ reason = "structured-response-route"
67
+ else:
68
+ raise RoutingBlocked("unknown host operation")
69
+
70
+ if task_kind == "translation" and not source.strip():
71
+ raise RoutingBlocked("translation route requires complete source_text")
72
+ if task_kind == "response" and source.strip():
73
+ raise RoutingBlocked("source_text cannot be downgraded to response")
74
+ if task_kind == "response" and operation in TRANSLATION_OPERATIONS:
75
+ raise RoutingBlocked("translation operation cannot be downgraded to response")
76
+ if task_kind == "translation" and operation in RESPONSE_OPERATIONS:
77
+ raise RoutingBlocked("response operation conflicts with translation source")
78
+
79
+ language_field = "target_language" if task_kind == "translation" else "response_language"
80
+ language = _string(context, language_field).strip() or _string(context, "language").strip()
81
+ if language.casefold() in {"auto", "all"} or not EXACT_LANGUAGE.fullmatch(language):
82
+ raise RoutingBlocked(f"{language_field} must be an exact language or locale tag")
83
+ return Route(task_kind, language, source if task_kind == "translation" else "", content_type, reason)
84
+
85
+
86
+ def main() -> int:
87
+ parser = argparse.ArgumentParser(description="Resolve trusted host metadata to a language-guard route")
88
+ parser.add_argument("--input", type=Path)
89
+ args = parser.parse_args()
90
+ try:
91
+ raw = args.input.read_text(encoding="utf-8-sig") if args.input else sys.stdin.read().lstrip("\ufeff")
92
+ route = route_host_context(json.loads(raw))
93
+ except (OSError, json.JSONDecodeError, RoutingBlocked) as error:
94
+ print(json.dumps({"status": "BLOCK", "error": str(error)}))
95
+ return 1
96
+ print(json.dumps({"status": "PASS", **asdict(route)}, ensure_ascii=False))
97
+ return 0
98
+
99
+
100
+ if __name__ == "__main__":
101
+ raise SystemExit(main())
@@ -0,0 +1,401 @@
1
+ #!/usr/bin/env python3
2
+ """Deterministic, provider-neutral planning for EU website localization jobs.
3
+
4
+ The planner does not call a model or publish content. It turns one trusted
5
+ website source object into one independently retryable job per target locale.
6
+ Every job key is bound to the source and all policy inputs that can affect the
7
+ result, so a queue can safely deduplicate retries without reusing stale copy.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import argparse
13
+ import hashlib
14
+ import importlib.util
15
+ import json
16
+ import re
17
+ import sys
18
+ import unicodedata
19
+ from dataclasses import asdict, dataclass
20
+ from pathlib import Path
21
+ from typing import Any, Iterable, Mapping, Sequence
22
+
23
+
24
+ SCHEMA = "blun.website-localization-plan.v2"
25
+ JOB_SCHEMA = "blun.website-localization-job.v2"
26
+ EU_LANGUAGE_SOURCE = (
27
+ "https://european-union.europa.eu/principles-countries-history/languages_en"
28
+ )
29
+ MAX_SOURCE_BYTES = 1_000_000
30
+ MAX_FIELD_LENGTH = 256
31
+ EXACT_LOCALE = re.compile(
32
+ r"^[A-Za-z]{2,8}(?:-(?:[A-Za-z]{4}|[A-Za-z]{2}|[0-9]{3}|[A-Za-z0-9]{5,8}))*$"
33
+ )
34
+ CONTENT_TYPES = frozenset({
35
+ "headline",
36
+ "cta",
37
+ "marketing",
38
+ "ui",
39
+ "documentation",
40
+ "seo",
41
+ "legal",
42
+ "commercial",
43
+ })
44
+ QUALITY_PASSES = ("target_native", "source_fidelity")
45
+ COMMERCIAL_PROFILE = "translate-native.commercial.v2"
46
+
47
+
48
+ class LocalizationPlanBlocked(ValueError):
49
+ """Raised when trusted localization metadata is incomplete or ambiguous."""
50
+
51
+
52
+ @dataclass(frozen=True)
53
+ class LocaleProfile:
54
+ locale: str
55
+ eu_code: str
56
+ language: str
57
+ native_name: str
58
+ script: str
59
+ quality_profile_version: str
60
+ quality_profile_sha256: str
61
+ direction: str = "ltr"
62
+
63
+
64
+ # The European Union currently has 24 official languages. A single explicit
65
+ # website locale is selected for each language so every queue item has regional
66
+ # conventions rather than an ambiguous bare language tag. This public default
67
+ # uses Austrian German; projects can introduce other regional profiles in a
68
+ # future schema revision without embedding brand-specific vocabulary here.
69
+ def _load_quality_profiles():
70
+ path = Path(__file__).with_name("website_localization_quality_profiles.py")
71
+ spec = importlib.util.spec_from_file_location("blun_website_quality_profiles", path)
72
+ if spec is None or spec.loader is None:
73
+ raise RuntimeError("cannot load website-localization quality profiles")
74
+ module = importlib.util.module_from_spec(spec)
75
+ sys.modules[spec.name] = module
76
+ spec.loader.exec_module(module)
77
+ return module
78
+
79
+
80
+ _QUALITY_PROFILES = _load_quality_profiles()
81
+
82
+
83
+ def _locale(locale: str, eu_code: str, language: str, native_name: str, script: str) -> LocaleProfile:
84
+ quality = _QUALITY_PROFILES.quality_profile_for(locale)
85
+ return LocaleProfile(
86
+ locale, eu_code, language, native_name, script,
87
+ quality["version"], quality["sha256"],
88
+ )
89
+
90
+
91
+ EU_OFFICIAL_LOCALES: tuple[LocaleProfile, ...] = (
92
+ _locale("bg-BG", "BG", "bg", "български", "Cyrl"),
93
+ _locale("hr-HR", "HR", "hr", "hrvatski", "Latn"),
94
+ _locale("cs-CZ", "CS", "cs", "čeština", "Latn"),
95
+ _locale("da-DK", "DA", "da", "dansk", "Latn"),
96
+ _locale("nl-NL", "NL", "nl", "Nederlands", "Latn"),
97
+ _locale("en-IE", "EN", "en", "English", "Latn"),
98
+ _locale("et-EE", "ET", "et", "eesti", "Latn"),
99
+ _locale("fi-FI", "FI", "fi", "suomi", "Latn"),
100
+ _locale("fr-FR", "FR", "fr", "français", "Latn"),
101
+ _locale("de-AT", "DE", "de", "Deutsch", "Latn"),
102
+ _locale("el-GR", "EL", "el", "ελληνικά", "Grek"),
103
+ _locale("hu-HU", "HU", "hu", "magyar", "Latn"),
104
+ _locale("ga-IE", "GA", "ga", "Gaeilge", "Latn"),
105
+ _locale("it-IT", "IT", "it", "italiano", "Latn"),
106
+ _locale("lv-LV", "LV", "lv", "latviešu", "Latn"),
107
+ _locale("lt-LT", "LT", "lt", "lietuvių", "Latn"),
108
+ _locale("mt-MT", "MT", "mt", "Malti", "Latn"),
109
+ _locale("pl-PL", "PL", "pl", "polski", "Latn"),
110
+ _locale("pt-PT", "PT", "pt", "português", "Latn"),
111
+ _locale("ro-RO", "RO", "ro", "română", "Latn"),
112
+ _locale("sk-SK", "SK", "sk", "slovenčina", "Latn"),
113
+ _locale("sl-SI", "SL", "sl", "slovenščina", "Latn"),
114
+ _locale("es-ES", "ES", "es", "español", "Latn"),
115
+ _locale("sv-SE", "SV", "sv", "svenska", "Latn"),
116
+ )
117
+ _PROFILE_BY_LOCALE = {profile.locale: profile for profile in EU_OFFICIAL_LOCALES}
118
+ if set(_PROFILE_BY_LOCALE) != {profile.locale for profile in _QUALITY_PROFILES.PROFILES}:
119
+ raise RuntimeError("EU locale and quality-profile registries differ")
120
+
121
+
122
+ def quality_profile_for(locale: str) -> dict[str, Any]:
123
+ return _QUALITY_PROFILES.quality_profile_for(locale)
124
+
125
+
126
+ @dataclass(frozen=True)
127
+ class LocalizationJob:
128
+ job_id: str
129
+ source_id: str
130
+ source_revision: str
131
+ source_text: str
132
+ source_hash: str
133
+ source_locale: str
134
+ target: LocaleProfile
135
+ content_type: str
136
+ glossary_version: str
137
+ policy_version: str
138
+ provider_id: str
139
+ model_id: str
140
+ model_version: str
141
+ software_version: str
142
+
143
+ def as_payload(self) -> dict[str, Any]:
144
+ """Return the provider-neutral queue payload for one target locale."""
145
+ return {
146
+ "schema": JOB_SCHEMA,
147
+ "job_id": self.job_id,
148
+ "idempotency_key": self.job_id,
149
+ "source": {
150
+ "id": self.source_id,
151
+ "revision": self.source_revision,
152
+ "text": self.source_text,
153
+ "sha256": self.source_hash,
154
+ "locale": self.source_locale,
155
+ },
156
+ "target": asdict(self.target),
157
+ "content_type": self.content_type,
158
+ "glossary_version": self.glossary_version,
159
+ "policy_version": self.policy_version,
160
+ "provider": {
161
+ "id": self.provider_id,
162
+ "model_id": self.model_id,
163
+ "model_version": self.model_version,
164
+ },
165
+ "software_version": self.software_version,
166
+ "quality_passes": list(QUALITY_PASSES),
167
+ "release_required": True,
168
+ **({"commercial_profile": COMMERCIAL_PROFILE} if self.content_type == "commercial" else {}),
169
+ }
170
+
171
+
172
+ @dataclass(frozen=True)
173
+ class LocalizationPlan:
174
+ plan_id: str
175
+ source_hash: str
176
+ source_locale: str
177
+ jobs: tuple[LocalizationJob, ...]
178
+
179
+ def as_payload(self) -> dict[str, Any]:
180
+ return {
181
+ "schema": SCHEMA,
182
+ "plan_id": self.plan_id,
183
+ "source_hash": self.source_hash,
184
+ "source_locale": self.source_locale,
185
+ "job_count": len(self.jobs),
186
+ "jobs": [job.as_payload() for job in self.jobs],
187
+ }
188
+
189
+
190
+ def _canonical_json(value: Mapping[str, Any]) -> bytes:
191
+ return json.dumps(
192
+ value,
193
+ ensure_ascii=False,
194
+ allow_nan=False,
195
+ sort_keys=True,
196
+ separators=(",", ":"),
197
+ ).encode("utf-8")
198
+
199
+
200
+ def _digest(value: Mapping[str, Any]) -> str:
201
+ return hashlib.sha256(_canonical_json(value)).hexdigest()
202
+
203
+
204
+ def _bound_field(name: str, value: Any) -> str:
205
+ if not isinstance(value, str):
206
+ raise LocalizationPlanBlocked(f"{name} must be a string")
207
+ if not value or value != value.strip():
208
+ raise LocalizationPlanBlocked(f"{name} must be non-empty without surrounding whitespace")
209
+ if "\x00" in value or len(value) > MAX_FIELD_LENGTH:
210
+ raise LocalizationPlanBlocked(f"{name} is invalid or too long")
211
+ if not unicodedata.is_normalized("NFC", value):
212
+ raise LocalizationPlanBlocked(f"{name} must use NFC Unicode")
213
+ return value
214
+
215
+
216
+ def _source_text(value: Any) -> str:
217
+ if not isinstance(value, str) or not value.strip():
218
+ raise LocalizationPlanBlocked("source_text must be a non-empty string")
219
+ if "\x00" in value:
220
+ raise LocalizationPlanBlocked("source_text contains NUL")
221
+ if not unicodedata.is_normalized("NFC", value):
222
+ raise LocalizationPlanBlocked("source_text must use NFC Unicode")
223
+ if len(value.encode("utf-8")) > MAX_SOURCE_BYTES:
224
+ raise LocalizationPlanBlocked("source_text exceeds the byte limit")
225
+ return value
226
+
227
+
228
+ def canonicalize_locale(value: Any) -> str:
229
+ value = _bound_field("locale", value)
230
+ if value.casefold() in {"auto", "all"}:
231
+ raise LocalizationPlanBlocked("locale must name one exact language or locale")
232
+ if not EXACT_LOCALE.fullmatch(value):
233
+ raise LocalizationPlanBlocked("locale must be an exact BCP-47 language tag")
234
+ parts = value.split("-")
235
+ canonical = [parts[0].lower()]
236
+ for part in parts[1:]:
237
+ if len(part) == 4 and part.isalpha():
238
+ canonical.append(part.title())
239
+ elif (len(part) == 2 and part.isalpha()) or (len(part) == 3 and part.isdigit()):
240
+ canonical.append(part.upper())
241
+ else:
242
+ canonical.append(part.lower())
243
+ return "-".join(canonical)
244
+
245
+
246
+ def _target_profiles(
247
+ source_locale: str,
248
+ target_locales: Iterable[Any] | None,
249
+ ) -> tuple[LocaleProfile, ...]:
250
+ source_language = source_locale.split("-", 1)[0]
251
+ if target_locales is None:
252
+ return tuple(
253
+ profile for profile in EU_OFFICIAL_LOCALES
254
+ if profile.language != source_language
255
+ )
256
+ if isinstance(target_locales, (str, bytes)):
257
+ raise LocalizationPlanBlocked("target_locales must be an array")
258
+ requested = list(target_locales)
259
+ if not requested:
260
+ raise LocalizationPlanBlocked("target_locales must not be empty")
261
+ canonical = [canonicalize_locale(item) for item in requested]
262
+ if len(set(canonical)) != len(canonical):
263
+ raise LocalizationPlanBlocked("target_locales contains duplicates")
264
+ unknown = [locale for locale in canonical if locale not in _PROFILE_BY_LOCALE]
265
+ if unknown:
266
+ raise LocalizationPlanBlocked(
267
+ "target_locales contains unsupported EU locale: " + ", ".join(unknown)
268
+ )
269
+ if any(_PROFILE_BY_LOCALE[locale].language == source_language for locale in canonical):
270
+ raise LocalizationPlanBlocked("target_locales must exclude the source language")
271
+ selected = set(canonical)
272
+ return tuple(profile for profile in EU_OFFICIAL_LOCALES if profile.locale in selected)
273
+
274
+
275
+ def plan_website_localization(
276
+ *,
277
+ source_id: Any,
278
+ source_revision: Any,
279
+ source_text: Any,
280
+ source_locale: Any,
281
+ content_type: Any,
282
+ glossary_version: Any,
283
+ policy_version: Any,
284
+ provider_id: Any,
285
+ model_id: Any,
286
+ model_version: Any,
287
+ software_version: Any,
288
+ target_locales: Iterable[Any] | None = None,
289
+ ) -> LocalizationPlan:
290
+ """Create one deterministic, independently releasable job per EU locale."""
291
+ source_id = _bound_field("source_id", source_id)
292
+ source_revision = _bound_field("source_revision", source_revision)
293
+ source_text = _source_text(source_text)
294
+ source_locale = canonicalize_locale(source_locale)
295
+ content_type = _bound_field("content_type", content_type)
296
+ if content_type not in CONTENT_TYPES:
297
+ raise LocalizationPlanBlocked("content_type is not a supported website content type")
298
+ glossary_version = _bound_field("glossary_version", glossary_version)
299
+ policy_version = _bound_field("policy_version", policy_version)
300
+ provider_id = _bound_field("provider_id", provider_id)
301
+ model_id = _bound_field("model_id", model_id)
302
+ model_version = _bound_field("model_version", model_version)
303
+ software_version = _bound_field("software_version", software_version)
304
+ profiles = _target_profiles(source_locale, target_locales)
305
+ source_hash = hashlib.sha256(source_text.encode("utf-8")).hexdigest()
306
+
307
+ common = {
308
+ "schema": JOB_SCHEMA,
309
+ "source_id": source_id,
310
+ "source_revision": source_revision,
311
+ "source_hash": source_hash,
312
+ "source_locale": source_locale,
313
+ "content_type": content_type,
314
+ "glossary_version": glossary_version,
315
+ "policy_version": policy_version,
316
+ "provider_id": provider_id,
317
+ "model_id": model_id,
318
+ "model_version": model_version,
319
+ "software_version": software_version,
320
+ "quality_passes": QUALITY_PASSES,
321
+ }
322
+ if content_type == "commercial":
323
+ common["commercial_profile"] = COMMERCIAL_PROFILE
324
+ jobs = tuple(
325
+ LocalizationJob(
326
+ job_id="blun-l10n-" + _digest({**common, "target": asdict(profile)}),
327
+ source_id=source_id,
328
+ source_revision=source_revision,
329
+ source_text=source_text,
330
+ source_hash=source_hash,
331
+ source_locale=source_locale,
332
+ target=profile,
333
+ content_type=content_type,
334
+ glossary_version=glossary_version,
335
+ policy_version=policy_version,
336
+ provider_id=provider_id,
337
+ model_id=model_id,
338
+ model_version=model_version,
339
+ software_version=software_version,
340
+ )
341
+ for profile in profiles
342
+ )
343
+ plan_binding = {
344
+ **common,
345
+ "target_locales": [job.target.locale for job in jobs],
346
+ "job_ids": [job.job_id for job in jobs],
347
+ }
348
+ return LocalizationPlan(
349
+ plan_id="blun-l10n-plan-" + _digest(plan_binding),
350
+ source_hash=source_hash,
351
+ source_locale=source_locale,
352
+ jobs=jobs,
353
+ )
354
+
355
+
356
+ _PLAN_FIELDS = frozenset({
357
+ "source_id",
358
+ "source_revision",
359
+ "source_text",
360
+ "source_locale",
361
+ "content_type",
362
+ "glossary_version",
363
+ "policy_version",
364
+ "provider_id",
365
+ "model_id",
366
+ "model_version",
367
+ "software_version",
368
+ "target_locales",
369
+ })
370
+
371
+
372
+ def plan_from_mapping(value: Any) -> LocalizationPlan:
373
+ if not isinstance(value, dict):
374
+ raise LocalizationPlanBlocked("input must be a JSON object")
375
+ unknown = sorted(set(value) - _PLAN_FIELDS)
376
+ if unknown:
377
+ raise LocalizationPlanBlocked("input contains unknown fields: " + ", ".join(unknown))
378
+ missing = sorted((_PLAN_FIELDS - {"target_locales"}) - set(value))
379
+ if missing:
380
+ raise LocalizationPlanBlocked("input is missing fields: " + ", ".join(missing))
381
+ return plan_website_localization(**value)
382
+
383
+
384
+ def main(argv: Sequence[str] | None = None) -> int:
385
+ parser = argparse.ArgumentParser(
386
+ description="Create deterministic EU website-localization queue jobs",
387
+ )
388
+ parser.add_argument("--input", type=Path, help="JSON request; defaults to stdin")
389
+ args = parser.parse_args(argv)
390
+ try:
391
+ raw = args.input.read_text(encoding="utf-8-sig") if args.input else sys.stdin.read().lstrip("\ufeff")
392
+ plan = plan_from_mapping(json.loads(raw))
393
+ except (OSError, json.JSONDecodeError, LocalizationPlanBlocked) as error:
394
+ print(json.dumps({"status": "BLOCK", "error": str(error)}, ensure_ascii=False))
395
+ return 1
396
+ print(json.dumps({"status": "PASS", **plan.as_payload()}, ensure_ascii=False))
397
+ return 0
398
+
399
+
400
+ if __name__ == "__main__":
401
+ raise SystemExit(main())