swarph-cli 0.69.9__tar.gz → 0.70.0__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 (454) hide show
  1. {swarph_cli-0.69.9/src/swarph_cli.egg-info → swarph_cli-0.70.0}/PKG-INFO +1 -1
  2. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/pyproject.toml +1 -1
  3. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/__init__.py +1 -1
  4. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/mesh.py +172 -30
  5. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/monitor.py +16 -8
  6. {swarph_cli-0.69.9 → swarph_cli-0.70.0/src/swarph_cli.egg-info}/PKG-INFO +1 -1
  7. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli.egg-info/SOURCES.txt +1 -0
  8. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_644_monitor_supervision.py +86 -11
  9. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_739_wake_writer.py +48 -0
  10. swarph_cli-0.70.0/tests/test_961_cursor_grok_footer.py +331 -0
  11. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/LICENSE +0 -0
  12. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/README.md +0 -0
  13. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/setup.cfg +0 -0
  14. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/__main__.py +0 -0
  15. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/bench/__init__.py +0 -0
  16. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/bench/backends.py +0 -0
  17. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/bench/claude_cli.py +0 -0
  18. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/bench/pack.py +0 -0
  19. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/bench/prices.py +0 -0
  20. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/bench/providers.py +0 -0
  21. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/bench/quality.py +0 -0
  22. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/bench/refresh_prices.py +0 -0
  23. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/bench/rules.py +0 -0
  24. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/bench/runner.py +0 -0
  25. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/bench/validate.py +0 -0
  26. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/caller.py +0 -0
  27. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/capture/__init__.py +0 -0
  28. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/capture/harden.py +0 -0
  29. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/capture/lineage.py +0 -0
  30. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/capture/liveness.py +0 -0
  31. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/capture/manifest.py +0 -0
  32. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/capture/paths.py +0 -0
  33. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/capture/verify.py +0 -0
  34. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/cell.py +0 -0
  35. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/chain_token.py +0 -0
  36. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/channel.py +0 -0
  37. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/__init__.py +0 -0
  38. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/_content.py +0 -0
  39. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/_display.py +0 -0
  40. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/_gateway_client.py +0 -0
  41. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/add.py +0 -0
  42. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/bench.py +0 -0
  43. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/board.py +0 -0
  44. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/board_merge_check.py +0 -0
  45. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/brain.py +0 -0
  46. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/brain_ask.py +0 -0
  47. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/cell.py +0 -0
  48. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/cell_probe.py +0 -0
  49. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  50. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/channel.py +0 -0
  51. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/chat.py +0 -0
  52. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/codegraph.py +0 -0
  53. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/codegraph_hook.py +0 -0
  54. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/codex_hook_output.py +0 -0
  55. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/codex_waker.py +0 -0
  56. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/compress.py +0 -0
  57. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/daemon.py +0 -0
  58. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/dreaming.py +0 -0
  59. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/event.py +0 -0
  60. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/gateway.py +0 -0
  61. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/gh_route.py +0 -0
  62. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/group.py +0 -0
  63. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/guide.py +0 -0
  64. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/highlight.py +0 -0
  65. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/hook_interpreter.py +0 -0
  66. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/hook_output.py +0 -0
  67. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/hooks.py +0 -0
  68. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/import_session.py +0 -0
  69. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/init.py +0 -0
  70. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
  71. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/install_hook.py +0 -0
  72. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  73. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
  74. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
  75. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/install_wake_hook.py +0 -0
  76. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/lane.py +0 -0
  77. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/mcp_server.py +0 -0
  78. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/memory.py +0 -0
  79. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
  80. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/memory_sync.py +0 -0
  81. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/okf_links.py +0 -0
  82. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/onboard.py +0 -0
  83. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
  84. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
  85. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/protocol_handler.py +0 -0
  86. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/ratify.py +0 -0
  87. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/schedule.py +0 -0
  88. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/security.py +0 -0
  89. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/service.py +0 -0
  90. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/spawn.py +0 -0
  91. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/timeline.py +0 -0
  92. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/version.py +0 -0
  93. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/wake_hook_output.py +0 -0
  94. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/watchdog.py +0 -0
  95. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/compress/__init__.py +0 -0
  96. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/compress/levers.py +0 -0
  97. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/compress/marker.py +0 -0
  98. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/compress/verify.py +0 -0
  99. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/console_safe.py +0 -0
  100. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/delivery_queue.py +0 -0
  101. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dm_frame.py +0 -0
  102. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/__init__.py +0 -0
  103. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/candidates.py +0 -0
  104. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/clone.py +0 -0
  105. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/enrich.py +0 -0
  106. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/index_check.py +0 -0
  107. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/notify.py +0 -0
  108. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/organize.py +0 -0
  109. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/probes.py +0 -0
  110. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/report.py +0 -0
  111. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/run.py +0 -0
  112. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/slm.py +0 -0
  113. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/transcripts.py +0 -0
  114. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/verify.py +0 -0
  115. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/gateway/__init__.py +0 -0
  116. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/gateway/feature_registry.py +0 -0
  117. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/gateway/lanes_control.py +0 -0
  118. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/gateway/schema.sql +0 -0
  119. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/gateway/server.py +0 -0
  120. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/gateway/services_control.py +0 -0
  121. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/gateway_default.py +0 -0
  122. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/gh_identity.py +0 -0
  123. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/guide/GUIDE.md +0 -0
  124. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/identity.py +0 -0
  125. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/main.py +0 -0
  126. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/multiplexer.py +0 -0
  127. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/orphan_daemons.py +0 -0
  128. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/pane_probe.py +0 -0
  129. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/parsers/__init__.py +0 -0
  130. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/parsers/claude.py +0 -0
  131. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
  132. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
  133. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
  134. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
  135. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
  136. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
  137. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
  138. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
  139. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
  140. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
  141. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
  142. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
  143. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/peer_executor.py +0 -0
  144. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/peer_executor_windows.py +0 -0
  145. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/peer_reconciliation.py +0 -0
  146. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/peer_reply_outbox.py +0 -0
  147. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/peer_service_host.py +0 -0
  148. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/peer_service_payload.py +0 -0
  149. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/peer_service_reply.py +0 -0
  150. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/peer_staging.py +0 -0
  151. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/query_filters.py +0 -0
  152. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/scripts/__init__.py +0 -0
  153. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
  154. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  155. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
  156. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
  157. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
  158. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/service/__init__.py +0 -0
  159. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/service/app.py +0 -0
  160. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/service/providers.py +0 -0
  161. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/session_bridge.py +0 -0
  162. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
  163. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
  164. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/source_text.py +0 -0
  165. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/stall_alert.py +0 -0
  166. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/codex-waker.default +0 -0
  167. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
  168. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
  169. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
  170. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
  171. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
  172. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
  173. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
  174. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
  175. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
  176. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
  177. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  178. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  179. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  180. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/timeline_paths.py +0 -0
  181. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/tokens.py +0 -0
  182. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  183. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  184. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli.egg-info/requires.txt +0 -0
  185. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli.egg-info/top_level.txt +0 -0
  186. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_1006_session_board.py +0 -0
  187. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_124_register_merge.py +0 -0
  188. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_126_pending_from_complete.py +0 -0
  189. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_132_ensure_monitor_state_dir.py +0 -0
  190. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_138_memory_navigate.py +0 -0
  191. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_139_tool_probe.py +0 -0
  192. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_183_card_anchor.py +0 -0
  193. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_184_notify_honesty.py +0 -0
  194. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_201_win32_bash_refusal.py +0 -0
  195. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_243_onboard_per_peer_token.py +0 -0
  196. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_307_task4_ask_reply.py +0 -0
  197. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
  198. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_319_refusal_exit_codes.py +0 -0
  199. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_323_mistral_backend.py +0 -0
  200. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_332_report_organize_findings.py +0 -0
  201. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_356_query_filters.py +0 -0
  202. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_397_gh_identity_router.py +0 -0
  203. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_397_router_preflight.py +0 -0
  204. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_402_one_identity_resolver.py +0 -0
  205. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_467b_onboard_operator_credential.py +0 -0
  206. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_472_register_caller_binding.py +0 -0
  207. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_523_guide.py +0 -0
  208. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_524_conftest_hermeticity.py +0 -0
  209. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_532_accept_flag.py +0 -0
  210. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_535_version_capability.py +0 -0
  211. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_546_default_gateway.py +0 -0
  212. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_547_verbs_from_registry.py +0 -0
  213. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_548_gateway_remainder.py +0 -0
  214. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_552_codegraph_gateway.py +0 -0
  215. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_555_relay_row_shape.py +0 -0
  216. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_556_relay_freshness.py +0 -0
  217. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_559_edges_truncated.py +0 -0
  218. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_564_bootstrap_first_peer.py +0 -0
  219. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
  220. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_579_register_bootstrap_message.py +0 -0
  221. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_591_cli_step_graph.py +0 -0
  222. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_648_install_unit.py +0 -0
  223. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_654_reexec.py +0 -0
  224. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_656_owned_slm.py +0 -0
  225. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_684_dreaming_verb.py +0 -0
  226. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_689_pkg_version_order.py +0 -0
  227. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_715_grok_wake.py +0 -0
  228. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_716_timeline_one_path.py +0 -0
  229. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_723_grok_0251.py +0 -0
  230. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_724_ps1_ascii_or_bom.py +0 -0
  231. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_725_stdio_reconfigure.py +0 -0
  232. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_726_grok_limit_screen.py +0 -0
  233. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_729_dm_once.py +0 -0
  234. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_729_wake_watchdog.py +0 -0
  235. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_734_dreaming_enrich_degrade.py +0 -0
  236. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_735_dm_mcp_tools.py +0 -0
  237. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_740_edit_project.py +0 -0
  238. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_762_cards_edit_priority.py +0 -0
  239. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_764_enrich_collapse.py +0 -0
  240. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_801_confirm_flow_cli.py +0 -0
  241. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_802_stage_history.py +0 -0
  242. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_825_codegraph_hook_noise.py +0 -0
  243. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_825_install_posttooluse_only.py +0 -0
  244. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_825_remaining_cap_shred_bind.py +0 -0
  245. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_829_counterfactual_column.py +0 -0
  246. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_830_hook_binding_version.py +0 -0
  247. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_84_cli_labels.py +0 -0
  248. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_866_doctrine_guide.py +0 -0
  249. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_872_identity_refuses_not_warns.py +0 -0
  250. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_874_source_text.py +0 -0
  251. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
  252. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_937_dreaming_notify.py +0 -0
  253. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_938_organize_line_limit.py +0 -0
  254. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_941_bench_backends.py +0 -0
  255. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_941_harness_342.py +0 -0
  256. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_945_same_line_pairing.py +0 -0
  257. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_955_prompt_audit.py +0 -0
  258. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_957_auth_http.py +0 -0
  259. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_959_venv_interpreter.py +0 -0
  260. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_960_channel.py +0 -0
  261. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
  262. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_963_plugin_manifest.py +0 -0
  263. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_964_channel_cell.py +0 -0
  264. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_965_mesh_wait.py +0 -0
  265. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_975_channel_hop.py +0 -0
  266. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_add_security_gate.py +0 -0
  267. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_artifact_add.py +0 -0
  268. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_artifact_hash.py +0 -0
  269. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_artifact_lib.py +0 -0
  270. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_artifact_mcp.py +0 -0
  271. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_artifact_skill.py +0 -0
  272. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_artifact_tool.py +0 -0
  273. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_artifact_uri.py +0 -0
  274. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_bench_backends.py +0 -0
  275. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_bench_claude_cli.py +0 -0
  276. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_bench_command.py +0 -0
  277. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_bench_pack.py +0 -0
  278. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_bench_prices.py +0 -0
  279. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_bench_quality.py +0 -0
  280. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_bench_runner.py +0 -0
  281. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_bench_validate.py +0 -0
  282. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_board_card_thread.py +0 -0
  283. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_board_command.py +0 -0
  284. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_board_merge_check.py +0 -0
  285. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_board_obligation_close.py +0 -0
  286. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_brain_ask_command.py +0 -0
  287. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_brain_ask_env_diagnosis.py +0 -0
  288. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_brain_ask_identity_explicitness.py +0 -0
  289. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_brain_command.py +0 -0
  290. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_caller_meta_guard.py +0 -0
  291. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_capture_lineage.py +0 -0
  292. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_capture_liveness.py +0 -0
  293. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_capture_manifest.py +0 -0
  294. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_capture_paths.py +0 -0
  295. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_capture_security.py +0 -0
  296. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_cell_command_dispatch.py +0 -0
  297. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_cell_harden.py +0 -0
  298. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_cell_loader.py +0 -0
  299. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_cell_selfcheck.py +0 -0
  300. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_cell_verify.py +0 -0
  301. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_chain_token.py +0 -0
  302. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_channel_command.py +0 -0
  303. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_channel_poll_never_breaks_dms.py +0 -0
  304. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_channel_post.py +0 -0
  305. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_channel_wake_policy_filter.py +0 -0
  306. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_chat_command.py +0 -0
  307. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_claude_parser.py +0 -0
  308. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_claude_tmux_template.py +0 -0
  309. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_codegraph_caller_scope.py +0 -0
  310. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_codegraph_command.py +0 -0
  311. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_codegraph_hook.py +0 -0
  312. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_codegraph_mcp.py +0 -0
  313. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_codex_waker.py +0 -0
  314. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_compress_caller_convention.py +0 -0
  315. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_compress_command.py +0 -0
  316. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_compress_levers.py +0 -0
  317. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_compress_marker.py +0 -0
  318. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_compress_verify.py +0 -0
  319. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_console_encoding_e2e.py +0 -0
  320. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_content_composition.py +0 -0
  321. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_cursor_membrane.py +0 -0
  322. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_daemon_command.py +0 -0
  323. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_daemon_console_encoding.py +0 -0
  324. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_daemon_delivery.py +0 -0
  325. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_delivery_queue.py +0 -0
  326. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_display_sanitize.py +0 -0
  327. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_ensure_monitor_no_peer_default.py +0 -0
  328. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_event_emit.py +0 -0
  329. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_feature_to_uri.py +0 -0
  330. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_gateway_brain.py +0 -0
  331. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_gateway_client.py +0 -0
  332. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_gateway_command.py +0 -0
  333. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_gateway_message_idempotency.py +0 -0
  334. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_gh_route_doctor_mesh_name.py +0 -0
  335. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_group_command.py +0 -0
  336. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_guide_commands_parse.py +0 -0
  337. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_guide_selfcheck.py +0 -0
  338. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_heartbeat_check.py +0 -0
  339. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_highlight_command.py +0 -0
  340. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_hook_output.py +0 -0
  341. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_hooks_activity_marker.py +0 -0
  342. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_hooks_add.py +0 -0
  343. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_hooks_bundle.py +0 -0
  344. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_hooks_init.py +0 -0
  345. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_hooks_legacy_generation_removal.py +0 -0
  346. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_hooks_lifecycle.py +0 -0
  347. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_hooks_merge.py +0 -0
  348. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_hooks_verify.py +0 -0
  349. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_hooks_win32_bash_wrapper.py +0 -0
  350. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_hooks_win32_command_path.py +0 -0
  351. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_import_command.py +0 -0
  352. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_inbox_identity_provenance.py +0 -0
  353. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_init_command.py +0 -0
  354. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_init_provider_menu.py +0 -0
  355. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_install_codex_hooks.py +0 -0
  356. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_install_hook.py +0 -0
  357. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_install_multiplexer_command.py +0 -0
  358. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_install_postcompact_hook.py +0 -0
  359. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_install_wake_hook.py +0 -0
  360. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_lane_command.py +0 -0
  361. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_main.py +0 -0
  362. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_main_unknown_verb_guard.py +0 -0
  363. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_mcp_missing_hint.py +0 -0
  364. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_mcp_server.py +0 -0
  365. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_memory_command.py +0 -0
  366. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_memory_emit_snippet.py +0 -0
  367. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_memory_gateway.py +0 -0
  368. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_memory_sync.py +0 -0
  369. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_memory_sync_membrane.py +0 -0
  370. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_mesh_command.py +0 -0
  371. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_mesh_inbox_mark_read.py +0 -0
  372. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_mesh_inbox_sanitize.py +0 -0
  373. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_mesh_originate_thread_623.py +0 -0
  374. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_mesh_send_unknown_peer.py +0 -0
  375. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_mesh_sidecar.py +0 -0
  376. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  377. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  378. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_meta_edge_identity.py +0 -0
  379. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_monitor_channel_poll.py +0 -0
  380. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_monitor_cli.py +0 -0
  381. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_monitor_daemon_collision.py +0 -0
  382. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_monitor_self_name_verification.py +0 -0
  383. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_monitor_state_split.py +0 -0
  384. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_multiplexer.py +0 -0
  385. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_muse_membrane.py +0 -0
  386. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_muse_own_cli.py +0 -0
  387. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_obligation_close_is_surfaced.py +0 -0
  388. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_okf_links.py +0 -0
  389. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_onboard_command.py +0 -0
  390. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_opencode_membrane.py +0 -0
  391. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_opencode_plugin.py +0 -0
  392. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_orphan_daemons.py +0 -0
  393. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_packaged_artifacts_exist.py +0 -0
  394. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_packaged_skills.py +0 -0
  395. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_peer_executor.py +0 -0
  396. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_peer_executor_windows.py +0 -0
  397. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_peer_reply_drain_command.py +0 -0
  398. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_peer_reply_drain_supervisor.py +0 -0
  399. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_peer_reply_outbox.py +0 -0
  400. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_peer_reply_transport.py +0 -0
  401. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_peer_service_e2e.py +0 -0
  402. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_peer_service_host.py +0 -0
  403. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_peer_service_payload.py +0 -0
  404. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_peer_service_reply.py +0 -0
  405. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_postcompact_recall.py +0 -0
  406. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_process_liveness.py +0 -0
  407. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_protocol_handler.py +0 -0
  408. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_providers_isolation.py +0 -0
  409. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_ratify_command.py +0 -0
  410. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_readme_claims_no_gateway_default.py +0 -0
  411. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_repl_caller_convention.py +0 -0
  412. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_schedule_command.py +0 -0
  413. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_schedule_create_contract.py +0 -0
  414. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_security.py +0 -0
  415. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_service_app.py +0 -0
  416. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_service_command.py +0 -0
  417. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_service_providers.py +0 -0
  418. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_session_bridge_inject.py +0 -0
  419. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_session_bridge_probe.py +0 -0
  420. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_session_bridge_resolve.py +0 -0
  421. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_session_path_role_gate.py +0 -0
  422. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_shipped_units_match_the_cli.py +0 -0
  423. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_smoke_chat.py +0 -0
  424. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_smoke_one_shot.py +0 -0
  425. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_smoke_phase_5_5.py +0 -0
  426. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_spawn_base_hoist.py +0 -0
  427. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_spawn_command.py +0 -0
  428. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_spawn_identity_stamp.py +0 -0
  429. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_spawn_live_pin.py +0 -0
  430. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_spawn_mitosis_lineage.py +0 -0
  431. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_spawn_tmux_session.py +0 -0
  432. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_spawn_windows_relaunch.py +0 -0
  433. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_stall_alert.py +0 -0
  434. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_subprocess_decode_encoding.py +0 -0
  435. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_timeline_command.py +0 -0
  436. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_tmux_wake_runstate_defer.py +0 -0
  437. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_tmux_wake_session_reanchor.py +0 -0
  438. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_tmux_wake_stale.py +0 -0
  439. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_tmux_wake_submit_verify.py +0 -0
  440. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_token_file_one_parser.py +0 -0
  441. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_token_precedence_explicit_first.py +0 -0
  442. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_unified_token_resolver.py +0 -0
  443. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_version_command.py +0 -0
  444. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_version_consistency.py +0 -0
  445. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_vibe_membrane.py +0 -0
  446. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_watchdog.py +0 -0
  447. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_watchdog_dm_wake.py +0 -0
  448. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  449. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  450. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_watchdog_emit_health.py +0 -0
  451. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_watchdog_install_flags.py +0 -0
  452. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_watchdog_model_rung.py +0 -0
  453. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_watchdog_stale_peers.py +0 -0
  454. {swarph_cli-0.69.9 → swarph_cli-0.70.0}/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.69.9
3
+ Version: 0.70.0
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.69.9"
7
+ version = "0.70.0"
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.69.9"
19
+ __version__ = "0.70.0"
20
20
 
21
21
  __all__ = ["__version__"]
@@ -2136,6 +2136,33 @@ _COMPOSER_PLACEHOLDERS = ("Add a follow-up", "Ask Codex to do anything")
2136
2136
  #: running agent into an idle one forever.
2137
2137
  _CURSOR_RUN_HINT = "ctrl+c to stop"
2138
2138
 
2139
+ #: A live cursor turn draws a braille spinner and then ``Thinking``,
2140
+ #: ``Running``, or ``Working`` on the row directly above the Tip/composer
2141
+ #: block. A quote of that row anywhere else, including a code echo, is not
2142
+ #: a turn. The match is anchored: stripped text has to start with the glyph.
2143
+ _CURSOR_SPINNER_ROW = re.compile(
2144
+ r"^[\u2800-\u28FF]+\s*(?:Thinking|Running|Working)\b"
2145
+ )
2146
+
2147
+ #: How many non-empty rows at the bottom can hold the cursor composer.
2148
+ #: Measured on cursor-lin 2026-09-30: the composer is the 4th non-empty row
2149
+ #: from the bottom (composer, task count, model footer, tilde). A quote of
2150
+ #: that row higher in the pane is scrollback, not the composer. Blank rows
2151
+ #: between those chrome lines do not count: a raw tail of N lines drops the
2152
+ #: composer off the window when the screen interleaves blanks.
2153
+ _CURSOR_COMPOSER_TAIL = 6
2154
+
2155
+ #: The 1.18.33 permission dialog draws ``△ Permission required`` above a
2156
+ #: stack of option rows. On the measured screen that row is the 10th
2157
+ #: non-empty row from the bottom, outside the composer tail.
2158
+ _OPENCODE_PERMISSION_TAIL = 12
2159
+
2160
+
2161
+ def _nonempty_tail(lines: list[str], n: int) -> list[str]:
2162
+ """The last ``n`` rows that contain text. Blank rows are not rows."""
2163
+ filled = [ln for ln in lines if ln.strip()]
2164
+ return filled[-n:]
2165
+
2139
2166
 
2140
2167
  def _only_wake_text(content: str) -> bool:
2141
2168
  """True when the composer holds wake text and nothing else.
@@ -2172,19 +2199,21 @@ def _opencode_permission_row(line: str) -> bool:
2172
2199
  return s.startswith("┃") and "△" in s and "Permission required" in s
2173
2200
 
2174
2201
 
2202
+ def _is_opencode_composer_row(line: str) -> bool:
2203
+ """One row of the opencode box: the mode row, the ``╹▀`` border, or the
2204
+ permission dialog. A quote of ``┃ Build ·`` above the tail is not this."""
2205
+ s = line.strip()
2206
+ return s.startswith("╹▀") or _opencode_mode_row(line) or _opencode_permission_row(line)
2207
+
2208
+
2175
2209
  def _is_opencode_pane(lines: list[str]) -> bool:
2176
2210
  """The opencode composer box, measured on 1.18.33.
2177
2211
 
2178
- The input row is a ``┃`` line. The mode row under it names Build or
2179
- Plan and a ``·``. The bottom of the box is ``╹▀``. The permission
2180
- dialog keeps the ``┃`` row and drops the mode row and the border.
2181
- Cursor and claude do not draw any of these.
2212
+ Decided by ``_bottom_tui``: the mode row, the ``╹▀`` border, or the
2213
+ permission dialog has to be the bottom-most composer in the non-empty
2214
+ tail. A cursor pane that merely quotes ``┃ Build ·`` is not opencode.
2182
2215
  """
2183
- for ln in lines:
2184
- s = ln.strip()
2185
- if s.startswith("╹▀") or _opencode_mode_row(ln) or _opencode_permission_row(ln):
2186
- return True
2187
- return False
2216
+ return bool(lines) and _bottom_tui(lines) == "opencode"
2188
2217
 
2189
2218
 
2190
2219
  def _opencode_running(lines: list[str]) -> bool:
@@ -2281,29 +2310,115 @@ def _opencode_composer_state(lines: list[str]) -> Optional[str]:
2281
2310
  return "busy"
2282
2311
 
2283
2312
 
2284
- def _is_grok_pane(lines: list[str]) -> bool:
2285
- """Grok Build composer, measured on sacrificial 1.0.41 and 0.2.51 TUIs.
2313
+ def _is_grok_composer_row(lines: list[str], index: int) -> bool:
2314
+ """A grok composer starts at a ``│ ❯`` row. Continuation rows that also
2315
+ start with ``│`` may sit between it and the ``╰`` footer, which is how
2316
+ a wrapped draft is drawn. A cursor diff hunk (``▎``) never counts."""
2317
+ row = lines[index].strip()
2318
+ if row.startswith("▎") or not (row.startswith("│") and "❯" in row):
2319
+ return False
2320
+ j = index + 1
2321
+ while j < len(lines):
2322
+ nxt = lines[j].strip()
2323
+ if nxt.startswith("▎"):
2324
+ return False
2325
+ if nxt.startswith("╰"):
2326
+ return True
2327
+ if nxt.startswith("│"):
2328
+ j += 1
2329
+ continue
2330
+ return False
2331
+ return False
2286
2332
 
2287
- The footer names ``Grok 4.`` inside the box, or the splash prints
2288
- ``Grok Build``. History also draws a ``❯`` for the submitted prompt;
2289
- that row has no box bar, so it is not the composer. A usage-limit
2290
- modal drops both of those and is recognized by ``_grok_block_reason``.
2291
- """
2292
- return any("Grok 4." in ln or ln.strip().startswith("Grok Build") for ln in lines)
2293
2333
 
2334
+ def _is_cursor_composer_row(line: str) -> bool:
2335
+ """Any cursor composer row, including a human draft. The empty one
2336
+ reads ``→ Add a follow-up``. A ``▎`` hunk never counts."""
2337
+ row = line.strip()
2338
+ return row.startswith("→") and not row.startswith("▎")
2294
2339
 
2295
- def _grok_input(lines: list[str]) -> Optional[str]:
2296
- """Text in the live ``│ ❯`` box, '' when empty, None if no box."""
2297
- row = None
2298
- for ln in lines:
2299
- if "❯" in ln and "│" in ln:
2300
- row = ln
2301
- if row is None:
2340
+
2341
+ def _bottom_tui(lines: list[str]) -> "str | None":
2342
+ """Who owns the bottom-most composer row in the non-empty tail.
2343
+
2344
+ ``"cursor"``, ``"grok"``, or ``"opencode"``. A match above that tail
2345
+ is scrollback. The lower composer wins, so a quoted grok box or a
2346
+ quoted ``┃ Build ·`` row above the cursor composer does not take the pane.
2347
+ """
2348
+ tail = _nonempty_tail(lines, _CURSOR_COMPOSER_TAIL)
2349
+ for i in range(len(tail) - 1, -1, -1):
2350
+ if _is_cursor_composer_row(tail[i]):
2351
+ return "cursor"
2352
+ if _is_grok_composer_row(tail, i):
2353
+ return "grok"
2354
+ if _is_opencode_composer_row(tail[i]):
2355
+ return "opencode"
2356
+ # A permission dialog is taller than the composer tail. Look further
2357
+ # for that one row. A mode row or a cursor/grok composer in the same
2358
+ # stretch is a quote, not a reason to take the pane.
2359
+ wider = _nonempty_tail(lines, _OPENCODE_PERMISSION_TAIL)
2360
+ for i in range(len(wider) - 1, -1, -1):
2361
+ if _is_cursor_composer_row(wider[i]) or _is_grok_composer_row(wider, i):
2362
+ return None
2363
+ if not _is_opencode_composer_row(wider[i]):
2364
+ continue
2365
+ if _opencode_permission_row(wider[i]):
2366
+ return "opencode"
2302
2367
  return None
2303
- after = row.split("❯", 1)[1]
2368
+ return None
2369
+
2370
+
2371
+ def _is_cursor_composer(lines: list[str]) -> bool:
2372
+ """The cursor composer ROW, not a quote of its placeholder.
2373
+
2374
+ The row is the bottom-most composer inside the non-empty tail, the
2375
+ same row ``_composer_state`` reads. Its stripped text has to start
2376
+ with ``→ Add a follow-up``. A mention of that placeholder anywhere
2377
+ else in the pane is not the composer.
2378
+ """
2379
+ return bool(lines) and _bottom_tui(lines) == "cursor"
2380
+
2381
+
2382
+ def _is_grok_pane(lines: list[str]) -> bool:
2383
+ """The grok TUI, by the composer row at the bottom of the pane.
2384
+
2385
+ Cursor's footer can read ``Grok 4.7``. That string is not the test.
2386
+ A grok composer is a ``│ ❯`` row immediately followed by ``╰``, and
2387
+ only when that pair is the bottom-most composer in the non-empty tail.
2388
+ A ``│``/``❯`` pair or a ``╰`` / ``always-approve`` pair anywhere else,
2389
+ including a cursor diff hunk that starts with ``▎``, is not a grok pane.
2390
+ A history ``❯`` without the box bar is not the composer. A usage-limit
2391
+ modal drops the box and is recognized by ``_grok_block_reason``.
2392
+ """
2393
+ return bool(lines) and _bottom_tui(lines) == "grok"
2394
+
2395
+
2396
+ def _grok_box_text(lines: list[str], index: int) -> str:
2397
+ """The draft inside a grok box, including ``│`` continuation rows."""
2398
+ after = lines[index].split("❯", 1)[1]
2304
2399
  if "│" in after:
2305
2400
  after = after.split("│", 1)[0]
2306
- return after.strip()
2401
+ parts = [after.strip()]
2402
+ j = index + 1
2403
+ while j < len(lines) and lines[j].strip().startswith("│"):
2404
+ body = lines[j].strip()[1:]
2405
+ if "│" in body:
2406
+ body = body.split("│", 1)[0]
2407
+ parts.append(body.strip())
2408
+ j += 1
2409
+ return " ".join(p for p in parts if p)
2410
+
2411
+
2412
+ def _grok_input(lines: list[str]) -> Optional[str]:
2413
+ """Text in the live grok box, '' when empty, None if that box is not
2414
+ the bottom composer. Continuation rows count as part of the draft."""
2415
+ tail = _nonempty_tail(lines, _CURSOR_COMPOSER_TAIL)
2416
+ for i in range(len(tail) - 1, -1, -1):
2417
+ if _is_cursor_composer_row(tail[i]) or _is_opencode_composer_row(tail[i]):
2418
+ return None
2419
+ if _is_grok_composer_row(tail, i):
2420
+ return _grok_box_text(tail, i)
2421
+ return None
2307
2422
 
2308
2423
 
2309
2424
  # One grok screen, measured on the live 0.2.51 pane (52 rows). Lines above
@@ -2421,10 +2536,28 @@ def _agent_running(target: str) -> Optional[bool]:
2421
2536
  return _opencode_running(lines)
2422
2537
  if _is_grok_pane(lines):
2423
2538
  return _grok_running(lines)
2424
- composer = _composer_line(lines)
2425
- if composer is None:
2539
+ tail = _nonempty_tail(lines, _CURSOR_COMPOSER_TAIL)
2540
+ composer = _composer_line(tail)
2541
+ if composer is None or not composer.startswith("→"):
2426
2542
  return None
2427
- return _CURSOR_RUN_HINT in composer
2543
+ if _CURSOR_RUN_HINT in composer:
2544
+ return True
2545
+ # The spinner is the non-empty row directly above the Tip/composer
2546
+ # block: the Tip row when it is present, otherwise the composer.
2547
+ # No other row in the tail can make the pane read as running.
2548
+ idx = None
2549
+ for i in range(len(tail) - 1, -1, -1):
2550
+ if _is_cursor_composer_row(tail[i]):
2551
+ idx = i
2552
+ break
2553
+ if idx is None or idx == 0:
2554
+ return False
2555
+ anchor = idx - 1
2556
+ if tail[anchor].strip().startswith("Tip:"):
2557
+ if anchor == 0:
2558
+ return False
2559
+ anchor -= 1
2560
+ return bool(_CURSOR_SPINNER_ROW.match(tail[anchor].strip()))
2428
2561
 
2429
2562
 
2430
2563
  def _composer_state(target: str) -> Optional[str]:
@@ -2452,6 +2585,12 @@ def _composer_state(target: str) -> Optional[str]:
2452
2585
  if _is_grok_pane(lines):
2453
2586
  return _grok_composer_state(lines)
2454
2587
  composer = _composer_line(lines)
2588
+ if composer is not None and composer.startswith("→"):
2589
+ # A cursor marker above the bottom tail is a quoted row. The
2590
+ # composer itself is off-screen, so this pane is not readable.
2591
+ composer = _composer_line(_nonempty_tail(lines, _CURSOR_COMPOSER_TAIL))
2592
+ if composer is None or not composer.startswith("→"):
2593
+ return None
2455
2594
  if composer is None:
2456
2595
  return None
2457
2596
  content = composer[1:].strip()
@@ -2976,8 +3115,11 @@ def _monitor_loop(state: MonitorState) -> int:
2976
3115
  try:
2977
3116
  _monitor_iteration(state)
2978
3117
  except KeyboardInterrupt:
3118
+ # An interrupt during an iteration is not a clean return. Swallowing
3119
+ # it and returning 0 made _run_pinned delete monitor.pid, so the
3120
+ # watchdog then reported the cell unsupervised.
2979
3121
  state.shutdown_requested = True
2980
- break
3122
+ raise
2981
3123
  except Exception as exc:
2982
3124
  print(f"{state.log_prefix} iteration error: {type(exc).__name__}: {exc}",
2983
3125
  file=sys.stderr)
@@ -345,13 +345,17 @@ def _service_leaf(text: "str | None") -> "str | None":
345
345
  def _supervisor_for_pidfile(explicit: "str | None") -> "str | None":
346
346
  """What start writes into monitor.pid.
347
347
 
348
- An explicit claim wins. Otherwise read this process's own cgroup now,
349
- while the pid is still ours. A later status must not open /proc/<pid>
350
- for a dead monitor: that pid may have been reused.
348
+ An explicit claim wins over the cgroup. Otherwise record a unit only
349
+ when its name says it supervises a monitor (swarph-monitor*.service).
350
+ A foreign leaf such as claude-tmux@X.service is not a monitor supervisor.
351
+ The read is this process's own cgroup, taken now, while the pid is ours.
351
352
  """
352
353
  if explicit:
353
354
  return explicit
354
- return _service_leaf(_read_cgroup(os.getpid()))
355
+ leaf = _service_leaf(_read_cgroup(os.getpid()))
356
+ if leaf and leaf.startswith("swarph-monitor"):
357
+ return leaf
358
+ return None
355
359
 
356
360
 
357
361
  def _derive_systemd_unit(pid: "int | None") -> "str | None":
@@ -641,12 +645,16 @@ def _run_pinned(state, pidfile: Path, self_name: str, sinks: list, poll_s: int,
641
645
  supervisor: "str | None" = None) -> int:
642
646
  mesh.write_pidfile(pidfile, self_name=self_name, sinks=sinks, poll_s=poll_s,
643
647
  supervisor=supervisor)
648
+ clean = False
644
649
  try:
645
- return mesh._monitor_loop(state)
650
+ result = mesh._monitor_loop(state)
651
+ clean = True
652
+ return result
646
653
  finally:
647
- # Only clear the pidfile if it is still OURS — a racing `start` may have
648
- # legitimately reclaimed it, and deleting its record would strand it.
649
- if mesh.pidfile_status(pidfile)[0] == "live_ours":
654
+ # A clean return can drop the file. SIGINT and any other unwind must
655
+ # not: the recorded supervisor is what the watchdog names after the
656
+ # process is gone. `monitor stop` still deletes the file itself.
657
+ if clean and mesh.pidfile_status(pidfile)[0] == "live_ours":
650
658
  pidfile.unlink(missing_ok=True)
651
659
 
652
660
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.69.9
3
+ Version: 0.70.0
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
@@ -256,6 +256,7 @@ tests/test_957_auth_http.py
256
256
  tests/test_959_venv_interpreter.py
257
257
  tests/test_960_channel.py
258
258
  tests/test_960_ensure_monitor_heartbeat.py
259
+ tests/test_961_cursor_grok_footer.py
259
260
  tests/test_963_plugin_manifest.py
260
261
  tests/test_964_channel_cell.py
261
262
  tests/test_965_mesh_wait.py
@@ -76,23 +76,98 @@ def test_pidfile_records_supervisor_when_given(tmp_path):
76
76
  assert rec["supervisor"] == "task:Swarph cursor-win Monitor"
77
77
 
78
78
 
79
- def test_start_records_the_cgroup_unit_and_keeps_it_after_the_process(monkeypatch, tmp_path):
80
- """A scratch transient unit is written at start from this process's cgroup.
79
+ def _start_once(monkeypatch, tmp_path, cgroup, extra=()):
80
+ _env(monkeypatch)
81
+ monkeypatch.setattr(monitor, "_verify_self_is_registered",
82
+ lambda *a, **k: (True, "registered"))
83
+ monkeypatch.setattr(mesh, "_monitor_iteration", lambda state: None)
84
+ monkeypatch.setattr(monitor, "_read_cgroup", lambda pid: cgroup)
85
+ assert _run(["start", "--once", *extra], tmp_path) == 0
86
+ return json.loads((tmp_path / "monitor.pid").read_text(encoding="utf-8"))
81
87
 
82
- The pidfile is what a later watchdog reads. The key stays when the
83
- process is gone; this test does not open another process's listing.
88
+
89
+ def test_a_swarph_monitor_cgroup_is_recorded_and_a_foreign_one_is_not(monkeypatch, tmp_path):
90
+ """Only a swarph-monitor*.service leaf is a monitor supervisor.
91
+
92
+ claude-tmux@X.service and any other foreign unit are not recorded.
93
+ An explicit --supervisor still wins over the cgroup leaf; if it stops
94
+ winning, the recorded value is the leaf and this assertion fails.
84
95
  """
96
+ for unit in (
97
+ "swarph-monitor.service",
98
+ "swarph-monitor-gemini-researcher.service",
99
+ "swarph-monitor@fixture-cell.service",
100
+ ):
101
+ rec = _start_once(monkeypatch, tmp_path, f"0::/system.slice/{unit}\n")
102
+ assert rec["supervisor"] == unit
103
+ (tmp_path / "monitor.pid").unlink()
104
+ rec = _start_once(
105
+ monkeypatch, tmp_path, "0::/user.slice/claude-tmux@123.service\n")
106
+ assert "supervisor" not in rec
107
+ (tmp_path / "monitor.pid").unlink()
108
+ rec = _start_once(
109
+ monkeypatch, tmp_path, "0::/system.slice/swarph-monitor.service\n",
110
+ extra=("--supervisor", "task:Named"))
111
+ assert rec["supervisor"] == "task:Named"
112
+
113
+
114
+ def test_interrupt_inside_the_real_loop_keeps_the_supervisor(monkeypatch, tmp_path):
115
+ """Fails at 8b4d11f on an assertion.
116
+
117
+ KeyboardInterrupt raised inside the real iteration used to be swallowed,
118
+ the loop returned 0, and _run_pinned deleted monitor.pid. The loop itself
119
+ is not patched.
120
+ """
121
+ def raise_inside(_state):
122
+ raise KeyboardInterrupt
123
+
124
+ monkeypatch.setattr(mesh, "_monitor_iteration", raise_inside)
125
+ state = mesh.MonitorState(
126
+ self_name="fixture-cell", state_dir=tmp_path,
127
+ gateway="http://gateway.test", token="tok", sinks=[])
128
+ pidfile = tmp_path / "monitor.pid"
129
+ with pytest.raises(KeyboardInterrupt):
130
+ monitor._run_pinned(state, pidfile, "fixture-cell", [], 30,
131
+ supervisor="swarph-monitor.service")
132
+ assert pidfile.exists()
133
+ rec = json.loads(pidfile.read_text(encoding="utf-8"))
134
+ assert rec["supervisor"] == "swarph-monitor.service"
135
+
136
+ rec["pid"] = 4000000
137
+ pidfile.write_text(json.dumps(rec), encoding="utf-8")
85
138
  _env(monkeypatch)
139
+ monkeypatch.setattr(mesh, "_monitor_iteration", lambda _state: None)
86
140
  monkeypatch.setattr(monitor, "_verify_self_is_registered",
87
141
  lambda *a, **k: (True, "registered"))
88
- monkeypatch.setattr(mesh, "_monitor_iteration", lambda state: None)
89
- monkeypatch.setattr(
90
- monitor, "_read_cgroup",
91
- lambda pid: "0::/user.slice/scratch-761.service\n")
92
142
  assert _run(["start", "--once"], tmp_path) == 0
93
- rec = json.loads((tmp_path / "monitor.pid").read_text(encoding="utf-8"))
94
- assert rec["supervisor"] == "scratch-761.service"
95
- assert rec["pid"] == os.getpid()
143
+ reclaimed = json.loads(pidfile.read_text(encoding="utf-8"))
144
+ assert reclaimed["pid"] == os.getpid()
145
+
146
+
147
+ def test_sigint_and_an_exception_leave_the_recorded_supervisor(monkeypatch, tmp_path):
148
+ """Unwinding used to unlink monitor.pid, so the watchdog then said unsupervised."""
149
+ pidfile = tmp_path / "monitor.pid"
150
+
151
+ def raise_kbd(_state):
152
+ raise KeyboardInterrupt
153
+
154
+ monkeypatch.setattr(mesh, "_monitor_loop", raise_kbd)
155
+ with pytest.raises(KeyboardInterrupt):
156
+ monitor._run_pinned(object(), pidfile, "fixture-cell", [], 30,
157
+ supervisor="swarph-monitor.service")
158
+ assert json.loads(pidfile.read_text(encoding="utf-8"))["supervisor"] == (
159
+ "swarph-monitor.service")
160
+
161
+ def raise_exc(_state):
162
+ raise RuntimeError("boom")
163
+
164
+ monkeypatch.setattr(mesh, "_monitor_loop", raise_exc)
165
+ pidfile.unlink()
166
+ with pytest.raises(RuntimeError):
167
+ monitor._run_pinned(object(), pidfile, "fixture-cell", [], 30,
168
+ supervisor="swarph-monitor-gemini-researcher.service")
169
+ assert json.loads(pidfile.read_text(encoding="utf-8"))["supervisor"] == (
170
+ "swarph-monitor-gemini-researcher.service")
96
171
 
97
172
 
98
173
  def test_start_does_not_record_the_session_manager(monkeypatch, tmp_path):
@@ -429,3 +429,51 @@ def test_main_sends_one_writer_down_dm_and_does_not_start(tmp_path):
429
429
  assert "token-file" not in text
430
430
  assert "monitor start" not in text
431
431
  assert "gridiron" not in text
432
+
433
+
434
+ @pytest.mark.skipif(
435
+ sys.platform == "win32",
436
+ reason="Windows cannot execute the POSIX swarph stub as argv0",
437
+ )
438
+ def test_main_names_the_recorded_supervisor_not_the_ownership_fallback(tmp_path):
439
+ """Fails if main() passes None instead of recorded_supervisor.
440
+
441
+ Ownership would then name the template unit. The pidfile names the
442
+ non-template unit, and that is the string the send must carry.
443
+ """
444
+ from swarph_cli.commands.mesh import write_pidfile
445
+
446
+ root = tmp_path / "state"
447
+ _cell(root, "fixture-cell", fresh=True)
448
+ write_pidfile(
449
+ root / "fixture-cell" / "mesh-sidecar" / "monitor.pid",
450
+ self_name="fixture-cell", sinks=[], poll_s=30,
451
+ supervisor="swarph-monitor.service")
452
+ stub = tmp_path / "swarph"
453
+ log = tmp_path / "calls.log"
454
+ stub.write_text(textwrap.dedent("""\
455
+ #!/bin/sh
456
+ echo "$@" >> "$STUB_LOG"
457
+ case "$1 $2" in
458
+ "monitor status") exit 2 ;;
459
+ *) exit 0 ;;
460
+ esac
461
+ """), encoding="utf-8")
462
+ stub.chmod(0o755)
463
+ env = {
464
+ **os.environ,
465
+ "PYTHONPATH": os.path.abspath("src"),
466
+ "SWARPH_STATE_ROOT": str(root),
467
+ "WAKE_WATCHDOG_STATE": str(tmp_path / "wake.json"),
468
+ "SWARPH_BIN": str(stub),
469
+ "STUB_LOG": str(log),
470
+ }
471
+ env.pop("SWARPH_SELF", None)
472
+ proc = subprocess.run(
473
+ [sys.executable, "-m", "swarph_cli.scripts.wake_watchdog",
474
+ "--as", "lab-ovh", "--escalate", "drop-on-meta-edge"],
475
+ env=env, capture_output=True, text=True)
476
+ assert proc.returncode == 0
477
+ text = log.read_text(encoding="utf-8")
478
+ assert "supervised by swarph-monitor.service" in text
479
+ assert "swarph-monitor@fixture-cell.service" not in text