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.
- package/CHANGELOG.md +11 -185
- package/LIESMICH.txt +51 -13
- package/README.md +44 -47
- package/agent-spine-plugin/.codex-plugin/plugin.json +16 -4
- package/agent-spine-plugin/CHANGELOG.md +37 -5
- package/agent-spine-plugin/README.md +3 -3
- package/agent-spine-plugin/blun.plugin.json +45 -10
- package/agent-spine-plugin/docs/artifact-evaluation.md +93 -0
- package/agent-spine-plugin/docs/host-integration.md +42 -27
- package/agent-spine-plugin/docs/preflight-recall.md +4 -2
- package/agent-spine-plugin/docs/session-timeline.md +97 -236
- package/agent-spine-plugin/docs/world-model.md +25 -0
- package/agent-spine-plugin/hooks/codex.json +1 -1
- package/agent-spine-plugin/hooks/hooks.json +1 -1
- package/agent-spine-plugin/package.json +1 -3
- package/agent-spine-plugin/scripts/check-hosts.js +3 -3
- package/agent-spine-plugin/scripts/release-check.js +10 -5
- package/agent-spine-plugin/scripts/run-checks.js +4 -1
- package/agent-spine-plugin/scripts/run-tests-hermetic.js +32 -6
- package/agent-spine-plugin/src/cli-learning.js +15 -0
- package/agent-spine-plugin/src/cli.js +2 -0
- package/agent-spine-plugin/src/hook.js +32 -32
- package/agent-spine-plugin/src/lib/action-lesson-recall.js +73 -8
- package/agent-spine-plugin/src/lib/briefing.js +146 -36
- package/agent-spine-plugin/src/lib/channel-continuity.js +19 -0
- package/agent-spine-plugin/src/lib/delivery-agent-usage.js +14 -7
- package/agent-spine-plugin/src/lib/gateway-group-response.js +128 -0
- package/agent-spine-plugin/src/lib/gateway-runs.js +24 -15
- package/agent-spine-plugin/src/lib/hook-briefing-use.js +13 -3
- package/agent-spine-plugin/src/lib/hook-context.js +16 -3
- package/agent-spine-plugin/src/lib/hook-output.js +129 -5
- package/agent-spine-plugin/src/lib/hook-timeline.js +5 -3
- package/agent-spine-plugin/src/lib/indexed-memory.js +2 -2
- package/agent-spine-plugin/src/lib/learning-artifact-evaluator.js +114 -0
- package/agent-spine-plugin/src/lib/learning-context.js +11 -4
- package/agent-spine-plugin/src/lib/learning-measurements.js +2 -2
- package/agent-spine-plugin/src/lib/mcp-runtime.js +89 -3
- package/agent-spine-plugin/src/lib/mcp-source-context.js +12 -2
- package/agent-spine-plugin/src/lib/mcp-timeline-tools.js +91 -8
- package/agent-spine-plugin/src/lib/mcp-world-tools.js +2 -2
- package/agent-spine-plugin/src/lib/owned-file-lock.js +20 -1
- package/agent-spine-plugin/src/lib/persona-runtime.js +2 -2
- package/agent-spine-plugin/src/lib/preflight-delivery-id.js +27 -0
- package/agent-spine-plugin/src/lib/preflight.js +4 -4
- package/agent-spine-plugin/src/lib/session-timeline-codex.js +15 -0
- package/agent-spine-plugin/src/lib/session-timeline-contract.js +12 -4
- package/agent-spine-plugin/src/lib/session-timeline-event-extract.js +36 -7
- package/agent-spine-plugin/src/lib/session-timeline-host-origin.js +13 -10
- package/agent-spine-plugin/src/lib/session-timeline-invocation.js +1 -1
- package/agent-spine-plugin/src/lib/session-timeline-king.js +14 -0
- package/agent-spine-plugin/src/lib/session-timeline-prior.js +18 -12
- package/agent-spine-plugin/src/lib/session-timeline-provider.js +5 -0
- package/agent-spine-plugin/src/lib/session-timeline-query.js +2 -0
- package/agent-spine-plugin/src/lib/session-timeline-results.js +35 -10
- package/agent-spine-plugin/src/lib/session-timeline-source-open.js +30 -0
- package/agent-spine-plugin/src/lib/session-timeline.js +122 -75
- package/agent-spine-plugin/src/lib/source-roots.js +3 -2
- package/agent-spine-plugin/src/lib/task-knowledge-context.js +22 -1
- package/agent-spine-plugin/src/lib/timeline-continuation-update.js +100 -0
- package/agent-spine-plugin/src/lib/timeline-tool-guard.js +30 -7
- package/agent-spine-plugin/src/lib/timeline-user-feedback.js +217 -0
- package/agent-spine-plugin/src/lib/timeline-world-capture.js +233 -0
- package/agent-spine-plugin/src/lib/world-knowledge.js +59 -2
- package/agent-spine-plugin/src/lib/world-model.js +64 -9
- package/agent-spine-plugin/src/worker.js +13 -1
- package/bin/blun.js +43 -28
- package/bin/core-bootstrap.js +5 -4
- package/bin/king.js +43 -28
- package/bin/launcher-mode.js +1 -10
- package/bin/launcher-runtime.js +128 -295
- package/bin/managed-node.js +0 -0
- package/bin/managed-plugin-selection.cjs +0 -1
- package/bin/native-module-repair.js +0 -0
- package/bin/node-runtime.js +0 -0
- package/bin/node-version.js +0 -0
- package/bin/plugin-bootstrap.js +56 -120
- package/bin/private-paths.js +11 -34
- package/bin/standard-tools-bootstrap.js +34 -114
- package/bin/turn-thinking-policy.cjs +3 -11
- package/bin/update-copy.js +200 -0
- package/bin/update-lease.js +0 -0
- package/bin/update-notice.js +136 -289
- package/bin/verify-agent-behavior.cjs +122 -0
- package/bin/verify-agent-components.cjs +104 -0
- package/bin/verify-bundled-agent-sources.cjs +57 -0
- package/blun.mjs +143076 -135288
- package/bundled-agent-sources.json +701 -0
- package/package.json +12 -15
- package/standard-skills/translate-native/README.md +1293 -0
- package/standard-skills/translate-native/SKILL.md +172 -22
- package/standard-skills/translate-native/VERSION +1 -1
- package/standard-skills/translate-native/agents/openai.yaml +18 -0
- package/standard-skills/translate-native/assets/icon.svg +8 -0
- package/standard-skills/translate-native/docs/BLUN_CODE_INTEGRATION.md +76 -0
- package/standard-skills/translate-native/docs/PREMORTEM.md +489 -0
- package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION.md +2035 -0
- package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_API.md +1302 -0
- package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_EVIDENCE_HTTP.md +136 -0
- package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_HEALTH_HTTP.md +130 -0
- package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_HTTP_PROVIDER.md +175 -0
- package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_RECEIPT_VERIFIER_HTTP.md +86 -0
- package/standard-skills/translate-native/integrations/AGENT_RULES.md +32 -0
- package/standard-skills/translate-native/integrations/adapters/blun-code-language-guard.js +514 -0
- package/standard-skills/translate-native/integrations/adapters/node-language-guard.js +230 -0
- package/standard-skills/translate-native/integrations/audit_log.py +327 -0
- package/standard-skills/translate-native/integrations/claude_language_hook.js +1536 -0
- package/standard-skills/translate-native/integrations/commercial_localization_profile.py +42 -0
- package/standard-skills/translate-native/integrations/delivery-policy.example.json +28 -0
- package/standard-skills/translate-native/integrations/enforced_delivery.py +543 -0
- package/standard-skills/translate-native/integrations/guard_service.py +435 -0
- package/standard-skills/translate-native/integrations/language_gateway.py +67 -0
- package/standard-skills/translate-native/integrations/mcp_auth_headers.py +198 -0
- package/standard-skills/translate-native/integrations/mcp_http_gateway.py +429 -0
- package/standard-skills/translate-native/integrations/non_language_html_entities.js +1485 -0
- package/standard-skills/translate-native/integrations/pre_output_guard.py +65 -0
- package/standard-skills/translate-native/integrations/task_router.py +101 -0
- package/standard-skills/translate-native/integrations/website_localization.py +401 -0
- package/standard-skills/translate-native/integrations/website_localization_api.py +581 -0
- package/standard-skills/translate-native/integrations/website_localization_benchmark.py +1885 -0
- package/standard-skills/translate-native/integrations/website_localization_benchmark_campaign.py +1772 -0
- package/standard-skills/translate-native/integrations/website_localization_benchmark_candidate.py +506 -0
- package/standard-skills/translate-native/integrations/website_localization_benchmark_http.py +400 -0
- package/standard-skills/translate-native/integrations/website_localization_benchmark_review_store.py +781 -0
- package/standard-skills/translate-native/integrations/website_localization_benchmark_reviewer_http.py +500 -0
- package/standard-skills/translate-native/integrations/website_localization_benchmark_runtime.py +1107 -0
- package/standard-skills/translate-native/integrations/website_localization_benchmark_suite.py +463 -0
- package/standard-skills/translate-native/integrations/website_localization_cms.py +2835 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_client.py +875 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_dispatch.py +805 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_http.py +588 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_lifecycle_monitor.py +991 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_receiver.py +1441 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_receiver_runtime.py +414 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_receiver_store.py +1073 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_removal_dispatch.py +865 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_client.py +583 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery.py +964 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_runtime.py +665 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_http.py +1153 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_runtime.py +675 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_source_service.py +1125 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification.py +674 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_http.py +444 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver.py +1469 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver_runtime.py +1142 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_processing_monitor.py +634 -0
- package/standard-skills/translate-native/integrations/website_localization_cms_terminal_receiver_client.py +804 -0
- package/standard-skills/translate-native/integrations/website_localization_deepl_baseline.py +922 -0
- package/standard-skills/translate-native/integrations/website_localization_evidence_http.py +482 -0
- package/standard-skills/translate-native/integrations/website_localization_health.py +1541 -0
- package/standard-skills/translate-native/integrations/website_localization_health_http.py +372 -0
- package/standard-skills/translate-native/integrations/website_localization_http_provider.py +297 -0
- package/standard-skills/translate-native/integrations/website_localization_native_reference_http.py +479 -0
- package/standard-skills/translate-native/integrations/website_localization_native_reference_intake.py +363 -0
- package/standard-skills/translate-native/integrations/website_localization_native_reference_queue.py +1449 -0
- package/standard-skills/translate-native/integrations/website_localization_native_reference_store.py +420 -0
- package/standard-skills/translate-native/integrations/website_localization_quality_profiles.py +235 -0
- package/standard-skills/translate-native/integrations/website_localization_queue.py +671 -0
- package/standard-skills/translate-native/integrations/website_localization_receipt_verifier_http.py +516 -0
- package/standard-skills/translate-native/integrations/website_localization_release.py +928 -0
- package/standard-skills/translate-native/integrations/website_localization_release_coordinator.py +1008 -0
- package/standard-skills/translate-native/integrations/website_localization_runner.py +276 -0
- package/standard-skills/translate-native/integrations/website_localization_runtime.py +862 -0
- package/standard-skills/translate-native/integrations/website_localization_service.py +350 -0
- package/standard-skills/translate-native/integrations/website_localization_supervisor.py +511 -0
- package/standard-skills/translate-native/integrations/website_localization_worker.py +663 -0
- package/standard-skills/translate-native/provenance.json +3 -4
- package/standard-skills/translate-native/references/commercial-localization.md +177 -0
- package/standard-skills/translate-native/scripts/blun_language_guard.py +7 -1
- package/standard-skills/translate-native/scripts/check_commercial_review.py +80 -0
- package/standard-skills/translate-native/scripts/commercial_localization_profile.py +333 -0
- package/standard-tools/language-guard/LICENSE +21 -0
- package/standard-tools/language-guard/VERSION +1 -0
- package/standard-tools/language-guard/blun_language_guard.py +7 -1
- package/standard-tools/language-guard/check_commercial_review.py +80 -0
- package/standard-tools/language-guard/commercial_localization_profile.py +333 -0
- package/standard-tools/language-guard/language_gateway.py +62 -0
- package/standard-tools/language-guard/pre_output_guard.py +64 -0
- package/standard-tools/language-guard/provenance.json +4 -11
- package/standard-tools/manifest.json +34 -11
- package/telegram-plugin/commands/access.md +2 -10
- package/telegram-plugin/dist/bridge.mjs +64041 -687
- package/telegram-plugin/dist/mcp-server.mjs +72810 -9027
- package/telegram-plugin/dist/noise.mjs +28 -63511
- package/agent-spine-plugin/CONTRIBUTING.md +0 -52
- package/agent-spine-plugin/SECURITY.md +0 -47
- package/agent-spine-plugin/docs/assignment-continuation.md +0 -48
- package/agent-spine-plugin/docs/releasing.md +0 -85
- package/agent-spine-plugin/docs/structured-completion.md +0 -67
- package/bin/abort-listener-policy.cjs +0 -43
- package/bin/active-steer-priority-policy.cjs +0 -24
- package/bin/agent-api-http-adapter.mjs +0 -446
- package/bin/agent-api-private-http-server.mjs +0 -288
- package/bin/agent-api-runtime.mjs +0 -252
- package/bin/agent-api-service-environment.mjs +0 -236
- package/bin/agent-api-service-host.mjs +0 -209
- package/bin/agent-api-service-process.mjs +0 -171
- package/bin/agent-api-session-registry.mjs +0 -428
- package/bin/agent-api-tool-broker.cjs +0 -248
- package/bin/agent-api-turn-controller.mjs +0 -461
- package/bin/agent-api-usage-journal.cjs +0 -259
- package/bin/agent-resume-snapshot.cjs +0 -241
- package/bin/agentspine-king-goal-inbox.mjs +0 -111
- package/bin/agentspine-king-goal-intake.mjs +0 -106
- package/bin/approval-rejection-stop.cjs +0 -15
- package/bin/assistant-message-offload-policy.cjs +0 -284
- package/bin/baseline-skill-performance-policy.cjs +0 -39
- package/bin/bash-search-scope-policy.cjs +0 -49
- package/bin/codebase-search-runtime.cjs +0 -23
- package/bin/cognitive-action-checkpoint.cjs +0 -1104
- package/bin/cognitive-attention-delivery.cjs +0 -76
- package/bin/cognitive-attention-policy.cjs +0 -143
- package/bin/cognitive-attention-runtime.cjs +0 -91
- package/bin/cognitive-context-projection.cjs +0 -73
- package/bin/cognitive-cross-portal-acceptance.cjs +0 -443
- package/bin/cognitive-effective-view.cjs +0 -77
- package/bin/cognitive-focus-projection.cjs +0 -206
- package/bin/cognitive-focus-scope.cjs +0 -37
- package/bin/cognitive-goal-autostart-policy.cjs +0 -72
- package/bin/cognitive-goal-time-trigger-controller.cjs +0 -146
- package/bin/cognitive-memory-adapter.cjs +0 -282
- package/bin/cognitive-memory-command.cjs +0 -293
- package/bin/cognitive-memory-provider.cjs +0 -92
- package/bin/cognitive-salience-policy.cjs +0 -159
- package/bin/cognitive-state-store.cjs +0 -508
- package/bin/cognitive-turn-lifecycle.cjs +0 -624
- package/bin/cognitive-work-focus.cjs +0 -180
- package/bin/compaction-history-archive.cjs +0 -166
- package/bin/compaction-history-startup.cjs +0 -50
- package/bin/compaction-model-policy.cjs +0 -31
- package/bin/compaction-stage-policy.cjs +0 -21
- package/bin/compaction-transaction-policy.cjs +0 -122
- package/bin/config-write-dedup-policy.cjs +0 -27
- package/bin/context-budget-ledger.cjs +0 -31
- package/bin/context-doctor-policy.cjs +0 -70
- package/bin/context-insight-policy.cjs +0 -36
- package/bin/context-performance-policy.cjs +0 -19
- package/bin/context-pressure-policy.cjs +0 -20
- package/bin/cron-run-output.cjs +0 -45
- package/bin/cron-run-store.cjs +0 -145
- package/bin/curiosity-scout-policy.cjs +0 -49
- package/bin/default-model-output-budget-policy.cjs +0 -28
- package/bin/durable-task-resume-policy.cjs +0 -130
- package/bin/durable-task-resume-runtime.cjs +0 -117
- package/bin/durable-task-resume-store.cjs +0 -88
- package/bin/editable-tool-approval-policy.cjs +0 -540
- package/bin/editable-tool-approval-runtime.cjs +0 -99
- package/bin/effective-system-prompt-cache-policy.cjs +0 -33
- package/bin/error-memory-performance-policy.cjs +0 -113
- package/bin/file-observation-policy.cjs +0 -133
- package/bin/foreground-output-capture-policy.cjs +0 -41
- package/bin/generated-source-health.cjs +0 -142
- package/bin/glob-pattern-policy.cjs +0 -13
- package/bin/goal-completion-evidence-policy.cjs +0 -120
- package/bin/grep-output-limit-policy.cjs +0 -39
- package/bin/historical-media-projection-policy.cjs +0 -48
- package/bin/history-offload-pressure-policy.cjs +0 -33
- package/bin/html-to-research-markdown.cjs +0 -147
- package/bin/identity-context-policy.cjs +0 -764
- package/bin/identity-journal-policy.cjs +0 -107
- package/bin/input-draft-persistence.cjs +0 -77
- package/bin/king-tui-function-contract.json +0 -33
- package/bin/launcher-restart-policy.cjs +0 -150
- package/bin/live-response-repetition-guard.cjs +0 -196
- package/bin/llm-config-log-dedup-policy.cjs +0 -76
- package/bin/loop-event-record-policy.cjs +0 -174
- package/bin/managed-context-startup-policy.cjs +0 -27
- package/bin/media-activity-layout-policy.cjs +0 -34
- package/bin/media-auto-retrieval-policy.cjs +0 -90
- package/bin/media-result-policy.cjs +0 -59
- package/bin/micro-compaction-policy.cjs +0 -145
- package/bin/mistake-relevance-policy.cjs +0 -319
- package/bin/model-retry-progress-policy.cjs +0 -46
- package/bin/native-large-file-io.cjs +0 -42
- package/bin/native-runtime-cache.cjs +0 -76
- package/bin/natural-presence-policy.cjs +0 -28
- package/bin/noninteractive-shell-env-policy.cjs +0 -19
- package/bin/observer-hooks.cjs +0 -14
- package/bin/outbound-claim-provenance.cjs +0 -150
- package/bin/oversized-context-offload-policy.cjs +0 -86
- package/bin/pending-media-policy.cjs +0 -182
- package/bin/pending-token-estimate-policy.cjs +0 -41
- package/bin/personal-memory-consent-policy.cjs +0 -72
- package/bin/personal-memory-performance-policy.cjs +0 -12
- package/bin/personality-choice-policy.cjs +0 -101
- package/bin/personality-memory-adapter.cjs +0 -379
- package/bin/personality-mode.cjs +0 -46
- package/bin/personality-setup-policy.cjs +0 -197
- package/bin/proactive-compaction-policy.cjs +0 -25
- package/bin/profile-identity-resolution.cjs +0 -136
- package/bin/profile-runtime.cjs +0 -318
- package/bin/profile-tool-exclusion-policy.cjs +0 -37
- package/bin/programmatic-context-isolation.cjs +0 -25
- package/bin/programmatic-tool-runtime.mjs +0 -627
- package/bin/provider-idle-timeout-policy.cjs +0 -14
- package/bin/provider-model-refresh-deadline.cjs +0 -53
- package/bin/provider-model-refresh-policy.cjs +0 -107
- package/bin/rate-limit-recovery-policy.cjs +0 -47
- package/bin/read-batch-policy.cjs +0 -32
- package/bin/read-continuation-policy.cjs +0 -59
- package/bin/recurring-cron-history-policy.cjs +0 -124
- package/bin/relationship-continuity-policy.cjs +0 -143
- package/bin/relationship-curiosity-policy.cjs +0 -107
- package/bin/relationship-learning-policy.cjs +0 -168
- package/bin/release-artifact-freeze-policy.cjs +0 -30
- package/bin/reload-plugin-bootstrap.cjs +0 -18
- package/bin/reload-queue-policy.cjs +0 -38
- package/bin/repeated-assistant-response-policy.cjs +0 -232
- package/bin/repeated-injection-projection.cjs +0 -107
- package/bin/repeated-user-message-projection.cjs +0 -8
- package/bin/research-page-result.cjs +0 -74
- package/bin/retry-checkpoint-policy.cjs +0 -13
- package/bin/runtime-exit-ledger.cjs +0 -144
- package/bin/scoped-cron-run-policy.cjs +0 -358
- package/bin/session-checkpoint-policy.cjs +0 -25
- package/bin/session-compaction-policy.cjs +0 -84
- package/bin/session-replay-policy.cjs +0 -20
- package/bin/session-replay-window-policy.cjs +0 -40
- package/bin/session-resume-checkpoint.cjs +0 -254
- package/bin/session-scrollback-archive.cjs +0 -229
- package/bin/skill-activation-performance-policy.cjs +0 -69
- package/bin/skill-listing-performance-policy.cjs +0 -92
- package/bin/soul-organization-policy.cjs +0 -78
- package/bin/soul-preservation-policy.cjs +0 -20
- package/bin/startup-preferences.cjs +0 -131
- package/bin/streaming-flush-performance-policy.cjs +0 -28
- package/bin/structured-agent-swarm-output.cjs +0 -325
- package/bin/structured-subagent-output.cjs +0 -252
- package/bin/subagent-context-fork-policy.cjs +0 -155
- package/bin/subagent-max-tokens-handoff-policy.cjs +0 -69
- package/bin/subagent-parent-responsiveness.cjs +0 -19
- package/bin/subagent-skill-policy.cjs +0 -206
- package/bin/subagent-timeout-policy.cjs +0 -182
- package/bin/subagent-tool-policy.cjs +0 -60
- package/bin/subagent-usage-rollup-policy.cjs +0 -29
- package/bin/system-prompt-context-policy.cjs +0 -124
- package/bin/system-prompt-token-cache-policy.cjs +0 -60
- package/bin/telegram-addressed-focus.cjs +0 -55
- package/bin/telegram-addressed-priority.cjs +0 -12
- package/bin/telegram-approval-relay.cjs +0 -290
- package/bin/telegram-bot-priority.cjs +0 -17
- package/bin/telegram-console-status-policy.cjs +0 -174
- package/bin/telegram-context-projection-policy.cjs +0 -141
- package/bin/telegram-delivery-lifecycle.cjs +0 -125
- package/bin/telegram-direct-focus-policy.cjs +0 -273
- package/bin/telegram-mcp-compatibility.cjs +0 -49
- package/bin/telegram-media-delivery-policy.cjs +0 -42
- package/bin/telegram-private-conversation-policy.cjs +0 -185
- package/bin/telegram-queue-handoff-policy.cjs +0 -73
- package/bin/telegram-remote-status-policy.cjs +0 -120
- package/bin/telegram-session-queue-runtime.mjs +0 -306
- package/bin/telegram-text-chunk-policy.cjs +0 -63
- package/bin/telegram-truncated-reply-policy.cjs +0 -37
- package/bin/telegram-urgent-policy.cjs +0 -45
- package/bin/telemetry-spool-policy.cjs +0 -57
- package/bin/thinking-activity-status-policy.cjs +0 -132
- package/bin/thinking-only-guard.cjs +0 -80
- package/bin/todo-list-turn-policy.cjs +0 -131
- package/bin/tool-call-loop-policy.cjs +0 -51
- package/bin/tool-file-persistence.cjs +0 -141
- package/bin/tool-result-offload-policy.cjs +0 -359
- package/bin/tool-result-offload-telemetry.cjs +0 -12
- package/bin/tool-schema-token-cache-policy.cjs +0 -41
- package/bin/tool-stream-preview-policy.cjs +0 -9
- package/bin/tui-functional-contract.cjs +0 -55
- package/bin/turn-tool-performance-policy.cjs +0 -486
- package/bin/usage-cache-efficiency-policy.cjs +0 -26
- package/bin/user-home-path-policy.cjs +0 -13
- package/bin/user-message-offload-policy.cjs +0 -103
- package/bin/user-prompt-hook-origin-policy.cjs +0 -34
- package/bin/user-tool-record-policy.cjs +0 -7
- package/bin/validated-learning-insight-policy.cjs +0 -58
- package/bin/validated-learning-outcome-trace.cjs +0 -107
- package/bin/validated-learning-performance-policy.cjs +0 -53
- package/bin/validated-learning-signal.cjs +0 -463
- package/bin/windows-bash-dialect-policy.cjs +0 -25
- package/bin/windows-node-crash-dump.cjs +0 -110
- package/bin/write-continuation-policy.cjs +0 -69
- package/codebase-index/README.md +0 -82
- package/codebase-index/codebase_index.py +0 -470
- package/standard-skills/agent-browser/SKILL.md +0 -19
- package/standard-skills/agent-browser/references/runtime.md +0 -8
- package/standard-skills/blun-session-inspector/SKILL.md +0 -41
- package/standard-skills/blun-session-inspector/scripts/inspect-session.cjs +0 -437
- package/standard-skills/design-taste-frontend/SKILL.md +0 -1206
- package/standard-skills/full-output-enforcement/SKILL.md +0 -49
- package/standard-skills/high-end-visual-design/SKILL.md +0 -98
- package/standard-skills/image-to-code/SKILL.md +0 -1228
- package/standard-skills/industrial-brutalist-ui/SKILL.md +0 -92
- package/standard-skills/minimalist-ui/SKILL.md +0 -85
- package/standard-skills/motion-design-taste/SKILL.md +0 -74
- package/standard-skills/playwright-testing/SKILL.md +0 -19
- package/standard-skills/playwright-testing/references/runtime.md +0 -7
- package/standard-skills/premortem/SKILL.md +0 -148
- package/standard-skills/redesign-existing-projects/SKILL.md +0 -178
- package/standard-skills/research-evidence/SKILL.md +0 -39
- package/standard-skills/research-evidence/references/evidence-format.md +0 -104
- package/standard-skills/research-evidence/scripts/evidence-collection.cjs +0 -260
- package/standard-skills/research-evidence/scripts/score-report.cjs +0 -130
- package/standard-skills/screenshot-lesen/SKILL.md +0 -52
- package/standard-skills/stitch-design-taste/DESIGN.md +0 -121
- package/standard-skills/stitch-design-taste/SKILL.md +0 -184
- package/standard-skills/telegram-channel/SKILL.md +0 -18
- package/standard-skills/telegram-channel/references/runtime.md +0 -7
- package/standard-skills/venture-flywheel/SKILL.md +0 -32
- package/standard-skills/venture-flywheel/identity/project-identity.cjs +0 -146
- package/standard-skills/venture-flywheel/policy/capability-engine.cjs +0 -114
- package/standard-skills/venture-flywheel/policy/repository-trust.cjs +0 -229
- package/standard-skills/venture-flywheel/references/BEISPIELE-phase0.md +0 -146
- package/standard-skills/venture-flywheel/references/CAPABILITY-MAP.md +0 -34
- package/standard-skills/venture-flywheel/references/SPEC-phase0-identity-trust.md +0 -77
- package/standard-skills/venture-flywheel/references/SPEC-phase0-state-events.md +0 -93
- package/standard-skills/venture-flywheel/schemas/capability-decision.schema.json +0 -13
- package/standard-skills/venture-flywheel/schemas/execution-event.schema.json +0 -44
- package/standard-skills/venture-flywheel/schemas/project-identity.schema.json +0 -32
- package/standard-skills/venture-flywheel/schemas/repository-trust.schema.json +0 -57
- package/standard-skills/venture-flywheel/schemas/run-transition.schema.json +0 -59
- package/standard-skills/venture-flywheel/state/execution-event.cjs +0 -191
- package/standard-skills/venture-flywheel/state/task-state-machine.cjs +0 -190
- package/standard-skills/web-lesen/SKILL.md +0 -73
- package/standard-skills/web-lesen/scripts/crawl_public.py +0 -379
- package/standard-skills/windows-mcp/SKILL.md +0 -19
- package/standard-skills/windows-mcp/references/runtime.md +0 -9
- package/telegram-plugin/DELIVERY.md +0 -36
- package/telegram-plugin/bin/telegram-approval-relay.cjs +0 -290
- package/telegram-plugin/bin/telegram-console-status-policy.cjs +0 -175
- package/telegram-plugin/bin/telegram-delivery-lifecycle.cjs +0 -125
- package/telegram-plugin/bin/telegram-direct-reply-policy.cjs +0 -48
- package/telegram-plugin/bin/telegram-launcher-status-queue.cjs +0 -122
- package/telegram-plugin/bin/telegram-private-conversation-policy.cjs +0 -186
- package/telegram-plugin/bin/telegram-remote-status-policy.cjs +0 -121
- package/telegram-plugin/bin/telegram-reply-parts.cjs +0 -149
- package/telegram-plugin/bin/telegram-text-chunk-policy.cjs +0 -63
- package/telegram-plugin/bin/telegram-typing-keepalive.cjs +0 -89
- package/telegram-plugin/compat/mcp-server-fa511cd1.mjs +0 -73825
- /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())
|