swarph-cli 0.72.7__tar.gz → 0.72.8__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 (479) hide show
  1. {swarph_cli-0.72.7/src/swarph_cli.egg-info → swarph_cli-0.72.8}/PKG-INFO +1 -1
  2. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/pyproject.toml +1 -1
  3. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/__init__.py +1 -1
  4. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/watchdog.py +113 -9
  5. {swarph_cli-0.72.7 → swarph_cli-0.72.8/src/swarph_cli.egg-info}/PKG-INFO +1 -1
  6. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli.egg-info/SOURCES.txt +1 -0
  7. swarph_cli-0.72.8/tests/test_1059_watchdog_unread_env.py +179 -0
  8. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_watchdog.py +2 -0
  9. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/LICENSE +0 -0
  10. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/README.md +0 -0
  11. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/setup.cfg +0 -0
  12. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/__main__.py +0 -0
  13. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/bench/__init__.py +0 -0
  14. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/bench/backends.py +0 -0
  15. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/bench/claude_cli.py +0 -0
  16. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/bench/codex_cli.py +0 -0
  17. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/bench/pack.py +0 -0
  18. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/bench/prices.py +0 -0
  19. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/bench/providers.py +0 -0
  20. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/bench/quality.py +0 -0
  21. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/bench/refresh_prices.py +0 -0
  22. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/bench/rules.py +0 -0
  23. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/bench/runner.py +0 -0
  24. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/bench/signal_score.py +0 -0
  25. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/bench/validate.py +0 -0
  26. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/caller.py +0 -0
  27. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/capture/__init__.py +0 -0
  28. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/capture/harden.py +0 -0
  29. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/capture/lineage.py +0 -0
  30. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/capture/liveness.py +0 -0
  31. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/capture/manifest.py +0 -0
  32. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/capture/paths.py +0 -0
  33. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/capture/verify.py +0 -0
  34. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/cell.py +0 -0
  35. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/chain_token.py +0 -0
  36. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/channel.py +0 -0
  37. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/__init__.py +0 -0
  38. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/_content.py +0 -0
  39. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/_display.py +0 -0
  40. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/_gateway_client.py +0 -0
  41. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/add.py +0 -0
  42. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/bench.py +0 -0
  43. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/board.py +0 -0
  44. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/board_merge_check.py +0 -0
  45. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/brain.py +0 -0
  46. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/brain_ask.py +0 -0
  47. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/cell.py +0 -0
  48. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/cell_probe.py +0 -0
  49. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  50. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/channel.py +0 -0
  51. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/chat.py +0 -0
  52. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/codegraph.py +0 -0
  53. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/codegraph_hook.py +0 -0
  54. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/codex_hook_output.py +0 -0
  55. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/codex_waker.py +0 -0
  56. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/compress.py +0 -0
  57. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/daemon.py +0 -0
  58. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/dreaming.py +0 -0
  59. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/event.py +0 -0
  60. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/gateway.py +0 -0
  61. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/gh_route.py +0 -0
  62. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/group.py +0 -0
  63. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/guide.py +0 -0
  64. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/highlight.py +0 -0
  65. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/hook_interpreter.py +0 -0
  66. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/hook_output.py +0 -0
  67. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/hooks.py +0 -0
  68. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/import_session.py +0 -0
  69. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/init.py +0 -0
  70. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
  71. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/install_hook.py +0 -0
  72. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  73. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
  74. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
  75. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/install_wake_hook.py +0 -0
  76. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/lane.py +0 -0
  77. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/mcp_server.py +0 -0
  78. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/memory.py +0 -0
  79. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
  80. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/memory_sync.py +0 -0
  81. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/mesh.py +0 -0
  82. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/monitor.py +0 -0
  83. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/okf_links.py +0 -0
  84. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/onboard.py +0 -0
  85. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
  86. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
  87. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/protocol_handler.py +0 -0
  88. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/ratify.py +0 -0
  89. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/rowclear_hook.py +0 -0
  90. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/schedule.py +0 -0
  91. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/security.py +0 -0
  92. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/service.py +0 -0
  93. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/spawn.py +0 -0
  94. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/timeline.py +0 -0
  95. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/version.py +0 -0
  96. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/commands/wake_hook_output.py +0 -0
  97. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/compress/__init__.py +0 -0
  98. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/compress/levers.py +0 -0
  99. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/compress/marker.py +0 -0
  100. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/compress/verify.py +0 -0
  101. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/console_safe.py +0 -0
  102. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/delivery_queue.py +0 -0
  103. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/dm_frame.py +0 -0
  104. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/dreaming/__init__.py +0 -0
  105. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/dreaming/candidates.py +0 -0
  106. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/dreaming/clone.py +0 -0
  107. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/dreaming/enrich.py +0 -0
  108. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/dreaming/index_check.py +0 -0
  109. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/dreaming/notify.py +0 -0
  110. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/dreaming/organize.py +0 -0
  111. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/dreaming/probes.py +0 -0
  112. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/dreaming/report.py +0 -0
  113. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/dreaming/run.py +0 -0
  114. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/dreaming/slm.py +0 -0
  115. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/dreaming/transcripts.py +0 -0
  116. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/dreaming/verify.py +0 -0
  117. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/gateway/__init__.py +0 -0
  118. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/gateway/feature_registry.py +0 -0
  119. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/gateway/lanes_control.py +0 -0
  120. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/gateway/schema.sql +0 -0
  121. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/gateway/server.py +0 -0
  122. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/gateway/services_control.py +0 -0
  123. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/gateway_default.py +0 -0
  124. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/gh_identity.py +0 -0
  125. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/guide/GUIDE.md +0 -0
  126. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/identity.py +0 -0
  127. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/main.py +0 -0
  128. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/multiplexer.py +0 -0
  129. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/operator/__init__.py +0 -0
  130. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/operator/__main__.py +0 -0
  131. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/operator/api.py +0 -0
  132. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/operator/cli.py +0 -0
  133. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/operator/config.py +0 -0
  134. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/operator/fmt.py +0 -0
  135. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/orphan_daemons.py +0 -0
  136. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/pane_probe.py +0 -0
  137. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/parsers/__init__.py +0 -0
  138. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/parsers/claude.py +0 -0
  139. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
  140. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
  141. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
  142. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
  143. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
  144. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
  145. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
  146. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
  147. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
  148. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
  149. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
  150. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
  151. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/peer_executor.py +0 -0
  152. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/peer_executor_windows.py +0 -0
  153. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/peer_reconciliation.py +0 -0
  154. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/peer_reply_outbox.py +0 -0
  155. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/peer_service_host.py +0 -0
  156. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/peer_service_payload.py +0 -0
  157. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/peer_service_reply.py +0 -0
  158. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/peer_staging.py +0 -0
  159. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/query_filters.py +0 -0
  160. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/rowclear.py +0 -0
  161. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/scripts/__init__.py +0 -0
  162. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
  163. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  164. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
  165. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
  166. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
  167. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/service/__init__.py +0 -0
  168. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/service/app.py +0 -0
  169. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/service/providers.py +0 -0
  170. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/session_bridge.py +0 -0
  171. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
  172. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
  173. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/source_text.py +0 -0
  174. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/stall_alert.py +0 -0
  175. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/systemd/codex-waker.default +0 -0
  176. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
  177. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
  178. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
  179. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
  180. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
  181. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
  182. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
  183. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
  184. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
  185. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
  186. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  187. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  188. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  189. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/timeline_paths.py +0 -0
  190. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli/tokens.py +0 -0
  191. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  192. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  193. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli.egg-info/requires.txt +0 -0
  194. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/src/swarph_cli.egg-info/top_level.txt +0 -0
  195. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_1006_board_publisher.py +0 -0
  196. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_1006_session_board.py +0 -0
  197. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_1010_strand_only.py +0 -0
  198. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_1013_spawn_channel.py +0 -0
  199. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_1017_muse_tui.py +0 -0
  200. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_1050_surrogate_wake.py +0 -0
  201. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_1052_opencode_deaf.py +0 -0
  202. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_124_register_merge.py +0 -0
  203. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_126_pending_from_complete.py +0 -0
  204. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_132_ensure_monitor_state_dir.py +0 -0
  205. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_138_memory_navigate.py +0 -0
  206. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_139_tool_probe.py +0 -0
  207. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_183_card_anchor.py +0 -0
  208. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_184_notify_honesty.py +0 -0
  209. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_201_win32_bash_refusal.py +0 -0
  210. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_243_onboard_per_peer_token.py +0 -0
  211. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_307_task4_ask_reply.py +0 -0
  212. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
  213. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_319_refusal_exit_codes.py +0 -0
  214. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_323_mistral_backend.py +0 -0
  215. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_332_report_organize_findings.py +0 -0
  216. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_356_query_filters.py +0 -0
  217. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_397_gh_identity_router.py +0 -0
  218. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_397_router_preflight.py +0 -0
  219. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_402_one_identity_resolver.py +0 -0
  220. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_432_rowclear.py +0 -0
  221. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_467b_onboard_operator_credential.py +0 -0
  222. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_472_register_caller_binding.py +0 -0
  223. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_484_codex_queue_sink.py +0 -0
  224. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_523_guide.py +0 -0
  225. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_524_conftest_hermeticity.py +0 -0
  226. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_532_accept_flag.py +0 -0
  227. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_535_version_capability.py +0 -0
  228. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_538_signal_bench.py +0 -0
  229. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_546_default_gateway.py +0 -0
  230. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_547_verbs_from_registry.py +0 -0
  231. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_548_gateway_remainder.py +0 -0
  232. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_552_codegraph_gateway.py +0 -0
  233. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_555_relay_row_shape.py +0 -0
  234. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_556_relay_freshness.py +0 -0
  235. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_559_edges_truncated.py +0 -0
  236. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_564_bootstrap_first_peer.py +0 -0
  237. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
  238. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_579_register_bootstrap_message.py +0 -0
  239. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_591_cli_step_graph.py +0 -0
  240. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_644_monitor_supervision.py +0 -0
  241. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_648_install_unit.py +0 -0
  242. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_654_reexec.py +0 -0
  243. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_656_owned_slm.py +0 -0
  244. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_684_dreaming_verb.py +0 -0
  245. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_689_pkg_version_order.py +0 -0
  246. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_715_grok_wake.py +0 -0
  247. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_716_timeline_one_path.py +0 -0
  248. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_723_grok_0251.py +0 -0
  249. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_724_ps1_ascii_or_bom.py +0 -0
  250. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_725_stdio_reconfigure.py +0 -0
  251. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_726_grok_limit_screen.py +0 -0
  252. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_729_arm_check_delivery_gap.py +0 -0
  253. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_729_dm_once.py +0 -0
  254. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_729_wake_watchdog.py +0 -0
  255. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_734_dreaming_enrich_degrade.py +0 -0
  256. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_735_dm_mcp_tools.py +0 -0
  257. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_739_wake_writer.py +0 -0
  258. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_740_edit_project.py +0 -0
  259. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_762_cards_edit_priority.py +0 -0
  260. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_764_enrich_collapse.py +0 -0
  261. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_801_confirm_flow_cli.py +0 -0
  262. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_802_stage_history.py +0 -0
  263. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_825_codegraph_hook_noise.py +0 -0
  264. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_825_install_posttooluse_only.py +0 -0
  265. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_825_remaining_cap_shred_bind.py +0 -0
  266. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_829_counterfactual_column.py +0 -0
  267. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_830_hook_binding_version.py +0 -0
  268. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_84_cli_labels.py +0 -0
  269. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_866_doctrine_guide.py +0 -0
  270. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_872_identity_refuses_not_warns.py +0 -0
  271. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_874_source_text.py +0 -0
  272. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
  273. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_937_dreaming_notify.py +0 -0
  274. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_938_organize_line_limit.py +0 -0
  275. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_941_bench_backends.py +0 -0
  276. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_941_harness_342.py +0 -0
  277. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_945_same_line_pairing.py +0 -0
  278. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_955_prompt_audit.py +0 -0
  279. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_957_auth_http.py +0 -0
  280. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_959_venv_interpreter.py +0 -0
  281. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_960_channel.py +0 -0
  282. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
  283. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_961_cursor_grok_footer.py +0 -0
  284. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_961_loud_stop.py +0 -0
  285. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_963_plugin_manifest.py +0 -0
  286. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_964_channel_cell.py +0 -0
  287. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_965_mesh_wait.py +0 -0
  288. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_975_channel_hop.py +0 -0
  289. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_add_security_gate.py +0 -0
  290. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_artifact_add.py +0 -0
  291. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_artifact_hash.py +0 -0
  292. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_artifact_lib.py +0 -0
  293. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_artifact_mcp.py +0 -0
  294. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_artifact_skill.py +0 -0
  295. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_artifact_tool.py +0 -0
  296. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_artifact_uri.py +0 -0
  297. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_bench_backends.py +0 -0
  298. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_bench_claude_cli.py +0 -0
  299. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_bench_codex_cli.py +0 -0
  300. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_bench_command.py +0 -0
  301. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_bench_pack.py +0 -0
  302. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_bench_prices.py +0 -0
  303. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_bench_quality.py +0 -0
  304. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_bench_runner.py +0 -0
  305. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_bench_validate.py +0 -0
  306. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_board_card_thread.py +0 -0
  307. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_board_command.py +0 -0
  308. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_board_merge_check.py +0 -0
  309. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_board_obligation_close.py +0 -0
  310. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_brain_ask_command.py +0 -0
  311. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_brain_ask_env_diagnosis.py +0 -0
  312. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_brain_ask_identity_explicitness.py +0 -0
  313. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_brain_command.py +0 -0
  314. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_caller_meta_guard.py +0 -0
  315. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_capture_lineage.py +0 -0
  316. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_capture_liveness.py +0 -0
  317. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_capture_manifest.py +0 -0
  318. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_capture_paths.py +0 -0
  319. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_capture_security.py +0 -0
  320. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_cell_command_dispatch.py +0 -0
  321. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_cell_harden.py +0 -0
  322. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_cell_loader.py +0 -0
  323. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_cell_selfcheck.py +0 -0
  324. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_cell_verify.py +0 -0
  325. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_chain_token.py +0 -0
  326. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_channel_command.py +0 -0
  327. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_channel_poll_never_breaks_dms.py +0 -0
  328. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_channel_post.py +0 -0
  329. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_channel_wake_policy_filter.py +0 -0
  330. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_chat_command.py +0 -0
  331. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_claude_parser.py +0 -0
  332. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_claude_tmux_template.py +0 -0
  333. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_codegraph_caller_scope.py +0 -0
  334. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_codegraph_command.py +0 -0
  335. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_codegraph_hook.py +0 -0
  336. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_codegraph_mcp.py +0 -0
  337. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_codex_waker.py +0 -0
  338. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_compress_caller_convention.py +0 -0
  339. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_compress_command.py +0 -0
  340. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_compress_levers.py +0 -0
  341. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_compress_marker.py +0 -0
  342. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_compress_verify.py +0 -0
  343. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_console_encoding_e2e.py +0 -0
  344. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_content_composition.py +0 -0
  345. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_cursor_membrane.py +0 -0
  346. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_daemon_command.py +0 -0
  347. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_daemon_console_encoding.py +0 -0
  348. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_daemon_delivery.py +0 -0
  349. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_delivery_queue.py +0 -0
  350. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_display_sanitize.py +0 -0
  351. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_ensure_monitor_no_peer_default.py +0 -0
  352. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_event_emit.py +0 -0
  353. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_feature_to_uri.py +0 -0
  354. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_gateway_brain.py +0 -0
  355. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_gateway_client.py +0 -0
  356. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_gateway_command.py +0 -0
  357. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_gateway_message_idempotency.py +0 -0
  358. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_gh_route_doctor_mesh_name.py +0 -0
  359. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_group_command.py +0 -0
  360. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_guide_commands_parse.py +0 -0
  361. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_guide_selfcheck.py +0 -0
  362. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_heartbeat_check.py +0 -0
  363. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_highlight_command.py +0 -0
  364. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_hook_output.py +0 -0
  365. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_hooks_activity_marker.py +0 -0
  366. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_hooks_add.py +0 -0
  367. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_hooks_bundle.py +0 -0
  368. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_hooks_init.py +0 -0
  369. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_hooks_legacy_generation_removal.py +0 -0
  370. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_hooks_lifecycle.py +0 -0
  371. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_hooks_merge.py +0 -0
  372. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_hooks_verify.py +0 -0
  373. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_hooks_win32_bash_wrapper.py +0 -0
  374. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_hooks_win32_command_path.py +0 -0
  375. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_import_command.py +0 -0
  376. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_inbox_identity_provenance.py +0 -0
  377. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_init_command.py +0 -0
  378. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_init_provider_menu.py +0 -0
  379. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_install_codex_hooks.py +0 -0
  380. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_install_hook.py +0 -0
  381. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_install_multiplexer_command.py +0 -0
  382. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_install_postcompact_hook.py +0 -0
  383. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_install_wake_hook.py +0 -0
  384. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_lane_command.py +0 -0
  385. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_main.py +0 -0
  386. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_main_unknown_verb_guard.py +0 -0
  387. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_mcp_missing_hint.py +0 -0
  388. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_mcp_server.py +0 -0
  389. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_memory_command.py +0 -0
  390. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_memory_emit_snippet.py +0 -0
  391. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_memory_gateway.py +0 -0
  392. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_memory_sync.py +0 -0
  393. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_memory_sync_membrane.py +0 -0
  394. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_mesh_command.py +0 -0
  395. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_mesh_inbox_mark_read.py +0 -0
  396. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_mesh_inbox_sanitize.py +0 -0
  397. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_mesh_originate_thread_623.py +0 -0
  398. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_mesh_send_unknown_peer.py +0 -0
  399. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_mesh_sidecar.py +0 -0
  400. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  401. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_mesh_sidecar_muse_sink.py +0 -0
  402. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  403. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_meta_edge_identity.py +0 -0
  404. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_monitor_channel_poll.py +0 -0
  405. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_monitor_cli.py +0 -0
  406. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_monitor_daemon_collision.py +0 -0
  407. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_monitor_self_name_verification.py +0 -0
  408. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_monitor_state_split.py +0 -0
  409. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_multiplexer.py +0 -0
  410. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_muse_membrane.py +0 -0
  411. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_muse_own_cli.py +0 -0
  412. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_obligation_close_is_surfaced.py +0 -0
  413. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_okf_links.py +0 -0
  414. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_onboard_command.py +0 -0
  415. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_opencode_membrane.py +0 -0
  416. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_opencode_plugin.py +0 -0
  417. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_operator_me.py +0 -0
  418. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_orphan_daemons.py +0 -0
  419. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_packaged_artifacts_exist.py +0 -0
  420. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_packaged_skills.py +0 -0
  421. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_peer_executor.py +0 -0
  422. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_peer_executor_windows.py +0 -0
  423. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_peer_reply_drain_command.py +0 -0
  424. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_peer_reply_drain_supervisor.py +0 -0
  425. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_peer_reply_outbox.py +0 -0
  426. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_peer_reply_transport.py +0 -0
  427. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_peer_service_e2e.py +0 -0
  428. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_peer_service_host.py +0 -0
  429. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_peer_service_payload.py +0 -0
  430. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_peer_service_reply.py +0 -0
  431. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_postcompact_recall.py +0 -0
  432. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_process_liveness.py +0 -0
  433. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_protocol_handler.py +0 -0
  434. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_providers_isolation.py +0 -0
  435. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_ratify_command.py +0 -0
  436. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_readme_claims_no_gateway_default.py +0 -0
  437. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_repl_caller_convention.py +0 -0
  438. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_schedule_command.py +0 -0
  439. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_schedule_create_contract.py +0 -0
  440. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_security.py +0 -0
  441. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_service_app.py +0 -0
  442. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_service_command.py +0 -0
  443. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_service_providers.py +0 -0
  444. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_session_bridge_inject.py +0 -0
  445. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_session_bridge_probe.py +0 -0
  446. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_session_bridge_resolve.py +0 -0
  447. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_session_path_role_gate.py +0 -0
  448. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_shipped_units_match_the_cli.py +0 -0
  449. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_smoke_chat.py +0 -0
  450. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_smoke_one_shot.py +0 -0
  451. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_smoke_phase_5_5.py +0 -0
  452. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_spawn_base_hoist.py +0 -0
  453. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_spawn_command.py +0 -0
  454. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_spawn_identity_stamp.py +0 -0
  455. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_spawn_live_pin.py +0 -0
  456. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_spawn_mitosis_lineage.py +0 -0
  457. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_spawn_tmux_session.py +0 -0
  458. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_spawn_windows_relaunch.py +0 -0
  459. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_stall_alert.py +0 -0
  460. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_subprocess_decode_encoding.py +0 -0
  461. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_timeline_command.py +0 -0
  462. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_tmux_wake_runstate_defer.py +0 -0
  463. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_tmux_wake_session_reanchor.py +0 -0
  464. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_tmux_wake_stale.py +0 -0
  465. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_tmux_wake_submit_verify.py +0 -0
  466. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_token_file_one_parser.py +0 -0
  467. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_token_precedence_explicit_first.py +0 -0
  468. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_unified_token_resolver.py +0 -0
  469. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_version_command.py +0 -0
  470. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_version_consistency.py +0 -0
  471. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_vibe_membrane.py +0 -0
  472. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_watchdog_dm_wake.py +0 -0
  473. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  474. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  475. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_watchdog_emit_health.py +0 -0
  476. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_watchdog_install_flags.py +0 -0
  477. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_watchdog_model_rung.py +0 -0
  478. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_watchdog_stale_peers.py +0 -0
  479. {swarph_cli-0.72.7 → swarph_cli-0.72.8}/tests/test_windows_waker_installer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.72.7
3
+ Version: 0.72.8
4
4
  Summary: The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`.
5
5
  Author: Pierre Samson, Claude Opus
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "swarph-cli"
7
- version = "0.72.7"
7
+ version = "0.72.8"
8
8
  description = "The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -16,6 +16,6 @@ The architecture splits CLI from substrate so:
16
16
 
17
17
  from __future__ import annotations
18
18
 
19
- __version__ = "0.72.7"
19
+ __version__ = "0.72.8"
20
20
 
21
21
  __all__ = ["__version__"]
@@ -468,6 +468,14 @@ def _gateway_configured(gateway: str) -> bool:
468
468
  """
469
469
  return bool((gateway or "").strip())
470
470
 
471
+ def _resolve_unread_token(token: Optional[str]) -> Optional[str]:
472
+ """Token precedence for the unread check: explicit arg (cron setups
473
+ pass env straight through), else the watchdog identity's token file
474
+ (card #1059 — template units have no env). The VALUE is never logged.
475
+ """
476
+ return token or _read_service_token()
477
+
478
+
471
479
  def _gateway_unread_count(gateway: str, peer: str, token: Optional[str]) -> Optional[int]:
472
480
  """Query gateway for unread DM count addressed to peer.
473
481
 
@@ -483,15 +491,15 @@ def _gateway_unread_count(gateway: str, peer: str, token: Optional[str]) -> Opti
483
491
  the code it was documenting — found 2026-08-12 when the contradiction
484
492
  caused a real reader to conclude a gateway/token outage makes A1 MORE
485
493
  eager, when it makes A1 permanently inert for that cell instead.
494
+
495
+ Prefers the count-only endpoint (card #1059: no bodies cross the wire);
496
+ a 404 (gateway predates the endpoint) falls back to the list query so a
497
+ mixed fleet never goes blind from the client side alone.
486
498
  """
487
499
  if not _gateway_configured(gateway):
488
500
  return None
489
- query = urllib.parse.urlencode({"to_node": peer, "unread_only": "true", "limit": 1})
490
- url = f"{gateway.rstrip('/')}/messages?{query}"
491
- req = urllib.request.Request(url)
492
- if token:
493
- req.add_header("Authorization", f"Bearer {token}")
494
- else:
501
+ token = _resolve_unread_token(token)
502
+ if not token:
495
503
  # A missing token and a network outage both surface as None to the
496
504
  # caller (correctly — the decision matrix treats them the same way),
497
505
  # but they are different FACTS an operator would want to know apart.
@@ -503,10 +511,50 @@ def _gateway_unread_count(gateway: str, peer: str, token: Optional[str]) -> Opti
503
511
  f"unread-count check may 401 if the gateway requires auth; "
504
512
  f"A1 stays inert until one is set",
505
513
  file=sys.stderr)
514
+ count = _gateway_unread_via_count(gateway, peer, token)
515
+ if count is not None:
516
+ return count
517
+ return _gateway_unread_via_list(gateway, peer, token)
518
+
519
+
520
+ def _gateway_request(gateway: str, path: str, query: dict,
521
+ token: Optional[str]):
522
+ """GET path+query with an optional Bearer token. Returns the decoded
523
+ JSON body, or (None, status) on transport failure — the status lets
524
+ callers tell a missing route (404: fall back) from a dead end."""
525
+ url = f"{gateway.rstrip('/')}{path}?{urllib.parse.urlencode(query)}"
526
+ req = urllib.request.Request(url)
527
+ if token:
528
+ req.add_header("Authorization", f"Bearer {token}")
506
529
  try:
507
530
  with urllib.request.urlopen(req, timeout=5) as resp:
508
- data = json.loads(resp.read().decode("utf-8"))
509
- except (urllib.error.URLError, urllib.error.HTTPError, OSError, json.JSONDecodeError):
531
+ return json.loads(resp.read().decode("utf-8")), resp.status
532
+ except urllib.error.HTTPError as exc:
533
+ return None, exc.code
534
+ except (urllib.error.URLError, OSError, json.JSONDecodeError):
535
+ return None, None
536
+
537
+
538
+ def _gateway_unread_via_count(gateway: str, peer: str,
539
+ token: Optional[str]) -> Optional[int]:
540
+ """Count-only read: {"to_node", "n"}. No bodies cross the wire, so a
541
+ least-privilege watchdog identity suffices (card #1059)."""
542
+ data, _status = _gateway_request(
543
+ gateway, "/messages/unread-count", {"to_node": peer}, token)
544
+ if isinstance(data, dict) and isinstance(data.get("n"), int):
545
+ return data["n"]
546
+ return None
547
+
548
+
549
+ def _gateway_unread_via_list(gateway: str, peer: str,
550
+ token: Optional[str]) -> Optional[int]:
551
+ """Pre-count-endpoint fallback: parse the list query. Kept so a mixed
552
+ fleet (new client, old gateway) degrades to body reads, never to
553
+ blindness — the 404 is the only trigger, never a 403/401."""
554
+ data, status = _gateway_request(
555
+ gateway, "/messages",
556
+ {"to_node": peer, "unread_only": "true", "limit": 1}, token)
557
+ if status == 404:
510
558
  return None
511
559
  # Gateway shape varies — handle both list and {"messages": [...]} forms
512
560
  if isinstance(data, list):
@@ -1874,6 +1922,37 @@ def _execstart_flags(args: argparse.Namespace) -> str:
1874
1922
  return (" " + " ".join(parts)) if parts else ""
1875
1923
 
1876
1924
 
1925
+ def _watchdog_service_token_path() -> Path:
1926
+ """Where the watchdog identity's token lives. Overridable for tests
1927
+ and unusual homes; never a unit file, never argv (card #1059)."""
1928
+ return Path(os.environ.get(
1929
+ "SWARPH_WATCHDOG_TOKEN_FILE",
1930
+ "~/.config/swarph/watchdog-service.token")).expanduser()
1931
+
1932
+
1933
+ def _read_service_token() -> Optional[str]:
1934
+ """The watchdog identity's token from its file, or None (absent /
1935
+ unreadable / blank). The VALUE never hits logs — callers log only
1936
+ whether one was found."""
1937
+ try:
1938
+ tok = _watchdog_service_token_path().read_text(
1939
+ encoding="utf-8").strip()
1940
+ except OSError:
1941
+ return None
1942
+ return tok or None
1943
+
1944
+
1945
+ def _write_service_token_file(path: Path, token: str) -> None:
1946
+ """Write the token file 0600 (parents created). Refuses multiline
1947
+ values — an env file line cannot hold them, and silently writing
1948
+ half a credential is worse than failing."""
1949
+ if "\n" in token:
1950
+ raise ValueError("token contains a newline")
1951
+ path.parent.mkdir(parents=True, exist_ok=True)
1952
+ path.write_text(token.strip() + "\n", encoding="utf-8")
1953
+ os.chmod(path, 0o600)
1954
+
1955
+
1877
1956
  def run_install_service(args: argparse.Namespace) -> int:
1878
1957
  """Install systemd timer + service for periodic watchdog --check.
1879
1958
 
@@ -1950,6 +2029,25 @@ def run_install_service(args: argparse.Namespace) -> int:
1950
2029
  1,
1951
2030
  )
1952
2031
 
2032
+ # Card #1059/ruling_1332: template units run without the installer's
2033
+ # env, so the unread check reads None and A1 stays inert (exit 7,
2034
+ # opencode + cursor-lin measured live). The watchdog identity's
2035
+ # token lives in a 0600 token FILE (path via SWARPH_WATCHDOG_TOKEN_FILE,
2036
+ # default ~/.config/swarph/watchdog-service.token) — never in a unit,
2037
+ # never in argv, never in the EnvironmentFile default. The installer
2038
+ # writes it from its own env; rotation is re-running the install.
2039
+ install_token = os.environ.get("MESH_GATEWAY_TOKEN")
2040
+ token_path = _watchdog_service_token_path()
2041
+ if install_token is not None and "\n" in install_token:
2042
+ print("ERROR: MESH_GATEWAY_TOKEN contains a newline — refusing "
2043
+ "to write the token file.", file=sys.stderr)
2044
+ return 4
2045
+ if not install_token:
2046
+ print("WARNING: MESH_GATEWAY_TOKEN not in installer env — "
2047
+ "template runs will exit 7 (noop_unread_unknown) until the "
2048
+ f"token file exists at {token_path}; re-run --install-service "
2049
+ "with it set.", file=sys.stderr)
2050
+
1953
2051
  targets = [
1954
2052
  (_SYSTEMD_UNIT_DIR / service_name, service_content),
1955
2053
  (_SYSTEMD_UNIT_DIR / timer_name, timer_content),
@@ -1970,6 +2068,9 @@ def run_install_service(args: argparse.Namespace) -> int:
1970
2068
  for path, content in targets:
1971
2069
  print(f"\n# would write {path}:", file=sys.stderr)
1972
2070
  print(content, file=sys.stderr)
2071
+ if install_token:
2072
+ print(f"\n# would write {token_path} (0600, value not shown):",
2073
+ file=sys.stderr)
1973
2074
  if legacy_note:
1974
2075
  print(f"\n{legacy_note}", file=sys.stderr)
1975
2076
  print(
@@ -1992,7 +2093,10 @@ def run_install_service(args: argparse.Namespace) -> int:
1992
2093
  for path, content in targets:
1993
2094
  path.write_text(content, encoding="utf-8")
1994
2095
  print(f"wrote {path}", file=sys.stderr)
1995
- except (OSError, PermissionError) as exc:
2096
+ if install_token:
2097
+ _write_service_token_file(token_path, install_token)
2098
+ print(f"wrote {token_path} (0600)", file=sys.stderr)
2099
+ except (OSError, PermissionError, ValueError) as exc:
1996
2100
  print(f"ERROR: failed to write unit files: {exc}", file=sys.stderr)
1997
2101
  return 5
1998
2102
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.72.7
3
+ Version: 0.72.8
4
4
  Summary: The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`.
5
5
  Author: Pierre Samson, Claude Opus
6
6
  License: MIT
@@ -195,6 +195,7 @@ tests/test_1013_spawn_channel.py
195
195
  tests/test_1017_muse_tui.py
196
196
  tests/test_1050_surrogate_wake.py
197
197
  tests/test_1052_opencode_deaf.py
198
+ tests/test_1059_watchdog_unread_env.py
198
199
  tests/test_124_register_merge.py
199
200
  tests/test_126_pending_from_complete.py
200
201
  tests/test_132_ensure_monitor_state_dir.py
@@ -0,0 +1,179 @@
1
+ """#1059 — template watchdog instances exit 7 (noop_unread_unknown):
2
+ systemd units run without MESH_GATEWAY_TOKEN, so unread reads None and
3
+ A1 stays permanently inert (opencode + cursor-lin, measured live).
4
+
5
+ Fix: --install-service writes the installer's MESH_GATEWAY_TOKEN into
6
+ the per-cell EnvironmentFile default (0600), never into the unit. The
7
+ check itself is unchanged — it already reads the token from env.
8
+
9
+ Seams: the dry-run preview (all three files print to stderr) for the
10
+ install shape, and run_check with a stubbed urlopen for the replay
11
+ (token flows into the Authorization header, unread is numeric, the
12
+ stale-cursor + live-session + idle-pane shape yields the A1 path).
13
+ """
14
+ from __future__ import annotations
15
+
16
+ import io
17
+ import json
18
+ import os
19
+ import urllib.request
20
+
21
+ from swarph_cli.commands import watchdog as wd
22
+ from swarph_cli.commands.watchdog import run_watchdog
23
+
24
+ TOKEN = "tok-1059-test"
25
+
26
+
27
+ def _dry_sections(capsys, monkeypatch, *, token=True):
28
+ if token:
29
+ monkeypatch.setenv("MESH_GATEWAY_TOKEN", TOKEN)
30
+ else:
31
+ monkeypatch.delenv("MESH_GATEWAY_TOKEN", raising=False)
32
+ rc = run_watchdog(argv=["--install-service", "--cell", "opencode",
33
+ "--dry-run"])
34
+ assert rc == 0
35
+ err = capsys.readouterr().err
36
+ sections = {}
37
+ current = None
38
+ for line in err.splitlines():
39
+ if line.startswith("# would write "):
40
+ # Windows stringifies the target path with backslashes;
41
+ # normalize so the section match below holds everywhere.
42
+ current = line.replace("\\", "/")
43
+ sections[current] = []
44
+ elif current is not None:
45
+ sections[current].append(line)
46
+ return sections, err
47
+
48
+
49
+ def test_install_writes_token_file_not_token_value(monkeypatch, capsys):
50
+ """With a token in env, the preview names the 0600 token file but
51
+ never shows the value — and no file section carries a token line."""
52
+ sections, err = _dry_sections(capsys, monkeypatch, token=True)
53
+ assert TOKEN not in err, "token value leaked into preview"
54
+ body = "\n".join(l for lines in sections.values() for l in lines)
55
+ assert "MESH_GATEWAY_TOKEN=" not in body
56
+ assert "0600" in err
57
+
58
+
59
+ def test_install_never_puts_token_in_unit(monkeypatch, capsys):
60
+ """No token in ExecStart or anywhere in the .service content —
61
+ units are world-readable."""
62
+ sections, _ = _dry_sections(capsys, monkeypatch, token=True)
63
+ for key, lines in sections.items():
64
+ if key.endswith(".service:"):
65
+ assert TOKEN not in "\n".join(lines), key
66
+
67
+
68
+ def test_token_file_round_trip_0600_and_newline_refused(monkeypatch, tmp_path):
69
+ """The helper writes the value with mode 600 and refuses multiline
70
+ values instead of writing half a credential."""
71
+ import stat
72
+ import sys
73
+ monkeypatch.setenv("SWARPH_WATCHDOG_TOKEN_FILE",
74
+ str(tmp_path / "sub" / "watchdog-service.token"))
75
+ path = wd._watchdog_service_token_path()
76
+ wd._write_service_token_file(path, TOKEN)
77
+ assert path.read_text(encoding="utf-8") == TOKEN + "\n"
78
+ if sys.platform != "win32": # POSIX file-mode bits not representable on Windows
79
+ assert stat.S_IMODE(path.stat().st_mode) == 0o600
80
+ assert wd._read_service_token() == TOKEN
81
+ try:
82
+ wd._write_service_token_file(path, "a\nb")
83
+ except ValueError:
84
+ pass
85
+ else:
86
+ raise AssertionError("multiline token accepted")
87
+ assert path.read_text(encoding="utf-8") == TOKEN + "\n"
88
+
89
+
90
+ def test_install_without_token_warns_unread_blind(monkeypatch, capsys):
91
+ """No token in env: loud warning (template runs would exit 7 on
92
+ unread), and no token line minted anywhere."""
93
+ sections, err = _dry_sections(capsys, monkeypatch, token=False)
94
+ body = "\n".join(l for lines in sections.values() for l in lines)
95
+ assert "MESH_GATEWAY_TOKEN=" not in body
96
+ assert "unread" in err.lower()
97
+
98
+
99
+ class _FakeResp:
100
+ def __init__(self, payload: bytes, status: int = 200):
101
+ self._payload = payload
102
+ self.status = status
103
+
104
+ def read(self):
105
+ return self._payload
106
+
107
+ def __enter__(self):
108
+ return self
109
+
110
+ def __exit__(self, *exc):
111
+ return False
112
+
113
+
114
+ def _rig_a1(monkeypatch, tmp_path, stub):
115
+ """Shared A1 replay rig: token in env, stale cursor, live session,
116
+ idle pane. Returns (run, seen) where run() executes the check."""
117
+ seen = {}
118
+ monkeypatch.setenv("MESH_GATEWAY_TOKEN", TOKEN)
119
+ monkeypatch.setattr(urllib.request, "urlopen", stub(seen))
120
+ cursor = tmp_path / "cursor.json"
121
+ cursor.write_text("{}")
122
+ os.utime(cursor, (7200, 7200))
123
+ monkeypatch.setattr(wd, "_tmux_session_exists", lambda name: True)
124
+ monkeypatch.setattr(wd, "_pane_activity_age_sec", lambda name: 99999.0)
125
+ monkeypatch.setattr(wd, "_tmux_send_keys", lambda *a, **k: True)
126
+ log = tmp_path / "watch.log"
127
+
128
+ def run(capsys):
129
+ rc = run_watchdog(argv=[
130
+ "--check", "--cell", "opencode", "--no-respawn", "--verbose",
131
+ f"--cursor={cursor}", "--tmux-session", "opencode",
132
+ "--peer", "opencode", "--gateway", "http://gw:1",
133
+ "--liveness-cmd", "true", f"--log={log}",
134
+ "--threshold", "60", "--pane-activity-threshold", "60"])
135
+ return rc, capsys.readouterr()
136
+ return run, seen
137
+
138
+
139
+ def test_replay_token_env_yields_a1(monkeypatch, tmp_path, capsys):
140
+ """Accept replay: token in env, stale cursor, live session, idle
141
+ pane, count endpoint says 3 -> the A1 path with a numeric
142
+ unread_count — the Authorization header carries the token, and no
143
+ list query is ever made (no bodies cross the wire)."""
144
+ calls = []
145
+
146
+ def stub(seen):
147
+ def fake_urlopen(req, timeout=5):
148
+ seen["auth"] = req.get_header("Authorization")
149
+ calls.append(req.full_url)
150
+ return _FakeResp(json.dumps(
151
+ {"to_node": "opencode", "n": 3}).encode())
152
+ return fake_urlopen
153
+
154
+ run, seen = _rig_a1(monkeypatch, tmp_path, stub)
155
+ rc, out = run(capsys)
156
+ assert seen.get("auth") == f"Bearer {TOKEN}", seen
157
+ assert all("/messages/unread-count?" in u for u in calls), calls
158
+ assert '"decision": "a1_send_keys"' in out.out + out.err, out
159
+ assert '"unread_count": 3' in out.out + out.err, out
160
+ assert rc == 1
161
+
162
+
163
+ def test_replay_count_404_falls_back_to_list(monkeypatch, tmp_path, capsys):
164
+ """Mixed fleet: gateway predates the count endpoint (404) -> the
165
+ client falls back to the list query rather than going blind."""
166
+ def stub(seen):
167
+ def fake_urlopen(req, timeout=5):
168
+ if req.full_url.startswith("http://gw:1/messages/unread-count?"):
169
+ raise urllib.error.HTTPError(
170
+ req.full_url, 404, "nope", {}, None)
171
+ return _FakeResp(json.dumps(
172
+ {"messages": [{}, {}, {}]}).encode())
173
+ return fake_urlopen
174
+
175
+ run, _ = _rig_a1(monkeypatch, tmp_path, stub)
176
+ rc, out = run(capsys)
177
+ assert '"decision": "a1_send_keys"' in out.out + out.err, out
178
+ assert '"unread_count": 3' in out.out + out.err, out
179
+ assert rc == 1
@@ -1108,6 +1108,7 @@ def test_gateway_unread_count_warns_loudly_when_token_missing(monkeypatch, capsy
1108
1108
  from swarph_cli.commands import watchdog
1109
1109
 
1110
1110
  class _FakeResp:
1111
+ status = 200 # the transport seam reports status (404 = fall back)
1111
1112
  def __enter__(self): return self
1112
1113
  def __exit__(self, *a): return False
1113
1114
  def read(self): return b'{"messages": []}'
@@ -1126,6 +1127,7 @@ def test_gateway_unread_count_silent_when_token_present(monkeypatch, capsys):
1126
1127
  from swarph_cli.commands import watchdog
1127
1128
 
1128
1129
  class _FakeResp:
1130
+ status = 200 # the transport seam reports status (404 = fall back)
1129
1131
  def __enter__(self): return self
1130
1132
  def __exit__(self, *a): return False
1131
1133
  def read(self): return b'{"messages": []}'
File without changes
File without changes
File without changes