blun-king-cli 9.1.587 → 9.1.588

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (436) hide show
  1. package/CHANGELOG.md +11 -185
  2. package/LIESMICH.txt +51 -13
  3. package/README.md +44 -47
  4. package/agent-spine-plugin/.codex-plugin/plugin.json +16 -4
  5. package/agent-spine-plugin/CHANGELOG.md +37 -5
  6. package/agent-spine-plugin/README.md +3 -3
  7. package/agent-spine-plugin/blun.plugin.json +45 -10
  8. package/agent-spine-plugin/docs/artifact-evaluation.md +93 -0
  9. package/agent-spine-plugin/docs/host-integration.md +42 -27
  10. package/agent-spine-plugin/docs/preflight-recall.md +4 -2
  11. package/agent-spine-plugin/docs/session-timeline.md +97 -236
  12. package/agent-spine-plugin/docs/world-model.md +25 -0
  13. package/agent-spine-plugin/hooks/codex.json +1 -1
  14. package/agent-spine-plugin/hooks/hooks.json +1 -1
  15. package/agent-spine-plugin/package.json +1 -3
  16. package/agent-spine-plugin/scripts/check-hosts.js +3 -3
  17. package/agent-spine-plugin/scripts/release-check.js +10 -5
  18. package/agent-spine-plugin/scripts/run-checks.js +4 -1
  19. package/agent-spine-plugin/scripts/run-tests-hermetic.js +32 -6
  20. package/agent-spine-plugin/src/cli-learning.js +15 -0
  21. package/agent-spine-plugin/src/cli.js +2 -0
  22. package/agent-spine-plugin/src/hook.js +32 -32
  23. package/agent-spine-plugin/src/lib/action-lesson-recall.js +73 -8
  24. package/agent-spine-plugin/src/lib/briefing.js +146 -36
  25. package/agent-spine-plugin/src/lib/channel-continuity.js +19 -0
  26. package/agent-spine-plugin/src/lib/delivery-agent-usage.js +14 -7
  27. package/agent-spine-plugin/src/lib/gateway-group-response.js +128 -0
  28. package/agent-spine-plugin/src/lib/gateway-runs.js +24 -15
  29. package/agent-spine-plugin/src/lib/hook-briefing-use.js +13 -3
  30. package/agent-spine-plugin/src/lib/hook-context.js +16 -3
  31. package/agent-spine-plugin/src/lib/hook-output.js +129 -5
  32. package/agent-spine-plugin/src/lib/hook-timeline.js +5 -3
  33. package/agent-spine-plugin/src/lib/indexed-memory.js +2 -2
  34. package/agent-spine-plugin/src/lib/learning-artifact-evaluator.js +114 -0
  35. package/agent-spine-plugin/src/lib/learning-context.js +11 -4
  36. package/agent-spine-plugin/src/lib/learning-measurements.js +2 -2
  37. package/agent-spine-plugin/src/lib/mcp-runtime.js +89 -3
  38. package/agent-spine-plugin/src/lib/mcp-source-context.js +12 -2
  39. package/agent-spine-plugin/src/lib/mcp-timeline-tools.js +91 -8
  40. package/agent-spine-plugin/src/lib/mcp-world-tools.js +2 -2
  41. package/agent-spine-plugin/src/lib/owned-file-lock.js +20 -1
  42. package/agent-spine-plugin/src/lib/persona-runtime.js +2 -2
  43. package/agent-spine-plugin/src/lib/preflight-delivery-id.js +27 -0
  44. package/agent-spine-plugin/src/lib/preflight.js +4 -4
  45. package/agent-spine-plugin/src/lib/session-timeline-codex.js +15 -0
  46. package/agent-spine-plugin/src/lib/session-timeline-contract.js +12 -4
  47. package/agent-spine-plugin/src/lib/session-timeline-event-extract.js +36 -7
  48. package/agent-spine-plugin/src/lib/session-timeline-host-origin.js +13 -10
  49. package/agent-spine-plugin/src/lib/session-timeline-invocation.js +1 -1
  50. package/agent-spine-plugin/src/lib/session-timeline-king.js +14 -0
  51. package/agent-spine-plugin/src/lib/session-timeline-prior.js +18 -12
  52. package/agent-spine-plugin/src/lib/session-timeline-provider.js +5 -0
  53. package/agent-spine-plugin/src/lib/session-timeline-query.js +2 -0
  54. package/agent-spine-plugin/src/lib/session-timeline-results.js +35 -10
  55. package/agent-spine-plugin/src/lib/session-timeline-source-open.js +30 -0
  56. package/agent-spine-plugin/src/lib/session-timeline.js +122 -75
  57. package/agent-spine-plugin/src/lib/source-roots.js +3 -2
  58. package/agent-spine-plugin/src/lib/task-knowledge-context.js +22 -1
  59. package/agent-spine-plugin/src/lib/timeline-continuation-update.js +100 -0
  60. package/agent-spine-plugin/src/lib/timeline-tool-guard.js +30 -7
  61. package/agent-spine-plugin/src/lib/timeline-user-feedback.js +217 -0
  62. package/agent-spine-plugin/src/lib/timeline-world-capture.js +233 -0
  63. package/agent-spine-plugin/src/lib/world-knowledge.js +59 -2
  64. package/agent-spine-plugin/src/lib/world-model.js +64 -9
  65. package/agent-spine-plugin/src/worker.js +13 -1
  66. package/bin/blun.js +43 -28
  67. package/bin/core-bootstrap.js +5 -4
  68. package/bin/king.js +43 -28
  69. package/bin/launcher-mode.js +1 -10
  70. package/bin/launcher-runtime.js +128 -295
  71. package/bin/managed-node.js +0 -0
  72. package/bin/managed-plugin-selection.cjs +0 -1
  73. package/bin/native-module-repair.js +0 -0
  74. package/bin/node-runtime.js +0 -0
  75. package/bin/node-version.js +0 -0
  76. package/bin/plugin-bootstrap.js +56 -120
  77. package/bin/private-paths.js +11 -34
  78. package/bin/standard-tools-bootstrap.js +34 -114
  79. package/bin/turn-thinking-policy.cjs +3 -11
  80. package/bin/update-copy.js +200 -0
  81. package/bin/update-lease.js +0 -0
  82. package/bin/update-notice.js +136 -289
  83. package/bin/verify-agent-behavior.cjs +122 -0
  84. package/bin/verify-agent-components.cjs +104 -0
  85. package/bin/verify-bundled-agent-sources.cjs +57 -0
  86. package/blun.mjs +143076 -135288
  87. package/bundled-agent-sources.json +701 -0
  88. package/package.json +12 -15
  89. package/standard-skills/translate-native/README.md +1293 -0
  90. package/standard-skills/translate-native/SKILL.md +172 -22
  91. package/standard-skills/translate-native/VERSION +1 -1
  92. package/standard-skills/translate-native/agents/openai.yaml +18 -0
  93. package/standard-skills/translate-native/assets/icon.svg +8 -0
  94. package/standard-skills/translate-native/docs/BLUN_CODE_INTEGRATION.md +76 -0
  95. package/standard-skills/translate-native/docs/PREMORTEM.md +489 -0
  96. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION.md +2035 -0
  97. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_API.md +1302 -0
  98. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_EVIDENCE_HTTP.md +136 -0
  99. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_HEALTH_HTTP.md +130 -0
  100. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_HTTP_PROVIDER.md +175 -0
  101. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_RECEIPT_VERIFIER_HTTP.md +86 -0
  102. package/standard-skills/translate-native/integrations/AGENT_RULES.md +32 -0
  103. package/standard-skills/translate-native/integrations/adapters/blun-code-language-guard.js +514 -0
  104. package/standard-skills/translate-native/integrations/adapters/node-language-guard.js +230 -0
  105. package/standard-skills/translate-native/integrations/audit_log.py +327 -0
  106. package/standard-skills/translate-native/integrations/claude_language_hook.js +1536 -0
  107. package/standard-skills/translate-native/integrations/commercial_localization_profile.py +42 -0
  108. package/standard-skills/translate-native/integrations/delivery-policy.example.json +28 -0
  109. package/standard-skills/translate-native/integrations/enforced_delivery.py +543 -0
  110. package/standard-skills/translate-native/integrations/guard_service.py +435 -0
  111. package/standard-skills/translate-native/integrations/language_gateway.py +67 -0
  112. package/standard-skills/translate-native/integrations/mcp_auth_headers.py +198 -0
  113. package/standard-skills/translate-native/integrations/mcp_http_gateway.py +429 -0
  114. package/standard-skills/translate-native/integrations/non_language_html_entities.js +1485 -0
  115. package/standard-skills/translate-native/integrations/pre_output_guard.py +65 -0
  116. package/standard-skills/translate-native/integrations/task_router.py +101 -0
  117. package/standard-skills/translate-native/integrations/website_localization.py +401 -0
  118. package/standard-skills/translate-native/integrations/website_localization_api.py +581 -0
  119. package/standard-skills/translate-native/integrations/website_localization_benchmark.py +1885 -0
  120. package/standard-skills/translate-native/integrations/website_localization_benchmark_campaign.py +1772 -0
  121. package/standard-skills/translate-native/integrations/website_localization_benchmark_candidate.py +506 -0
  122. package/standard-skills/translate-native/integrations/website_localization_benchmark_http.py +400 -0
  123. package/standard-skills/translate-native/integrations/website_localization_benchmark_review_store.py +781 -0
  124. package/standard-skills/translate-native/integrations/website_localization_benchmark_reviewer_http.py +500 -0
  125. package/standard-skills/translate-native/integrations/website_localization_benchmark_runtime.py +1107 -0
  126. package/standard-skills/translate-native/integrations/website_localization_benchmark_suite.py +463 -0
  127. package/standard-skills/translate-native/integrations/website_localization_cms.py +2835 -0
  128. package/standard-skills/translate-native/integrations/website_localization_cms_client.py +875 -0
  129. package/standard-skills/translate-native/integrations/website_localization_cms_dispatch.py +805 -0
  130. package/standard-skills/translate-native/integrations/website_localization_cms_http.py +588 -0
  131. package/standard-skills/translate-native/integrations/website_localization_cms_lifecycle_monitor.py +991 -0
  132. package/standard-skills/translate-native/integrations/website_localization_cms_receiver.py +1441 -0
  133. package/standard-skills/translate-native/integrations/website_localization_cms_receiver_runtime.py +414 -0
  134. package/standard-skills/translate-native/integrations/website_localization_cms_receiver_store.py +1073 -0
  135. package/standard-skills/translate-native/integrations/website_localization_cms_removal_dispatch.py +865 -0
  136. package/standard-skills/translate-native/integrations/website_localization_cms_source_client.py +583 -0
  137. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery.py +964 -0
  138. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_runtime.py +665 -0
  139. package/standard-skills/translate-native/integrations/website_localization_cms_source_http.py +1153 -0
  140. package/standard-skills/translate-native/integrations/website_localization_cms_source_runtime.py +675 -0
  141. package/standard-skills/translate-native/integrations/website_localization_cms_source_service.py +1125 -0
  142. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification.py +674 -0
  143. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_http.py +444 -0
  144. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver.py +1469 -0
  145. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver_runtime.py +1142 -0
  146. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_processing_monitor.py +634 -0
  147. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_receiver_client.py +804 -0
  148. package/standard-skills/translate-native/integrations/website_localization_deepl_baseline.py +922 -0
  149. package/standard-skills/translate-native/integrations/website_localization_evidence_http.py +482 -0
  150. package/standard-skills/translate-native/integrations/website_localization_health.py +1541 -0
  151. package/standard-skills/translate-native/integrations/website_localization_health_http.py +372 -0
  152. package/standard-skills/translate-native/integrations/website_localization_http_provider.py +297 -0
  153. package/standard-skills/translate-native/integrations/website_localization_native_reference_http.py +479 -0
  154. package/standard-skills/translate-native/integrations/website_localization_native_reference_intake.py +363 -0
  155. package/standard-skills/translate-native/integrations/website_localization_native_reference_queue.py +1449 -0
  156. package/standard-skills/translate-native/integrations/website_localization_native_reference_store.py +420 -0
  157. package/standard-skills/translate-native/integrations/website_localization_quality_profiles.py +235 -0
  158. package/standard-skills/translate-native/integrations/website_localization_queue.py +671 -0
  159. package/standard-skills/translate-native/integrations/website_localization_receipt_verifier_http.py +516 -0
  160. package/standard-skills/translate-native/integrations/website_localization_release.py +928 -0
  161. package/standard-skills/translate-native/integrations/website_localization_release_coordinator.py +1008 -0
  162. package/standard-skills/translate-native/integrations/website_localization_runner.py +276 -0
  163. package/standard-skills/translate-native/integrations/website_localization_runtime.py +862 -0
  164. package/standard-skills/translate-native/integrations/website_localization_service.py +350 -0
  165. package/standard-skills/translate-native/integrations/website_localization_supervisor.py +511 -0
  166. package/standard-skills/translate-native/integrations/website_localization_worker.py +663 -0
  167. package/standard-skills/translate-native/provenance.json +3 -4
  168. package/standard-skills/translate-native/references/commercial-localization.md +177 -0
  169. package/standard-skills/translate-native/scripts/blun_language_guard.py +7 -1
  170. package/standard-skills/translate-native/scripts/check_commercial_review.py +80 -0
  171. package/standard-skills/translate-native/scripts/commercial_localization_profile.py +333 -0
  172. package/standard-tools/language-guard/LICENSE +21 -0
  173. package/standard-tools/language-guard/VERSION +1 -0
  174. package/standard-tools/language-guard/blun_language_guard.py +7 -1
  175. package/standard-tools/language-guard/check_commercial_review.py +80 -0
  176. package/standard-tools/language-guard/commercial_localization_profile.py +333 -0
  177. package/standard-tools/language-guard/language_gateway.py +62 -0
  178. package/standard-tools/language-guard/pre_output_guard.py +64 -0
  179. package/standard-tools/language-guard/provenance.json +4 -11
  180. package/standard-tools/manifest.json +34 -11
  181. package/telegram-plugin/commands/access.md +2 -10
  182. package/telegram-plugin/dist/bridge.mjs +64041 -687
  183. package/telegram-plugin/dist/mcp-server.mjs +72810 -9027
  184. package/telegram-plugin/dist/noise.mjs +28 -63511
  185. package/agent-spine-plugin/CONTRIBUTING.md +0 -52
  186. package/agent-spine-plugin/SECURITY.md +0 -47
  187. package/agent-spine-plugin/docs/assignment-continuation.md +0 -48
  188. package/agent-spine-plugin/docs/releasing.md +0 -85
  189. package/agent-spine-plugin/docs/structured-completion.md +0 -67
  190. package/bin/abort-listener-policy.cjs +0 -43
  191. package/bin/active-steer-priority-policy.cjs +0 -24
  192. package/bin/agent-api-http-adapter.mjs +0 -446
  193. package/bin/agent-api-private-http-server.mjs +0 -288
  194. package/bin/agent-api-runtime.mjs +0 -252
  195. package/bin/agent-api-service-environment.mjs +0 -236
  196. package/bin/agent-api-service-host.mjs +0 -209
  197. package/bin/agent-api-service-process.mjs +0 -171
  198. package/bin/agent-api-session-registry.mjs +0 -428
  199. package/bin/agent-api-tool-broker.cjs +0 -248
  200. package/bin/agent-api-turn-controller.mjs +0 -461
  201. package/bin/agent-api-usage-journal.cjs +0 -259
  202. package/bin/agent-resume-snapshot.cjs +0 -241
  203. package/bin/agentspine-king-goal-inbox.mjs +0 -111
  204. package/bin/agentspine-king-goal-intake.mjs +0 -106
  205. package/bin/approval-rejection-stop.cjs +0 -15
  206. package/bin/assistant-message-offload-policy.cjs +0 -284
  207. package/bin/baseline-skill-performance-policy.cjs +0 -39
  208. package/bin/bash-search-scope-policy.cjs +0 -49
  209. package/bin/codebase-search-runtime.cjs +0 -23
  210. package/bin/cognitive-action-checkpoint.cjs +0 -1104
  211. package/bin/cognitive-attention-delivery.cjs +0 -76
  212. package/bin/cognitive-attention-policy.cjs +0 -143
  213. package/bin/cognitive-attention-runtime.cjs +0 -91
  214. package/bin/cognitive-context-projection.cjs +0 -73
  215. package/bin/cognitive-cross-portal-acceptance.cjs +0 -443
  216. package/bin/cognitive-effective-view.cjs +0 -77
  217. package/bin/cognitive-focus-projection.cjs +0 -206
  218. package/bin/cognitive-focus-scope.cjs +0 -37
  219. package/bin/cognitive-goal-autostart-policy.cjs +0 -72
  220. package/bin/cognitive-goal-time-trigger-controller.cjs +0 -146
  221. package/bin/cognitive-memory-adapter.cjs +0 -282
  222. package/bin/cognitive-memory-command.cjs +0 -293
  223. package/bin/cognitive-memory-provider.cjs +0 -92
  224. package/bin/cognitive-salience-policy.cjs +0 -159
  225. package/bin/cognitive-state-store.cjs +0 -508
  226. package/bin/cognitive-turn-lifecycle.cjs +0 -624
  227. package/bin/cognitive-work-focus.cjs +0 -180
  228. package/bin/compaction-history-archive.cjs +0 -166
  229. package/bin/compaction-history-startup.cjs +0 -50
  230. package/bin/compaction-model-policy.cjs +0 -31
  231. package/bin/compaction-stage-policy.cjs +0 -21
  232. package/bin/compaction-transaction-policy.cjs +0 -122
  233. package/bin/config-write-dedup-policy.cjs +0 -27
  234. package/bin/context-budget-ledger.cjs +0 -31
  235. package/bin/context-doctor-policy.cjs +0 -70
  236. package/bin/context-insight-policy.cjs +0 -36
  237. package/bin/context-performance-policy.cjs +0 -19
  238. package/bin/context-pressure-policy.cjs +0 -20
  239. package/bin/cron-run-output.cjs +0 -45
  240. package/bin/cron-run-store.cjs +0 -145
  241. package/bin/curiosity-scout-policy.cjs +0 -49
  242. package/bin/default-model-output-budget-policy.cjs +0 -28
  243. package/bin/durable-task-resume-policy.cjs +0 -130
  244. package/bin/durable-task-resume-runtime.cjs +0 -117
  245. package/bin/durable-task-resume-store.cjs +0 -88
  246. package/bin/editable-tool-approval-policy.cjs +0 -540
  247. package/bin/editable-tool-approval-runtime.cjs +0 -99
  248. package/bin/effective-system-prompt-cache-policy.cjs +0 -33
  249. package/bin/error-memory-performance-policy.cjs +0 -113
  250. package/bin/file-observation-policy.cjs +0 -133
  251. package/bin/foreground-output-capture-policy.cjs +0 -41
  252. package/bin/generated-source-health.cjs +0 -142
  253. package/bin/glob-pattern-policy.cjs +0 -13
  254. package/bin/goal-completion-evidence-policy.cjs +0 -120
  255. package/bin/grep-output-limit-policy.cjs +0 -39
  256. package/bin/historical-media-projection-policy.cjs +0 -48
  257. package/bin/history-offload-pressure-policy.cjs +0 -33
  258. package/bin/html-to-research-markdown.cjs +0 -147
  259. package/bin/identity-context-policy.cjs +0 -764
  260. package/bin/identity-journal-policy.cjs +0 -107
  261. package/bin/input-draft-persistence.cjs +0 -77
  262. package/bin/king-tui-function-contract.json +0 -33
  263. package/bin/launcher-restart-policy.cjs +0 -150
  264. package/bin/live-response-repetition-guard.cjs +0 -196
  265. package/bin/llm-config-log-dedup-policy.cjs +0 -76
  266. package/bin/loop-event-record-policy.cjs +0 -174
  267. package/bin/managed-context-startup-policy.cjs +0 -27
  268. package/bin/media-activity-layout-policy.cjs +0 -34
  269. package/bin/media-auto-retrieval-policy.cjs +0 -90
  270. package/bin/media-result-policy.cjs +0 -59
  271. package/bin/micro-compaction-policy.cjs +0 -145
  272. package/bin/mistake-relevance-policy.cjs +0 -319
  273. package/bin/model-retry-progress-policy.cjs +0 -46
  274. package/bin/native-large-file-io.cjs +0 -42
  275. package/bin/native-runtime-cache.cjs +0 -76
  276. package/bin/natural-presence-policy.cjs +0 -28
  277. package/bin/noninteractive-shell-env-policy.cjs +0 -19
  278. package/bin/observer-hooks.cjs +0 -14
  279. package/bin/outbound-claim-provenance.cjs +0 -150
  280. package/bin/oversized-context-offload-policy.cjs +0 -86
  281. package/bin/pending-media-policy.cjs +0 -182
  282. package/bin/pending-token-estimate-policy.cjs +0 -41
  283. package/bin/personal-memory-consent-policy.cjs +0 -72
  284. package/bin/personal-memory-performance-policy.cjs +0 -12
  285. package/bin/personality-choice-policy.cjs +0 -101
  286. package/bin/personality-memory-adapter.cjs +0 -379
  287. package/bin/personality-mode.cjs +0 -46
  288. package/bin/personality-setup-policy.cjs +0 -197
  289. package/bin/proactive-compaction-policy.cjs +0 -25
  290. package/bin/profile-identity-resolution.cjs +0 -136
  291. package/bin/profile-runtime.cjs +0 -318
  292. package/bin/profile-tool-exclusion-policy.cjs +0 -37
  293. package/bin/programmatic-context-isolation.cjs +0 -25
  294. package/bin/programmatic-tool-runtime.mjs +0 -627
  295. package/bin/provider-idle-timeout-policy.cjs +0 -14
  296. package/bin/provider-model-refresh-deadline.cjs +0 -53
  297. package/bin/provider-model-refresh-policy.cjs +0 -107
  298. package/bin/rate-limit-recovery-policy.cjs +0 -47
  299. package/bin/read-batch-policy.cjs +0 -32
  300. package/bin/read-continuation-policy.cjs +0 -59
  301. package/bin/recurring-cron-history-policy.cjs +0 -124
  302. package/bin/relationship-continuity-policy.cjs +0 -143
  303. package/bin/relationship-curiosity-policy.cjs +0 -107
  304. package/bin/relationship-learning-policy.cjs +0 -168
  305. package/bin/release-artifact-freeze-policy.cjs +0 -30
  306. package/bin/reload-plugin-bootstrap.cjs +0 -18
  307. package/bin/reload-queue-policy.cjs +0 -38
  308. package/bin/repeated-assistant-response-policy.cjs +0 -232
  309. package/bin/repeated-injection-projection.cjs +0 -107
  310. package/bin/repeated-user-message-projection.cjs +0 -8
  311. package/bin/research-page-result.cjs +0 -74
  312. package/bin/retry-checkpoint-policy.cjs +0 -13
  313. package/bin/runtime-exit-ledger.cjs +0 -144
  314. package/bin/scoped-cron-run-policy.cjs +0 -358
  315. package/bin/session-checkpoint-policy.cjs +0 -25
  316. package/bin/session-compaction-policy.cjs +0 -84
  317. package/bin/session-replay-policy.cjs +0 -20
  318. package/bin/session-replay-window-policy.cjs +0 -40
  319. package/bin/session-resume-checkpoint.cjs +0 -254
  320. package/bin/session-scrollback-archive.cjs +0 -229
  321. package/bin/skill-activation-performance-policy.cjs +0 -69
  322. package/bin/skill-listing-performance-policy.cjs +0 -92
  323. package/bin/soul-organization-policy.cjs +0 -78
  324. package/bin/soul-preservation-policy.cjs +0 -20
  325. package/bin/startup-preferences.cjs +0 -131
  326. package/bin/streaming-flush-performance-policy.cjs +0 -28
  327. package/bin/structured-agent-swarm-output.cjs +0 -325
  328. package/bin/structured-subagent-output.cjs +0 -252
  329. package/bin/subagent-context-fork-policy.cjs +0 -155
  330. package/bin/subagent-max-tokens-handoff-policy.cjs +0 -69
  331. package/bin/subagent-parent-responsiveness.cjs +0 -19
  332. package/bin/subagent-skill-policy.cjs +0 -206
  333. package/bin/subagent-timeout-policy.cjs +0 -182
  334. package/bin/subagent-tool-policy.cjs +0 -60
  335. package/bin/subagent-usage-rollup-policy.cjs +0 -29
  336. package/bin/system-prompt-context-policy.cjs +0 -124
  337. package/bin/system-prompt-token-cache-policy.cjs +0 -60
  338. package/bin/telegram-addressed-focus.cjs +0 -55
  339. package/bin/telegram-addressed-priority.cjs +0 -12
  340. package/bin/telegram-approval-relay.cjs +0 -290
  341. package/bin/telegram-bot-priority.cjs +0 -17
  342. package/bin/telegram-console-status-policy.cjs +0 -174
  343. package/bin/telegram-context-projection-policy.cjs +0 -141
  344. package/bin/telegram-delivery-lifecycle.cjs +0 -125
  345. package/bin/telegram-direct-focus-policy.cjs +0 -273
  346. package/bin/telegram-mcp-compatibility.cjs +0 -49
  347. package/bin/telegram-media-delivery-policy.cjs +0 -42
  348. package/bin/telegram-private-conversation-policy.cjs +0 -185
  349. package/bin/telegram-queue-handoff-policy.cjs +0 -73
  350. package/bin/telegram-remote-status-policy.cjs +0 -120
  351. package/bin/telegram-session-queue-runtime.mjs +0 -306
  352. package/bin/telegram-text-chunk-policy.cjs +0 -63
  353. package/bin/telegram-truncated-reply-policy.cjs +0 -37
  354. package/bin/telegram-urgent-policy.cjs +0 -45
  355. package/bin/telemetry-spool-policy.cjs +0 -57
  356. package/bin/thinking-activity-status-policy.cjs +0 -132
  357. package/bin/thinking-only-guard.cjs +0 -80
  358. package/bin/todo-list-turn-policy.cjs +0 -131
  359. package/bin/tool-call-loop-policy.cjs +0 -51
  360. package/bin/tool-file-persistence.cjs +0 -141
  361. package/bin/tool-result-offload-policy.cjs +0 -359
  362. package/bin/tool-result-offload-telemetry.cjs +0 -12
  363. package/bin/tool-schema-token-cache-policy.cjs +0 -41
  364. package/bin/tool-stream-preview-policy.cjs +0 -9
  365. package/bin/tui-functional-contract.cjs +0 -55
  366. package/bin/turn-tool-performance-policy.cjs +0 -486
  367. package/bin/usage-cache-efficiency-policy.cjs +0 -26
  368. package/bin/user-home-path-policy.cjs +0 -13
  369. package/bin/user-message-offload-policy.cjs +0 -103
  370. package/bin/user-prompt-hook-origin-policy.cjs +0 -34
  371. package/bin/user-tool-record-policy.cjs +0 -7
  372. package/bin/validated-learning-insight-policy.cjs +0 -58
  373. package/bin/validated-learning-outcome-trace.cjs +0 -107
  374. package/bin/validated-learning-performance-policy.cjs +0 -53
  375. package/bin/validated-learning-signal.cjs +0 -463
  376. package/bin/windows-bash-dialect-policy.cjs +0 -25
  377. package/bin/windows-node-crash-dump.cjs +0 -110
  378. package/bin/write-continuation-policy.cjs +0 -69
  379. package/codebase-index/README.md +0 -82
  380. package/codebase-index/codebase_index.py +0 -470
  381. package/standard-skills/agent-browser/SKILL.md +0 -19
  382. package/standard-skills/agent-browser/references/runtime.md +0 -8
  383. package/standard-skills/blun-session-inspector/SKILL.md +0 -41
  384. package/standard-skills/blun-session-inspector/scripts/inspect-session.cjs +0 -437
  385. package/standard-skills/design-taste-frontend/SKILL.md +0 -1206
  386. package/standard-skills/full-output-enforcement/SKILL.md +0 -49
  387. package/standard-skills/high-end-visual-design/SKILL.md +0 -98
  388. package/standard-skills/image-to-code/SKILL.md +0 -1228
  389. package/standard-skills/industrial-brutalist-ui/SKILL.md +0 -92
  390. package/standard-skills/minimalist-ui/SKILL.md +0 -85
  391. package/standard-skills/motion-design-taste/SKILL.md +0 -74
  392. package/standard-skills/playwright-testing/SKILL.md +0 -19
  393. package/standard-skills/playwright-testing/references/runtime.md +0 -7
  394. package/standard-skills/premortem/SKILL.md +0 -148
  395. package/standard-skills/redesign-existing-projects/SKILL.md +0 -178
  396. package/standard-skills/research-evidence/SKILL.md +0 -39
  397. package/standard-skills/research-evidence/references/evidence-format.md +0 -104
  398. package/standard-skills/research-evidence/scripts/evidence-collection.cjs +0 -260
  399. package/standard-skills/research-evidence/scripts/score-report.cjs +0 -130
  400. package/standard-skills/screenshot-lesen/SKILL.md +0 -52
  401. package/standard-skills/stitch-design-taste/DESIGN.md +0 -121
  402. package/standard-skills/stitch-design-taste/SKILL.md +0 -184
  403. package/standard-skills/telegram-channel/SKILL.md +0 -18
  404. package/standard-skills/telegram-channel/references/runtime.md +0 -7
  405. package/standard-skills/venture-flywheel/SKILL.md +0 -32
  406. package/standard-skills/venture-flywheel/identity/project-identity.cjs +0 -146
  407. package/standard-skills/venture-flywheel/policy/capability-engine.cjs +0 -114
  408. package/standard-skills/venture-flywheel/policy/repository-trust.cjs +0 -229
  409. package/standard-skills/venture-flywheel/references/BEISPIELE-phase0.md +0 -146
  410. package/standard-skills/venture-flywheel/references/CAPABILITY-MAP.md +0 -34
  411. package/standard-skills/venture-flywheel/references/SPEC-phase0-identity-trust.md +0 -77
  412. package/standard-skills/venture-flywheel/references/SPEC-phase0-state-events.md +0 -93
  413. package/standard-skills/venture-flywheel/schemas/capability-decision.schema.json +0 -13
  414. package/standard-skills/venture-flywheel/schemas/execution-event.schema.json +0 -44
  415. package/standard-skills/venture-flywheel/schemas/project-identity.schema.json +0 -32
  416. package/standard-skills/venture-flywheel/schemas/repository-trust.schema.json +0 -57
  417. package/standard-skills/venture-flywheel/schemas/run-transition.schema.json +0 -59
  418. package/standard-skills/venture-flywheel/state/execution-event.cjs +0 -191
  419. package/standard-skills/venture-flywheel/state/task-state-machine.cjs +0 -190
  420. package/standard-skills/web-lesen/SKILL.md +0 -73
  421. package/standard-skills/web-lesen/scripts/crawl_public.py +0 -379
  422. package/standard-skills/windows-mcp/SKILL.md +0 -19
  423. package/standard-skills/windows-mcp/references/runtime.md +0 -9
  424. package/telegram-plugin/DELIVERY.md +0 -36
  425. package/telegram-plugin/bin/telegram-approval-relay.cjs +0 -290
  426. package/telegram-plugin/bin/telegram-console-status-policy.cjs +0 -175
  427. package/telegram-plugin/bin/telegram-delivery-lifecycle.cjs +0 -125
  428. package/telegram-plugin/bin/telegram-direct-reply-policy.cjs +0 -48
  429. package/telegram-plugin/bin/telegram-launcher-status-queue.cjs +0 -122
  430. package/telegram-plugin/bin/telegram-private-conversation-policy.cjs +0 -186
  431. package/telegram-plugin/bin/telegram-remote-status-policy.cjs +0 -121
  432. package/telegram-plugin/bin/telegram-reply-parts.cjs +0 -149
  433. package/telegram-plugin/bin/telegram-text-chunk-policy.cjs +0 -63
  434. package/telegram-plugin/bin/telegram-typing-keepalive.cjs +0 -89
  435. package/telegram-plugin/compat/mcp-server-fa511cd1.mjs +0 -73825
  436. /package/{bin → scripts}/fix-node-pty-perms.js +0 -0
@@ -0,0 +1,671 @@
1
+ #!/usr/bin/env python3
2
+ """Transactional queue state for provider-neutral website localization jobs.
3
+
4
+ The trusted host owns and opens the SQLite connection. This module deliberately
5
+ does not choose a filesystem path: secure, platform-specific database placement
6
+ belongs to the host. Queue completion records worker output, but never means
7
+ that the output is reviewed, signed, or ready for publication.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import hashlib
13
+ import json
14
+ import re
15
+ import secrets
16
+ import sqlite3
17
+ import time
18
+ import unicodedata
19
+ from contextlib import contextmanager
20
+ from dataclasses import dataclass
21
+ from typing import Any, Iterator, Mapping
22
+
23
+
24
+ SCHEMA_VERSION = 1
25
+ MAX_ATTEMPTS = 20
26
+ MAX_LEASE_SECONDS = 86_400.0
27
+ MAX_PAYLOAD_BYTES = 2_000_000
28
+ MAX_RESULT_BYTES = 2_000_000
29
+ QUALITY_PASSES = ["target_native", "source_fidelity"]
30
+ ERROR_CODE = re.compile(r"^[a-z][a-z0-9_.-]{0,127}$")
31
+ _STATUSES = ("pending", "leased", "retry_wait", "succeeded", "failed")
32
+ _COLUMNS = (
33
+ "job_id",
34
+ "target_locale",
35
+ "payload_json",
36
+ "payload_sha256",
37
+ "status",
38
+ "attempts",
39
+ "max_attempts",
40
+ "next_attempt_at",
41
+ "lease_owner",
42
+ "lease_token",
43
+ "lease_expires_at",
44
+ "last_error_code",
45
+ "last_error_detail_hash",
46
+ "result_json",
47
+ "result_sha256",
48
+ "created_at",
49
+ "updated_at",
50
+ )
51
+ _PLAN_COLUMNS = ("plan_id", "job_id", "target_locale", "created_at")
52
+
53
+
54
+ class LocalizationQueueBlocked(RuntimeError):
55
+ """Raised when queue state or an attempted transition is unsafe."""
56
+
57
+
58
+ @dataclass(frozen=True)
59
+ class ClaimedJob:
60
+ job_id: str
61
+ plan_ids: tuple[str, ...]
62
+ target_locale: str
63
+ payload: dict[str, Any]
64
+ attempt: int
65
+ max_attempts: int
66
+ lease_owner: str
67
+ lease_token: str
68
+ lease_expires_at: float
69
+
70
+
71
+ @dataclass(frozen=True)
72
+ class JobStatus:
73
+ job_id: str
74
+ plan_ids: tuple[str, ...]
75
+ target_locale: str
76
+ status: str
77
+ attempts: int
78
+ max_attempts: int
79
+ next_attempt_at: float
80
+ lease_expires_at: float | None
81
+ last_error_code: str | None
82
+ last_error_detail_hash: str | None
83
+ result_sha256: str | None
84
+ created_at: float
85
+ updated_at: float
86
+
87
+
88
+ def _canonical_json(value: Any) -> str:
89
+ try:
90
+ return json.dumps(
91
+ value,
92
+ ensure_ascii=False,
93
+ allow_nan=False,
94
+ sort_keys=True,
95
+ separators=(",", ":"),
96
+ )
97
+ except (TypeError, ValueError) as error:
98
+ raise LocalizationQueueBlocked("payload must be finite JSON") from error
99
+
100
+
101
+ def _hash_text(value: str) -> str:
102
+ return hashlib.sha256(value.encode("utf-8")).hexdigest()
103
+
104
+
105
+ def _validate_unicode(value: Any, *, field: str) -> None:
106
+ if isinstance(value, str):
107
+ if "\x00" in value or not unicodedata.is_normalized("NFC", value):
108
+ raise LocalizationQueueBlocked(f"{field} contains unsafe Unicode text")
109
+ elif isinstance(value, list):
110
+ for item in value:
111
+ _validate_unicode(item, field=field)
112
+ elif isinstance(value, dict):
113
+ for key, item in value.items():
114
+ if not isinstance(key, str):
115
+ raise LocalizationQueueBlocked(f"{field} object keys must be strings")
116
+ _validate_unicode(key, field=field)
117
+ _validate_unicode(item, field=field)
118
+
119
+
120
+ def _field(name: str, value: Any, *, limit: int = 256) -> str:
121
+ if not isinstance(value, str) or not value or value != value.strip():
122
+ raise LocalizationQueueBlocked(f"{name} must be a non-empty string without outer whitespace")
123
+ if len(value) > limit or "\x00" in value or not unicodedata.is_normalized("NFC", value):
124
+ raise LocalizationQueueBlocked(f"{name} is invalid or too long")
125
+ return value
126
+
127
+
128
+ def _timestamp(name: str, value: float | int | None) -> float:
129
+ if value is None:
130
+ return time.time()
131
+ if isinstance(value, bool) or not isinstance(value, (int, float)):
132
+ raise LocalizationQueueBlocked(f"{name} must be a finite timestamp")
133
+ value = float(value)
134
+ if value < 0 or value != value or value in {float("inf"), float("-inf")}:
135
+ raise LocalizationQueueBlocked(f"{name} must be a finite timestamp")
136
+ return value
137
+
138
+
139
+ def _duration(name: str, value: float | int, *, allow_zero: bool = False) -> float:
140
+ value = _timestamp(name, value)
141
+ if (value < 0 if allow_zero else value <= 0) or value > MAX_LEASE_SECONDS:
142
+ raise LocalizationQueueBlocked(f"{name} is outside the supported range")
143
+ return value
144
+
145
+
146
+ def _error(code: Any, detail: Any = None) -> tuple[str, str | None]:
147
+ code = _field("error_code", code, limit=128)
148
+ if not ERROR_CODE.fullmatch(code):
149
+ raise LocalizationQueueBlocked("error_code has an invalid format")
150
+ if detail is None:
151
+ return code, None
152
+ if not isinstance(detail, str):
153
+ raise LocalizationQueueBlocked("error_detail must be a string")
154
+ if "\x00" in detail or not unicodedata.is_normalized("NFC", detail):
155
+ raise LocalizationQueueBlocked("error_detail contains unsafe Unicode text")
156
+ return code, _hash_text(detail)
157
+
158
+
159
+ @contextmanager
160
+ def _transaction(connection: sqlite3.Connection) -> Iterator[None]:
161
+ if connection.in_transaction:
162
+ raise LocalizationQueueBlocked("queue operation cannot join an external transaction")
163
+ try:
164
+ connection.execute("BEGIN IMMEDIATE")
165
+ yield
166
+ except Exception:
167
+ connection.rollback()
168
+ raise
169
+ else:
170
+ connection.commit()
171
+
172
+
173
+ class LocalizationQueue:
174
+ """Atomic at-least-once queue with expiring, attempt-bound worker leases."""
175
+
176
+ def __init__(self, connection: sqlite3.Connection):
177
+ if not isinstance(connection, sqlite3.Connection):
178
+ raise LocalizationQueueBlocked("connection must be sqlite3.Connection")
179
+ self.connection = connection
180
+ self.connection.row_factory = sqlite3.Row
181
+ self.connection.execute("PRAGMA foreign_keys = ON")
182
+ self.connection.execute("PRAGMA busy_timeout = 5000")
183
+ version = int(self.connection.execute("PRAGMA user_version").fetchone()[0])
184
+ if version not in {0, SCHEMA_VERSION}:
185
+ raise LocalizationQueueBlocked("unsupported localization queue schema")
186
+ if version == 0:
187
+ self._create_schema()
188
+ self._verify_schema()
189
+
190
+ def _create_schema(self) -> None:
191
+ with _transaction(self.connection):
192
+ version = int(self.connection.execute("PRAGMA user_version").fetchone()[0])
193
+ if version == SCHEMA_VERSION:
194
+ return
195
+ if version != 0:
196
+ raise LocalizationQueueBlocked("unsupported localization queue schema")
197
+ self.connection.execute(f"""
198
+ CREATE TABLE localization_jobs (
199
+ job_id TEXT PRIMARY KEY,
200
+ target_locale TEXT NOT NULL,
201
+ payload_json TEXT NOT NULL,
202
+ payload_sha256 TEXT NOT NULL,
203
+ status TEXT NOT NULL CHECK (status IN {_STATUSES}),
204
+ attempts INTEGER NOT NULL DEFAULT 0 CHECK (attempts >= 0),
205
+ max_attempts INTEGER NOT NULL CHECK (max_attempts BETWEEN 1 AND {MAX_ATTEMPTS}),
206
+ next_attempt_at REAL NOT NULL,
207
+ lease_owner TEXT,
208
+ lease_token TEXT,
209
+ lease_expires_at REAL,
210
+ last_error_code TEXT,
211
+ last_error_detail_hash TEXT,
212
+ result_json TEXT,
213
+ result_sha256 TEXT,
214
+ created_at REAL NOT NULL,
215
+ updated_at REAL NOT NULL
216
+ )
217
+ """)
218
+ self.connection.execute("""
219
+ CREATE TABLE localization_plan_jobs (
220
+ plan_id TEXT NOT NULL,
221
+ job_id TEXT NOT NULL,
222
+ target_locale TEXT NOT NULL,
223
+ created_at REAL NOT NULL,
224
+ PRIMARY KEY (plan_id, job_id),
225
+ UNIQUE (plan_id, target_locale),
226
+ FOREIGN KEY (job_id) REFERENCES localization_jobs (job_id)
227
+ )
228
+ """)
229
+ self.connection.execute("""
230
+ CREATE INDEX localization_jobs_ready
231
+ ON localization_jobs (status, next_attempt_at, created_at, job_id)
232
+ """)
233
+ self.connection.execute("""
234
+ CREATE INDEX localization_plan_jobs_job
235
+ ON localization_plan_jobs (job_id, plan_id)
236
+ """)
237
+ self.connection.execute(f"PRAGMA user_version = {SCHEMA_VERSION}")
238
+
239
+ def _verify_schema(self) -> None:
240
+ rows = self.connection.execute("PRAGMA table_info(localization_jobs)").fetchall()
241
+ columns = tuple(row["name"] for row in rows)
242
+ if columns != _COLUMNS:
243
+ raise LocalizationQueueBlocked("localization queue schema is incomplete or altered")
244
+ plan_rows = self.connection.execute("PRAGMA table_info(localization_plan_jobs)").fetchall()
245
+ plan_columns = tuple(row["name"] for row in plan_rows)
246
+ if plan_columns != _PLAN_COLUMNS:
247
+ raise LocalizationQueueBlocked("localization plan mapping schema is incomplete or altered")
248
+
249
+ def enqueue_plan(
250
+ self,
251
+ plan: Any,
252
+ *,
253
+ max_attempts: int = 3,
254
+ now: float | int | None = None,
255
+ ) -> int:
256
+ if isinstance(max_attempts, bool) or not isinstance(max_attempts, int):
257
+ raise LocalizationQueueBlocked("max_attempts must be an integer")
258
+ if not 1 <= max_attempts <= MAX_ATTEMPTS:
259
+ raise LocalizationQueueBlocked("max_attempts is outside the supported range")
260
+ now = _timestamp("now", now)
261
+ plan_id = _field("plan_id", getattr(plan, "plan_id", None))
262
+ jobs = getattr(plan, "jobs", None)
263
+ if not isinstance(jobs, tuple) or not jobs:
264
+ raise LocalizationQueueBlocked("plan must contain an immutable non-empty job tuple")
265
+
266
+ prepared: list[tuple[str, str, str, str]] = []
267
+ targets: set[str] = set()
268
+ for job in jobs:
269
+ job_id = _field("job_id", getattr(job, "job_id", None))
270
+ payload_method = getattr(job, "as_payload", None)
271
+ if not callable(payload_method):
272
+ raise LocalizationQueueBlocked("every plan job must expose as_payload")
273
+ payload = payload_method()
274
+ if not isinstance(payload, dict):
275
+ raise LocalizationQueueBlocked("job payload must be a JSON object")
276
+ _validate_unicode(payload, field="job payload")
277
+ if payload.get("job_id") != job_id or payload.get("idempotency_key") != job_id:
278
+ raise LocalizationQueueBlocked("job identity and idempotency key must match")
279
+ target = payload.get("target")
280
+ target_locale = target.get("locale") if isinstance(target, dict) else None
281
+ target_locale = _field("target_locale", target_locale)
282
+ if target_locale in targets:
283
+ raise LocalizationQueueBlocked("plan contains duplicate target locales")
284
+ targets.add(target_locale)
285
+ if payload.get("quality_passes") != QUALITY_PASSES:
286
+ raise LocalizationQueueBlocked("job must require the two ordered quality passes")
287
+ if payload.get("release_required") is not True:
288
+ raise LocalizationQueueBlocked("job must require a signed release")
289
+ payload_json = _canonical_json(payload)
290
+ if len(payload_json.encode("utf-8")) > MAX_PAYLOAD_BYTES:
291
+ raise LocalizationQueueBlocked("job payload exceeds the byte limit")
292
+ prepared.append((job_id, target_locale, payload_json, _hash_text(payload_json)))
293
+
294
+ inserted = 0
295
+ with _transaction(self.connection):
296
+ for job_id, target_locale, payload_json, payload_hash in prepared:
297
+ existing = self.connection.execute(
298
+ "SELECT payload_sha256 FROM localization_jobs WHERE job_id = ?",
299
+ (job_id,),
300
+ ).fetchone()
301
+ if existing is not None:
302
+ if existing["payload_sha256"] != payload_hash:
303
+ raise LocalizationQueueBlocked("idempotency collision with different job content")
304
+ else:
305
+ self.connection.execute("""
306
+ INSERT INTO localization_jobs (
307
+ job_id, target_locale, payload_json, payload_sha256,
308
+ status, attempts, max_attempts, next_attempt_at,
309
+ created_at, updated_at
310
+ ) VALUES (?, ?, ?, ?, 'pending', 0, ?, ?, ?, ?)
311
+ """, (
312
+ job_id,
313
+ target_locale,
314
+ payload_json,
315
+ payload_hash,
316
+ max_attempts,
317
+ now,
318
+ now,
319
+ now,
320
+ ))
321
+ inserted += 1
322
+ mapped = self.connection.execute("""
323
+ SELECT job_id FROM localization_plan_jobs
324
+ WHERE plan_id = ? AND target_locale = ?
325
+ """, (plan_id, target_locale)).fetchone()
326
+ if mapped is not None and mapped["job_id"] != job_id:
327
+ raise LocalizationQueueBlocked("plan target locale maps to different job content")
328
+ self.connection.execute("""
329
+ INSERT OR IGNORE INTO localization_plan_jobs (
330
+ plan_id, job_id, target_locale, created_at
331
+ ) VALUES (?, ?, ?, ?)
332
+ """, (plan_id, job_id, target_locale, now))
333
+ return inserted
334
+
335
+ def claim(
336
+ self,
337
+ worker_id: Any,
338
+ *,
339
+ now: float | int | None = None,
340
+ lease_seconds: float | int = 300,
341
+ eligible_plan_ids: tuple[str, ...] | None = None,
342
+ ) -> ClaimedJob | None:
343
+ worker_id = _field("worker_id", worker_id, limit=128)
344
+ now = _timestamp("now", now)
345
+ lease_seconds = _duration("lease_seconds", lease_seconds)
346
+ if eligible_plan_ids is not None:
347
+ if not isinstance(eligible_plan_ids, tuple):
348
+ raise LocalizationQueueBlocked("eligible_plan_ids must be an immutable tuple")
349
+ eligible_plan_ids = tuple(sorted({
350
+ _field("eligible_plan_id", value) for value in eligible_plan_ids
351
+ }))
352
+ blocked_error: str | None = None
353
+ claimed: ClaimedJob | None = None
354
+ with _transaction(self.connection):
355
+ self.connection.execute("""
356
+ UPDATE localization_jobs
357
+ SET status = 'failed', lease_owner = NULL, lease_token = NULL,
358
+ lease_expires_at = NULL, last_error_code = 'lease_expired',
359
+ updated_at = ?
360
+ WHERE status = 'leased' AND lease_expires_at <= ?
361
+ AND attempts >= max_attempts
362
+ """, (now, now))
363
+ self.connection.execute("""
364
+ UPDATE localization_jobs
365
+ SET status = 'retry_wait', next_attempt_at = ?,
366
+ lease_owner = NULL, lease_token = NULL, lease_expires_at = NULL,
367
+ last_error_code = 'lease_expired', updated_at = ?
368
+ WHERE status = 'leased' AND lease_expires_at <= ?
369
+ AND attempts < max_attempts
370
+ """, (now, now, now))
371
+ if eligible_plan_ids is not None and not eligible_plan_ids:
372
+ row = None
373
+ elif eligible_plan_ids is None:
374
+ row = self.connection.execute("""
375
+ SELECT * FROM localization_jobs
376
+ WHERE status IN ('pending', 'retry_wait')
377
+ AND next_attempt_at <= ? AND attempts < max_attempts
378
+ ORDER BY created_at, target_locale, job_id
379
+ LIMIT 1
380
+ """, (now,)).fetchone()
381
+ else:
382
+ placeholders = ",".join("?" for _ in eligible_plan_ids)
383
+ row = self.connection.execute(f"""
384
+ SELECT jobs.* FROM localization_jobs AS jobs
385
+ WHERE jobs.status IN ('pending', 'retry_wait')
386
+ AND jobs.next_attempt_at <= ? AND jobs.attempts < jobs.max_attempts
387
+ AND EXISTS (
388
+ SELECT 1 FROM localization_plan_jobs AS mapping
389
+ WHERE mapping.job_id = jobs.job_id
390
+ AND mapping.plan_id IN ({placeholders})
391
+ )
392
+ ORDER BY jobs.created_at, jobs.target_locale, jobs.job_id
393
+ LIMIT 1
394
+ """, (now, *eligible_plan_ids)).fetchone()
395
+ if row is not None:
396
+ payload_hash = _hash_text(row["payload_json"])
397
+ if payload_hash != row["payload_sha256"]:
398
+ self.connection.execute("""
399
+ UPDATE localization_jobs
400
+ SET status = 'failed', last_error_code = 'payload_integrity',
401
+ updated_at = ?
402
+ WHERE job_id = ?
403
+ """, (now, row["job_id"]))
404
+ blocked_error = "queued payload failed its integrity check"
405
+ else:
406
+ try:
407
+ payload = json.loads(row["payload_json"])
408
+ except json.JSONDecodeError:
409
+ self.connection.execute("""
410
+ UPDATE localization_jobs
411
+ SET status = 'failed', last_error_code = 'payload_integrity',
412
+ updated_at = ?
413
+ WHERE job_id = ?
414
+ """, (now, row["job_id"]))
415
+ blocked_error = "queued payload is no longer valid JSON"
416
+ else:
417
+ lease_token = secrets.token_urlsafe(32)
418
+ lease_expires_at = now + lease_seconds
419
+ updated = self.connection.execute("""
420
+ UPDATE localization_jobs
421
+ SET status = 'leased', attempts = attempts + 1,
422
+ lease_owner = ?, lease_token = ?, lease_expires_at = ?,
423
+ last_error_code = NULL, last_error_detail_hash = NULL,
424
+ updated_at = ?
425
+ WHERE job_id = ? AND status IN ('pending', 'retry_wait')
426
+ """, (
427
+ worker_id,
428
+ lease_token,
429
+ lease_expires_at,
430
+ now,
431
+ row["job_id"],
432
+ ))
433
+ if updated.rowcount != 1:
434
+ raise LocalizationQueueBlocked("claim lost its transactional job identity")
435
+ claimed = ClaimedJob(
436
+ job_id=row["job_id"],
437
+ plan_ids=self._plan_ids(row["job_id"]),
438
+ target_locale=row["target_locale"],
439
+ payload=payload,
440
+ attempt=int(row["attempts"]) + 1,
441
+ max_attempts=int(row["max_attempts"]),
442
+ lease_owner=worker_id,
443
+ lease_token=lease_token,
444
+ lease_expires_at=lease_expires_at,
445
+ )
446
+ if blocked_error:
447
+ raise LocalizationQueueBlocked(blocked_error)
448
+ return claimed
449
+
450
+ def renew(
451
+ self,
452
+ claim: ClaimedJob,
453
+ *,
454
+ now: float | int | None = None,
455
+ lease_seconds: float | int = 300,
456
+ ) -> ClaimedJob:
457
+ claim = self._claim_identity(claim)
458
+ now = _timestamp("now", now)
459
+ lease_seconds = _duration("lease_seconds", lease_seconds)
460
+ new_expiry = now + lease_seconds
461
+ with _transaction(self.connection):
462
+ self._require_live_lease(claim, now)
463
+ updated = self.connection.execute("""
464
+ UPDATE localization_jobs
465
+ SET lease_expires_at = ?, updated_at = ?
466
+ WHERE job_id = ? AND status = 'leased'
467
+ AND lease_owner = ? AND lease_token = ?
468
+ """, (
469
+ new_expiry,
470
+ now,
471
+ claim.job_id,
472
+ claim.lease_owner,
473
+ claim.lease_token,
474
+ ))
475
+ if updated.rowcount != 1:
476
+ raise LocalizationQueueBlocked("lease renewal lost its job identity")
477
+ return ClaimedJob(
478
+ **{**claim.__dict__, "lease_expires_at": new_expiry},
479
+ )
480
+
481
+ def complete(
482
+ self,
483
+ claim: ClaimedJob,
484
+ result: Any,
485
+ *,
486
+ now: float | int | None = None,
487
+ ) -> JobStatus:
488
+ claim = self._claim_identity(claim)
489
+ now = _timestamp("now", now)
490
+ if not isinstance(result, dict):
491
+ raise LocalizationQueueBlocked("worker result must be a JSON object")
492
+ _validate_unicode(result, field="worker result")
493
+ result_json = _canonical_json(result)
494
+ if len(result_json.encode("utf-8")) > MAX_RESULT_BYTES:
495
+ raise LocalizationQueueBlocked("worker result exceeds the byte limit")
496
+ result_hash = _hash_text(result_json)
497
+ with _transaction(self.connection):
498
+ self._require_live_lease(claim, now)
499
+ updated = self.connection.execute("""
500
+ UPDATE localization_jobs
501
+ SET status = 'succeeded', lease_owner = NULL, lease_token = NULL,
502
+ lease_expires_at = NULL, result_json = ?, result_sha256 = ?,
503
+ last_error_code = NULL, last_error_detail_hash = NULL,
504
+ updated_at = ?
505
+ WHERE job_id = ? AND status = 'leased'
506
+ AND lease_owner = ? AND lease_token = ?
507
+ """, (
508
+ result_json,
509
+ result_hash,
510
+ now,
511
+ claim.job_id,
512
+ claim.lease_owner,
513
+ claim.lease_token,
514
+ ))
515
+ if updated.rowcount != 1:
516
+ raise LocalizationQueueBlocked("completion lost its job identity")
517
+ return self.status(claim.job_id)
518
+
519
+ def retry(
520
+ self,
521
+ claim: ClaimedJob,
522
+ error_code: Any,
523
+ *,
524
+ error_detail: Any = None,
525
+ delay_seconds: float | int = 0,
526
+ now: float | int | None = None,
527
+ ) -> JobStatus:
528
+ claim = self._claim_identity(claim)
529
+ error_code, detail_hash = _error(error_code, error_detail)
530
+ now = _timestamp("now", now)
531
+ delay_seconds = _duration("delay_seconds", delay_seconds, allow_zero=True)
532
+ with _transaction(self.connection):
533
+ row = self._require_live_lease(claim, now)
534
+ terminal = int(row["attempts"]) >= int(row["max_attempts"])
535
+ status = "failed" if terminal else "retry_wait"
536
+ next_attempt_at = now if terminal else now + delay_seconds
537
+ updated = self.connection.execute("""
538
+ UPDATE localization_jobs
539
+ SET status = ?, next_attempt_at = ?, lease_owner = NULL,
540
+ lease_token = NULL, lease_expires_at = NULL,
541
+ last_error_code = ?, last_error_detail_hash = ?, updated_at = ?
542
+ WHERE job_id = ? AND status = 'leased'
543
+ AND lease_owner = ? AND lease_token = ?
544
+ """, (
545
+ status,
546
+ next_attempt_at,
547
+ error_code,
548
+ detail_hash,
549
+ now,
550
+ claim.job_id,
551
+ claim.lease_owner,
552
+ claim.lease_token,
553
+ ))
554
+ if updated.rowcount != 1:
555
+ raise LocalizationQueueBlocked("retry lost its job identity")
556
+ return self.status(claim.job_id)
557
+
558
+ def fail(
559
+ self,
560
+ claim: ClaimedJob,
561
+ error_code: Any,
562
+ *,
563
+ error_detail: Any = None,
564
+ now: float | int | None = None,
565
+ ) -> JobStatus:
566
+ claim = self._claim_identity(claim)
567
+ error_code, detail_hash = _error(error_code, error_detail)
568
+ now = _timestamp("now", now)
569
+ with _transaction(self.connection):
570
+ self._require_live_lease(claim, now)
571
+ updated = self.connection.execute("""
572
+ UPDATE localization_jobs
573
+ SET status = 'failed', lease_owner = NULL, lease_token = NULL,
574
+ lease_expires_at = NULL, last_error_code = ?,
575
+ last_error_detail_hash = ?, updated_at = ?
576
+ WHERE job_id = ? AND status = 'leased'
577
+ AND lease_owner = ? AND lease_token = ?
578
+ """, (
579
+ error_code,
580
+ detail_hash,
581
+ now,
582
+ claim.job_id,
583
+ claim.lease_owner,
584
+ claim.lease_token,
585
+ ))
586
+ if updated.rowcount != 1:
587
+ raise LocalizationQueueBlocked("failure transition lost its job identity")
588
+ return self.status(claim.job_id)
589
+
590
+ def status(self, job_id: Any) -> JobStatus:
591
+ job_id = _field("job_id", job_id)
592
+ row = self.connection.execute("""
593
+ SELECT job_id, target_locale, status, attempts, max_attempts,
594
+ next_attempt_at, lease_expires_at, last_error_code,
595
+ last_error_detail_hash, result_sha256, created_at, updated_at
596
+ FROM localization_jobs WHERE job_id = ?
597
+ """, (job_id,)).fetchone()
598
+ if row is None:
599
+ raise LocalizationQueueBlocked("unknown localization job")
600
+ return JobStatus(plan_ids=self._plan_ids(job_id), **dict(row))
601
+
602
+ def result(self, job_id: Any) -> dict[str, Any]:
603
+ """Return one completed result only after rechecking its stored bytes."""
604
+ job_id = _field("job_id", job_id)
605
+ row = self.connection.execute("""
606
+ SELECT status, result_json, result_sha256
607
+ FROM localization_jobs WHERE job_id = ?
608
+ """, (job_id,)).fetchone()
609
+ if row is None:
610
+ raise LocalizationQueueBlocked("unknown localization job")
611
+ if row["status"] != "succeeded":
612
+ raise LocalizationQueueBlocked("localization job has no completed result")
613
+ if not isinstance(row["result_json"], str) or not isinstance(row["result_sha256"], str):
614
+ raise LocalizationQueueBlocked("completed result is missing")
615
+ if _hash_text(row["result_json"]) != row["result_sha256"]:
616
+ raise LocalizationQueueBlocked("completed result failed its integrity check")
617
+ try:
618
+ result = json.loads(row["result_json"])
619
+ except json.JSONDecodeError as error:
620
+ raise LocalizationQueueBlocked("completed result is no longer valid JSON") from error
621
+ if not isinstance(result, dict) or result.get("job_id") != job_id:
622
+ raise LocalizationQueueBlocked("completed result has the wrong job identity")
623
+ _validate_unicode(result, field="worker result")
624
+ if _canonical_json(result) != row["result_json"]:
625
+ raise LocalizationQueueBlocked("completed result is not canonical JSON")
626
+ return result
627
+
628
+ def plan_counts(self, plan_id: Any) -> dict[str, int]:
629
+ plan_id = _field("plan_id", plan_id)
630
+ counts = {status: 0 for status in _STATUSES}
631
+ rows = self.connection.execute("""
632
+ SELECT jobs.status, COUNT(*) AS count
633
+ FROM localization_plan_jobs AS plans
634
+ JOIN localization_jobs AS jobs ON jobs.job_id = plans.job_id
635
+ WHERE plans.plan_id = ? GROUP BY jobs.status
636
+ """, (plan_id,)).fetchall()
637
+ for row in rows:
638
+ counts[row["status"]] = int(row["count"])
639
+ return counts
640
+
641
+ def _plan_ids(self, job_id: str) -> tuple[str, ...]:
642
+ rows = self.connection.execute("""
643
+ SELECT plan_id FROM localization_plan_jobs
644
+ WHERE job_id = ? ORDER BY plan_id
645
+ """, (job_id,)).fetchall()
646
+ plan_ids = tuple(row["plan_id"] for row in rows)
647
+ if not plan_ids:
648
+ raise LocalizationQueueBlocked("localization job has no owning plan")
649
+ return plan_ids
650
+
651
+ def _claim_identity(self, claim: Any) -> ClaimedJob:
652
+ if not isinstance(claim, ClaimedJob):
653
+ raise LocalizationQueueBlocked("transition requires the exact claimed job")
654
+ _field("job_id", claim.job_id)
655
+ _field("lease_owner", claim.lease_owner, limit=128)
656
+ _field("lease_token", claim.lease_token, limit=128)
657
+ return claim
658
+
659
+ def _require_live_lease(self, claim: ClaimedJob, now: float) -> sqlite3.Row:
660
+ row = self.connection.execute("""
661
+ SELECT status, attempts, max_attempts, lease_owner, lease_token,
662
+ lease_expires_at
663
+ FROM localization_jobs WHERE job_id = ?
664
+ """, (claim.job_id,)).fetchone()
665
+ if row is None or row["status"] != "leased":
666
+ raise LocalizationQueueBlocked("job no longer has an active lease")
667
+ if row["lease_owner"] != claim.lease_owner or row["lease_token"] != claim.lease_token:
668
+ raise LocalizationQueueBlocked("claim does not own the active lease")
669
+ if row["lease_expires_at"] <= now:
670
+ raise LocalizationQueueBlocked("claim lease has expired")
671
+ return row