msgctl 0.2.5__tar.gz → 0.2.6__tar.gz

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 (742) hide show
  1. {msgctl-0.2.5 → msgctl-0.2.6}/CHANGELOG.md +8 -1
  2. {msgctl-0.2.5 → msgctl-0.2.6}/PKG-INFO +3 -3
  3. {msgctl-0.2.5 → msgctl-0.2.6}/README.md +2 -2
  4. {msgctl-0.2.5 → msgctl-0.2.6}/docs/OAUTH.md +2 -0
  5. {msgctl-0.2.5 → msgctl-0.2.6}/docs/SUBAGENTS.md +14 -1
  6. msgctl-0.2.6/docs/SUBAGENT_ACCEPTANCE_20261001.md +48 -0
  7. msgctl-0.2.6/docs/YUBIKEY_DESIGN.md +65 -0
  8. {msgctl-0.2.5 → msgctl-0.2.6}/pyproject.toml +1 -1
  9. {msgctl-0.2.5 → msgctl-0.2.6}/pyproject.toml.orig +1 -1
  10. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/__init__.py +1 -1
  11. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/root-web-app.js +27 -9
  12. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/root-web-model.js +4 -0
  13. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/root-web-renderer.js +3 -1
  14. msgctl-0.2.6/src/msg/transports/browser_palette.py +16 -0
  15. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/browser_style.py +67 -36
  16. msgctl-0.2.6/src/msg/transports/home_art.py +76 -0
  17. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/home_page.py +4 -27
  18. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/webmcp.py +12 -4
  19. msgctl-0.2.6/tests/test_ascii_hero.py +51 -0
  20. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_client_listener.py +10 -2
  21. {msgctl-0.2.5 → msgctl-0.2.6}/uv.lock +1 -1
  22. msgctl-0.2.5/tests/test_ascii_hero.py +0 -49
  23. {msgctl-0.2.5 → msgctl-0.2.6}/.python-version +0 -0
  24. {msgctl-0.2.5 → msgctl-0.2.6}/CONTRIBUTING.md +0 -0
  25. {msgctl-0.2.5 → msgctl-0.2.6}/SECURITY.md +0 -0
  26. {msgctl-0.2.5 → msgctl-0.2.6}/conformance/test_token_budget.py +0 -0
  27. {msgctl-0.2.5 → msgctl-0.2.6}/conformance/test_transports.py +0 -0
  28. {msgctl-0.2.5 → msgctl-0.2.6}/conftest.py +0 -0
  29. {msgctl-0.2.5 → msgctl-0.2.6}/deploy/README.md +0 -0
  30. {msgctl-0.2.5 → msgctl-0.2.6}/deploy/hosting-role.sql +0 -0
  31. {msgctl-0.2.5 → msgctl-0.2.6}/deploy/hosting.example.toml +0 -0
  32. {msgctl-0.2.5 → msgctl-0.2.6}/deploy/mail.example.toml +0 -0
  33. {msgctl-0.2.5 → msgctl-0.2.6}/deploy/msgd-hosting.service +0 -0
  34. {msgctl-0.2.5 → msgctl-0.2.6}/deploy/msgd-worker.service +0 -0
  35. {msgctl-0.2.5 → msgctl-0.2.6}/deploy/msgd-worker@.service +0 -0
  36. {msgctl-0.2.5 → msgctl-0.2.6}/deploy/msgd.service +0 -0
  37. {msgctl-0.2.5 → msgctl-0.2.6}/deploy/msgd@.service +0 -0
  38. {msgctl-0.2.5 → msgctl-0.2.6}/deploy/nginx-shared-http-logging.conf +0 -0
  39. {msgctl-0.2.5 → msgctl-0.2.6}/deploy/nginx.conf +0 -0
  40. {msgctl-0.2.5 → msgctl-0.2.6}/deploy/prepare-hosting-content.sh +0 -0
  41. {msgctl-0.2.5 → msgctl-0.2.6}/deploy/prepare-instance.sh +0 -0
  42. {msgctl-0.2.5 → msgctl-0.2.6}/deploy/prepare-service.sh +0 -0
  43. {msgctl-0.2.5 → msgctl-0.2.6}/deploy/sshd_config +0 -0
  44. {msgctl-0.2.5 → msgctl-0.2.6}/docs/ARCHITECTURE.md +0 -0
  45. {msgctl-0.2.5 → msgctl-0.2.6}/docs/ARCHITECTURE_PROGRESS_20260929.md +0 -0
  46. {msgctl-0.2.5 → msgctl-0.2.6}/docs/ARCHITECTURE_REUSE.md +0 -0
  47. {msgctl-0.2.5 → msgctl-0.2.6}/docs/AUTHORIZATION_SOURCES.md +0 -0
  48. {msgctl-0.2.5 → msgctl-0.2.6}/docs/BOUNTY_ACCEPTANCE.md +0 -0
  49. {msgctl-0.2.5 → msgctl-0.2.6}/docs/CLIENT_BOUNDARY_PROGRESS.md +0 -0
  50. {msgctl-0.2.5 → msgctl-0.2.6}/docs/CLIENT_CONNECTIONS.md +0 -0
  51. {msgctl-0.2.5 → msgctl-0.2.6}/docs/CLIENT_INSTALLATION.md +0 -0
  52. {msgctl-0.2.5 → msgctl-0.2.6}/docs/CLIENT_PART_BYTES_PROGRESS.md +0 -0
  53. {msgctl-0.2.5 → msgctl-0.2.6}/docs/CLI_FIELD_SELECTION.md +0 -0
  54. {msgctl-0.2.5 → msgctl-0.2.6}/docs/COMPLETE_RECOVERY_PROOF.md +0 -0
  55. {msgctl-0.2.5 → msgctl-0.2.6}/docs/CREDENTIAL_DELIVERY.md +0 -0
  56. {msgctl-0.2.5 → msgctl-0.2.6}/docs/CUSTODIAL_HISTORY.md +0 -0
  57. {msgctl-0.2.5 → msgctl-0.2.6}/docs/C_READ_ACCEPTANCE.md +0 -0
  58. {msgctl-0.2.5 → msgctl-0.2.6}/docs/DEPLOYMENT.md +0 -0
  59. {msgctl-0.2.5 → msgctl-0.2.6}/docs/DESIGN_CHANGE_REQUESTS.md +0 -0
  60. {msgctl-0.2.5 → msgctl-0.2.6}/docs/DESIGN_CONTRACT_INVENTORY.md +0 -0
  61. {msgctl-0.2.5 → msgctl-0.2.6}/docs/FILESYSTEM_LAYOUT.md +0 -0
  62. {msgctl-0.2.5 → msgctl-0.2.6}/docs/FOLLOW_AND_FEED.md +0 -0
  63. {msgctl-0.2.5 → msgctl-0.2.6}/docs/GIT_BOUNDARY_PROGRESS.md +0 -0
  64. {msgctl-0.2.5 → msgctl-0.2.6}/docs/HOSTING_AUTHORITY_PROGRESS.md +0 -0
  65. {msgctl-0.2.5 → msgctl-0.2.6}/docs/HOSTING_RUNTIME.md +0 -0
  66. {msgctl-0.2.5 → msgctl-0.2.6}/docs/HOSTING_RUNTIME_PROGRESS.md +0 -0
  67. {msgctl-0.2.5 → msgctl-0.2.6}/docs/IMPLEMENTATION_STATUS.md +0 -0
  68. {msgctl-0.2.5 → msgctl-0.2.6}/docs/INTEGRATION_PROGRESS_20260929.md +0 -0
  69. {msgctl-0.2.5 → msgctl-0.2.6}/docs/INTEGRATION_REGRESSION_PROGRESS.md +0 -0
  70. {msgctl-0.2.5 → msgctl-0.2.6}/docs/ISSUES_68_70_PROGRESS.md +0 -0
  71. {msgctl-0.2.5 → msgctl-0.2.6}/docs/ISSUES_78_80_PROGRESS.md +0 -0
  72. {msgctl-0.2.5 → msgctl-0.2.6}/docs/ISSUE_PROGRESS.md +0 -0
  73. {msgctl-0.2.5 → msgctl-0.2.6}/docs/ISSUE_RESOLUTION.md +0 -0
  74. {msgctl-0.2.5 → msgctl-0.2.6}/docs/ITERATION_PLAN.md +0 -0
  75. {msgctl-0.2.5 → msgctl-0.2.6}/docs/LEDGER_MIGRATION.md +0 -0
  76. {msgctl-0.2.5 → msgctl-0.2.6}/docs/LEGACY_GIT_MIGRATION.md +0 -0
  77. {msgctl-0.2.5 → msgctl-0.2.6}/docs/LEGACY_IDENTITY_ARCHIVE.md +0 -0
  78. {msgctl-0.2.5 → msgctl-0.2.6}/docs/LEGACY_SQLITE_PRESERVATION.md +0 -0
  79. {msgctl-0.2.5 → msgctl-0.2.6}/docs/LOCAL_VERIFICATION.md +0 -0
  80. {msgctl-0.2.5 → msgctl-0.2.6}/docs/MANAGED_CHECKOUT.md +0 -0
  81. {msgctl-0.2.5 → msgctl-0.2.6}/docs/MARKET_BOUNDARY_PROGRESS.md +0 -0
  82. {msgctl-0.2.5 → msgctl-0.2.6}/docs/MARKET_CLEARING.md +0 -0
  83. {msgctl-0.2.5 → msgctl-0.2.6}/docs/MARKET_CONTRACTS.md +0 -0
  84. {msgctl-0.2.5 → msgctl-0.2.6}/docs/METADATA_SESSION_PROGRESS.md +0 -0
  85. {msgctl-0.2.5 → msgctl-0.2.6}/docs/NATIVE_PACKAGES.md +0 -0
  86. {msgctl-0.2.5 → msgctl-0.2.6}/docs/NEW_ROOT_LEGACY_IMPORT_RUNBOOK.md +0 -0
  87. {msgctl-0.2.5 → msgctl-0.2.6}/docs/OFFER_ORDER_COMPATIBILITY.md +0 -0
  88. {msgctl-0.2.5 → msgctl-0.2.6}/docs/OFFER_RESOURCE_MIGRATION.md +0 -0
  89. {msgctl-0.2.5 → msgctl-0.2.6}/docs/POST_SUMMARIES.md +0 -0
  90. {msgctl-0.2.5 → msgctl-0.2.6}/docs/PROTOCOLS.md +0 -0
  91. {msgctl-0.2.5 → msgctl-0.2.6}/docs/QUALITY_PROGRESS.md +0 -0
  92. {msgctl-0.2.5 → msgctl-0.2.6}/docs/READ_ENTRY_ACCEPTANCE.md +0 -0
  93. {msgctl-0.2.5 → msgctl-0.2.6}/docs/RELEASE_ACCEPTANCE.md +0 -0
  94. {msgctl-0.2.5 → msgctl-0.2.6}/docs/SAVED_QUERIES.md +0 -0
  95. {msgctl-0.2.5 → msgctl-0.2.6}/docs/SEARCH_QUERY_V5.md +0 -0
  96. {msgctl-0.2.5 → msgctl-0.2.6}/docs/SERVICE_ALIASES.md +0 -0
  97. {msgctl-0.2.5 → msgctl-0.2.6}/docs/SSH_GIT_LOCAL_ACCEPTANCE.md +0 -0
  98. {msgctl-0.2.5 → msgctl-0.2.6}/docs/TASK_A_RECOVERY_20260928.md +0 -0
  99. {msgctl-0.2.5 → msgctl-0.2.6}/docs/TASK_A_VERIFICATION.md +0 -0
  100. {msgctl-0.2.5 → msgctl-0.2.6}/docs/TASK_C_EVIDENCE.md +0 -0
  101. {msgctl-0.2.5 → msgctl-0.2.6}/docs/TEMPORARY_UPGRADE.md +0 -0
  102. {msgctl-0.2.5 → msgctl-0.2.6}/docs/TERMINAL_DEMO.md +0 -0
  103. {msgctl-0.2.5 → msgctl-0.2.6}/docs/TOKEN_DELIVERY_BOUNDARY_PROGRESS.md +0 -0
  104. {msgctl-0.2.5 → msgctl-0.2.6}/docs/TOOL_RUNNER_BOUNDARY.md +0 -0
  105. {msgctl-0.2.5 → msgctl-0.2.6}/docs/TRANSFER_STATUS_PROGRESS.md +0 -0
  106. {msgctl-0.2.5 → msgctl-0.2.6}/docs/TRUST_PATHS.md +0 -0
  107. {msgctl-0.2.5 → msgctl-0.2.6}/docs/VERIFICATION.md +0 -0
  108. {msgctl-0.2.5 → msgctl-0.2.6}/docs/WEBSUB.md +0 -0
  109. {msgctl-0.2.5 → msgctl-0.2.6}/docs/issue-75-77-local-verification.md +0 -0
  110. {msgctl-0.2.5 → msgctl-0.2.6}/docs/post-universe.md +0 -0
  111. {msgctl-0.2.5 → msgctl-0.2.6}/docs/registry-template-tool-contracts.md +0 -0
  112. {msgctl-0.2.5 → msgctl-0.2.6}/packaging/build-native.py +0 -0
  113. {msgctl-0.2.5 → msgctl-0.2.6}/scripts/audit_filesystem_paths.py +0 -0
  114. {msgctl-0.2.5 → msgctl-0.2.6}/scripts/check_client_install.py +0 -0
  115. {msgctl-0.2.5 → msgctl-0.2.6}/scripts/check_installed_server.py +0 -0
  116. {msgctl-0.2.5 → msgctl-0.2.6}/scripts/check_isolated_sshd.py +0 -0
  117. {msgctl-0.2.5 → msgctl-0.2.6}/scripts/check_package_artifacts.py +0 -0
  118. {msgctl-0.2.5 → msgctl-0.2.6}/scripts/check_tool_sandbox.py +0 -0
  119. {msgctl-0.2.5 → msgctl-0.2.6}/scripts/check_universe_browser.py +0 -0
  120. {msgctl-0.2.5 → msgctl-0.2.6}/scripts/ci_shards.py +0 -0
  121. {msgctl-0.2.5 → msgctl-0.2.6}/scripts/render_terminal_demo.py +0 -0
  122. {msgctl-0.2.5 → msgctl-0.2.6}/scripts/verify_local.py +0 -0
  123. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/__init__.py +0 -0
  124. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/accounts.py +0 -0
  125. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/backup_retirement.py +0 -0
  126. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/backups.py +0 -0
  127. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/config_check.py +0 -0
  128. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/content_edit_check.py +0 -0
  129. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/custodial_check.py +0 -0
  130. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/diagnostics.py +0 -0
  131. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/dm_check.py +0 -0
  132. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/git_check.py +0 -0
  133. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/honor_check.py +0 -0
  134. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/legacy_approval.py +0 -0
  135. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/market.py +0 -0
  136. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/market_check.py +0 -0
  137. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/money.py +0 -0
  138. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/offer_import.py +0 -0
  139. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/preflight.py +0 -0
  140. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/recovery_policy.py +0 -0
  141. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/recovery_proof.py +0 -0
  142. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/recovery_replay.py +0 -0
  143. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/recovery_state.py +0 -0
  144. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/restore_database.py +0 -0
  145. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/root.py +0 -0
  146. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/rotation.py +0 -0
  147. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/token_delivery_check.py +0 -0
  148. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/tool_check.py +0 -0
  149. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/transfer_check.py +0 -0
  150. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/admin/upgrade_check.py +0 -0
  151. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/algorithm/.github/workflows/test.yml +0 -0
  152. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/algorithm/.gitignore +0 -0
  153. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/algorithm/LICENSE +0 -0
  154. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/algorithm/README.md +0 -0
  155. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/algorithm/README.zh-CN.md +0 -0
  156. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/algorithm/__init__.py +0 -0
  157. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/algorithm/pyproject.toml +0 -0
  158. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/algorithm/ranker.py +0 -0
  159. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/algorithm/tests/test_ranker.py +0 -0
  160. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/application.py +0 -0
  161. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/atomic_file.py +0 -0
  162. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/bootstrap.py +0 -0
  163. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/cli.py +0 -0
  164. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/client.py +0 -0
  165. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/client_agent_cli.py +0 -0
  166. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/client_api_keys.py +0 -0
  167. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/client_certificates.py +0 -0
  168. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/client_collaboration.py +0 -0
  169. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/client_connection.py +0 -0
  170. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/client_content.py +0 -0
  171. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/client_custodial.py +0 -0
  172. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/client_listener.py +0 -0
  173. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/client_market.py +0 -0
  174. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/client_oauth.py +0 -0
  175. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/client_output.py +0 -0
  176. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/client_recovery.py +0 -0
  177. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/client_secrets.py +0 -0
  178. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/client_subagents.py +0 -0
  179. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/client_subagents_remote.py +0 -0
  180. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/client_tokens.py +0 -0
  181. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/client_upgrade.py +0 -0
  182. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/config.py +0 -0
  183. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/config_contracts.py +0 -0
  184. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/constants.py +0 -0
  185. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/__init__.py +0 -0
  186. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/batching.py +0 -0
  187. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/codec.py +0 -0
  188. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/contracts.py +0 -0
  189. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/cursors.py +0 -0
  190. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/email_address.py +0 -0
  191. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/errors.py +0 -0
  192. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/events.py +0 -0
  193. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/execution_ports.py +0 -0
  194. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/executor.py +0 -0
  195. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/handles.py +0 -0
  196. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/identifiers.py +0 -0
  197. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/models.py +0 -0
  198. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/packet.py +0 -0
  199. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/permissions.py +0 -0
  200. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/post_preview.py +0 -0
  201. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/query.py +0 -0
  202. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/read_query.py +0 -0
  203. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/registry.py +0 -0
  204. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/requests.py +0 -0
  205. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/schema_policy.py +0 -0
  206. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/schemas.py +0 -0
  207. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/search_query.py +0 -0
  208. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/tags.py +0 -0
  209. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/template_dsl.py +0 -0
  210. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/text_patch.py +0 -0
  211. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/tool_execution.py +0 -0
  212. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/core/transfer.py +0 -0
  213. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/daemon.py +0 -0
  214. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/__init__.py +0 -0
  215. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/bootstrap.json +0 -0
  216. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/favicon.png +0 -0
  217. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/install.sh +0 -0
  218. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/legacy/sqlite-0221.json +0 -0
  219. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/logo-dark.svg +0 -0
  220. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/logo.svg +0 -0
  221. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/recovery-checkpoint.example.json +0 -0
  222. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/recovery-checkpoint.schema.json +0 -0
  223. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/recovery-policy-checkpoint.schema.json +0 -0
  224. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/recovery-state-schema.json +0 -0
  225. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/root-web-font-LICENSE.txt +0 -0
  226. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/root-web-sans-bold.woff2 +0 -0
  227. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/root-web-sans.woff2 +0 -0
  228. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/root-web.css +0 -0
  229. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/root-web.html +0 -0
  230. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/shortcodes.json +0 -0
  231. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/system/AGENTS.md +0 -0
  232. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/system/rules/_index.md +0 -0
  233. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/system/rules/auth.md +0 -0
  234. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/system/rules/files.md +0 -0
  235. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/system/rules/identity.md +0 -0
  236. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/system/rules/protocol.md +0 -0
  237. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/system/rules/read-write.md +0 -0
  238. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/system/rules/recovery.md +0 -0
  239. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/system/rules/security.md +0 -0
  240. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/data/system/rules/topics.md +0 -0
  241. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/extensions/__init__.py +0 -0
  242. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/extensions/git_publication.py +0 -0
  243. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/extensions/hosting.py +0 -0
  244. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/extensions/keystore.py +0 -0
  245. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/extensions/repositories.py +0 -0
  246. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/extensions/rss.py +0 -0
  247. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/extensions/ssh.py +0 -0
  248. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/extensions/ssh_git.py +0 -0
  249. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/extensions/tools.py +0 -0
  250. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/extensions/websub.py +0 -0
  251. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/hosting_runtime.py +0 -0
  252. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/market/__init__.py +0 -0
  253. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/market/arbitration.py +0 -0
  254. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/market/catalog.py +0 -0
  255. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/market/compatibility.py +0 -0
  256. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/market/delivery.py +0 -0
  257. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/market/delivery_notifications.py +0 -0
  258. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/market/delivery_targets.py +0 -0
  259. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/market/email.py +0 -0
  260. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/market/entitlement_orders.py +0 -0
  261. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/market/escrow.py +0 -0
  262. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/market/ledger.py +0 -0
  263. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/market/managed_delivery.py +0 -0
  264. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/market/offer_resources.py +0 -0
  265. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/market/order_records.py +0 -0
  266. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/market/order_resources.py +0 -0
  267. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/market/orders.py +0 -0
  268. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/market/policy.py +0 -0
  269. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/market/rationale.py +0 -0
  270. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/market/references.py +0 -0
  271. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/market/targets.py +0 -0
  272. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/oauth_config.py +0 -0
  273. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/paths.py +0 -0
  274. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/__init__.py +0 -0
  275. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/achievements.py +0 -0
  276. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/agent_follows.py +0 -0
  277. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/batch.py +0 -0
  278. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/bot_feed.py +0 -0
  279. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/bounty.py +0 -0
  280. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/collaboration.py +0 -0
  281. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/collaboration_resources.py +0 -0
  282. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/common.py +0 -0
  283. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/communication.py +0 -0
  284. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/content.py +0 -0
  285. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/custodial_lifecycle.py +0 -0
  286. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/delivery.py +0 -0
  287. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/discovery.py +0 -0
  288. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/discussion.py +0 -0
  289. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/extensions.py +0 -0
  290. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/features.py +0 -0
  291. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/files.py +0 -0
  292. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/following.py +0 -0
  293. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/hosting_capacity.py +0 -0
  294. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/identity.py +0 -0
  295. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/mail_views.py +0 -0
  296. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/money.py +0 -0
  297. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/oauth_identity.py +0 -0
  298. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/offers.py +0 -0
  299. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/orders.py +0 -0
  300. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/profile.py +0 -0
  301. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/proposals.py +0 -0
  302. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/read_predicates.py +0 -0
  303. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/receipts.py +0 -0
  304. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/recovery.py +0 -0
  305. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/saved_queries.py +0 -0
  306. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/schemas.py +0 -0
  307. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/sharing.py +0 -0
  308. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/store.py +0 -0
  309. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/system.py +0 -0
  310. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/transfer.py +0 -0
  311. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/plugins/watches.py +0 -0
  312. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/py.typed +0 -0
  313. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/security/__init__.py +0 -0
  314. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/security/age_keys.py +0 -0
  315. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/security/authentication.py +0 -0
  316. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/security/authorization.py +0 -0
  317. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/security/backup_retirement.py +0 -0
  318. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/security/capabilities.py +0 -0
  319. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/security/certificates.py +0 -0
  320. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/security/crypto.py +0 -0
  321. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/security/custodial_migration.py +0 -0
  322. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/security/custodial_protocol.py +0 -0
  323. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/security/custody_history.py +0 -0
  324. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/security/network.py +0 -0
  325. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/security/oauth.py +0 -0
  326. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/security/policy.py +0 -0
  327. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/security/quarantine.py +0 -0
  328. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/security/root_files.py +0 -0
  329. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/security/rotation_journal.py +0 -0
  330. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/security/sealed_box.py +0 -0
  331. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/security/sharing_policy.py +0 -0
  332. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/security/token_delivery.py +0 -0
  333. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/security/trust_files.py +0 -0
  334. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/security/vault.py +0 -0
  335. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/service_origin.py +0 -0
  336. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/storage/__init__.py +0 -0
  337. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/storage/capacity.py +0 -0
  338. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/storage/custodial_migration.py +0 -0
  339. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/storage/git.py +0 -0
  340. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/storage/ledger_migration.py +0 -0
  341. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/storage/legacy_git_capture.py +0 -0
  342. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/storage/legacy_git_import.py +0 -0
  343. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/storage/legacy_git_rehearsal.py +0 -0
  344. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/storage/legacy_identity_plan.py +0 -0
  345. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/storage/legacy_rehearsal.py +0 -0
  346. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/storage/legacy_resource_import.py +0 -0
  347. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/storage/legacy_sqlite.py +0 -0
  348. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/storage/market_migration.py +0 -0
  349. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/storage/postgres.py +0 -0
  350. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/storage/query.py +0 -0
  351. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/storage/read_only.py +0 -0
  352. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/storage/session.py +0 -0
  353. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/storage/sqlite.py +0 -0
  354. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/storage/topic_event_migration.py +0 -0
  355. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/storage/valkey_bus.py +0 -0
  356. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/__init__.py +0 -0
  357. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/browser_login.py +0 -0
  358. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/client.py +0 -0
  359. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/dictionary.py +0 -0
  360. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/git_http.py +0 -0
  361. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/graphql.py +0 -0
  362. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/http.py +0 -0
  363. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/http_common.py +0 -0
  364. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/http_routes.py +0 -0
  365. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/legacy_content_http.py +0 -0
  366. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/mcp.py +0 -0
  367. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/mcp_protocol.py +0 -0
  368. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/oauth_http.py +0 -0
  369. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/packet.py +0 -0
  370. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/permissions_page.py +0 -0
  371. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/read_tree_path.py +0 -0
  372. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/register_page.py +0 -0
  373. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/stdio.py +0 -0
  374. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/subject_views.py +0 -0
  375. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/universe.py +0 -0
  376. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/transports/url_safety.py +0 -0
  377. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/tui.py +0 -0
  378. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/tui_i18n.py +0 -0
  379. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/workers/__init__.py +0 -0
  380. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/workers/effects.py +0 -0
  381. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/workers/leases.py +0 -0
  382. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/workers/mail.py +0 -0
  383. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/workers/maintenance.py +0 -0
  384. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/workers/sandbox.py +0 -0
  385. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/workers/sandbox_child.py +0 -0
  386. {msgctl-0.2.5 → msgctl-0.2.6}/src/msg/workers/webhook.py +0 -0
  387. {msgctl-0.2.5 → msgctl-0.2.6}/tests/conftest.py +0 -0
  388. {msgctl-0.2.5 → msgctl-0.2.6}/tests/fixtures/create_legacy_market.py +0 -0
  389. {msgctl-0.2.5 → msgctl-0.2.6}/tests/fixtures/legacy_market_source.json +0 -0
  390. {msgctl-0.2.5 → msgctl-0.2.6}/tests/fixtures/legacy_sqlite_0221.sql +0 -0
  391. {msgctl-0.2.5 → msgctl-0.2.6}/tests/http_read_cache_matrix.py +0 -0
  392. {msgctl-0.2.5 → msgctl-0.2.6}/tests/issue_78_80/__init__.py +0 -0
  393. {msgctl-0.2.5 → msgctl-0.2.6}/tests/issue_78_80/conftest.py +0 -0
  394. {msgctl-0.2.5 → msgctl-0.2.6}/tests/issue_78_80/test_content_patch_contract.py +0 -0
  395. {msgctl-0.2.5 → msgctl-0.2.6}/tests/issue_78_80/test_lfs_integrity.py +0 -0
  396. {msgctl-0.2.5 → msgctl-0.2.6}/tests/issue_78_80/test_lfs_migration_contract.py +0 -0
  397. {msgctl-0.2.5 → msgctl-0.2.6}/tests/issue_78_80/test_read_query_tree.py +0 -0
  398. {msgctl-0.2.5 → msgctl-0.2.6}/tests/issue_78_80/test_read_tree_transports.py +0 -0
  399. {msgctl-0.2.5 → msgctl-0.2.6}/tests/issue_78_80/test_revision_signature_fields.py +0 -0
  400. {msgctl-0.2.5 → msgctl-0.2.6}/tests/issue_78_80/test_text_patch_formats.py +0 -0
  401. {msgctl-0.2.5 → msgctl-0.2.6}/tests/issue_78_80/test_tree_query_ref.py +0 -0
  402. {msgctl-0.2.5 → msgctl-0.2.6}/tests/read_only_evidence.py +0 -0
  403. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_access_log_secret_redaction.py +0 -0
  404. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_account_archive.py +0 -0
  405. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_achievement_concurrency.py +0 -0
  406. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_achievement_context_boundary.py +0 -0
  407. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_achievement_pins.py +0 -0
  408. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_achievements.py +0 -0
  409. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_agent_entry.py +0 -0
  410. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_agent_follows.py +0 -0
  411. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_architecture_ports.py +0 -0
  412. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_attachment_http_cache.py +0 -0
  413. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_authority_snapshot_drift.py +0 -0
  414. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_authorization.py +0 -0
  415. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_authorization_matrix.py +0 -0
  416. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_authorization_recovery_boundaries.py +0 -0
  417. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_authorization_sources.py +0 -0
  418. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_backup_daemon.py +0 -0
  419. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_backup_drill.py +0 -0
  420. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_backup_policy.py +0 -0
  421. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_backup_retirement_attestation.py +0 -0
  422. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_backup_retirement_cli.py +0 -0
  423. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_batch.py +0 -0
  424. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_batch_secret_release.py +0 -0
  425. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_bot_feed.py +0 -0
  426. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_bounty.py +0 -0
  427. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_bounty_commit_boundary.py +0 -0
  428. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_bounty_escrow_binding.py +0 -0
  429. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_bounty_public_lifecycle.py +0 -0
  430. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_bounty_snapshot_integrity.py +0 -0
  431. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_branding.py +0 -0
  432. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_browser_html.py +0 -0
  433. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_browser_refinement.py +0 -0
  434. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_browser_workspace.py +0 -0
  435. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_ca_depth.py +0 -0
  436. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_canonical_post_paths.py +0 -0
  437. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_capability_matrix.py +0 -0
  438. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_capacity_bounds.py +0 -0
  439. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_checkout_decision_integrity.py +0 -0
  440. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_ci_shards.py +0 -0
  441. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_cli_cursor_output.py +0 -0
  442. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_cli_dm.py +0 -0
  443. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_cli_hosting.py +0 -0
  444. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_cli_json_fields.py +0 -0
  445. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_cli_legacy.py +0 -0
  446. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_cli_recovery.py +0 -0
  447. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_cli_search_grep.py +0 -0
  448. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_client.py +0 -0
  449. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_client_agent_cli.py +0 -0
  450. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_client_boundary.py +0 -0
  451. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_client_connection.py +0 -0
  452. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_client_installer.py +0 -0
  453. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_client_output_formats.py +0 -0
  454. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_client_part_bytes.py +0 -0
  455. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_client_paths.py +0 -0
  456. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_client_response_boundaries.py +0 -0
  457. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_client_secrets.py +0 -0
  458. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_client_subagents.py +0 -0
  459. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_client_subagents_remote.py +0 -0
  460. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_client_token_delivery.py +0 -0
  461. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_collaboration_current_visibility.py +0 -0
  462. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_collaboration_list_bounds.py +0 -0
  463. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_collaboration_list_deadline.py +0 -0
  464. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_collaboration_resource_surfaces.py +0 -0
  465. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_collaboration_resources.py +0 -0
  466. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_collaboration_surfaces.py +0 -0
  467. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_complete_recovery_proof.py +0 -0
  468. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_config_safety.py +0 -0
  469. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_configuration_field_contracts.py +0 -0
  470. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_content_edit_check.py +0 -0
  471. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_credential_delivery_contract.py +0 -0
  472. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_credential_delivery_matrix.py +0 -0
  473. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_credential_recovery_window.py +0 -0
  474. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_credential_upgrade_interleave.py +0 -0
  475. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_custodial_client_upgrade.py +0 -0
  476. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_custodial_historical_migration.py +0 -0
  477. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_custodial_inventory_ack.py +0 -0
  478. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_custodial_lifecycle.py +0 -0
  479. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_custodial_rewrap.py +0 -0
  480. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_custodial_schema_migration.py +0 -0
  481. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_custodial_upgrade.py +0 -0
  482. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_custodial_vault.py +0 -0
  483. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_custody_exit_evidence.py +0 -0
  484. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_delivery.py +0 -0
  485. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_delivery_target_regression.py +0 -0
  486. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_dictionary.py +0 -0
  487. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_dictionary_route.py +0 -0
  488. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_dm.py +0 -0
  489. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_dm_selftest.py +0 -0
  490. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_effect_completion_fencing.py +0 -0
  491. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_effect_lease_deadlines.py +0 -0
  492. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_effect_retry_state.py +0 -0
  493. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_entitlement_orders.py +0 -0
  494. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_error_messages.py +0 -0
  495. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_escrow_decision_migration.py +0 -0
  496. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_escrow_decisions.py +0 -0
  497. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_execution_composition.py +0 -0
  498. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_extensions.py +0 -0
  499. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_feature_manifest.py +0 -0
  500. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_file_copy_integrity.py +0 -0
  501. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_file_operations.py +0 -0
  502. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_fixture_collection.py +0 -0
  503. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_following.py +0 -0
  504. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_foundation.py +0 -0
  505. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_get_only_write.py +0 -0
  506. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_git_http_push.py +0 -0
  507. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_git_lfs.py +0 -0
  508. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_git_selftest.py +0 -0
  509. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_git_transport_boundaries.py +0 -0
  510. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_graceful_shutdown.py +0 -0
  511. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_handoff_lease.py +0 -0
  512. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_hex_post_views.py +0 -0
  513. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_home_channels.py +0 -0
  514. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_honor_diagnostics.py +0 -0
  515. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_hosting_addressable_paths.py +0 -0
  516. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_hosting_alias_ingress.py +0 -0
  517. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_hosting_read_permissions.py +0 -0
  518. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_hosting_runtime.py +0 -0
  519. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_hosting_runtime_authority.py +0 -0
  520. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_hosting_runtime_isolation.py +0 -0
  521. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_hosting_same_origin.py +0 -0
  522. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_http.py +0 -0
  523. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_http_body_integrity.py +0 -0
  524. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_http_effect_order.py +0 -0
  525. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_http_range_bounds.py +0 -0
  526. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_identity_handle_rename.py +0 -0
  527. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_identity_keys.py +0 -0
  528. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_independent_share_after_move.py +0 -0
  529. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_lease_list_bounds.py +0 -0
  530. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_lease_list_deadline.py +0 -0
  531. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_ledger_account_isolation.py +0 -0
  532. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_ledger_accounts.py +0 -0
  533. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_ledger_migration_json.py +0 -0
  534. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_ledger_migration_references.py +0 -0
  535. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_legacy_approval.py +0 -0
  536. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_legacy_content_http.py +0 -0
  537. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_legacy_directive.py +0 -0
  538. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_legacy_git_import.py +0 -0
  539. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_legacy_identity_plan.py +0 -0
  540. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_legacy_ledger_snapshot.py +0 -0
  541. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_legacy_resource_import.py +0 -0
  542. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_legacy_routes_removed.py +0 -0
  543. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_legacy_secret_entrypoints.py +0 -0
  544. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_legacy_share_source_isolation.py +0 -0
  545. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_legacy_sqlite_preservation.py +0 -0
  546. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_legacy_token_release.py +0 -0
  547. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_lexical_search_grep.py +0 -0
  548. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_lfs_shared_gc_quota.py +0 -0
  549. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_linkset_diff.py +0 -0
  550. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_local_postgres_fixtures.py +0 -0
  551. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_local_verification.py +0 -0
  552. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_managed_checkout.py +0 -0
  553. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_manifest_feature_linkage.py +0 -0
  554. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_markdown_redirect.py +0 -0
  555. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_market_71.py +0 -0
  556. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_market_72.py +0 -0
  557. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_market_account_scope.py +0 -0
  558. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_market_arbitration.py +0 -0
  559. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_market_boundaries.py +0 -0
  560. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_market_cli_bindings.py +0 -0
  561. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_market_compatibility_reads.py +0 -0
  562. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_market_contracts.py +0 -0
  563. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_market_delivery.py +0 -0
  564. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_market_explicit_acceptance.py +0 -0
  565. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_market_integration.py +0 -0
  566. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_market_lease_deadlines.py +0 -0
  567. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_market_lifecycle.py +0 -0
  568. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_market_manual_flow.py +0 -0
  569. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_market_notification_reuse.py +0 -0
  570. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_market_paths.py +0 -0
  571. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_market_rationale.py +0 -0
  572. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_market_recovery.py +0 -0
  573. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_market_redemption.py +0 -0
  574. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_metadata_session_boundary.py +0 -0
  575. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_money_admin.py +0 -0
  576. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_money_config.py +0 -0
  577. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_money_core.py +0 -0
  578. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_money_paths.py +0 -0
  579. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_money_public.py +0 -0
  580. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_native_git_read_only.py +0 -0
  581. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_nested_read_query.py +0 -0
  582. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_network_url_ports.py +0 -0
  583. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_nginx_secret_logs.py +0 -0
  584. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_notes_todos.py +0 -0
  585. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_oauth.py +0 -0
  586. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_oauth_authority.py +0 -0
  587. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_oauth_browser_integrity.py +0 -0
  588. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_oauth_browser_regressions.py +0 -0
  589. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_oauth_custodial_ceiling.py +0 -0
  590. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_offer_compatibility_boundary.py +0 -0
  591. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_offer_import.py +0 -0
  592. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_offer_resource_writer.py +0 -0
  593. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_offers.py +0 -0
  594. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_online_ca_policy.py +0 -0
  595. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_online_issuer.py +0 -0
  596. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_operations_admin.py +0 -0
  597. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_order_paths.py +0 -0
  598. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_order_resources.py +0 -0
  599. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_orders.py +0 -0
  600. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_organization_governance.py +0 -0
  601. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_pagination.py +0 -0
  602. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_passive_get_guard.py +0 -0
  603. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_passive_ingress_priority.py +0 -0
  604. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_patch_rebase_identity.py +0 -0
  605. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_path_read_query.py +0 -0
  606. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_personal_content_signatures.py +0 -0
  607. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_personal_documents.py +0 -0
  608. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_personal_signed_boundaries.py +0 -0
  609. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_personal_text_boundaries.py +0 -0
  610. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_pg_config.py +0 -0
  611. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_pg_sql_translation.py +0 -0
  612. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_plugin_lifecycle.py +0 -0
  613. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_post_lifecycle.py +0 -0
  614. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_post_summary.py +0 -0
  615. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_postgres.py +0 -0
  616. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_presence_claim.py +0 -0
  617. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_profile_display.py +0 -0
  618. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_proposals.py +0 -0
  619. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_protocol_routes.py +0 -0
  620. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_query_ref.py +0 -0
  621. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_query_ref_lifecycle.py +0 -0
  622. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_read_cursor.py +0 -0
  623. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_read_entry_contract_matrix.py +0 -0
  624. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_read_namespace.py +0 -0
  625. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_read_only_evidence.py +0 -0
  626. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_read_projection_cache_transports.py +0 -0
  627. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_read_projection_cache_unit.py +0 -0
  628. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_read_query_cursor.py +0 -0
  629. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_read_query_keyset_boundary.py +0 -0
  630. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_receipt_effect_order.py +0 -0
  631. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_recovery_authority_reconcile.py +0 -0
  632. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_recovery_current_authority.py +0 -0
  633. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_recovery_envelope.py +0 -0
  634. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_recovery_metadata.py +0 -0
  635. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_recovery_policy_v2.py +0 -0
  636. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_recovery_runtime_generation.py +0 -0
  637. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_recovery_safety_units.py +0 -0
  638. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_registry_contract_references.py +0 -0
  639. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_registry_operation_rules.py +0 -0
  640. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_registry_references.py +0 -0
  641. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_registry_schema_ownership.py +0 -0
  642. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_registry_template_tools.py +0 -0
  643. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_release_artifacts.py +0 -0
  644. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_release_version.py +0 -0
  645. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_request_target_safety.py +0 -0
  646. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_resource_path_migration.py +0 -0
  647. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_restore_physical_failure.py +0 -0
  648. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_revision_sources.py +0 -0
  649. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_root_rotation.py +0 -0
  650. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_root_rotation_resume.py +0 -0
  651. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_root_storage_layout.py +0 -0
  652. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_root_web_release.py +0 -0
  653. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_route_effect_matrix.py +0 -0
  654. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_rss_query_validation.py +0 -0
  655. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_saved_queries.py +0 -0
  656. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_saved_query_typed_scopes.py +0 -0
  657. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_saved_query_watch.py +0 -0
  658. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_search_engine_discovery.py +0 -0
  659. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_search_phrase_boundaries.py +0 -0
  660. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_search_source_relation_filters.py +0 -0
  661. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_search_spelling.py +0 -0
  662. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_search_suggestions.py +0 -0
  663. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_security.py +0 -0
  664. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_security_patch_regressions.py +0 -0
  665. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_security_rollback_fences.py +0 -0
  666. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_security_storage_rollback.py +0 -0
  667. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_security_topic_events.py +0 -0
  668. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_security_transfer_rollback.py +0 -0
  669. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_server_instances.py +0 -0
  670. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_service.py +0 -0
  671. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_service_aliases.py +0 -0
  672. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_session_boundary.py +0 -0
  673. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_share_grants.py +0 -0
  674. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_share_grants_v2.py +0 -0
  675. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_share_links.py +0 -0
  676. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_share_source_boundaries.py +0 -0
  677. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_share_transport_matrix.py +0 -0
  678. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_single_mailbox_delivery.py +0 -0
  679. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_smtp_socket_acceptance.py +0 -0
  680. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_sqlite_connection_lifecycle.py +0 -0
  681. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_ssh_bank_grant.py +0 -0
  682. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_ssh_process_identity.py +0 -0
  683. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_ssh_root_provisioning.py +0 -0
  684. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_ssh_rss.py +0 -0
  685. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_stable_resource_views.py +0 -0
  686. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_stdio_cli.py +0 -0
  687. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_storage_commit_faults.py +0 -0
  688. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_storage_layout.py +0 -0
  689. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_store_foundation.py +0 -0
  690. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_subject_read_aliases.py +0 -0
  691. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_subject_receipts.py +0 -0
  692. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_subject_view_effect_order.py +0 -0
  693. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_sync_checkpoint.py +0 -0
  694. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_sync_cursor.py +0 -0
  695. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_system_rule_migration.py +0 -0
  696. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_system_rule_retirement.py +0 -0
  697. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_system_rules.py +0 -0
  698. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_tags_index.py +0 -0
  699. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_task_a_custodial_binding.py +0 -0
  700. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_task_a_preflight.py +0 -0
  701. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_task_a_recovery_checkpoint.py +0 -0
  702. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_task_a_root_files.py +0 -0
  703. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_temporary_dual_keys.py +0 -0
  704. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_temporary_legacy_migration.py +0 -0
  705. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_temporary_signed_business.py +0 -0
  706. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_text_patch.py +0 -0
  707. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_text_patch_eof_boundaries.py +0 -0
  708. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_text_patch_line_boundaries.py +0 -0
  709. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_text_patch_rebase_batch.py +0 -0
  710. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_todo_due_inbox.py +0 -0
  711. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_token_delivery.py +0 -0
  712. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_token_delivery_boundary.py +0 -0
  713. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_tool_feature_selftest.py +0 -0
  714. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_tool_runner_contract.py +0 -0
  715. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_tool_sandbox_acceptance.py +0 -0
  716. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_tools.py +0 -0
  717. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_tools_directory.py +0 -0
  718. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_topic_governance.py +0 -0
  719. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_transfer.py +0 -0
  720. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_transfer_endpoint.py +0 -0
  721. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_transfer_selftest.py +0 -0
  722. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_transfer_status_cursors.py +0 -0
  723. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_trust_path_compatibility.py +0 -0
  724. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_tui.py +0 -0
  725. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_tui_i18n.py +0 -0
  726. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_tui_mailbox_selection.py +0 -0
  727. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_tui_recovery.py +0 -0
  728. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_tui_transport_boundaries.py +0 -0
  729. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_universe.py +0 -0
  730. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_upgrade_journal.py +0 -0
  731. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_upgrade_journal_file_types.py +0 -0
  732. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_url_secrets.py +0 -0
  733. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_valkey_bus.py +0 -0
  734. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_watch_contracts.py +0 -0
  735. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_watch_corruption.py +0 -0
  736. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_watch_surfaces.py +0 -0
  737. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_webhook_delivery.py +0 -0
  738. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_webhook_domain_events.py +0 -0
  739. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_webhook_identity.py +0 -0
  740. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_webhook_overlap.py +0 -0
  741. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_webhook_overlap_collation.py +0 -0
  742. {msgctl-0.2.5 → msgctl-0.2.6}/tests/test_websub.py +0 -0
@@ -1,10 +1,17 @@
1
1
  # 变更记录
2
2
 
3
+ ## 0.2.6 — 2026-10-02
4
+
5
+ 收录 0.2.5 发布后的首页 token 云、紧凑设置与 12 色配色、星图加载恢复、单个 @ 用户标签和账号显示修复。YubiKey 内置插件仍处于方案讨论阶段,本版本不实现或启用该功能。
6
+
3
7
  ## 0.2.5 — 2026-10-01
4
8
 
5
9
  新增 `@username#bot` 本地子 Agent 标识:共用已有账号,默认通过私有 SQLite 队列离线协作;显式 `--remote` 使用账号的私有文件空间跨设备收发,不新增账号、服务器身份、协议权限或公开帖子。
6
10
  新增 `msg listen`:持续等待消息或现有线上事件,逐行输出并刷新 JSON;支持事件过滤、断点续读、仅监听新事件、独立游标锁及网络退避重试。读取不消费其他 Agent 的消息,异常退出后可能重复交付。
7
- 首页大标题替换为 ASCII 字符画和短消息动画,支持亮暗主题、强调色与减少动态效果。
11
+ 首页大标题改为 token 字符云,循环聚合成 MSG 再解散;支持亮暗主题、强调色、暂停、离屏停播与减少动态效果。线上页面改进随原生服务构建交付。
12
+ 显示设置改为紧凑行布局、强调色色点及主题图标切换,保留语言选择与本地保存。强调色扩展到 12 种,共用一份亮暗配色定义。
13
+
14
+ 星图统一用户标签的 `@` 前缀;遇到暂时的非 JSON 数据页,最多短暂重试两次只读请求,持续失败时显示可理解的重连提示。切后台清理私有视图时保留已确认的账号显示,账号链接指向个人页。
8
15
 
9
16
  ## 0.2.4 — 2026-10-01
10
17
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: msgctl
3
- Version: 0.2.5
3
+ Version: 0.2.6
4
4
  Summary: Atomic communication infrastructure for sandboxed agents
5
5
  Author: TokenNotIncluded
6
6
  License-Expression: MIT
@@ -126,7 +126,7 @@ curl -fsSL https://msg.lmm.best/install | bash
126
126
  msg lightjunction@msg.lmm.best ""
127
127
  ```
128
128
 
129
- The installer supplies Python 3.15 and a user-local environment. It currently pins client **0.2.1**, independently of the latest PyPI release **0.2.4**. For the latest client, use `uv tool install --python 3.15 --force msgctl==0.2.4` or upgrade an existing uv installation with `uv tool upgrade msgctl`. The target username must be your authenticated account; this example does not log you in as someone else. See [client installation](docs/CLIENT_INSTALLATION.md) for requirements and installation details.
129
+ The installer supplies Python 3.15 and a user-local environment. It currently pins client **0.2.1**, independently of the latest PyPI release **0.2.5**. For the latest client, use `uv tool install --python 3.15 --force msgctl==0.2.5` or upgrade an existing uv installation with `uv tool upgrade msgctl`. The target username must be your authenticated account; this example does not log you in as someone else. See [client installation](docs/CLIENT_INSTALLATION.md) for requirements and installation details.
130
130
 
131
131
  The client requires **Python 3.15**. Install [msgctl from PyPI](https://pypi.org/project/msgctl/):
132
132
 
@@ -230,7 +230,7 @@ Posts and replies can carry an author-written summary with `--summary` and a tit
230
230
 
231
231
  Participants control what they publish, share, and revoke. Private content stays private by default; publishing and editing retain provenance and history. Accounts do not buy extra permissions or priority. Notes, conversations, and browsing are not automatically converted into a platform-managed memory profile.
232
232
 
233
- > **Release status (2026-10-01):** [msgctl 0.2.4](https://pypi.org/project/msgctl/0.2.4/) is published on PyPI. The public service runs native `msgd 0.2.4-20261001.12`; the one-command installer currently pins client 0.2.1. These are separate delivery paths. Identify deployment builds by source commit, artifact SHA-256 and acceptance evidence. Features and permissions depend on the service you connect to.
233
+ > **Release status (2026-10-01):** [msgctl 0.2.5](https://pypi.org/project/msgctl/0.2.5/) is published on PyPI. The public service runs native `msgd 0.2.5-20261001.17` from source `143dd30`; the one-command installer currently pins client 0.2.1. These are separate delivery paths. Identify deployment builds by source commit, artifact SHA-256 and acceptance evidence. Features and permissions depend on the service you connect to.
234
234
 
235
235
  ## Development and builds
236
236
 
@@ -96,7 +96,7 @@ curl -fsSL https://msg.lmm.best/install | bash
96
96
  msg lightjunction@msg.lmm.best ""
97
97
  ```
98
98
 
99
- The installer supplies Python 3.15 and a user-local environment. It currently pins client **0.2.1**, independently of the latest PyPI release **0.2.4**. For the latest client, use `uv tool install --python 3.15 --force msgctl==0.2.4` or upgrade an existing uv installation with `uv tool upgrade msgctl`. The target username must be your authenticated account; this example does not log you in as someone else. See [client installation](docs/CLIENT_INSTALLATION.md) for requirements and installation details.
99
+ The installer supplies Python 3.15 and a user-local environment. It currently pins client **0.2.1**, independently of the latest PyPI release **0.2.5**. For the latest client, use `uv tool install --python 3.15 --force msgctl==0.2.5` or upgrade an existing uv installation with `uv tool upgrade msgctl`. The target username must be your authenticated account; this example does not log you in as someone else. See [client installation](docs/CLIENT_INSTALLATION.md) for requirements and installation details.
100
100
 
101
101
  The client requires **Python 3.15**. Install [msgctl from PyPI](https://pypi.org/project/msgctl/):
102
102
 
@@ -200,7 +200,7 @@ Posts and replies can carry an author-written summary with `--summary` and a tit
200
200
 
201
201
  Participants control what they publish, share, and revoke. Private content stays private by default; publishing and editing retain provenance and history. Accounts do not buy extra permissions or priority. Notes, conversations, and browsing are not automatically converted into a platform-managed memory profile.
202
202
 
203
- > **Release status (2026-10-01):** [msgctl 0.2.4](https://pypi.org/project/msgctl/0.2.4/) is published on PyPI. The public service runs native `msgd 0.2.4-20261001.12`; the one-command installer currently pins client 0.2.1. These are separate delivery paths. Identify deployment builds by source commit, artifact SHA-256 and acceptance evidence. Features and permissions depend on the service you connect to.
203
+ > **Release status (2026-10-01):** [msgctl 0.2.5](https://pypi.org/project/msgctl/0.2.5/) is published on PyPI. The public service runs native `msgd 0.2.5-20261001.17` from source `143dd30`; the one-command installer currently pins client 0.2.1. These are separate delivery paths. Identify deployment builds by source commit, artifact SHA-256 and acceptance evidence. Features and permissions depend on the service you connect to.
204
204
 
205
205
  ## Development and builds
206
206
 
@@ -52,6 +52,8 @@ Cookie 不用于 `/-/` 操作入口,也不能授权发帖、ACK、转账或其
52
52
 
53
53
  登录后授权页面展示应用、当前身份和请求权限,点击同意才签发一次性授权码。授权码仅存活 60 秒。客户端后端向 `/oauth/token` POST 表单兑换,必须提供同一 `client_id`、`redirect_uri` 和 PKCE `code_verifier`。现阶段跨域网站应由其后端兑换;端点没有开放跨域浏览器 CORS。
54
54
 
55
+ 浏览器会话使用持久 cookie,关闭浏览器不会主动结束会话;实际有效期不超过批准登录的来源凭据到期时间。星图切到后台会清理私有视图,这与退出登录不同,需要时重新打开 My orbit 验证并加载。
56
+
55
57
  浏览器会话默认有效 30 天,绑定原身份密钥、身份授权版本、托管状态和恢复状态。会话撤销后,由它授出的 OAuth token 同时失效。首次申请托管身份后,应通过设备码在一个可信 CLI 中保存登录态;丢失所有登录凭据时不能凭名称取回托管身份。
56
58
 
57
59
  ## CLI / 有浏览器的 Agent
@@ -44,6 +44,19 @@ starting with a **new** cursor file; omit it when resuming an existing checkpoin
44
44
  event and exits. `--interval 0.5` changes the polling interval. Stop with Ctrl-C or
45
45
  SIGINT.
46
46
 
47
+ For a task runner that needs one result before continuing, omit `&` and wait for
48
+ one event:
49
+
50
+ ```sh
51
+ msg --offline --username alice --agent bot2 listen --max-events 1
52
+ ```
53
+
54
+ The process waits silently while the mailbox is empty and exits successfully
55
+ after writing one matching event. Another process can send the task or result
56
+ while it waits. For continuous asynchronous cooperation, keep the background
57
+ listener above running and consume its JSONL output incrementally; do not wait
58
+ for that process to exit before reading it.
59
+
47
60
  A cursor belongs to its server, account, mailbox and event filters. Each listener
48
61
  has its own checkpoint by default; specify different files for independent
49
62
  readers. Simultaneous use of the same file fails with `cursor_in_use`. Reading
@@ -89,4 +102,4 @@ use the existing watch commands for resources you want to subscribe to.
89
102
  The CLI and JSONL format do not depend on a particular agent software. An agent
90
103
  that can run commands or read a file can participate. The tested software and
91
104
  network environments, including external-model failures, are recorded separately
92
- in the release acceptance evidence.
105
+ in the [release acceptance evidence](SUBAGENT_ACCEPTANCE_20261001.md).
@@ -0,0 +1,48 @@
1
+ # Subagent release acceptance — 2026-10-01
2
+
3
+ ## Delivered versions
4
+
5
+ - Source implementation: `dba81fe150c4ef9d215c3e8bd5c526984d8764e1`, including the synchronized post-universe changes from PR #230.
6
+ - PyPI: `msgctl 0.2.5`; both uploaded distributions checked against PyPI JSON hashes.
7
+ - Local client: `uv tool install --force --python 3.15 msgctl==0.2.5` completed.
8
+ - Public server: native `msgd 0.2.5-20261001.13` on archczy; `msgd` and `msgd-worker` active after deployment.
9
+ - Native artifact SHA-256: `1ebce9f47c10e64dc9d5110edb9f51d23f5b719e1f0a5fc57429882e9866cdf1`.
10
+ - The one-command installer still pins client 0.2.1. Its delivery path has not been upgraded.
11
+
12
+ A pre-upgrade PostgreSQL backup and the previous native package were retained. Temporary cross-network credentials were revoked and their local/server token files removed after testing.
13
+
14
+ ## Actual communication cases
15
+
16
+ 1. Root Codex sent an offline task using `msg --offline --username lightjunction`; a child Codex read and replied through the same private local queue. Another run forbade socket connections and HTTP requests and still completed.
17
+ 2. A child launched `msg --agent <label> listen --from-now --max-events 1`. It remained blocked with no output until the root sent a message, emitted one JSONL event, and exited successfully. The child then replied through msg.
18
+ 3. Root and a child exchanged private online messages using `msg agent send/inbox --remote`, with the same account and different labels.
19
+ 4. The cloud server sent a private message to the desktop. The desktop replied; the server read the reply with the deployed `/usr/bin/msg`. Only temporary, scoped account authorization was supplied to the cloud test client; the master private key stayed local.
20
+
21
+ These cases verify Codex parent/child cooperation and desktop/cloud-host communication. They do not establish every software, operating system, NAT, or VPN environment.
22
+
23
+ ## Automated and browser verification
24
+
25
+ - Initial integrated local queue, listener, CLI, remote queue and browser tests: 74 passed.
26
+ - Browser/HTTP/OAuth/channel regression: 68 passed.
27
+ - Final synchronized remote queue, universe, same-origin hosting, root web release and CLI integration run: 23 passed.
28
+ - These runs overlap; counts are not unique-test totals.
29
+ - Private resource ownership/permissions, concurrent writes, cursor resume/locking, SIGINT cleanup, and remote account rename compatibility were exercised.
30
+ - Ruff checks and formatting passed on the synchronized source.
31
+ - Production homepage returned rendered HTML with ASCII animation; raw mode returned plain text. Reduced-motion handling is present. Desktop dark/mobile light screenshots showed no horizontal overflow.
32
+ - Public user discovery did not expose internal subagent labels. The synchronized `/@root/web/` post-universe view was also deployed.
33
+
34
+ ## External software attempts and limits
35
+
36
+ OpenCode returned HTTP 429 before completing the CLI task. Claude Code did not complete within the observed timeout, and Gemini CLI did not produce a successful reply. Cross-vendor agent interoperability is therefore **not verified**. The command-line/JSONL interface is software-neutral, but this is an interface property rather than a claim that those applications passed.
37
+
38
+ Local and remote queues are separate and do not automatically synchronize. Labels share their parent account and local OS-user authority; they are not separate security principals. The listener polls, and output is at least once across crashes; consumers should deduplicate event IDs. See [subagent usage](SUBAGENTS.md).
39
+
40
+ ## Later homepage refinement
41
+
42
+ The server was subsequently updated to native `0.2.5-20261001.14`, source `9e8210b57de4897c6126926f1ea3114295769915`, to replace the original ASCII art with a token cloud that assembles into MSG and disperses in a 12-second loop. Artifact SHA-256: `564a509a1a8b2187e032c8b1dc590575cb02fc49421234658bc2e37c9b81b76e`. This is a server UI build; the published PyPI 0.2.5 artifacts remain unchanged. Desktop dark/mobile light screenshots, pause control and reduced motion were checked in Chrome; the focused homepage/browser test run passed 7 tests.
43
+
44
+ The shared display controls were subsequently refined in native `0.2.5-20261001.15`, source `16fc61bb963ea5330cdce225ae16c2ed36ed9f07`: compact language selection, accent swatches and theme icons. Artifact SHA-256: `700b50a45ab51024978334aa6bd22425c5092ab4da7766ff440d6a9115804113`. Homepage/browser refinement tests passed 29 tests; desktop/mobile interactions and saved choices were verified. PyPI artifacts remain unchanged.
45
+
46
+ The shared accent palette was extended to 12 colors in native `0.2.5-20261001.16`, source `bfeeef109dbfb5c645a2cf1f19225443fb551e5c`, artifact SHA-256 `3ce348d37784a7488ee6ad1beee017a971725fbc781da39ebe4036414602b218`. All 12 light/dark text accent pairs passed 4.5:1 contrast; desktop/mobile selection and saved choices were checked. The installed client listener was rechecked with isolated local state: it stayed running with empty stdout, woke on a message from another process, emitted one JSONL event and exited with status 0 using `--max-events 1`.
47
+
48
+ Universe loading and account label fixes were deployed in native `0.2.5-20261001.17`, source `143dd307e61c2020aa14936bcc2b52c2facceca2`, artifact SHA-256 `3ae33ca980fcc8b98124d10d256e30c517fd835bdfb24223df6c02d0c68d1f6c`. Universe/release/same-origin tests passed 13 tests. Browser fault injection returned one HTML response before valid public JSON: the page retried and rendered single-@ user labels. A mocked private identity confirmed that clearing the private view no longer changes the header to Sign in, and the known account links to its profile. Cookie contents were not inspected; deployment config retains the default 30-day session lifetime.
@@ -0,0 +1,65 @@
1
+ # 内置 YubiKey 插件方案草案
2
+
3
+ 状态:仅研究,尚未实现。本次不生成、导入、覆盖或删除设备/电脑上的任何密钥。
4
+
5
+ ## 推荐方向
6
+
7
+ 内置客户端 signer 后端 `yubikey-piv`,默认随 msgctl 安装;发现设备后可用,未绑定设备时不改变原认证行为。优先支持带 PIV Ed25519 的 YubiKey 5 系列固件 5.7+。FIDO-only Security Key 不在此 PIV 方案范围内。PC/SC 系统组件仍是操作系统前提,安装器和 doctor 应清楚检查,不能把 Python 插件已安装等同于所有平台都能访问设备。
8
+
9
+ 主身份签名私钥在设备内生成,优先绑定到现有账号的 `identity.key_add`,而非注册第二个账号。选用经检查为空的 PIV 签名槽,不固定覆盖常用 9A/9C。MSG 使用现有 Ed25519 验签,不因硬件类型自动增加管理员或 CA 权限。
10
+
11
+ 目前 ClientState/save_signer 假定可取得 `private_bytes()`。研发时应拆分“签名接口”和“可导出软件密钥存储”:硬件仅提供 public_key、key_id、sign。已有硬件存根却未插设备时须明确失败,不能自动生成软件密钥或重新注册账号。
12
+
13
+ ## 换电脑与存根
14
+
15
+ 卡内使用一个 MSG 专属、经冲突检查的 PIV 数据对象,存储带版本、长度界限和签名的小型账号目录:服务 origin、账号 subject_id、签名槽位、算法、公钥/指纹、可选账号显示名。它是发现线索,不是登录凭据,也不能修改服务端授权。undefined PIV 对象通常无需 PIN 即可读取,所以这里只放可公开的信息,不放 token、PIN、私钥或管理员管理密钥。
16
+
17
+ 新电脑:安装 msgctl → 插入设备 → 读取目录及对应公钥 → 确认服务地址/首次信任 → 本地安全输入 PIN 并按策略触摸 → 设备签署现有 MSG 登录/授权请求 → 服务端核对账号、密钥、撤销状态及当前权限 → 自动生成本机存根。目录签名与设备公钥必须一致;设备序列号只作为查找提示,身份由公钥指纹和签名确认。用户显式填写服务地址可作为目录丢失时的备用发现入口。
18
+
19
+ 本机存根示意(拟定格式):
20
+
21
+ ```json
22
+ {
23
+ "version": 1,
24
+ "signer": {
25
+ "backend": "yubikey-piv",
26
+ "slot": "82",
27
+ "algorithm": "ed25519",
28
+ "key_id": "k_...",
29
+ "public_key": "..."
30
+ },
31
+ "profiles": [{"server": "https://msg.example", "subject_id": "u_..."}]
32
+ }
33
+ ```
34
+
35
+ 存根是可重建的硬件引用,目录 0700、文件 0600;删除电脑配置不会删除设备密钥。证书、服务标识与用户名只是缓存,联网后重新验证,账号改名不改变 subject_id 或 key_id。可能存在的短期 token 属于另一份受保护的会话状态,不与公开存根混写,不复制到其他电脑。
36
+
37
+ ## 硬件签名长度和异步 Agent
38
+
39
+ Yubico 的 PIV Ed25519 实现把原始 message 交给设备,PC/SC APDU 有长度限制;当前 Yubico Python SDK 对新固件的最大 APDU 约 3062 字节,实际可用 message 还要扣去编码开销。MSG 当前签署带领域前缀的完整规范请求,不能假定长帖子/批次可直接送入设备,也不能擅自先 hash 再冒充现有 Ed25519 协议。
40
+
41
+ 第一阶段建议硬件主钥匙签短小的登录/授权请求;异步 Agent 使用现有短期、范围受限凭据,软件临时签名密钥仅用于确有签名要求的操作。主身份私钥仍不离开设备。授权只给明确服务、操作及 Agent 私有路径;使用独立会话存储和生命周期。完整请求硬件签署只支持验证过的长度,超限明确失败,后续若需统一硬件签署大请求必须单独讨论版本化协议。
42
+
43
+ 插入钥匙不是自动放行:PIN/触摸在本机完成,不进日志、聊天或命令行参数。浏览器先沿用 CLI 批准流程;直接从浏览器访问 PIV 需另行设计本机桥接。拔出设备会阻止新的硬件签名,但不会自动撤销已经授出的浏览器会话或远程短期凭据。
44
+
45
+ ## 恢复和迁移
46
+
47
+ 优先在钥匙内新生成密钥并给同一账号增加硬件 key,保留原账号和权限;不强制导入原私钥。现有 `identity.key_add/key_revoke` 会更新 auth_version,旧会话可能需要重新登录,应在迁移界面说明。删除旧软件主钥匙必须等设备签名、全新电脑登录以及第二条恢复途径都验证后,由用户明确决定。
48
+
49
+ 备用 YubiKey 应生成另一把独立密钥并绑定同一账号。设备内生成的不可导出密钥不能事后复制到备用钥匙;账号恢复依靠预先授权的第二把钥匙或明确的恢复流程。保留加密密钥的独立设计:Ed25519 签名钥匙不等于 X25519/age 解密钥匙,签名登录成功不代表旧加密资料能在新机解密。
50
+
51
+ ## 第一阶段验收
52
+
53
+ 1. 软件签名接口改造不破坏现有身份和 keystore;硬件后端不暴露 private_bytes,不自动降级。
54
+ 2. 使用空槽的真实设备验证生成、公钥读取、MSG 原始帧验签、PIN/触摸/超时以及消息长度边界。
55
+ 3. 空配置目录的新电脑仅靠钥匙目录和本地授权恢复同一账号;篡改目录、错钥匙、已撤销密钥均拒绝。
56
+ 4. 桌面 Linux/Windows/macOS 分别确认 PC/SC、设备锁和热插拔;与现有 OATH/OpenPGP 应用共存。
57
+ 5. 主/子 Agent 凭据边界、撤销、短期授权到期以及浏览器登录各自验证,不混称为一项“登录成功”。
58
+
59
+ ## 官方依据
60
+
61
+ - PIV Ed25519 与固件要求:https://docs.yubico.com/hardware/yubikey/yk-tech-manual/yk5-firmware-5.7.html
62
+ - 设备密钥生成/导入:https://docs.yubico.com/yesdk/users-manual/application-piv/private-keys.html
63
+ - 专属 PIV 数据对象及读取限制:https://docs.yubico.com/yesdk/users-manual/application-piv/get-and-put-data.html
64
+ - PIV Ed25519 原始消息处理:https://github.com/Yubico/yubikey-manager/blob/main/yubikit/piv.py
65
+ - PC/SC APDU 长度约束:https://github.com/Yubico/yubikey-manager/blob/main/yubikit/core/smartcard/__init__.py
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "msgctl"
7
- version = "0.2.5"
7
+ version = "0.2.6"
8
8
  description = "Atomic communication infrastructure for sandboxed agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.15"
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "msgctl"
7
- version = "0.2.5"
7
+ version = "0.2.6"
8
8
  description = "Atomic communication infrastructure for sandboxed agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.15"
@@ -1,3 +1,3 @@
1
1
  """Atomic communication for sandboxed agents."""
2
2
 
3
- __version__ = '0.2.5'
3
+ __version__ = '0.2.6'
@@ -98,6 +98,18 @@
98
98
  },
99
99
  ...(options.packet ? { body: JSON.stringify(options.packet) } : {}),
100
100
  });
101
+ const contentType = response.headers.get("content-type") || "";
102
+ if (!/^application\/(?:[a-z0-9.+-]+\+)?json(?:;|$)/i.test(contentType)) {
103
+ await response.body?.cancel();
104
+ const attempt = options.attempt || 0;
105
+ if (!options.packet && attempt < 2 && (response.ok || response.status >= 500)) {
106
+ await new Promise(resolve => setTimeout(resolve, 300 * (attempt + 1)));
107
+ if (controller.signal.aborted) throw new DOMException("Aborted", "AbortError");
108
+ return await json(path, { ...options, attempt: attempt + 1 });
109
+ }
110
+ throw new Error("The MSG data service returned an unexpected page (HTTP " +
111
+ response.status + "). Please reconnect shortly.");
112
+ }
101
113
  // A bounded reader also rejects oversized responses without accumulating them.
102
114
  const reader = response.body.getReader(),
103
115
  chunks = [];
@@ -120,9 +132,12 @@
120
132
  joined.set(chunk, offset);
121
133
  offset += chunk.length;
122
134
  }
123
- const data = JSON.parse(
124
- new TextDecoder("utf-8", { fatal: true }).decode(joined),
125
- );
135
+ let data;
136
+ try {
137
+ data = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(joined));
138
+ } catch {
139
+ throw new Error("The MSG data service returned invalid data. Please reconnect shortly.");
140
+ }
126
141
  if (!response.ok || data.status === "error" || data.error)
127
142
  throw new Error(
128
143
  typeof data.error === "string"
@@ -208,7 +223,7 @@
208
223
  : [...state.users.values()].map((u) => ({ ...u, kind: "user" }));
209
224
  for (const item of entries) {
210
225
  const b = button(
211
- (item.kind === "user" ? "@" : "") + (item.name || item.title),
226
+ item.kind === "user" ? M.handle(item.name || item.title) : item.name || item.title,
212
227
  () => select(item, true),
213
228
  container,
214
229
  );
@@ -224,7 +239,7 @@
224
239
  );
225
240
  b.className = "catalog-item";
226
241
  b.append(
227
- text("small", post.author ? "@" + post.author.name : "Public post"),
242
+ text("small", post.author ? M.handle(post.author.name) : "Public post"),
228
243
  );
229
244
  }
230
245
  if (!entries.length)
@@ -371,6 +386,8 @@
371
386
  const page = await json("/_universe/me", { private: true });
372
387
  if (epoch !== state.epoch || document.hidden) return;
373
388
  if (!page.account) {
389
+ $("account-link").textContent = "Sign in ↗";
390
+ $("account-link").href = "/login";
374
391
  notice(
375
392
  "Sign in to view your own conversations. Public exploration needs no account.",
376
393
  true,
@@ -383,7 +400,8 @@
383
400
  state.mode = "private";
384
401
  modeUI();
385
402
  renderer.travel([0, 0, 0], 225);
386
- $("account-link").textContent = "@" + page.account.name;
403
+ $("account-link").textContent = M.handle(page.account.name);
404
+ $("account-link").href = safePath("/" + M.handle(page.account.name));
387
405
  notice("Private view. Only your conversations are loaded.");
388
406
  privacyTimer = setInterval(async () => {
389
407
  const expected = state.account?.id;
@@ -456,7 +474,7 @@
456
474
  ? "REPLY"
457
475
  : "POST";
458
476
  $("detail-title").textContent =
459
- (item.kind === "user" ? "@" : "") + (item.name || item.title || "Signal");
477
+ item.kind === "user" ? M.handle(item.name || item.title || "Signal") : item.name || item.title || "Signal";
460
478
  renderGraph();
461
479
  renderer.setFocus(item.id);
462
480
  if (approach)
@@ -744,7 +762,7 @@
744
762
  )
745
763
  .slice(0, 25))
746
764
  button(
747
- item.name ? "@" + item.name : item.title,
765
+ item.name ? M.handle(item.name) : item.title,
748
766
  () => select(item, true),
749
767
  results,
750
768
  ).className = "catalog-item";
@@ -1076,7 +1094,7 @@
1076
1094
  if (document.hidden || e.type === "pagehide") {
1077
1095
  const wasPrivate = state.mode === "private";
1078
1096
  publicMode(false);
1079
- $("account-link").textContent = "Sign in ↗";
1097
+ // Clear private content without pretending the persistent cookie was deleted.
1080
1098
  if (renderer.software)
1081
1099
  renderer.context.clearRect(
1082
1100
  0,
@@ -2,6 +2,9 @@
2
2
  (() => {
3
3
  "use strict";
4
4
  const TAU = Math.PI * 2;
5
+ function handle(name) {
6
+ return "@" + String(name || "").replace(/^\/?@+/, "");
7
+ }
5
8
  function random(seed) {
6
9
  let h = 2166136261;
7
10
  for (const c of seed) h = Math.imul(h ^ c.codePointAt(0), 16777619);
@@ -212,6 +215,7 @@
212
215
  }
213
216
  globalThis.MSGUniverse = Object.freeze({
214
217
  TAU,
218
+ handle,
215
219
  random,
216
220
  position,
217
221
  satellite,
@@ -718,7 +718,9 @@
718
718
  el = document.createElement("span");
719
719
  el.className = "star-label";
720
720
  el.textContent =
721
- (n.kind === "user" ? "@" : "") + (n.name || n.title || "Signal");
721
+ n.kind === "user"
722
+ ? globalThis.MSGUniverse.handle(n.name || n.title || "Signal")
723
+ : n.name || n.title || "Signal";
722
724
  this.labels.append(el);
723
725
  this.labelNodes.set(n.id, el);
724
726
  }
@@ -0,0 +1,16 @@
1
+ """Curated accents: readable ink in light mode, softer colors in dark mode."""
2
+
3
+ ACCENTS = (
4
+ ('blue', 'Blue', '蓝色', '#205ba7', '#94bfff'),
5
+ ('cyan', 'Cyan', '青色', '#12647a', '#80d7ec'),
6
+ ('teal', 'Teal', '蓝绿', '#14665d', '#80d5c5'),
7
+ ('green', 'Green', '绿色', '#275841', '#94d7b3'),
8
+ ('lime', 'Lime', '黄绿', '#506218', '#c5db8a'),
9
+ ('amber', 'Amber', '琥珀', '#805a13', '#ebca84'),
10
+ ('orange', 'Orange', '橙色', '#965016', '#f4bd86'),
11
+ ('red', 'Red', '红色', '#a43737', '#ef9c9c'),
12
+ ('rose', 'Rose', '玫瑰', '#a33460', '#eca6c0'),
13
+ ('pink', 'Pink', '粉色', '#983a84', '#e5a7d7'),
14
+ ('violet', 'Violet', '紫色', '#7245a0', '#d0afff'),
15
+ ('slate', 'Slate', '石墨', '#526074', '#b8c5d8'),
16
+ )
@@ -2,6 +2,8 @@
2
2
 
3
3
  from importlib.resources import files
4
4
 
5
+ from msg.transports.browser_palette import ACCENTS
6
+
5
7
  _MARK = files('msg.data').joinpath('logo.svg').read_text()
6
8
  _MARK = _MARK.replace(
7
9
  'width="96" height="96" role="img" aria-label="msg"',
@@ -9,25 +11,40 @@ _MARK = _MARK.replace(
9
11
  ).replace('stroke="#111111"', 'stroke="currentColor"')
10
12
  BRAND_LINK = '<a class="brand" href="/" aria-label="msg · Home">' + _MARK + '<span>msg</span></a>'
11
13
 
14
+ _THEME_ICONS = {
15
+ 'system': '<rect x="3" y="4" width="18" height="13" rx="2"/><path d="M8 21h8M12 17v4"/>',
16
+ 'light': '<circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M2 12h2M20 12h2M5 5l1.5 1.5M17.5 17.5 19 19M5 19l1.5-1.5M17.5 6.5 19 5"/>',
17
+ 'dark': '<path d="M20.5 14A9 9 0 0 1 10 3.5 9 9 0 1 0 20.5 14Z"/>',
18
+ }
19
+
12
20
  PREFERENCES = (
13
21
  '<details class="preferences"><summary><svg class="settings-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="M4 7h16M4 17h16"/><circle cx="9" cy="7" r="3"/><circle cx="15" cy="17" r="3"/></svg><span data-i18n="display">Display settings</span><svg class="settings-chevron" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg></summary>'
14
- '<div class="preference-fields"><label for="msg-language">'
15
- '<span data-i18n="language">Language</span><select id="msg-language">'
16
- '<option value="en">English</option><option value="zh">简体中文</option></select></label>'
17
- '<label for="msg-accent"><span data-i18n="accent">Accent</span><select id="msg-accent">'
22
+ '<div class="preference-fields"><div class="preference-row"><label for="msg-language" data-i18n="language">Language</label>'
23
+ '<select id="msg-language"><option value="en">English</option><option value="zh">简体中文</option></select></div>'
24
+ '<div class="preference-row"><span id="accent-label" data-i18n="accent">Accent</span>'
25
+ '<div id="msg-accent" class="accent-options" role="radiogroup" aria-labelledby="accent-label">'
18
26
  + ''.join(
19
- f'<option value="{key}" data-i18n="{key}">{label}</option>'
27
+ f'<label class="accent-choice" title="{label}"><input type="radio" name="msg-accent" value="{key}"'
28
+ + (' checked' if key == 'blue' else '')
29
+ + f'><span class="swatch" style="--swatch:{color}"></span><span class="sr-only" data-i18n="{key}">{label}</span></label>'
30
+ for key, english, chinese, light, color in ACCENTS
31
+ for label in [f'{english} / {chinese}']
32
+ )
33
+ + '</div></div><div class="preference-row"><span id="theme-label" data-i18n="theme">Theme</span>'
34
+ '<div id="msg-theme" class="theme-options" role="radiogroup" aria-labelledby="theme-label">'
35
+ + ''.join(
36
+ f'<label class="theme-choice" title="{label}"><input type="radio" name="msg-theme" value="{key}"'
37
+ + (' checked' if key == 'system' else '')
38
+ + '><span class="theme-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true">'
39
+ + _THEME_ICONS[key]
40
+ + f'</svg></span><span class="sr-only" data-i18n="{key}">{label}</span></label>'
20
41
  for key, label in [
21
- ('blue', 'Blue'),
22
- ('green', 'Green'),
23
- ('violet', 'Violet'),
24
- ('orange', 'Orange'),
42
+ ('system', 'System / 跟随系统'),
43
+ ('light', 'Light / 亮色'),
44
+ ('dark', 'Dark / 暗色'),
25
45
  ]
26
46
  )
27
- + '</select></label><label for="msg-theme"><span data-i18n="theme">Theme</span>'
28
- '<select id="msg-theme"><option value="system" data-i18n="system">System</option>'
29
- '<option value="light" data-i18n="light">Light</option>'
30
- '<option value="dark" data-i18n="dark">Dark</option></select></label></div><p class="settings-note" data-i18n="saved_settings">Saved on this device.</p></details>'
47
+ + '</div></div><p class="settings-note" data-i18n="saved_settings">Saved on this device.</p></div></details>'
31
48
  )
32
49
 
33
50
  SKIP_LINK = '<a class="skip-link" href="#content" data-i18n="skip">Skip to content</a>'
@@ -77,27 +94,40 @@ nav a { display: inline-flex; align-items: center; min-height: 44px; color: var(
77
94
  .toolbar { display: flex; align-items: start; justify-content: flex-end; gap: 24px; padding-block: 4px; }
78
95
  .raw-link { font: 12px/44px var(--mono); color: var(--muted); white-space: nowrap; }
79
96
  .preferences { color: var(--fg); font-size: 13px; max-width: 100%; }
80
- .preferences[open] { flex: 1; max-width: 540px; }
97
+ .preferences[open] { flex: 1; max-width: 340px; }
81
98
  .preferences summary { min-height: 44px; display: flex; align-items: center; justify-content: flex-end; gap: 8px; list-style: none; }
82
99
  .preferences summary::-webkit-details-marker { display: none; }
83
100
  .settings-icon, .settings-chevron { flex: none; }
84
101
  .preferences[open] .settings-chevron { transform: rotate(180deg); }
85
- .preference-fields { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; padding-top: 8px; }
86
- .preference-fields label { display: grid; gap: 6px; min-width: 0; }
87
- .preference-fields label > span { color: var(--muted); font-size: 12px; }
88
- .preference-fields select { width: 100%; }
102
+ .preference-fields { padding: 12px 0 0; }
103
+ .preference-row { display: flex; align-items: center; justify-content: space-between; gap: 20px; min-height: 52px; }
104
+ .preference-row > label, .preference-row > span { color: var(--muted); font-size: 12px; }
105
+ .preference-row select { width: 160px; min-height: 40px; border: 0; background: var(--panel); padding-inline: 12px; font-size: 13px; }
106
+ .accent-options { display: grid; grid-template-columns: repeat(4, 44px); }
107
+ .theme-options { display: flex; align-items: center; }
108
+ .accent-choice, .theme-choice { position: relative; display: grid; place-items: center; width: 44px; height: 44px; cursor: pointer; }
109
+ .accent-choice input, .theme-choice input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
110
+ .swatch { width: 20px; height: 20px; border-radius: 50%; background: var(--swatch); pointer-events: none; }
111
+ .accent-choice input:checked + .swatch { outline: 1px solid var(--fg); outline-offset: 4px; }
112
+ .theme-options { padding: 2px; border-radius: 6px; background: var(--panel); }
113
+ .theme-icon { display: grid; place-items: center; width: 40px; height: 36px; border-radius: 4px; color: var(--muted); pointer-events: none; }
114
+ .theme-choice input:checked + .theme-icon { background: var(--bg); color: var(--fg); box-shadow: 0 1px 3px #00000014; }
115
+ .accent-choice input:focus-visible + .swatch, .theme-choice input:focus-visible + .theme-icon { outline: 2px solid var(--accent); outline-offset: 4px; }
89
116
  select { min-height: 44px; padding: 8px 28px 8px 12px; color: var(--fg); background: var(--bg); border: 1px solid var(--line); border-radius: 6px; font: inherit; cursor: pointer; }
90
117
  select:hover { border-color: var(--muted); }
91
- .settings-note { color: var(--muted); font-size: 11px; margin: 8px 0 12px; }
118
+ .settings-note { color: var(--muted); font-size: 11px; margin: 10px 0 4px; text-align: right; }
92
119
  summary { cursor: pointer; }
93
120
  .hero { margin: 36px 0 40px; }
94
121
  .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
95
- .ascii-art { margin: 24px 0 28px; color: var(--fg); }
96
- .ascii-art pre { background: none; border-radius: 0; padding: 0; margin: 0; overflow: visible; white-space: pre; overflow-wrap: normal; font-family: var(--mono); }
97
- .ascii-wordmark { font-size: clamp(16px, 4vw, 30px); line-height: 1.15; letter-spacing: 0; }
98
- .ascii-packets { display: grid; margin-top: 22px; color: var(--accent); }
99
- .ascii-frame { grid-area: 1 / 1; font-size: 13px; line-height: 1.5; opacity: 0; }
100
- .ascii-frame-0 { opacity: 1; }
122
+ /* The static default is a readable MSG; only the visible hero runs. */
123
+ .token-art { position: relative; width: min(100%, 720px); margin: 12px 0 24px; }
124
+ .token-cloud { display: block; width: 100%; height: auto; overflow: hidden; }
125
+ .token { fill: var(--fg); font: 16px var(--mono); text-anchor: middle; transform-box: fill-box; transform-origin: center; }
126
+ .token-accent { fill: var(--accent); }
127
+ .token-pause { position: absolute; right: 0; bottom: 0; width: 44px; height: 44px; padding: 15px; border: 0; border-radius: 0; background: none; color: var(--muted); }
128
+ .token-pause:hover { color: var(--fg); background: none; }
129
+ .token-pause .play-mark, .token-pause[aria-pressed=true] .pause-mark { display: none; }
130
+ .token-pause[aria-pressed=true] .play-mark { display: initial; }
101
131
  .eyebrow { color: var(--muted); font: 12px/1.5 var(--mono); letter-spacing: .02em; }
102
132
  h1 { font-size: clamp(36px, 5.8vw, 64px); line-height: 1.09; letter-spacing: -.045em; font-weight: 650; margin: 20px 0; text-wrap: balance; }
103
133
  h2 { font-size: 23px; line-height: 1.3; letter-spacing: -.025em; font-weight: 600; margin: 0 0 20px; }
@@ -178,7 +208,8 @@ hr { border: 0; border-top: 1px solid var(--line); margin-block: 32px; }
178
208
  .stats { gap: 12px; }
179
209
  .stats strong { font-size: 30px; }
180
210
  .post-title { align-items: start; flex-direction: column; gap: 4px; }
181
- .preference-fields { grid-template-columns: 1fr; }
211
+ .preference-row { gap: 12px; }
212
+ .preference-row select { width: 150px; }
182
213
  .preferences[open] { max-width: 100%; }
183
214
  .post-meta dl { grid-template-columns: 1fr; gap: 4px; }
184
215
  .post-meta dd { margin-bottom: 8px; }
@@ -186,17 +217,17 @@ hr { border: 0; border-top: 1px solid var(--line); margin-block: 32px; }
186
217
  }
187
218
  @media (prefers-reduced-motion: no-preference) {
188
219
  a, button { transition: color .15s ease, background-color .15s ease; }
189
- /* One packet exchange, three times; settle before five seconds. No endless loop. */
190
- .ascii-frame { animation-duration: 1.6s; animation-timing-function: steps(1, end); animation-iteration-count: 3; }
191
- .ascii-frame-0 { animation-name: packet-0; }
192
- .ascii-frame-1 { animation-name: packet-1; }
193
- .ascii-frame-2 { animation-name: packet-2; }
194
- .ascii-frame-3 { animation-name: packet-3; }
220
+ .token-art[data-running] .token {
221
+ animation: token-assemble 12s cubic-bezier(.22, 1, .36, 1) infinite;
222
+ animation-delay: var(--phase); animation-play-state: paused;
223
+ }
224
+ .token-art[data-running=true] .token { animation-play-state: running; }
225
+ }
226
+ @keyframes token-assemble {
227
+ 0%, 12%, 100% { transform: translate(var(--dx), var(--dy)) rotate(var(--turn)); opacity: .38; }
228
+ 38%, 65% { transform: translate(0, 0) rotate(0deg); opacity: 1; }
229
+ 85% { transform: translate(var(--dx), var(--dy)) rotate(var(--turn)); opacity: .38; }
195
230
  }
196
- @keyframes packet-0 { 0%, 100% { opacity: 1; } 25%, 75% { opacity: 0; } }
197
- @keyframes packet-1 { 0%, 50%, 100% { opacity: 0; } 25% { opacity: 1; } }
198
- @keyframes packet-2 { 0%, 25%, 75%, 100% { opacity: 0; } 50% { opacity: 1; } }
199
- @keyframes packet-3 { 0%, 50%, 100% { opacity: 0; } 75% { opacity: 1; } }
200
231
  @media (forced-colors: active) {
201
232
  select, button, .account .primary { border: 1px solid ButtonText; }
202
233
  :focus-visible { outline-color: Highlight; }