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,634 @@
1
+ #!/usr/bin/env python3
2
+ """Durably observe CMS processing after a terminal notification was accepted."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import hashlib
7
+ import json
8
+ import math
9
+ import re
10
+ import secrets
11
+ import sqlite3
12
+ from contextlib import contextmanager
13
+ from dataclasses import dataclass
14
+ from typing import Any, Callable, Iterator, Mapping
15
+
16
+
17
+ SCHEMA_VERSION = 1
18
+ STATUS_SCHEMA = "blun.cms-terminal-receiver-status-response.v1"
19
+ NOTIFICATION_SCHEMA = "blun.cms-source-terminal-notification.v1"
20
+ NOTIFICATION_FIELDS = {
21
+ "schema", "notification_id", "event_id", "site_id", "plan_id",
22
+ "website_version", "source_sequence", "job_count", "change_sha256",
23
+ "lifecycle_binding_sha256", "terminal_status", "lifecycle_sha256",
24
+ }
25
+ STATUSES = ("pending", "leased", "watching", "retry_wait", "succeeded", "failed")
26
+ REMOTE_STATUSES = ("pending", "leased", "retry_wait", "succeeded", "failed")
27
+ TERMINAL_STATUSES = {
28
+ "cancelled", "deleted", "deletion_failed", "localization_failed",
29
+ "publication_blocked", "publication_failed", "published", "superseded",
30
+ }
31
+ TOKEN = re.compile(r"^[A-Za-z0-9_.:-]{1,256}$")
32
+ SHA256 = re.compile(r"^[0-9a-f]{64}$")
33
+ ERROR_CODE = re.compile(r"^[a-z][a-z0-9_.-]{0,127}$")
34
+ _COLUMNS = (
35
+ "event_id", "notification_id", "site_id", "terminal_status",
36
+ "notification_sha256", "status", "poll_attempts", "failures",
37
+ "max_failures", "next_poll_at", "lease_owner", "lease_token",
38
+ "lease_expires_at", "last_error_code", "receiver_status",
39
+ "receiver_attempts", "receiver_max_attempts", "receiver_next_attempt_at",
40
+ "receiver_lease_expires_at", "receiver_lease_expired",
41
+ "receiver_error_code", "receiver_processed_at", "created_at", "updated_at",
42
+ )
43
+
44
+
45
+ class TerminalProcessingMonitorBlocked(RuntimeError):
46
+ """Stable, content-free processing-observation failure."""
47
+
48
+ def __init__(self, code: str):
49
+ super().__init__(code)
50
+ self.code = code
51
+
52
+
53
+ @dataclass(frozen=True)
54
+ class TerminalProcessingStatus:
55
+ event_id: str
56
+ notification_id: str
57
+ site_id: str
58
+ notification_sha256: str
59
+ status: str
60
+ poll_attempts: int
61
+ failures: int
62
+ max_failures: int
63
+ next_poll_at: float
64
+ lease_expires_at: float | None
65
+ lease_expired: bool
66
+ last_error_code: str | None
67
+ receiver_status: str | None
68
+ receiver_attempts: int | None
69
+ receiver_max_attempts: int | None
70
+ receiver_error_code: str | None
71
+ receiver_processed_at: float | None
72
+
73
+
74
+ @dataclass(frozen=True)
75
+ class TerminalProcessingOutcome:
76
+ event_id: str
77
+ notification_id: str
78
+ status: str
79
+ attempt: int
80
+ error_code: str | None
81
+
82
+
83
+ @dataclass(frozen=True)
84
+ class TerminalProcessingHealth:
85
+ status: str
86
+ counts: dict[str, int]
87
+ due: int
88
+ expired_leases: int
89
+ failed: int
90
+
91
+
92
+ def _blocked(code: str) -> None:
93
+ raise TerminalProcessingMonitorBlocked("terminal_processing_monitor." + code)
94
+
95
+
96
+ def _token(value: Any) -> bool:
97
+ return isinstance(value, str) and TOKEN.fullmatch(value) is not None
98
+
99
+
100
+ def _sha(value: Any) -> bool:
101
+ return isinstance(value, str) and SHA256.fullmatch(value) is not None
102
+
103
+
104
+ def _error(value: Any) -> bool:
105
+ return isinstance(value, str) and ERROR_CODE.fullmatch(value) is not None
106
+
107
+
108
+ def _canonical_sha256(value: Any) -> str:
109
+ try:
110
+ rendered = json.dumps(
111
+ value, ensure_ascii=False, allow_nan=False, sort_keys=True,
112
+ separators=(",", ":"),
113
+ )
114
+ except (TypeError, ValueError, RecursionError):
115
+ _blocked("notification_invalid")
116
+ return hashlib.sha256(rendered.encode("utf-8")).hexdigest()
117
+
118
+
119
+ def _time(value: Any, *, nullable: bool = False) -> float | None:
120
+ if value is None and nullable:
121
+ return None
122
+ if (
123
+ isinstance(value, bool) or not isinstance(value, (int, float))
124
+ or not math.isfinite(float(value)) or float(value) < 0
125
+ ):
126
+ _blocked("time_invalid")
127
+ return float(value)
128
+
129
+
130
+ def _valid_time(value: Any, *, nullable: bool = False) -> bool:
131
+ if value is None:
132
+ return nullable
133
+ return (
134
+ not isinstance(value, bool) and isinstance(value, (int, float))
135
+ and math.isfinite(float(value)) and float(value) >= 0
136
+ )
137
+
138
+
139
+ def _duration(value: Any, code: str) -> float:
140
+ result = _time(value)
141
+ if result is None or not 0 < result <= 86_400:
142
+ _blocked(code)
143
+ return result
144
+
145
+
146
+ def _positive_integer(value: Any, code: str) -> int:
147
+ if isinstance(value, bool) or not isinstance(value, int) or not 1 <= value <= 20:
148
+ _blocked(code)
149
+ return value
150
+
151
+
152
+ @contextmanager
153
+ def _transaction(connection: sqlite3.Connection) -> Iterator[None]:
154
+ if connection.in_transaction:
155
+ _blocked("transaction_nested")
156
+ try:
157
+ connection.execute("BEGIN IMMEDIATE")
158
+ yield
159
+ connection.commit()
160
+ except Exception:
161
+ if connection.in_transaction:
162
+ connection.rollback()
163
+ raise
164
+
165
+
166
+ class DurableTerminalProcessingMonitor:
167
+ """Poll exact receiver status with durable leases and bounded failures."""
168
+
169
+ def __init__(
170
+ self,
171
+ connection: sqlite3.Connection,
172
+ *,
173
+ poll_interval_seconds: float | int = 30,
174
+ base_delay_seconds: float | int = 5,
175
+ max_delay_seconds: float | int = 300,
176
+ ):
177
+ if not isinstance(connection, sqlite3.Connection):
178
+ raise TypeError("connection must be sqlite3.Connection")
179
+ self.connection = connection
180
+ self.connection.row_factory = sqlite3.Row
181
+ self.poll_interval_seconds = _duration(
182
+ poll_interval_seconds, "poll_interval_invalid"
183
+ )
184
+ self.base_delay_seconds = _duration(base_delay_seconds, "delay_invalid")
185
+ self.max_delay_seconds = _duration(max_delay_seconds, "delay_invalid")
186
+ if self.base_delay_seconds > self.max_delay_seconds:
187
+ _blocked("delay_invalid")
188
+ self._initialize()
189
+
190
+ def _initialize(self) -> None:
191
+ with _transaction(self.connection):
192
+ self.connection.execute("""
193
+ CREATE TABLE IF NOT EXISTS cms_source_terminal_processing_meta (
194
+ singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
195
+ schema_version INTEGER NOT NULL CHECK (schema_version = 1)
196
+ )
197
+ """)
198
+ self.connection.execute("""
199
+ INSERT OR IGNORE INTO cms_source_terminal_processing_meta VALUES (1, 1)
200
+ """)
201
+ self.connection.execute("""
202
+ CREATE TABLE IF NOT EXISTS cms_source_terminal_processing (
203
+ event_id TEXT PRIMARY KEY, notification_id TEXT NOT NULL UNIQUE,
204
+ site_id TEXT NOT NULL, terminal_status TEXT NOT NULL,
205
+ notification_sha256 TEXT NOT NULL,
206
+ status TEXT NOT NULL CHECK (status IN (
207
+ 'pending','leased','watching','retry_wait','succeeded','failed'
208
+ )), poll_attempts INTEGER NOT NULL CHECK (poll_attempts >= 0),
209
+ failures INTEGER NOT NULL CHECK (failures >= 0),
210
+ max_failures INTEGER NOT NULL CHECK (max_failures BETWEEN 1 AND 20),
211
+ next_poll_at REAL NOT NULL, lease_owner TEXT, lease_token TEXT,
212
+ lease_expires_at REAL, last_error_code TEXT, receiver_status TEXT,
213
+ receiver_attempts INTEGER, receiver_max_attempts INTEGER,
214
+ receiver_next_attempt_at REAL, receiver_lease_expires_at REAL,
215
+ receiver_lease_expired INTEGER, receiver_error_code TEXT,
216
+ receiver_processed_at REAL, created_at REAL NOT NULL,
217
+ updated_at REAL NOT NULL,
218
+ CHECK ((status = 'leased' AND lease_owner IS NOT NULL
219
+ AND lease_token IS NOT NULL AND lease_expires_at IS NOT NULL)
220
+ OR (status <> 'leased' AND lease_owner IS NULL
221
+ AND lease_token IS NULL AND lease_expires_at IS NULL))
222
+ )
223
+ """)
224
+ self.connection.execute("""
225
+ CREATE INDEX IF NOT EXISTS cms_source_terminal_processing_due
226
+ ON cms_source_terminal_processing
227
+ (status, next_poll_at, created_at, event_id)
228
+ """)
229
+ self._validate_schema()
230
+
231
+ def _validate_schema(self) -> None:
232
+ meta_columns = tuple(row["name"] for row in self.connection.execute(
233
+ "PRAGMA table_info(cms_source_terminal_processing_meta)"
234
+ ).fetchall())
235
+ columns = tuple(row["name"] for row in self.connection.execute(
236
+ "PRAGMA table_info(cms_source_terminal_processing)"
237
+ ).fetchall())
238
+ meta = self.connection.execute(
239
+ "SELECT singleton, schema_version FROM cms_source_terminal_processing_meta"
240
+ ).fetchall()
241
+ if (
242
+ meta_columns != ("singleton", "schema_version")
243
+ or columns != _COLUMNS or len(meta) != 1
244
+ or tuple(meta[0]) != (1, SCHEMA_VERSION)
245
+ ):
246
+ _blocked("schema_altered")
247
+
248
+ def register(
249
+ self,
250
+ notification: Mapping[str, Any],
251
+ notification_sha256: str,
252
+ *,
253
+ max_failures: int = 5,
254
+ now: float | int,
255
+ ) -> TerminalProcessingStatus:
256
+ self._validate_schema()
257
+ now = _time(now)
258
+ max_failures = _positive_integer(max_failures, "max_failures_invalid")
259
+ try:
260
+ event_id = notification["event_id"]
261
+ notification_id = notification["notification_id"]
262
+ site_id = notification["site_id"]
263
+ terminal_status = notification["terminal_status"]
264
+ except (KeyError, TypeError):
265
+ _blocked("notification_invalid")
266
+ if (
267
+ set(notification) != NOTIFICATION_FIELDS
268
+ or notification.get("schema") != NOTIFICATION_SCHEMA
269
+ or not all(
270
+ _token(item) for item in (event_id, notification_id, site_id)
271
+ )
272
+ or terminal_status not in TERMINAL_STATUSES
273
+ or not _sha(notification_sha256)
274
+ or _canonical_sha256(notification) != notification_sha256
275
+ ):
276
+ _blocked("notification_invalid")
277
+ values = (
278
+ event_id, notification_id, site_id, terminal_status,
279
+ notification_sha256, max_failures,
280
+ )
281
+ with _transaction(self.connection):
282
+ row = self.connection.execute(
283
+ "SELECT notification_id, site_id, terminal_status, "
284
+ "notification_sha256, max_failures "
285
+ "FROM cms_source_terminal_processing WHERE event_id = ?",
286
+ (event_id,),
287
+ ).fetchone()
288
+ if row is None:
289
+ self.connection.execute("""
290
+ INSERT INTO cms_source_terminal_processing (
291
+ event_id, notification_id, site_id, terminal_status,
292
+ notification_sha256, status, poll_attempts, failures,
293
+ max_failures, next_poll_at, created_at, updated_at
294
+ ) VALUES (?, ?, ?, ?, ?, 'pending', 0, 0, ?, ?, ?, ?)
295
+ """, (*values, now, now, now))
296
+ elif tuple(row) != values[1:]:
297
+ _blocked("idempotency_collision")
298
+ return self.status(event_id, now=now)
299
+
300
+ def claim(
301
+ self,
302
+ worker_id: str,
303
+ *,
304
+ now: float | int,
305
+ lease_seconds: float | int = 600,
306
+ ) -> sqlite3.Row | None:
307
+ self._validate_schema()
308
+ if not _token(worker_id) or len(worker_id) > 128:
309
+ _blocked("worker_invalid")
310
+ now = _time(now)
311
+ lease_seconds = _duration(lease_seconds, "lease_invalid")
312
+ with _transaction(self.connection):
313
+ self.connection.execute("""
314
+ UPDATE cms_source_terminal_processing
315
+ SET status = CASE WHEN failures + 1 >= max_failures
316
+ THEN 'failed' ELSE 'retry_wait' END,
317
+ failures = failures + 1, next_poll_at = ?,
318
+ lease_owner = NULL, lease_token = NULL,
319
+ lease_expires_at = NULL,
320
+ last_error_code = 'terminal_processing_monitor.lease_expired',
321
+ updated_at = ?
322
+ WHERE status = 'leased' AND lease_expires_at <= ?
323
+ """, (now, now, now))
324
+ row = self.connection.execute("""
325
+ SELECT * FROM cms_source_terminal_processing
326
+ WHERE status IN ('pending','watching','retry_wait')
327
+ AND next_poll_at <= ? AND failures < max_failures
328
+ ORDER BY created_at, event_id LIMIT 1
329
+ """, (now,)).fetchone()
330
+ if row is None:
331
+ return None
332
+ self._validated_row(row)
333
+ token = secrets.token_urlsafe(32)
334
+ expires_at = now + lease_seconds
335
+ updated = self.connection.execute("""
336
+ UPDATE cms_source_terminal_processing
337
+ SET status = 'leased', poll_attempts = poll_attempts + 1,
338
+ lease_owner = ?, lease_token = ?, lease_expires_at = ?,
339
+ last_error_code = NULL, updated_at = ?
340
+ WHERE event_id = ? AND status IN ('pending','watching','retry_wait')
341
+ """, (worker_id, token, expires_at, now, row["event_id"]))
342
+ if updated.rowcount != 1:
343
+ _blocked("claim_lost")
344
+ return self.connection.execute(
345
+ "SELECT * FROM cms_source_terminal_processing WHERE event_id = ?",
346
+ (row["event_id"],),
347
+ ).fetchone()
348
+
349
+ def run_once(
350
+ self,
351
+ reader: Callable[[str, str], Any],
352
+ worker_id: str,
353
+ *,
354
+ now: float | int,
355
+ lease_seconds: float | int = 600,
356
+ ) -> TerminalProcessingOutcome | None:
357
+ if not callable(reader):
358
+ _blocked("reader_invalid")
359
+ now = _time(now)
360
+ claim = self.claim(worker_id, now=now, lease_seconds=lease_seconds)
361
+ if claim is None:
362
+ return None
363
+ try:
364
+ response = reader(claim["event_id"], claim["site_id"])
365
+ self._complete(claim, response, now)
366
+ except TerminalProcessingMonitorBlocked:
367
+ raise
368
+ except Exception as error:
369
+ code = getattr(error, "code", None)
370
+ retryable = (
371
+ getattr(error, "cms_notification_failure", False) is True
372
+ and getattr(error, "retryable", None) is True
373
+ )
374
+ if not _error(code):
375
+ code, retryable = "terminal_processing_monitor.reader_failure", False
376
+ self._fail(claim, code, retryable, now)
377
+ result = self.status(claim["event_id"], now=now)
378
+ return TerminalProcessingOutcome(
379
+ result.event_id, result.notification_id, result.status,
380
+ result.poll_attempts, result.last_error_code,
381
+ )
382
+
383
+ def _complete(self, claim: sqlite3.Row, response: Any, now: float) -> None:
384
+ if not isinstance(response, Mapping):
385
+ self._fail(
386
+ claim, "terminal_processing_monitor.response_invalid", False, now
387
+ )
388
+ return
389
+ value = dict(response)
390
+ fields = {
391
+ "schema", "notification_id", "event_id", "site_id",
392
+ "terminal_status", "notification_sha256", "processing_status",
393
+ "attempts", "max_attempts", "next_attempt_at", "lease_expires_at",
394
+ "lease_expired", "last_error_code", "processed_at",
395
+ "capabilities_sha256",
396
+ }
397
+ remote = value.get("processing_status")
398
+ attempts = value.get("attempts")
399
+ maximum = value.get("max_attempts")
400
+ error = value.get("last_error_code")
401
+ valid = (
402
+ set(value) == fields and value.get("schema") == STATUS_SCHEMA
403
+ and value.get("notification_id") == claim["notification_id"]
404
+ and value.get("event_id") == claim["event_id"]
405
+ and value.get("site_id") == claim["site_id"]
406
+ and value.get("terminal_status") == claim["terminal_status"]
407
+ and value.get("notification_sha256") == claim["notification_sha256"]
408
+ and remote in REMOTE_STATUSES
409
+ and isinstance(attempts, int) and not isinstance(attempts, bool)
410
+ and isinstance(maximum, int) and not isinstance(maximum, bool)
411
+ and 0 <= attempts <= maximum <= 20 and maximum >= 1
412
+ and _valid_time(value.get("next_attempt_at"))
413
+ and _valid_time(value.get("lease_expires_at"), nullable=True)
414
+ )
415
+ valid = bool(valid) and (
416
+ isinstance(value.get("lease_expired"), bool)
417
+ and (error is None or _error(error))
418
+ and _valid_time(value.get("processed_at"), nullable=True)
419
+ and _sha(value.get("capabilities_sha256"))
420
+ and (remote == "leased") == (value.get("lease_expires_at") is not None)
421
+ and (remote == "succeeded") == (value.get("processed_at") is not None)
422
+ and (error is None or remote in {"retry_wait", "failed"})
423
+ and (not value.get("lease_expired") or remote == "leased")
424
+ )
425
+ if not valid:
426
+ self._fail(
427
+ claim, "terminal_processing_monitor.response_invalid", False, now
428
+ )
429
+ return
430
+ status = "succeeded" if remote == "succeeded" else (
431
+ "failed" if remote == "failed" else "watching"
432
+ )
433
+ local_error = (
434
+ "terminal_processing_monitor.remote_failed"
435
+ if remote == "failed" else None
436
+ )
437
+ with _transaction(self.connection):
438
+ self._require_claim(claim, now)
439
+ updated = self.connection.execute("""
440
+ UPDATE cms_source_terminal_processing
441
+ SET status = ?, failures = 0, next_poll_at = ?,
442
+ lease_owner = NULL, lease_token = NULL, lease_expires_at = NULL,
443
+ last_error_code = ?, receiver_status = ?, receiver_attempts = ?,
444
+ receiver_max_attempts = ?, receiver_next_attempt_at = ?,
445
+ receiver_lease_expires_at = ?, receiver_lease_expired = ?,
446
+ receiver_error_code = ?, receiver_processed_at = ?, updated_at = ?
447
+ WHERE event_id = ? AND status = 'leased'
448
+ AND lease_owner = ? AND lease_token = ?
449
+ """, (
450
+ status, now if status != "watching" else now + self.poll_interval_seconds,
451
+ local_error, remote, attempts, maximum, value["next_attempt_at"],
452
+ value["lease_expires_at"], int(value["lease_expired"]), error,
453
+ value["processed_at"], now, claim["event_id"],
454
+ claim["lease_owner"], claim["lease_token"],
455
+ ))
456
+ if updated.rowcount != 1:
457
+ _blocked("completion_lost")
458
+
459
+ def _fail(
460
+ self, claim: sqlite3.Row, code: str, retryable: bool, now: float
461
+ ) -> None:
462
+ if not _error(code):
463
+ code, retryable = "terminal_processing_monitor.reader_failure", False
464
+ with _transaction(self.connection):
465
+ row = self._require_claim(claim, now)
466
+ failures = int(row["failures"]) + 1
467
+ terminal = not retryable or failures >= int(row["max_failures"])
468
+ delay = min(
469
+ self.max_delay_seconds,
470
+ self.base_delay_seconds * (2 ** max(0, failures - 1)),
471
+ )
472
+ updated = self.connection.execute("""
473
+ UPDATE cms_source_terminal_processing
474
+ SET status = ?, failures = ?, next_poll_at = ?,
475
+ lease_owner = NULL, lease_token = NULL, lease_expires_at = NULL,
476
+ last_error_code = ?, updated_at = ?
477
+ WHERE event_id = ? AND status = 'leased'
478
+ AND lease_owner = ? AND lease_token = ?
479
+ """, (
480
+ "failed" if terminal else "retry_wait", failures,
481
+ now if terminal else now + delay, code, now, claim["event_id"],
482
+ claim["lease_owner"], claim["lease_token"],
483
+ ))
484
+ if updated.rowcount != 1:
485
+ _blocked("failure_lost")
486
+
487
+ def status(self, event_id: str, *, now: float | int) -> TerminalProcessingStatus:
488
+ self._validate_schema()
489
+ if not _token(event_id):
490
+ _blocked("event_invalid")
491
+ now = _time(now)
492
+ row = self.connection.execute(
493
+ "SELECT * FROM cms_source_terminal_processing WHERE event_id = ?",
494
+ (event_id,),
495
+ ).fetchone()
496
+ if row is None:
497
+ _blocked("missing")
498
+ self._validated_row(row)
499
+ lease = row["lease_expires_at"]
500
+ return TerminalProcessingStatus(
501
+ row["event_id"], row["notification_id"], row["site_id"],
502
+ row["notification_sha256"], row["status"], int(row["poll_attempts"]),
503
+ int(row["failures"]), int(row["max_failures"]),
504
+ float(row["next_poll_at"]), None if lease is None else float(lease),
505
+ row["status"] == "leased" and float(lease) <= now,
506
+ row["last_error_code"], row["receiver_status"],
507
+ row["receiver_attempts"], row["receiver_max_attempts"],
508
+ row["receiver_error_code"], row["receiver_processed_at"],
509
+ )
510
+
511
+ def health(self, *, now: float | int) -> TerminalProcessingHealth:
512
+ now = _time(now)
513
+ self._validate_schema()
514
+ if tuple(self.connection.execute("PRAGMA quick_check").fetchone()) != ("ok",):
515
+ _blocked("database_integrity")
516
+ rows = self.connection.execute(
517
+ "SELECT * FROM cms_source_terminal_processing ORDER BY event_id"
518
+ ).fetchall()
519
+ for row in rows:
520
+ self._validated_row(row)
521
+ counts = {status: 0 for status in STATUSES}
522
+ for row in rows:
523
+ counts[row["status"]] += 1
524
+ due = sum(
525
+ row["status"] in {"pending", "watching", "retry_wait"}
526
+ and row["next_poll_at"] <= now for row in rows
527
+ )
528
+ expired = sum(
529
+ row["status"] == "leased" and row["lease_expires_at"] <= now
530
+ for row in rows
531
+ )
532
+ return TerminalProcessingHealth(
533
+ "blocked" if expired or counts["failed"] else "ok",
534
+ counts, due, expired, counts["failed"],
535
+ )
536
+
537
+ def _require_claim(self, claim: sqlite3.Row, now: float) -> sqlite3.Row:
538
+ row = self.connection.execute(
539
+ "SELECT * FROM cms_source_terminal_processing WHERE event_id = ?",
540
+ (claim["event_id"],),
541
+ ).fetchone()
542
+ if (
543
+ row is None or row["status"] != "leased"
544
+ or row["lease_owner"] != claim["lease_owner"]
545
+ or row["lease_token"] != claim["lease_token"]
546
+ ):
547
+ _blocked("claim_lost")
548
+ self._validated_row(row)
549
+ if float(row["lease_expires_at"]) <= now:
550
+ _blocked("lease_expired")
551
+ return row
552
+
553
+ @staticmethod
554
+ def _validated_row(row: sqlite3.Row) -> None:
555
+ status = row["status"]
556
+ leased = status == "leased"
557
+ receiver = row["receiver_status"]
558
+ receiver_bundle = (
559
+ row["receiver_attempts"], row["receiver_max_attempts"],
560
+ row["receiver_next_attempt_at"], row["receiver_lease_expired"],
561
+ )
562
+ has_receiver = receiver is not None
563
+ receiver_attempts_valid = (
564
+ has_receiver
565
+ and all(item is not None for item in receiver_bundle)
566
+ and isinstance(row["receiver_attempts"], int)
567
+ and not isinstance(row["receiver_attempts"], bool)
568
+ and isinstance(row["receiver_max_attempts"], int)
569
+ and not isinstance(row["receiver_max_attempts"], bool)
570
+ and 0 <= row["receiver_attempts"] <= row["receiver_max_attempts"] <= 20
571
+ and row["receiver_max_attempts"] >= 1
572
+ and _valid_time(row["receiver_next_attempt_at"])
573
+ and row["receiver_lease_expired"] in {0, 1}
574
+ )
575
+ if (
576
+ not all(_token(row[name]) for name in (
577
+ "event_id", "notification_id", "site_id"
578
+ ))
579
+ or row["terminal_status"] not in TERMINAL_STATUSES
580
+ or not _sha(row["notification_sha256"])
581
+ or status not in STATUSES
582
+ or not all(
583
+ isinstance(row[name], int) and not isinstance(row[name], bool)
584
+ for name in ("poll_attempts", "failures", "max_failures")
585
+ )
586
+ or not 0 <= row["failures"] <= row["max_failures"] <= 20
587
+ or row["max_failures"] < 1
588
+ or row["poll_attempts"] < row["failures"]
589
+ or leased != all(row[name] is not None for name in (
590
+ "lease_owner", "lease_token", "lease_expires_at"
591
+ ))
592
+ or (not leased and any(row[name] is not None for name in (
593
+ "lease_owner", "lease_token", "lease_expires_at"
594
+ )))
595
+ or (row["last_error_code"] is not None and not _error(
596
+ row["last_error_code"]
597
+ ))
598
+ or (status in {"retry_wait", "failed"}) != (
599
+ row["last_error_code"] is not None
600
+ )
601
+ or receiver is not None and receiver not in REMOTE_STATUSES
602
+ or has_receiver != receiver_attempts_valid
603
+ or (receiver == "leased") != (
604
+ row["receiver_lease_expires_at"] is not None
605
+ )
606
+ or not _valid_time(
607
+ row["receiver_lease_expires_at"], nullable=True,
608
+ )
609
+ or not _valid_time(row["receiver_processed_at"], nullable=True)
610
+ or (row["receiver_error_code"] is not None and not _error(
611
+ row["receiver_error_code"]
612
+ ))
613
+ or (receiver in {"retry_wait", "failed"}) != (
614
+ row["receiver_error_code"] is not None
615
+ )
616
+ or (receiver == "succeeded") != (
617
+ row["receiver_processed_at"] is not None
618
+ )
619
+ or (
620
+ row["receiver_lease_expired"] == 1
621
+ and receiver != "leased"
622
+ )
623
+ or (status == "succeeded") != (receiver == "succeeded")
624
+ or (
625
+ status == "watching"
626
+ and receiver not in {"pending", "leased", "retry_wait"}
627
+ )
628
+ or (status == "failed" and row["last_error_code"] is None)
629
+ or not _valid_time(row["next_poll_at"])
630
+ or not _valid_time(row["created_at"])
631
+ or not _valid_time(row["updated_at"])
632
+ or row["updated_at"] < row["created_at"]
633
+ ):
634
+ _blocked("state_invalid")