blun-king-cli 9.1.587 → 9.1.589
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 +155 -274
- 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,372 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Authenticated, content-free HTTP reader for localization service health.
|
|
3
|
+
|
|
4
|
+
The host supplies authentication and the already-composed read-only health
|
|
5
|
+
callback. This adapter performs no repair, retry, signing, model, or CMS work.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import hashlib
|
|
11
|
+
import json
|
|
12
|
+
import math
|
|
13
|
+
import re
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
from typing import Any, Callable, Mapping
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
HEALTH_PATH = "/v2/localization/health"
|
|
19
|
+
AUTH_REQUEST_SCHEMA = "blun.localization-health-http-auth-request.v1"
|
|
20
|
+
PRINCIPAL_SCHEMA = "blun.localization-health-reader-principal.v1"
|
|
21
|
+
RESPONSE_SCHEMA = "blun.website-localization-health-http.v1"
|
|
22
|
+
ERROR_SCHEMA = "blun.website-localization-health-http-error.v1"
|
|
23
|
+
HEALTH_SCHEMA = "blun.website-localization-health.v1"
|
|
24
|
+
TOKEN = re.compile(r"^[A-Za-z0-9_.:-]{1,256}$")
|
|
25
|
+
ERROR_CODE = re.compile(r"^[a-z][a-z0-9_.-]{0,127}$")
|
|
26
|
+
HEADER_NAME = re.compile(r"^[!#$%&'*+.^_`|~0-9A-Za-z-]{1,128}$")
|
|
27
|
+
MAX_HEADERS = 64
|
|
28
|
+
MAX_HEADER_VALUE = 4096
|
|
29
|
+
MAX_RESPONSE_BYTES = 4_000_000
|
|
30
|
+
HEALTH_STATUSES = {"healthy", "degraded", "blocked"}
|
|
31
|
+
PROVIDER_STATUSES = {"healthy", "blocked"}
|
|
32
|
+
WEBSITE_STATUSES = {
|
|
33
|
+
"processing", "localization_failed", "awaiting_approval", "ready",
|
|
34
|
+
"publishing", "publication_failed", "published", "cancelled",
|
|
35
|
+
"deleting", "deletion_failed", "deleted",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class HealthHTTPFailed(RuntimeError):
|
|
40
|
+
"""Stable content-free failure returned by the HTTP boundary."""
|
|
41
|
+
|
|
42
|
+
def __init__(self, code: str, *, status: int, retryable: bool = False):
|
|
43
|
+
if not isinstance(code, str) or ERROR_CODE.fullmatch(code) is None:
|
|
44
|
+
raise ValueError("health HTTP error code is invalid")
|
|
45
|
+
if status not in {400, 401, 403, 404, 503}:
|
|
46
|
+
raise ValueError("health HTTP status is invalid")
|
|
47
|
+
if not isinstance(retryable, bool):
|
|
48
|
+
raise ValueError("health HTTP retryability is invalid")
|
|
49
|
+
super().__init__(code)
|
|
50
|
+
self.code = code
|
|
51
|
+
self.status = status
|
|
52
|
+
self.retryable = retryable
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass(frozen=True)
|
|
56
|
+
class HealthReaderPrincipal:
|
|
57
|
+
reader_id: str
|
|
58
|
+
credential_id: str
|
|
59
|
+
credential_version: str
|
|
60
|
+
scope: str
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _canonical_json(value: Any) -> bytes:
|
|
64
|
+
try:
|
|
65
|
+
encoded = json.dumps(
|
|
66
|
+
value,
|
|
67
|
+
ensure_ascii=False,
|
|
68
|
+
allow_nan=False,
|
|
69
|
+
sort_keys=True,
|
|
70
|
+
separators=(",", ":"),
|
|
71
|
+
).encode("utf-8")
|
|
72
|
+
except (TypeError, ValueError, RecursionError):
|
|
73
|
+
raise HealthHTTPFailed(
|
|
74
|
+
"health.http.response_invalid", status=503, retryable=True,
|
|
75
|
+
) from None
|
|
76
|
+
if not encoded or len(encoded) > MAX_RESPONSE_BYTES:
|
|
77
|
+
raise HealthHTTPFailed(
|
|
78
|
+
"health.http.response_invalid", status=503, retryable=True,
|
|
79
|
+
)
|
|
80
|
+
return encoded
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _token(value: Any) -> str:
|
|
84
|
+
if not isinstance(value, str) or TOKEN.fullmatch(value) is None:
|
|
85
|
+
raise ValueError
|
|
86
|
+
return value
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _reason(value: Any) -> str:
|
|
90
|
+
if not isinstance(value, str) or ERROR_CODE.fullmatch(value) is None:
|
|
91
|
+
raise ValueError
|
|
92
|
+
return value
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _timestamp(value: Any) -> float:
|
|
96
|
+
if isinstance(value, bool) or not isinstance(value, (int, float)):
|
|
97
|
+
raise ValueError
|
|
98
|
+
result = float(value)
|
|
99
|
+
if not math.isfinite(result) or result < 0:
|
|
100
|
+
raise ValueError
|
|
101
|
+
return result
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _count(value: Any) -> int:
|
|
105
|
+
if isinstance(value, bool) or not isinstance(value, int) or value < 0:
|
|
106
|
+
raise ValueError
|
|
107
|
+
return value
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _exact_mapping(value: Any, fields: set[str]) -> dict[str, Any]:
|
|
111
|
+
if not isinstance(value, dict) or set(value) != fields:
|
|
112
|
+
raise ValueError
|
|
113
|
+
return value
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _counts(value: Any) -> dict[str, int]:
|
|
117
|
+
if not isinstance(value, dict) or len(value) > 64:
|
|
118
|
+
raise ValueError
|
|
119
|
+
return {_token(key): _count(item) for key, item in value.items()}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _reasons(value: Any) -> list[str]:
|
|
123
|
+
if not isinstance(value, list) or len(value) > 128:
|
|
124
|
+
raise ValueError
|
|
125
|
+
result = [_reason(item) for item in value]
|
|
126
|
+
if result != sorted(set(result)):
|
|
127
|
+
raise ValueError
|
|
128
|
+
return result
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _principal(value: Any) -> HealthReaderPrincipal:
|
|
132
|
+
try:
|
|
133
|
+
value = _exact_mapping(value, {
|
|
134
|
+
"schema", "reader_id", "credential_id", "credential_version",
|
|
135
|
+
"scope",
|
|
136
|
+
})
|
|
137
|
+
if value["schema"] != PRINCIPAL_SCHEMA or value["scope"] != "service-health":
|
|
138
|
+
raise ValueError
|
|
139
|
+
return HealthReaderPrincipal(
|
|
140
|
+
_token(value["reader_id"]),
|
|
141
|
+
_token(value["credential_id"]),
|
|
142
|
+
_token(value["credential_version"]),
|
|
143
|
+
value["scope"],
|
|
144
|
+
)
|
|
145
|
+
except (KeyError, ValueError, TypeError):
|
|
146
|
+
raise HealthHTTPFailed(
|
|
147
|
+
"health.http.authentication_failed", status=401,
|
|
148
|
+
) from None
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _report_payload(report: Any, *, expected_checked_at: float) -> dict[str, Any]:
|
|
152
|
+
try:
|
|
153
|
+
payload = report.as_payload()
|
|
154
|
+
payload = _exact_mapping(payload, {
|
|
155
|
+
"schema", "checked_at", "status", "components", "providers",
|
|
156
|
+
"website_versions",
|
|
157
|
+
})
|
|
158
|
+
if payload["schema"] != HEALTH_SCHEMA or payload["status"] not in HEALTH_STATUSES:
|
|
159
|
+
raise ValueError
|
|
160
|
+
if _timestamp(payload["checked_at"]) != expected_checked_at:
|
|
161
|
+
raise ValueError
|
|
162
|
+
|
|
163
|
+
components = payload["components"]
|
|
164
|
+
if not isinstance(components, list) or not components or len(components) > 16:
|
|
165
|
+
raise ValueError
|
|
166
|
+
component_names = []
|
|
167
|
+
for component in components:
|
|
168
|
+
component = _exact_mapping(
|
|
169
|
+
component, {"component", "status", "reasons", "counts"},
|
|
170
|
+
)
|
|
171
|
+
component_names.append(_token(component["component"]))
|
|
172
|
+
if component["status"] not in HEALTH_STATUSES:
|
|
173
|
+
raise ValueError
|
|
174
|
+
_reasons(component["reasons"])
|
|
175
|
+
_counts(component["counts"])
|
|
176
|
+
if len(component_names) != len(set(component_names)):
|
|
177
|
+
raise ValueError
|
|
178
|
+
expected_status = (
|
|
179
|
+
"blocked" if any(
|
|
180
|
+
item["status"] == "blocked" for item in components
|
|
181
|
+
) else "degraded" if any(
|
|
182
|
+
item["status"] == "degraded" for item in components
|
|
183
|
+
) else "healthy"
|
|
184
|
+
)
|
|
185
|
+
if payload["status"] != expected_status:
|
|
186
|
+
raise ValueError
|
|
187
|
+
|
|
188
|
+
providers = payload["providers"]
|
|
189
|
+
if not isinstance(providers, list) or len(providers) > 1024:
|
|
190
|
+
raise ValueError
|
|
191
|
+
for provider in providers:
|
|
192
|
+
provider = _exact_mapping(provider, {
|
|
193
|
+
"provider_id", "model_id", "model_version", "status", "reason",
|
|
194
|
+
})
|
|
195
|
+
_token(provider["provider_id"])
|
|
196
|
+
_token(provider["model_id"])
|
|
197
|
+
_token(provider["model_version"])
|
|
198
|
+
if provider["status"] not in PROVIDER_STATUSES:
|
|
199
|
+
raise ValueError
|
|
200
|
+
if provider["reason"] is not None:
|
|
201
|
+
_reason(provider["reason"])
|
|
202
|
+
|
|
203
|
+
versions = payload["website_versions"]
|
|
204
|
+
if not isinstance(versions, list) or len(versions) > 100_000:
|
|
205
|
+
raise ValueError
|
|
206
|
+
version_ids = []
|
|
207
|
+
for version in versions:
|
|
208
|
+
version = _exact_mapping(version, {
|
|
209
|
+
"event_id", "site_id", "website_version", "plan_id", "status",
|
|
210
|
+
"required_locales", "approved_locales", "queue_counts",
|
|
211
|
+
"blocked_locales",
|
|
212
|
+
})
|
|
213
|
+
version_ids.append(_token(version["event_id"]))
|
|
214
|
+
for name in ("site_id", "website_version", "plan_id"):
|
|
215
|
+
_token(version[name])
|
|
216
|
+
if version["status"] not in WEBSITE_STATUSES:
|
|
217
|
+
raise ValueError
|
|
218
|
+
required = _count(version["required_locales"])
|
|
219
|
+
approved = _count(version["approved_locales"])
|
|
220
|
+
if approved > required:
|
|
221
|
+
raise ValueError
|
|
222
|
+
_counts(version["queue_counts"])
|
|
223
|
+
blocked = version["blocked_locales"]
|
|
224
|
+
if not isinstance(blocked, list) or len(blocked) > required:
|
|
225
|
+
raise ValueError
|
|
226
|
+
observed = []
|
|
227
|
+
for item in blocked:
|
|
228
|
+
if not isinstance(item, list) or len(item) != 2:
|
|
229
|
+
raise ValueError
|
|
230
|
+
observed.append((_token(item[0]), _reason(item[1])))
|
|
231
|
+
if observed != sorted(set(observed)):
|
|
232
|
+
raise ValueError
|
|
233
|
+
if len(version_ids) != len(set(version_ids)):
|
|
234
|
+
raise ValueError
|
|
235
|
+
|
|
236
|
+
_canonical_json(payload)
|
|
237
|
+
return payload
|
|
238
|
+
except HealthHTTPFailed:
|
|
239
|
+
raise
|
|
240
|
+
except Exception:
|
|
241
|
+
raise HealthHTTPFailed(
|
|
242
|
+
"health.http.response_invalid", status=503, retryable=True,
|
|
243
|
+
) from None
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
class WebsiteLocalizationHealthHTTPApplication:
|
|
247
|
+
"""Strict authenticated WSGI reader for one composed health monitor."""
|
|
248
|
+
|
|
249
|
+
def __init__(
|
|
250
|
+
self,
|
|
251
|
+
health_provider: Callable[..., Any],
|
|
252
|
+
authenticator: Callable[[dict[str, Any]], Any],
|
|
253
|
+
*,
|
|
254
|
+
clock: Callable[[], float],
|
|
255
|
+
):
|
|
256
|
+
if not callable(health_provider):
|
|
257
|
+
raise ValueError("health provider must be callable")
|
|
258
|
+
if not callable(authenticator):
|
|
259
|
+
raise ValueError("health authenticator must be callable")
|
|
260
|
+
if not callable(clock):
|
|
261
|
+
raise ValueError("health clock must be callable")
|
|
262
|
+
self.health_provider = health_provider
|
|
263
|
+
self.authenticator = authenticator
|
|
264
|
+
self.clock = clock
|
|
265
|
+
|
|
266
|
+
@staticmethod
|
|
267
|
+
def _headers(environ: Mapping[str, Any]) -> tuple[tuple[str, str], ...]:
|
|
268
|
+
headers = []
|
|
269
|
+
for key, value in environ.items():
|
|
270
|
+
if key.startswith("HTTP_"):
|
|
271
|
+
name = key[5:].replace("_", "-").lower()
|
|
272
|
+
elif key in {"CONTENT_TYPE", "CONTENT_LENGTH"}:
|
|
273
|
+
name = key.replace("_", "-").lower()
|
|
274
|
+
else:
|
|
275
|
+
continue
|
|
276
|
+
if (
|
|
277
|
+
HEADER_NAME.fullmatch(name) is None
|
|
278
|
+
or not isinstance(value, str)
|
|
279
|
+
or len(value) > MAX_HEADER_VALUE
|
|
280
|
+
or "\r" in value
|
|
281
|
+
or "\n" in value
|
|
282
|
+
):
|
|
283
|
+
raise HealthHTTPFailed("health.http.headers_invalid", status=400)
|
|
284
|
+
headers.append((name, value))
|
|
285
|
+
headers.sort()
|
|
286
|
+
if len(headers) > MAX_HEADERS or len({name for name, _ in headers}) != len(headers):
|
|
287
|
+
raise HealthHTTPFailed("health.http.headers_invalid", status=400)
|
|
288
|
+
return tuple(headers)
|
|
289
|
+
|
|
290
|
+
@staticmethod
|
|
291
|
+
def _empty_body(environ: Mapping[str, Any]) -> bytes:
|
|
292
|
+
if environ.get("HTTP_TRANSFER_ENCODING") not in {None, ""}:
|
|
293
|
+
raise HealthHTTPFailed("health.http.transfer_encoding", status=400)
|
|
294
|
+
if environ.get("CONTENT_LENGTH") not in {None, "", "0"}:
|
|
295
|
+
raise HealthHTTPFailed("health.http.body_not_allowed", status=400)
|
|
296
|
+
return b""
|
|
297
|
+
|
|
298
|
+
def _authenticate(self, headers: tuple[tuple[str, str], ...], body: bytes) -> None:
|
|
299
|
+
request = {
|
|
300
|
+
"schema": AUTH_REQUEST_SCHEMA,
|
|
301
|
+
"method": "GET",
|
|
302
|
+
"path": HEALTH_PATH,
|
|
303
|
+
"headers": [list(item) for item in headers],
|
|
304
|
+
"body_sha256": hashlib.sha256(body).hexdigest(),
|
|
305
|
+
}
|
|
306
|
+
try:
|
|
307
|
+
_principal(self.authenticator(request))
|
|
308
|
+
except HealthHTTPFailed:
|
|
309
|
+
raise
|
|
310
|
+
except Exception:
|
|
311
|
+
raise HealthHTTPFailed(
|
|
312
|
+
"health.http.authentication_unavailable",
|
|
313
|
+
status=503,
|
|
314
|
+
retryable=True,
|
|
315
|
+
) from None
|
|
316
|
+
|
|
317
|
+
@staticmethod
|
|
318
|
+
def _send(start_response, status: int, payload: Mapping[str, Any]):
|
|
319
|
+
body = _canonical_json(dict(payload))
|
|
320
|
+
phrases = {
|
|
321
|
+
200: "OK", 400: "Bad Request", 401: "Unauthorized",
|
|
322
|
+
403: "Forbidden", 404: "Not Found", 503: "Service Unavailable",
|
|
323
|
+
}
|
|
324
|
+
start_response(f"{status} {phrases[status]}", [
|
|
325
|
+
("Content-Type", "application/json; charset=utf-8"),
|
|
326
|
+
("Content-Length", str(len(body))),
|
|
327
|
+
("Cache-Control", "no-store"),
|
|
328
|
+
("X-Content-Type-Options", "nosniff"),
|
|
329
|
+
("Referrer-Policy", "no-referrer"),
|
|
330
|
+
])
|
|
331
|
+
return [body]
|
|
332
|
+
|
|
333
|
+
def __call__(self, environ: Mapping[str, Any], start_response: Callable[..., Any]):
|
|
334
|
+
try:
|
|
335
|
+
if not isinstance(environ, Mapping):
|
|
336
|
+
raise HealthHTTPFailed("health.http.environment_invalid", status=400)
|
|
337
|
+
if environ.get("wsgi.url_scheme") != "https":
|
|
338
|
+
raise HealthHTTPFailed("health.http.https_required", status=400)
|
|
339
|
+
if environ.get("QUERY_STRING") not in {None, ""}:
|
|
340
|
+
raise HealthHTTPFailed("health.http.query_invalid", status=400)
|
|
341
|
+
if environ.get("REQUEST_METHOD") != "GET" or environ.get("PATH_INFO") != HEALTH_PATH:
|
|
342
|
+
raise HealthHTTPFailed("health.http.route_not_found", status=404)
|
|
343
|
+
body = self._empty_body(environ)
|
|
344
|
+
self._authenticate(self._headers(environ), body)
|
|
345
|
+
try:
|
|
346
|
+
now = _timestamp(self.clock())
|
|
347
|
+
report = _report_payload(
|
|
348
|
+
self.health_provider(now=now), expected_checked_at=now,
|
|
349
|
+
)
|
|
350
|
+
except HealthHTTPFailed:
|
|
351
|
+
raise
|
|
352
|
+
except Exception:
|
|
353
|
+
raise HealthHTTPFailed(
|
|
354
|
+
"health.http.monitor_unavailable", status=503, retryable=True,
|
|
355
|
+
) from None
|
|
356
|
+
status = 503 if report["status"] == "blocked" else 200
|
|
357
|
+
return self._send(start_response, status, {
|
|
358
|
+
"schema": RESPONSE_SCHEMA,
|
|
359
|
+
"report": report,
|
|
360
|
+
})
|
|
361
|
+
except HealthHTTPFailed as error:
|
|
362
|
+
return self._send(start_response, error.status, {
|
|
363
|
+
"schema": ERROR_SCHEMA,
|
|
364
|
+
"error_code": error.code,
|
|
365
|
+
"retryable": error.retryable,
|
|
366
|
+
})
|
|
367
|
+
except Exception:
|
|
368
|
+
return self._send(start_response, 503, {
|
|
369
|
+
"schema": ERROR_SCHEMA,
|
|
370
|
+
"error_code": "health.http.internal",
|
|
371
|
+
"retryable": True,
|
|
372
|
+
})
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Provider-neutral, request-bound HTTP adapter for localization LLM calls."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import re
|
|
8
|
+
import socket
|
|
9
|
+
import urllib.error
|
|
10
|
+
import urllib.parse
|
|
11
|
+
import urllib.request
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from hashlib import sha256
|
|
14
|
+
from typing import Any, Callable, Mapping, Protocol
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
REQUEST_SCHEMA = "blun.localization-provider-request.v1"
|
|
18
|
+
RESPONSE_SCHEMA = "blun.localization-provider-response.v1"
|
|
19
|
+
MAX_BODY_BYTES = 4_000_000
|
|
20
|
+
MAX_ENDPOINT_LENGTH = 2048
|
|
21
|
+
MAX_HEADER_VALUE_LENGTH = 4096
|
|
22
|
+
HEADER_NAME = re.compile(r"^[!#$%&'*+.^_`|~0-9A-Za-z-]{1,128}$")
|
|
23
|
+
ERROR_CODE = re.compile(r"^[a-z][a-z0-9_.-]{0,127}$")
|
|
24
|
+
REQUEST_ID = re.compile(r"^blun-l10n-call-[a-f0-9]{64}$")
|
|
25
|
+
WORKER_REQUEST_SCHEMA = "blun.website-localization-worker.v4"
|
|
26
|
+
WORKER_REQUEST_FIELDS = {
|
|
27
|
+
"schema", "request_id", "phase", "provider_id", "model_id",
|
|
28
|
+
"model_version", "system_instruction", "input",
|
|
29
|
+
}
|
|
30
|
+
WORKER_PHASES = {"transcreation", "target_native", "source_fidelity"}
|
|
31
|
+
RESERVED_HEADERS = {
|
|
32
|
+
"accept", "connection", "content-length", "content-type", "host",
|
|
33
|
+
"idempotency-key", "transfer-encoding", "x-localization-request-id",
|
|
34
|
+
"x-localization-request-sha256",
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class HTTPProviderFailed(RuntimeError):
|
|
39
|
+
"""Content-free failure consumed by the generic localization worker."""
|
|
40
|
+
|
|
41
|
+
localization_provider_failure = True
|
|
42
|
+
|
|
43
|
+
def __init__(self, code: str, *, retryable: bool):
|
|
44
|
+
if not isinstance(code, str) or ERROR_CODE.fullmatch(code) is None:
|
|
45
|
+
raise ValueError("provider error code is invalid")
|
|
46
|
+
if not isinstance(retryable, bool):
|
|
47
|
+
raise ValueError("provider retryability must be boolean")
|
|
48
|
+
super().__init__(code)
|
|
49
|
+
self.code = code
|
|
50
|
+
self.retryable = retryable
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass(frozen=True)
|
|
54
|
+
class HTTPResult:
|
|
55
|
+
status: int
|
|
56
|
+
headers: tuple[tuple[str, str], ...]
|
|
57
|
+
body: bytes
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class HTTPTransport(Protocol):
|
|
61
|
+
def post(self, url: str, headers: Mapping[str, str], body: bytes, *, timeout: float) -> HTTPResult: ...
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class _NoRedirect(urllib.request.HTTPRedirectHandler):
|
|
65
|
+
def redirect_request(self, req, fp, code, msg, headers, newurl):
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class URLTransport:
|
|
70
|
+
"""One-attempt stdlib HTTPS transport with redirects disabled."""
|
|
71
|
+
|
|
72
|
+
def __init__(self):
|
|
73
|
+
self._opener = urllib.request.build_opener(_NoRedirect)
|
|
74
|
+
|
|
75
|
+
def post(self, url: str, headers: Mapping[str, str], body: bytes, *, timeout: float) -> HTTPResult:
|
|
76
|
+
request = urllib.request.Request(url, data=body, headers=dict(headers), method="POST")
|
|
77
|
+
try:
|
|
78
|
+
response = self._opener.open(request, timeout=timeout)
|
|
79
|
+
except urllib.error.HTTPError as error:
|
|
80
|
+
headers = tuple(error.headers.items()) if error.headers is not None else ()
|
|
81
|
+
return HTTPResult(int(error.code), headers, b"")
|
|
82
|
+
except (urllib.error.URLError, TimeoutError, socket.timeout, OSError):
|
|
83
|
+
raise HTTPProviderFailed("network", retryable=True) from None
|
|
84
|
+
try:
|
|
85
|
+
raw = response.read(MAX_BODY_BYTES + 1)
|
|
86
|
+
return HTTPResult(int(response.status), tuple(response.headers.items()), raw)
|
|
87
|
+
except (TimeoutError, socket.timeout, OSError):
|
|
88
|
+
raise HTTPProviderFailed("network", retryable=True) from None
|
|
89
|
+
finally:
|
|
90
|
+
response.close()
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _canonical_json(value: Any) -> bytes:
|
|
94
|
+
try:
|
|
95
|
+
text = json.dumps(value, ensure_ascii=False, allow_nan=False, sort_keys=True, separators=(",", ":"))
|
|
96
|
+
except (TypeError, ValueError):
|
|
97
|
+
raise HTTPProviderFailed("request_invalid", retryable=False) from None
|
|
98
|
+
raw = text.encode("utf-8")
|
|
99
|
+
if not raw or len(raw) > MAX_BODY_BYTES:
|
|
100
|
+
raise HTTPProviderFailed("request_too_large", retryable=False)
|
|
101
|
+
return raw
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _pairs(items):
|
|
105
|
+
result = {}
|
|
106
|
+
for key, value in items:
|
|
107
|
+
if key in result:
|
|
108
|
+
raise ValueError("duplicate key")
|
|
109
|
+
result[key] = value
|
|
110
|
+
return result
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _constant(value):
|
|
114
|
+
raise ValueError("nonfinite number")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _endpoint(value: Any, allow_loopback_http: bool) -> str:
|
|
118
|
+
if (
|
|
119
|
+
not isinstance(value, str) or value != value.strip() or len(value) > MAX_ENDPOINT_LENGTH
|
|
120
|
+
or any(ord(char) <= 32 or ord(char) == 127 for char in value)
|
|
121
|
+
):
|
|
122
|
+
raise ValueError("endpoint is invalid")
|
|
123
|
+
try:
|
|
124
|
+
parsed = urllib.parse.urlsplit(value)
|
|
125
|
+
hostname = parsed.hostname
|
|
126
|
+
port = parsed.port
|
|
127
|
+
except ValueError:
|
|
128
|
+
raise ValueError("endpoint is invalid") from None
|
|
129
|
+
if parsed.username is not None or parsed.password is not None or parsed.query or parsed.fragment:
|
|
130
|
+
raise ValueError("endpoint is invalid")
|
|
131
|
+
if not hostname or not parsed.path.startswith("/") or parsed.path.startswith("//"):
|
|
132
|
+
raise ValueError("endpoint is invalid")
|
|
133
|
+
if parsed.scheme == "https":
|
|
134
|
+
pass
|
|
135
|
+
elif parsed.scheme == "http" and allow_loopback_http and hostname.lower() in {"localhost", "127.0.0.1", "::1"}:
|
|
136
|
+
pass
|
|
137
|
+
else:
|
|
138
|
+
raise ValueError("endpoint must use HTTPS")
|
|
139
|
+
if port is not None and not 1 <= port <= 65535:
|
|
140
|
+
raise ValueError("endpoint is invalid")
|
|
141
|
+
return value
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _auth_headers(provider: Callable[[], Mapping[str, str]]) -> dict[str, str]:
|
|
145
|
+
try:
|
|
146
|
+
supplied = provider()
|
|
147
|
+
except Exception:
|
|
148
|
+
raise HTTPProviderFailed("authentication", retryable=False) from None
|
|
149
|
+
if not isinstance(supplied, Mapping):
|
|
150
|
+
raise HTTPProviderFailed("authentication", retryable=False)
|
|
151
|
+
result = {}
|
|
152
|
+
normalized_names = set()
|
|
153
|
+
for name, value in supplied.items():
|
|
154
|
+
normalized = name.lower() if isinstance(name, str) else ""
|
|
155
|
+
if (
|
|
156
|
+
not isinstance(name, str) or HEADER_NAME.fullmatch(name) is None
|
|
157
|
+
or normalized in RESERVED_HEADERS or normalized in normalized_names
|
|
158
|
+
):
|
|
159
|
+
raise HTTPProviderFailed("authentication", retryable=False)
|
|
160
|
+
if (
|
|
161
|
+
not isinstance(value, str) or not value or len(value) > MAX_HEADER_VALUE_LENGTH
|
|
162
|
+
or "\r" in value or "\n" in value or any(ord(char) < 32 for char in value)
|
|
163
|
+
or any(ord(char) == 127 for char in value)
|
|
164
|
+
):
|
|
165
|
+
raise HTTPProviderFailed("authentication", retryable=False)
|
|
166
|
+
normalized_names.add(normalized)
|
|
167
|
+
result[name] = value
|
|
168
|
+
if not result:
|
|
169
|
+
raise HTTPProviderFailed("authentication", retryable=False)
|
|
170
|
+
return result
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _headers(value: Any) -> dict[str, str]:
|
|
174
|
+
if not isinstance(value, tuple):
|
|
175
|
+
raise HTTPProviderFailed("transport_invalid", retryable=True)
|
|
176
|
+
result = {}
|
|
177
|
+
for item in value:
|
|
178
|
+
if not isinstance(item, tuple) or len(item) != 2 or not all(isinstance(part, str) for part in item):
|
|
179
|
+
raise HTTPProviderFailed("transport_invalid", retryable=True)
|
|
180
|
+
name, content = item[0].lower(), item[1].strip()
|
|
181
|
+
if name in result:
|
|
182
|
+
raise HTTPProviderFailed("response_headers", retryable=True)
|
|
183
|
+
result[name] = content
|
|
184
|
+
return result
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class HTTPProviderAdapter:
|
|
188
|
+
"""One HTTP attempt for one deterministic worker phase."""
|
|
189
|
+
|
|
190
|
+
def __init__(
|
|
191
|
+
self,
|
|
192
|
+
endpoint: str,
|
|
193
|
+
authentication_headers: Callable[[], Mapping[str, str]],
|
|
194
|
+
*,
|
|
195
|
+
transport: HTTPTransport | None = None,
|
|
196
|
+
timeout: float = 60.0,
|
|
197
|
+
allow_loopback_http: bool = False,
|
|
198
|
+
):
|
|
199
|
+
if not isinstance(allow_loopback_http, bool):
|
|
200
|
+
raise TypeError("allow_loopback_http must be boolean")
|
|
201
|
+
self.endpoint = _endpoint(endpoint, allow_loopback_http)
|
|
202
|
+
if not callable(authentication_headers):
|
|
203
|
+
raise TypeError("authentication_headers must be callable")
|
|
204
|
+
if isinstance(timeout, bool) or not isinstance(timeout, (int, float)) or not 0 < timeout <= 300:
|
|
205
|
+
raise ValueError("timeout is outside the supported range")
|
|
206
|
+
self.authentication_headers = authentication_headers
|
|
207
|
+
self.transport = URLTransport() if transport is None else transport
|
|
208
|
+
if not callable(getattr(self.transport, "post", None)):
|
|
209
|
+
raise TypeError("transport must provide post")
|
|
210
|
+
self.timeout = float(timeout)
|
|
211
|
+
|
|
212
|
+
def invoke(self, request: Any) -> Mapping[str, Any]:
|
|
213
|
+
try:
|
|
214
|
+
payload_method = getattr(request, "as_payload", None)
|
|
215
|
+
request_id = getattr(request, "request_id", None)
|
|
216
|
+
except Exception:
|
|
217
|
+
raise HTTPProviderFailed("request_invalid", retryable=False) from None
|
|
218
|
+
if not callable(payload_method) or not isinstance(request_id, str) or not request_id:
|
|
219
|
+
raise HTTPProviderFailed("request_invalid", retryable=False)
|
|
220
|
+
try:
|
|
221
|
+
payload = payload_method()
|
|
222
|
+
except Exception:
|
|
223
|
+
raise HTTPProviderFailed("request_invalid", retryable=False) from None
|
|
224
|
+
if (
|
|
225
|
+
REQUEST_ID.fullmatch(request_id) is None
|
|
226
|
+
or not isinstance(payload, dict)
|
|
227
|
+
or set(payload) != WORKER_REQUEST_FIELDS
|
|
228
|
+
or payload.get("schema") != WORKER_REQUEST_SCHEMA
|
|
229
|
+
or payload.get("request_id") != request_id
|
|
230
|
+
or payload.get("phase") not in WORKER_PHASES
|
|
231
|
+
or not isinstance(payload.get("input"), dict)
|
|
232
|
+
or any(not isinstance(payload.get(field), str) or not payload[field] for field in (
|
|
233
|
+
"provider_id", "model_id", "model_version", "system_instruction",
|
|
234
|
+
))
|
|
235
|
+
):
|
|
236
|
+
raise HTTPProviderFailed("request_invalid", retryable=False)
|
|
237
|
+
request_bytes = _canonical_json(payload)
|
|
238
|
+
request_hash = sha256(request_bytes).hexdigest()
|
|
239
|
+
envelope = {
|
|
240
|
+
"schema": REQUEST_SCHEMA,
|
|
241
|
+
"request_id": request_id,
|
|
242
|
+
"request_sha256": request_hash,
|
|
243
|
+
"request": payload,
|
|
244
|
+
}
|
|
245
|
+
body = _canonical_json(envelope)
|
|
246
|
+
headers = _auth_headers(self.authentication_headers)
|
|
247
|
+
headers.update({
|
|
248
|
+
"Accept": "application/json",
|
|
249
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
250
|
+
"Idempotency-Key": request_id,
|
|
251
|
+
"X-Localization-Request-Id": request_id,
|
|
252
|
+
"X-Localization-Request-Sha256": request_hash,
|
|
253
|
+
})
|
|
254
|
+
try:
|
|
255
|
+
result = self.transport.post(self.endpoint, headers, body, timeout=self.timeout)
|
|
256
|
+
except HTTPProviderFailed:
|
|
257
|
+
raise
|
|
258
|
+
except Exception:
|
|
259
|
+
raise HTTPProviderFailed("network", retryable=True) from None
|
|
260
|
+
if (
|
|
261
|
+
not isinstance(result, HTTPResult) or isinstance(result.status, bool)
|
|
262
|
+
or not isinstance(result.status, int) or not 100 <= result.status <= 599
|
|
263
|
+
):
|
|
264
|
+
raise HTTPProviderFailed("transport_invalid", retryable=True)
|
|
265
|
+
if result.status != 200:
|
|
266
|
+
if 300 <= result.status <= 399:
|
|
267
|
+
raise HTTPProviderFailed("redirect", retryable=False)
|
|
268
|
+
retryable = result.status in {408, 425, 429} or 500 <= result.status <= 599
|
|
269
|
+
raise HTTPProviderFailed("http_status", retryable=retryable)
|
|
270
|
+
response_headers = _headers(result.headers)
|
|
271
|
+
content_type = response_headers.get("content-type", "").lower().replace(" ", "")
|
|
272
|
+
if content_type not in {"application/json", "application/json;charset=utf-8"}:
|
|
273
|
+
raise HTTPProviderFailed("response_content_type", retryable=True)
|
|
274
|
+
if not isinstance(result.body, bytes) or not result.body or len(result.body) > MAX_BODY_BYTES:
|
|
275
|
+
raise HTTPProviderFailed("response_size", retryable=True)
|
|
276
|
+
declared = response_headers.get("content-length")
|
|
277
|
+
if declared is not None and (not declared.isascii() or not declared.isdecimal() or int(declared) != len(result.body)):
|
|
278
|
+
raise HTTPProviderFailed("response_size", retryable=True)
|
|
279
|
+
try:
|
|
280
|
+
text = result.body.decode("utf-8")
|
|
281
|
+
if text.startswith("\ufeff"):
|
|
282
|
+
raise ValueError("BOM rejected")
|
|
283
|
+
envelope = json.loads(text, object_pairs_hook=_pairs, parse_constant=_constant)
|
|
284
|
+
except (UnicodeDecodeError, json.JSONDecodeError, ValueError, RecursionError):
|
|
285
|
+
raise HTTPProviderFailed("response_json", retryable=True) from None
|
|
286
|
+
if not isinstance(envelope, dict) or set(envelope) != {
|
|
287
|
+
"schema", "request_id", "request_sha256", "response",
|
|
288
|
+
}:
|
|
289
|
+
raise HTTPProviderFailed("response_binding", retryable=False)
|
|
290
|
+
if (
|
|
291
|
+
envelope["schema"] != RESPONSE_SCHEMA
|
|
292
|
+
or envelope["request_id"] != request_id
|
|
293
|
+
or envelope["request_sha256"] != request_hash
|
|
294
|
+
or not isinstance(envelope["response"], dict)
|
|
295
|
+
):
|
|
296
|
+
raise HTTPProviderFailed("response_binding", retryable=False)
|
|
297
|
+
return envelope["response"]
|