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,511 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Durable single-instance supervisor for the localization service tick.
|
|
3
|
+
|
|
4
|
+
The host injects the already configured service tick, clock, and sleeper. The
|
|
5
|
+
supervisor persists only operational metadata and never stores customer text,
|
|
6
|
+
provider responses, signatures, credentials, or exception messages.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import math
|
|
12
|
+
import re
|
|
13
|
+
import secrets
|
|
14
|
+
import sqlite3
|
|
15
|
+
import time
|
|
16
|
+
from contextlib import contextmanager
|
|
17
|
+
from dataclasses import asdict, dataclass
|
|
18
|
+
from typing import Any, Callable, Mapping
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
SCHEMA = "blun.website-localization-supervisor.v1"
|
|
22
|
+
TICK_SCHEMA = "blun.website-localization-service-tick.v1"
|
|
23
|
+
ERROR_CODE = re.compile(r"^[a-z][a-z0-9_.-]{0,127}$")
|
|
24
|
+
TOKEN = re.compile(r"^[A-Za-z0-9_.:-]{1,256}$")
|
|
25
|
+
PHASES = {
|
|
26
|
+
"tombstone", "delivery", "ingress", "release", "translation", "benchmark",
|
|
27
|
+
"idle", "supervisor",
|
|
28
|
+
}
|
|
29
|
+
STATUSES = {
|
|
30
|
+
"idle", "succeeded", "retry_wait", "failed", "blocked", "approved",
|
|
31
|
+
"delivery_ready", "delivered", "enqueued", "superseded", "cancelled",
|
|
32
|
+
}
|
|
33
|
+
BLOCKED_STATUSES = {"retry_wait", "failed", "blocked"}
|
|
34
|
+
_COLUMNS = (
|
|
35
|
+
"singleton", "schema", "revision", "lease_owner", "lease_token",
|
|
36
|
+
"lease_expires_at", "next_tick_at", "consecutive_blocked",
|
|
37
|
+
"last_started_at", "last_finished_at", "last_phase", "last_status",
|
|
38
|
+
"last_error_code", "updated_at",
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class LocalizationSupervisorBlocked(RuntimeError):
|
|
43
|
+
"""Stable supervisor failure that contains no stored customer value."""
|
|
44
|
+
|
|
45
|
+
def __init__(self, code: str):
|
|
46
|
+
super().__init__(code)
|
|
47
|
+
self.code = code
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass(frozen=True)
|
|
51
|
+
class SupervisorPolicy:
|
|
52
|
+
lease_seconds: float = 300.0
|
|
53
|
+
active_delay_seconds: float = 0.05
|
|
54
|
+
idle_delay_seconds: float = 1.0
|
|
55
|
+
blocked_base_seconds: float = 5.0
|
|
56
|
+
blocked_max_seconds: float = 300.0
|
|
57
|
+
stop_poll_seconds: float = 1.0
|
|
58
|
+
|
|
59
|
+
def validated(self) -> "SupervisorPolicy":
|
|
60
|
+
values = asdict(self)
|
|
61
|
+
for name, value in values.items():
|
|
62
|
+
if isinstance(value, bool) or not isinstance(value, (int, float)):
|
|
63
|
+
raise LocalizationSupervisorBlocked("supervisor.policy.invalid")
|
|
64
|
+
number = float(value)
|
|
65
|
+
if not math.isfinite(number) or number <= 0:
|
|
66
|
+
raise LocalizationSupervisorBlocked("supervisor.policy.invalid")
|
|
67
|
+
values[name] = number
|
|
68
|
+
if values["blocked_base_seconds"] > values["blocked_max_seconds"]:
|
|
69
|
+
raise LocalizationSupervisorBlocked("supervisor.policy.invalid")
|
|
70
|
+
return SupervisorPolicy(**values)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass(frozen=True)
|
|
74
|
+
class SupervisorStatus:
|
|
75
|
+
schema: str
|
|
76
|
+
status: str
|
|
77
|
+
revision: int
|
|
78
|
+
lease_active: bool
|
|
79
|
+
lease_expires_at: float | None
|
|
80
|
+
next_tick_at: float
|
|
81
|
+
consecutive_blocked: int
|
|
82
|
+
last_started_at: float | None
|
|
83
|
+
last_finished_at: float | None
|
|
84
|
+
last_phase: str | None
|
|
85
|
+
last_status: str | None
|
|
86
|
+
last_error_code: str | None
|
|
87
|
+
|
|
88
|
+
def as_payload(self) -> dict[str, Any]:
|
|
89
|
+
return asdict(self)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@dataclass(frozen=True)
|
|
93
|
+
class SupervisorRunOutcome:
|
|
94
|
+
schema: str
|
|
95
|
+
status: str
|
|
96
|
+
next_tick_at: float
|
|
97
|
+
tick: Mapping[str, Any] | None = None
|
|
98
|
+
|
|
99
|
+
def as_payload(self) -> dict[str, Any]:
|
|
100
|
+
return {
|
|
101
|
+
"schema": self.schema,
|
|
102
|
+
"status": self.status,
|
|
103
|
+
"next_tick_at": self.next_tick_at,
|
|
104
|
+
"tick": dict(self.tick) if self.tick is not None else None,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _timestamp(value: Any) -> float:
|
|
109
|
+
if isinstance(value, bool) or not isinstance(value, (int, float)):
|
|
110
|
+
raise LocalizationSupervisorBlocked("supervisor.clock.invalid")
|
|
111
|
+
value = float(value)
|
|
112
|
+
if not math.isfinite(value) or value < 0:
|
|
113
|
+
raise LocalizationSupervisorBlocked("supervisor.clock.invalid")
|
|
114
|
+
return value
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _identifier(value: Any, code: str) -> str:
|
|
118
|
+
if not isinstance(value, str) or TOKEN.fullmatch(value) is None:
|
|
119
|
+
raise LocalizationSupervisorBlocked(code)
|
|
120
|
+
return value
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@contextmanager
|
|
124
|
+
def _transaction(connection: sqlite3.Connection):
|
|
125
|
+
if connection.in_transaction:
|
|
126
|
+
raise LocalizationSupervisorBlocked("supervisor.state.transaction_active")
|
|
127
|
+
try:
|
|
128
|
+
connection.execute("BEGIN IMMEDIATE")
|
|
129
|
+
yield
|
|
130
|
+
connection.commit()
|
|
131
|
+
except Exception:
|
|
132
|
+
connection.rollback()
|
|
133
|
+
raise
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _tick_payload(value: Any) -> dict[str, Any]:
|
|
137
|
+
method = getattr(value, "as_payload", None)
|
|
138
|
+
if callable(method):
|
|
139
|
+
value = method()
|
|
140
|
+
if not isinstance(value, Mapping):
|
|
141
|
+
raise LocalizationSupervisorBlocked("supervisor.tick.result_invalid")
|
|
142
|
+
payload = dict(value)
|
|
143
|
+
expected = {
|
|
144
|
+
"schema", "phase", "status", "event_id", "plan_id", "job_id",
|
|
145
|
+
"target_locale", "delivery_id", "attempt", "error_code",
|
|
146
|
+
}
|
|
147
|
+
if set(payload) != expected or payload.get("schema") != TICK_SCHEMA:
|
|
148
|
+
raise LocalizationSupervisorBlocked("supervisor.tick.result_invalid")
|
|
149
|
+
phase = _identifier(payload.get("phase"), "supervisor.tick.result_invalid")
|
|
150
|
+
status = _identifier(payload.get("status"), "supervisor.tick.result_invalid")
|
|
151
|
+
if phase not in PHASES or status not in STATUSES:
|
|
152
|
+
raise LocalizationSupervisorBlocked("supervisor.tick.result_invalid")
|
|
153
|
+
for name in ("event_id", "plan_id", "job_id", "target_locale", "delivery_id"):
|
|
154
|
+
value = payload.get(name)
|
|
155
|
+
if value is not None:
|
|
156
|
+
_identifier(value, "supervisor.tick.result_invalid")
|
|
157
|
+
attempt = payload.get("attempt")
|
|
158
|
+
if attempt is not None and (
|
|
159
|
+
isinstance(attempt, bool) or not isinstance(attempt, int) or attempt < 1
|
|
160
|
+
):
|
|
161
|
+
raise LocalizationSupervisorBlocked("supervisor.tick.result_invalid")
|
|
162
|
+
error_code = payload.get("error_code")
|
|
163
|
+
if error_code is not None and (
|
|
164
|
+
not isinstance(error_code, str) or ERROR_CODE.fullmatch(error_code) is None
|
|
165
|
+
):
|
|
166
|
+
raise LocalizationSupervisorBlocked("supervisor.tick.result_invalid")
|
|
167
|
+
if (status in BLOCKED_STATUSES) != (error_code is not None):
|
|
168
|
+
raise LocalizationSupervisorBlocked("supervisor.tick.result_invalid")
|
|
169
|
+
del phase
|
|
170
|
+
return payload
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class LocalizationServiceSupervisor:
|
|
174
|
+
"""Lease and schedule a host-configured service tick durably."""
|
|
175
|
+
|
|
176
|
+
def __init__(
|
|
177
|
+
self,
|
|
178
|
+
connection: sqlite3.Connection,
|
|
179
|
+
tick: Callable[[], Any],
|
|
180
|
+
*,
|
|
181
|
+
worker_id: str,
|
|
182
|
+
policy: SupervisorPolicy = SupervisorPolicy(),
|
|
183
|
+
clock: Callable[[], float] = time.time,
|
|
184
|
+
token_factory: Callable[[], str] | None = None,
|
|
185
|
+
):
|
|
186
|
+
if not isinstance(connection, sqlite3.Connection):
|
|
187
|
+
raise LocalizationSupervisorBlocked("supervisor.state.connection_invalid")
|
|
188
|
+
if not callable(tick) or not callable(clock):
|
|
189
|
+
raise LocalizationSupervisorBlocked("supervisor.dependency.invalid")
|
|
190
|
+
self.connection = connection
|
|
191
|
+
self.connection.row_factory = sqlite3.Row
|
|
192
|
+
self.connection.execute("PRAGMA busy_timeout = 5000")
|
|
193
|
+
self.tick = tick
|
|
194
|
+
self.worker_id = _identifier(worker_id, "supervisor.worker_id.invalid")
|
|
195
|
+
self.policy = policy.validated() if isinstance(policy, SupervisorPolicy) else None
|
|
196
|
+
if self.policy is None:
|
|
197
|
+
raise LocalizationSupervisorBlocked("supervisor.policy.invalid")
|
|
198
|
+
self.clock = clock
|
|
199
|
+
self.token_factory = token_factory or (lambda: secrets.token_hex(32))
|
|
200
|
+
self._active_token: str | None = None
|
|
201
|
+
self._create_schema()
|
|
202
|
+
self._verify_schema()
|
|
203
|
+
|
|
204
|
+
def _create_schema(self) -> None:
|
|
205
|
+
with _transaction(self.connection):
|
|
206
|
+
self.connection.execute(f"""
|
|
207
|
+
CREATE TABLE IF NOT EXISTS localization_service_supervisor (
|
|
208
|
+
singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
|
|
209
|
+
schema TEXT NOT NULL CHECK (schema = '{SCHEMA}'),
|
|
210
|
+
revision INTEGER NOT NULL CHECK (revision >= 0),
|
|
211
|
+
lease_owner TEXT,
|
|
212
|
+
lease_token TEXT,
|
|
213
|
+
lease_expires_at REAL,
|
|
214
|
+
next_tick_at REAL NOT NULL,
|
|
215
|
+
consecutive_blocked INTEGER NOT NULL CHECK (consecutive_blocked >= 0),
|
|
216
|
+
last_started_at REAL,
|
|
217
|
+
last_finished_at REAL,
|
|
218
|
+
last_phase TEXT,
|
|
219
|
+
last_status TEXT,
|
|
220
|
+
last_error_code TEXT,
|
|
221
|
+
updated_at REAL NOT NULL,
|
|
222
|
+
CHECK (
|
|
223
|
+
(lease_owner IS NULL AND lease_token IS NULL AND lease_expires_at IS NULL)
|
|
224
|
+
OR
|
|
225
|
+
(lease_owner IS NOT NULL AND lease_token IS NOT NULL AND lease_expires_at IS NOT NULL)
|
|
226
|
+
)
|
|
227
|
+
)
|
|
228
|
+
""")
|
|
229
|
+
self.connection.execute("""
|
|
230
|
+
INSERT OR IGNORE INTO localization_service_supervisor (
|
|
231
|
+
singleton, schema, revision, next_tick_at,
|
|
232
|
+
consecutive_blocked, updated_at
|
|
233
|
+
) VALUES (1, ?, 0, 0, 0, 0)
|
|
234
|
+
""", (SCHEMA,))
|
|
235
|
+
|
|
236
|
+
def _verify_schema(self) -> None:
|
|
237
|
+
columns = tuple(
|
|
238
|
+
row["name"] for row in self.connection.execute(
|
|
239
|
+
"PRAGMA table_info(localization_service_supervisor)"
|
|
240
|
+
)
|
|
241
|
+
)
|
|
242
|
+
if columns != _COLUMNS:
|
|
243
|
+
raise LocalizationSupervisorBlocked("supervisor.state.schema_altered")
|
|
244
|
+
rows = self.connection.execute(
|
|
245
|
+
"SELECT * FROM localization_service_supervisor"
|
|
246
|
+
).fetchall()
|
|
247
|
+
if len(rows) != 1:
|
|
248
|
+
raise LocalizationSupervisorBlocked("supervisor.state.invalid")
|
|
249
|
+
self._status_from_row(rows[0], 0)
|
|
250
|
+
|
|
251
|
+
@staticmethod
|
|
252
|
+
def _status_from_row(row: sqlite3.Row, now: float) -> SupervisorStatus:
|
|
253
|
+
if row["singleton"] != 1 or row["schema"] != SCHEMA:
|
|
254
|
+
raise LocalizationSupervisorBlocked("supervisor.state.invalid")
|
|
255
|
+
revision = row["revision"]
|
|
256
|
+
blocked = row["consecutive_blocked"]
|
|
257
|
+
if any(
|
|
258
|
+
isinstance(value, bool) or not isinstance(value, int) or value < 0
|
|
259
|
+
for value in (revision, blocked)
|
|
260
|
+
):
|
|
261
|
+
raise LocalizationSupervisorBlocked("supervisor.state.invalid")
|
|
262
|
+
next_tick_at = _timestamp(row["next_tick_at"])
|
|
263
|
+
updated_at = _timestamp(row["updated_at"])
|
|
264
|
+
del updated_at
|
|
265
|
+
lease = (row["lease_owner"], row["lease_token"], row["lease_expires_at"])
|
|
266
|
+
if lease == (None, None, None):
|
|
267
|
+
lease_active = False
|
|
268
|
+
lease_expires_at = None
|
|
269
|
+
status = "waiting" if next_tick_at > now else "ready"
|
|
270
|
+
else:
|
|
271
|
+
_identifier(lease[0], "supervisor.state.invalid")
|
|
272
|
+
_identifier(lease[1], "supervisor.state.invalid")
|
|
273
|
+
lease_expires_at = _timestamp(lease[2])
|
|
274
|
+
lease_active = lease_expires_at > now
|
|
275
|
+
status = "leased" if lease_active else "recoverable"
|
|
276
|
+
times = []
|
|
277
|
+
for name in ("last_started_at", "last_finished_at"):
|
|
278
|
+
value = row[name]
|
|
279
|
+
times.append(None if value is None else _timestamp(value))
|
|
280
|
+
if times[1] is not None and times[0] is None:
|
|
281
|
+
raise LocalizationSupervisorBlocked("supervisor.state.invalid")
|
|
282
|
+
if times[0] is not None and times[1] is not None and times[1] < times[0]:
|
|
283
|
+
raise LocalizationSupervisorBlocked("supervisor.state.invalid")
|
|
284
|
+
last_phase, last_status, last_error = (
|
|
285
|
+
row["last_phase"], row["last_status"], row["last_error_code"]
|
|
286
|
+
)
|
|
287
|
+
if (last_phase is None) != (last_status is None):
|
|
288
|
+
raise LocalizationSupervisorBlocked("supervisor.state.invalid")
|
|
289
|
+
if last_phase is not None:
|
|
290
|
+
_identifier(last_phase, "supervisor.state.invalid")
|
|
291
|
+
_identifier(last_status, "supervisor.state.invalid")
|
|
292
|
+
if last_phase not in PHASES or last_status not in STATUSES:
|
|
293
|
+
raise LocalizationSupervisorBlocked("supervisor.state.invalid")
|
|
294
|
+
if last_error is not None and (
|
|
295
|
+
not isinstance(last_error, str) or ERROR_CODE.fullmatch(last_error) is None
|
|
296
|
+
):
|
|
297
|
+
raise LocalizationSupervisorBlocked("supervisor.state.invalid")
|
|
298
|
+
if last_status is None:
|
|
299
|
+
if last_error is not None or blocked != 0:
|
|
300
|
+
raise LocalizationSupervisorBlocked("supervisor.state.invalid")
|
|
301
|
+
elif (last_status in BLOCKED_STATUSES) != (last_error is not None):
|
|
302
|
+
raise LocalizationSupervisorBlocked("supervisor.state.invalid")
|
|
303
|
+
if (blocked > 0) != (last_status in BLOCKED_STATUSES):
|
|
304
|
+
raise LocalizationSupervisorBlocked("supervisor.state.invalid")
|
|
305
|
+
return SupervisorStatus(
|
|
306
|
+
SCHEMA, status, revision, lease_active, lease_expires_at,
|
|
307
|
+
next_tick_at, blocked, times[0], times[1], last_phase,
|
|
308
|
+
last_status, last_error,
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
def status(self, *, now: float | int | None = None) -> SupervisorStatus:
|
|
312
|
+
now = _timestamp(self.clock() if now is None else now)
|
|
313
|
+
try:
|
|
314
|
+
self._verify_schema()
|
|
315
|
+
row = self.connection.execute(
|
|
316
|
+
"SELECT * FROM localization_service_supervisor WHERE singleton = 1"
|
|
317
|
+
).fetchone()
|
|
318
|
+
if row is None:
|
|
319
|
+
raise LocalizationSupervisorBlocked("supervisor.state.invalid")
|
|
320
|
+
return self._status_from_row(row, now)
|
|
321
|
+
except LocalizationSupervisorBlocked:
|
|
322
|
+
raise
|
|
323
|
+
except Exception:
|
|
324
|
+
raise LocalizationSupervisorBlocked("supervisor.state.unavailable") from None
|
|
325
|
+
|
|
326
|
+
def _claim(self, now: float) -> tuple[str | None, SupervisorRunOutcome | None]:
|
|
327
|
+
token = _identifier(
|
|
328
|
+
self.token_factory(), "supervisor.lease_token.invalid",
|
|
329
|
+
)
|
|
330
|
+
with _transaction(self.connection):
|
|
331
|
+
row = self.connection.execute(
|
|
332
|
+
"SELECT * FROM localization_service_supervisor WHERE singleton = 1"
|
|
333
|
+
).fetchone()
|
|
334
|
+
if row is None:
|
|
335
|
+
raise LocalizationSupervisorBlocked("supervisor.state.invalid")
|
|
336
|
+
status = self._status_from_row(row, now)
|
|
337
|
+
if status.lease_active:
|
|
338
|
+
return None, SupervisorRunOutcome(
|
|
339
|
+
SCHEMA, "leased", status.lease_expires_at or now,
|
|
340
|
+
)
|
|
341
|
+
if status.status == "waiting":
|
|
342
|
+
return None, SupervisorRunOutcome(
|
|
343
|
+
SCHEMA, "waiting", status.next_tick_at,
|
|
344
|
+
)
|
|
345
|
+
expires = now + self.policy.lease_seconds
|
|
346
|
+
updated = self.connection.execute("""
|
|
347
|
+
UPDATE localization_service_supervisor
|
|
348
|
+
SET revision = revision + 1,
|
|
349
|
+
lease_owner = ?, lease_token = ?, lease_expires_at = ?,
|
|
350
|
+
last_started_at = ?, last_finished_at = NULL, updated_at = ?
|
|
351
|
+
WHERE singleton = 1 AND revision = ?
|
|
352
|
+
""", (
|
|
353
|
+
self.worker_id, token, expires, now, now, status.revision,
|
|
354
|
+
))
|
|
355
|
+
if updated.rowcount != 1:
|
|
356
|
+
raise LocalizationSupervisorBlocked("supervisor.state.conflict")
|
|
357
|
+
return token, None
|
|
358
|
+
|
|
359
|
+
def _finish(self, token: str, tick: dict[str, Any], now: float) -> float:
|
|
360
|
+
blocked = tick["status"] in BLOCKED_STATUSES
|
|
361
|
+
with _transaction(self.connection):
|
|
362
|
+
row = self.connection.execute(
|
|
363
|
+
"SELECT * FROM localization_service_supervisor WHERE singleton = 1"
|
|
364
|
+
).fetchone()
|
|
365
|
+
if row is None:
|
|
366
|
+
raise LocalizationSupervisorBlocked("supervisor.state.invalid")
|
|
367
|
+
status = self._status_from_row(row, now)
|
|
368
|
+
if (
|
|
369
|
+
row["lease_owner"] != self.worker_id
|
|
370
|
+
or row["lease_token"] != token
|
|
371
|
+
or row["lease_expires_at"] is None
|
|
372
|
+
or _timestamp(row["lease_expires_at"]) <= now
|
|
373
|
+
):
|
|
374
|
+
raise LocalizationSupervisorBlocked("supervisor.lease_lost")
|
|
375
|
+
failures = status.consecutive_blocked + 1 if blocked else 0
|
|
376
|
+
if blocked:
|
|
377
|
+
delay = min(
|
|
378
|
+
self.policy.blocked_max_seconds,
|
|
379
|
+
self.policy.blocked_base_seconds * (2 ** min(failures - 1, 30)),
|
|
380
|
+
)
|
|
381
|
+
elif tick["phase"] == "idle" and tick["status"] == "idle":
|
|
382
|
+
delay = self.policy.idle_delay_seconds
|
|
383
|
+
else:
|
|
384
|
+
delay = self.policy.active_delay_seconds
|
|
385
|
+
next_tick_at = now + delay
|
|
386
|
+
updated = self.connection.execute("""
|
|
387
|
+
UPDATE localization_service_supervisor
|
|
388
|
+
SET revision = revision + 1,
|
|
389
|
+
lease_owner = NULL, lease_token = NULL, lease_expires_at = NULL,
|
|
390
|
+
next_tick_at = ?, consecutive_blocked = ?,
|
|
391
|
+
last_finished_at = ?, last_phase = ?, last_status = ?,
|
|
392
|
+
last_error_code = ?, updated_at = ?
|
|
393
|
+
WHERE singleton = 1 AND lease_owner = ? AND lease_token = ?
|
|
394
|
+
""", (
|
|
395
|
+
next_tick_at, failures, now, tick["phase"], tick["status"],
|
|
396
|
+
tick["error_code"], now, self.worker_id, token,
|
|
397
|
+
))
|
|
398
|
+
if updated.rowcount != 1:
|
|
399
|
+
raise LocalizationSupervisorBlocked("supervisor.lease_lost")
|
|
400
|
+
return next_tick_at
|
|
401
|
+
|
|
402
|
+
def renew_active_lease(self, minimum_child_seconds: float | int) -> float:
|
|
403
|
+
"""Renew the current outer lease immediately before child work."""
|
|
404
|
+
if (
|
|
405
|
+
isinstance(minimum_child_seconds, bool)
|
|
406
|
+
or not isinstance(minimum_child_seconds, (int, float))
|
|
407
|
+
):
|
|
408
|
+
raise LocalizationSupervisorBlocked("supervisor.lease_guard.invalid")
|
|
409
|
+
minimum_child_seconds = float(minimum_child_seconds)
|
|
410
|
+
if (
|
|
411
|
+
not math.isfinite(minimum_child_seconds)
|
|
412
|
+
or minimum_child_seconds <= 0
|
|
413
|
+
or minimum_child_seconds >= self.policy.lease_seconds
|
|
414
|
+
):
|
|
415
|
+
raise LocalizationSupervisorBlocked("supervisor.lease_guard.invalid")
|
|
416
|
+
token = self._active_token
|
|
417
|
+
if token is None:
|
|
418
|
+
raise LocalizationSupervisorBlocked("supervisor.lease_guard.inactive")
|
|
419
|
+
now = _timestamp(self.clock())
|
|
420
|
+
expires = now + self.policy.lease_seconds
|
|
421
|
+
with _transaction(self.connection):
|
|
422
|
+
row = self.connection.execute(
|
|
423
|
+
"SELECT * FROM localization_service_supervisor WHERE singleton = 1"
|
|
424
|
+
).fetchone()
|
|
425
|
+
if row is None:
|
|
426
|
+
raise LocalizationSupervisorBlocked("supervisor.state.invalid")
|
|
427
|
+
status = self._status_from_row(row, now)
|
|
428
|
+
if (
|
|
429
|
+
not status.lease_active
|
|
430
|
+
or row["lease_owner"] != self.worker_id
|
|
431
|
+
or row["lease_token"] != token
|
|
432
|
+
):
|
|
433
|
+
raise LocalizationSupervisorBlocked("supervisor.lease_lost")
|
|
434
|
+
updated = self.connection.execute("""
|
|
435
|
+
UPDATE localization_service_supervisor
|
|
436
|
+
SET revision = revision + 1, lease_expires_at = ?, updated_at = ?
|
|
437
|
+
WHERE singleton = 1 AND lease_owner = ? AND lease_token = ?
|
|
438
|
+
AND lease_expires_at > ? AND revision = ?
|
|
439
|
+
""", (
|
|
440
|
+
expires, now, self.worker_id, token, now, status.revision,
|
|
441
|
+
))
|
|
442
|
+
if updated.rowcount != 1:
|
|
443
|
+
raise LocalizationSupervisorBlocked("supervisor.lease_lost")
|
|
444
|
+
return expires
|
|
445
|
+
|
|
446
|
+
def run_once(self, *, now: float | int | None = None) -> SupervisorRunOutcome:
|
|
447
|
+
started = _timestamp(self.clock() if now is None else now)
|
|
448
|
+
try:
|
|
449
|
+
token, skipped = self._claim(started)
|
|
450
|
+
except LocalizationSupervisorBlocked:
|
|
451
|
+
raise
|
|
452
|
+
except Exception:
|
|
453
|
+
raise LocalizationSupervisorBlocked("supervisor.state.unavailable") from None
|
|
454
|
+
if skipped is not None:
|
|
455
|
+
return skipped
|
|
456
|
+
assert token is not None
|
|
457
|
+
try:
|
|
458
|
+
self._active_token = token
|
|
459
|
+
try:
|
|
460
|
+
try:
|
|
461
|
+
tick = _tick_payload(self.tick())
|
|
462
|
+
except Exception:
|
|
463
|
+
tick = {
|
|
464
|
+
"schema": TICK_SCHEMA,
|
|
465
|
+
"phase": "supervisor",
|
|
466
|
+
"status": "blocked",
|
|
467
|
+
"event_id": None,
|
|
468
|
+
"plan_id": None,
|
|
469
|
+
"job_id": None,
|
|
470
|
+
"target_locale": None,
|
|
471
|
+
"delivery_id": None,
|
|
472
|
+
"attempt": None,
|
|
473
|
+
"error_code": "supervisor.tick.unhandled",
|
|
474
|
+
}
|
|
475
|
+
finally:
|
|
476
|
+
self._active_token = None
|
|
477
|
+
finished = _timestamp(self.clock())
|
|
478
|
+
if finished < started:
|
|
479
|
+
raise LocalizationSupervisorBlocked("supervisor.clock.invalid")
|
|
480
|
+
next_tick_at = self._finish(token, tick, finished)
|
|
481
|
+
return SupervisorRunOutcome(SCHEMA, "ran", next_tick_at, tick)
|
|
482
|
+
except LocalizationSupervisorBlocked:
|
|
483
|
+
raise
|
|
484
|
+
except Exception:
|
|
485
|
+
raise LocalizationSupervisorBlocked("supervisor.state.unavailable") from None
|
|
486
|
+
|
|
487
|
+
def run_forever(
|
|
488
|
+
self,
|
|
489
|
+
*,
|
|
490
|
+
stop_requested: Callable[[], bool],
|
|
491
|
+
sleeper: Callable[[float], Any] = time.sleep,
|
|
492
|
+
) -> SupervisorStatus:
|
|
493
|
+
if not callable(stop_requested) or not callable(sleeper):
|
|
494
|
+
raise LocalizationSupervisorBlocked("supervisor.dependency.invalid")
|
|
495
|
+
while True:
|
|
496
|
+
try:
|
|
497
|
+
should_stop = stop_requested()
|
|
498
|
+
except Exception:
|
|
499
|
+
raise LocalizationSupervisorBlocked("supervisor.stop.invalid") from None
|
|
500
|
+
if should_stop is not False and should_stop is not True:
|
|
501
|
+
raise LocalizationSupervisorBlocked("supervisor.stop.invalid")
|
|
502
|
+
if should_stop:
|
|
503
|
+
return self.status()
|
|
504
|
+
outcome = self.run_once()
|
|
505
|
+
now = _timestamp(self.clock())
|
|
506
|
+
delay = min(
|
|
507
|
+
self.policy.stop_poll_seconds,
|
|
508
|
+
max(0.0, outcome.next_tick_at - now),
|
|
509
|
+
)
|
|
510
|
+
if delay > 0:
|
|
511
|
+
sleeper(delay)
|